@remit/mailbox-service 0.0.114 → 0.0.115

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.
@@ -3,52 +3,26 @@
3
3
  *
4
4
  * Uses ImapFlow for all IMAP connections.
5
5
  */
6
- import type { IImapConnection, ImapConnectionConfig, MailCredentials } from "./types.js";
6
+ import type { IImapConnection, ImapConnectionConfig, ImapTransport, MailCredentials } from "./types.js";
7
7
  /**
8
8
  * Create an IMAP connection
9
9
  */
10
10
  export declare const createConnection: (config: ImapConnectionConfig) => IImapConnection;
11
- /**
12
- * Create an IMAP connection from account data using password credentials.
13
- */
14
- export declare const createConnectionFromAccount: (account: {
15
- imapHost: string;
16
- imapPort: number;
17
- imapTls: boolean;
18
- username: string;
19
- }, password: string) => IImapConnection;
20
11
  /**
21
12
  * Create an IMAP connection from account data using a MailCredentials union.
22
- * Use this instead of createConnectionFromAccount for OAuth accounts.
23
13
  */
24
14
  export declare const createConnectionWithCredentials: (account: {
25
15
  imapHost: string;
26
16
  imapPort: number;
27
17
  imapTls: boolean;
28
18
  username: string;
29
- }, credentials: MailCredentials) => IImapConnection;
30
- /**
31
- * Managed connection factory that caches and reuses a single connection.
32
- *
33
- * The factory handles connection lifecycle - callers should not disconnect.
34
- * Call close() on the factory to disconnect when done.
35
- */
36
- export interface ManagedConnectionFactory {
37
- /** Get the cached connection (creates on first call) */
38
- getConnection(): IImapConnection;
39
- /** Disconnect and cleanup */
40
- close(): Promise<void>;
19
+ }, credentials: MailCredentials, transport: ImapTransport) => IImapConnection;
20
+ export interface ConnectionScope {
21
+ getConnection: () => Promise<IImapConnection>;
22
+ disconnect: () => Promise<void>;
41
23
  }
42
24
  /**
43
- * Create a managed connection factory that caches a single connection.
44
- *
45
- * @example
46
- * ```typescript
47
- * const factory = createManagedConnectionFactory(config);
48
- * const conn = factory.getConnection();
49
- * await conn.connect();
50
- * // ... reuse conn via factory.getConnection() ...
51
- * await factory.close(); // Disconnect when done
52
- * ```
25
+ * A lazily-connected, cached connection shared across the operations of one
26
+ * unit of work. Call disconnect() when done.
53
27
  */
54
- export declare const createManagedConnectionFactory: (config: ImapConnectionConfig) => ManagedConnectionFactory;
28
+ export declare const connectionScope: (config: ImapConnectionConfig) => ConnectionScope;
@@ -3,52 +3,46 @@
3
3
  *
4
4
  * Uses ImapFlow for all IMAP connections.
5
5
  */
6
- import { createImapFlowConnectionFromAccount, createImapFlowConnectionWithCredentials, ImapFlowConnection, } from "./imapflow-connection.js";
6
+ import { ImapFlowConnection } from "./imapflow-connection.js";
7
7
  /**
8
8
  * Create an IMAP connection
9
9
  */
10
10
  export const createConnection = (config) => {
11
11
  return new ImapFlowConnection(config);
12
12
  };
13
- /**
14
- * Create an IMAP connection from account data using password credentials.
15
- */
16
- export const createConnectionFromAccount = (account, password) => {
17
- return createImapFlowConnectionFromAccount(account, password);
18
- };
19
13
  /**
20
14
  * Create an IMAP connection from account data using a MailCredentials union.
21
- * Use this instead of createConnectionFromAccount for OAuth accounts.
22
15
  */
23
- export const createConnectionWithCredentials = (account, credentials) => {
24
- return createImapFlowConnectionWithCredentials(account, credentials);
25
- };
16
+ export const createConnectionWithCredentials = (account, credentials, transport) => new ImapFlowConnection({
17
+ host: account.imapHost,
18
+ port: account.imapPort,
19
+ tls: account.imapTls,
20
+ transport,
21
+ user: account.username,
22
+ credentials,
23
+ });
26
24
  /**
27
- * Create a managed connection factory that caches a single connection.
28
- *
29
- * @example
30
- * ```typescript
31
- * const factory = createManagedConnectionFactory(config);
32
- * const conn = factory.getConnection();
33
- * await conn.connect();
34
- * // ... reuse conn via factory.getConnection() ...
35
- * await factory.close(); // Disconnect when done
36
- * ```
25
+ * A lazily-connected, cached connection shared across the operations of one
26
+ * unit of work. Call disconnect() when done.
37
27
  */
38
- export const createManagedConnectionFactory = (config) => {
28
+ export const connectionScope = (config) => {
39
29
  let connection = null;
40
- return {
41
- getConnection: () => {
42
- if (!connection) {
43
- connection = new ImapFlowConnection(config);
44
- }
45
- return connection;
46
- },
47
- close: async () => {
48
- if (connection) {
49
- await connection.disconnect();
50
- connection = null;
51
- }
52
- },
30
+ let connectPromise = null;
31
+ const getConnection = async () => {
32
+ if (connectPromise) {
33
+ return connectPromise;
34
+ }
35
+ const conn = createConnection(config);
36
+ connection = conn;
37
+ connectPromise = conn.connect().then(() => conn);
38
+ return connectPromise;
39
+ };
40
+ const disconnect = async () => {
41
+ if (connection) {
42
+ await connection.disconnect();
43
+ connection = null;
44
+ connectPromise = null;
45
+ }
53
46
  };
47
+ return { getConnection, disconnect };
54
48
  };
@@ -62,6 +62,7 @@ export declare class ImapFlowConnection {
62
62
  private _state;
63
63
  private config;
64
64
  private currentMailbox;
65
+ private currentMailboxReadOnly;
65
66
  constructor(config: ImapConnectionConfig);
66
67
  /**
67
68
  * Current connection state
@@ -357,21 +358,3 @@ export declare class ImapFlowConnection {
357
358
  */
358
359
  private ensureConnected;
359
360
  }
360
- /**
361
- * Create an ImapFlow connection from account data using password credentials.
362
- */
363
- export declare const createImapFlowConnectionFromAccount: (account: {
364
- imapHost: string;
365
- imapPort: number;
366
- imapTls: boolean;
367
- username: string;
368
- }, password: string) => ImapFlowConnection;
369
- /**
370
- * Create an ImapFlow connection using a credentials union (password or OAuth access token).
371
- */
372
- export declare const createImapFlowConnectionWithCredentials: (account: {
373
- imapHost: string;
374
- imapPort: number;
375
- imapTls: boolean;
376
- username: string;
377
- }, credentials: import("./types.js").MailCredentials) => ImapFlowConnection;
@@ -149,6 +149,7 @@ export class ImapFlowConnection {
149
149
  _state = "disconnected";
150
150
  config;
151
151
  currentMailbox = null;
152
+ currentMailboxReadOnly = true;
152
153
  constructor(config) {
153
154
  this.config = config;
154
155
  }
@@ -223,6 +224,7 @@ export class ImapFlowConnection {
223
224
  host: this.config.host,
224
225
  port: this.config.port,
225
226
  secure: this.config.tls,
227
+ transport: this.config.transport,
226
228
  servername: this.config.tlsOptions?.servername,
227
229
  auth,
228
230
  tls: tlsOptions,
@@ -356,8 +358,12 @@ export class ImapFlowConnection {
356
358
  // Idempotency guard: re-selecting the currently-open mailbox is a no-op
357
359
  // SELECT on the wire. `mailbox` exposes the live, already-selected box, so
358
360
  // we can return its status without paying for another round-trip — this is
359
- // what lets a batch issue one SELECT and many fetches on the same box.
360
- if (this.currentMailbox === mailboxPath && this.client?.mailbox) {
361
+ // what lets a batch issue one SELECT and many fetches on the same box. A
362
+ // read-only open does not cover a read-write request: a pooled connection
363
+ // a sync left EXAMINEd must SELECT before a STORE, MOVE or EXPUNGE.
364
+ if (this.currentMailbox === mailboxPath &&
365
+ this.client?.mailbox &&
366
+ (readOnly || !this.currentMailboxReadOnly)) {
361
367
  return this.toBoxStatus(this.client.mailbox, readOnly);
362
368
  }
363
369
  const mailbox = await this.client?.mailboxOpen(mailboxPath, {
@@ -367,6 +373,7 @@ export class ImapFlowConnection {
367
373
  throw new Error(`Failed to open mailbox: ${mailboxPath}`);
368
374
  }
369
375
  this.currentMailbox = mailboxPath;
376
+ this.currentMailboxReadOnly = readOnly;
370
377
  return this.toBoxStatus(mailbox, readOnly);
371
378
  };
372
379
  /**
@@ -995,6 +1002,9 @@ export class ImapFlowConnection {
995
1002
  uidValidity: true,
996
1003
  highestModseq: true,
997
1004
  });
1005
+ if (status === false) {
1006
+ throw new Error(`IMAP STATUS returned no result for ${mailboxPath}`);
1007
+ }
998
1008
  const deletedCount = await this.countDeleted(mailboxPath);
999
1009
  return {
1000
1010
  messages: status.messages ?? 0,
@@ -1052,30 +1062,6 @@ export class ImapFlowConnection {
1052
1062
  }
1053
1063
  };
1054
1064
  }
1055
- /**
1056
- * Create an ImapFlow connection from account data using password credentials.
1057
- */
1058
- export const createImapFlowConnectionFromAccount = (account, password) => {
1059
- return new ImapFlowConnection({
1060
- host: account.imapHost,
1061
- port: account.imapPort,
1062
- tls: account.imapTls,
1063
- user: account.username,
1064
- credentials: { kind: "password", password },
1065
- });
1066
- };
1067
- /**
1068
- * Create an ImapFlow connection using a credentials union (password or OAuth access token).
1069
- */
1070
- export const createImapFlowConnectionWithCredentials = (account, credentials) => {
1071
- return new ImapFlowConnection({
1072
- host: account.imapHost,
1073
- port: account.imapPort,
1074
- tls: account.imapTls,
1075
- user: account.username,
1076
- credentials,
1077
- });
1078
- };
1079
1065
  /**
1080
1066
  * Build the imapflow auth object from mail credentials.
1081
1067
  *
package/dist/index.d.ts CHANGED
@@ -11,7 +11,7 @@ export { type BodySyncQueueConfig, type BodySyncQueueLogger, BodySyncQueueServic
11
11
  export { isMessageBodySyncBroken, type ResolveExhaustedBodySyncDeps, type ResolveExhaustedBodySyncInput, type ResolveExhaustedBodySyncResult, resolveExhaustedBodySyncFailures, } from "./body-sync-terminal.js";
12
12
  export { backfillClassifications, type ClassificationBackfillCheckpoint, type ClassificationBackfillCheckpointStore, type ClassificationBackfillDeps, type ClassificationBackfillLogger, type ClassificationBackfillOptions, type ClassificationBackfillProgress, type ClassificationBackfillResult, type ClassificationBackfillTotals, } from "./classification-backfill.js";
13
13
  export { classifyParsedMessage, extractPrimaryFromEmail, } from "./classify-message.js";
14
- export { createConnection, createConnectionFromAccount, createConnectionWithCredentials, createManagedConnectionFactory, type ManagedConnectionFactory, } from "./connection-factory.js";
14
+ export { type ConnectionScope, connectionScope, createConnection, createConnectionWithCredentials, } from "./connection-factory.js";
15
15
  export { type ImapTestConfig, type SmtpTestConfig, type TestResult, testImapConnection, testSmtpConnection, } from "./connection-test.js";
16
16
  export { type CreatedFilter, createFilter, type FilterPage, type FilterPatch, listFilters, type NewFilter, resolveFilterUpdate, updateFilter, } from "./filter-operations.js";
17
17
  export { extractListId, normalizeListId } from "./filters/list-id.js";
@@ -27,7 +27,7 @@ export { classifyPlacement, type FolderPlacement, type PlacementAction, type Pla
27
27
  export { classifyDisplayNameCorrespondence, extractOffDomainLinkDomains, extractSenderMismatch, type SenderMismatchContext, type SenderMismatchSignals, } from "./heuristics/senderMismatch.js";
28
28
  export { SOCIAL_DOMAINS } from "./heuristics/socialDomains.js";
29
29
  export { TRANSACTIONAL_DOMAINS } from "./heuristics/transactionalDomains.js";
30
- export { createImapFlowConnectionFromAccount, createImapFlowConnectionWithCredentials, ImapFlowConnection, } from "./imapflow-connection.js";
30
+ export { ImapFlowConnection } from "./imapflow-connection.js";
31
31
  export { backfillListIds, type ListIdBackfillCheckpoint, type ListIdBackfillCheckpointStore, type ListIdBackfillDeps, type ListIdBackfillLogger, type ListIdBackfillOptions, type ListIdBackfillProgress, type ListIdBackfillResult, type ListIdBackfillTotals, } from "./list-id-backfill.js";
32
32
  export { guardConnectionCursor, guardMailboxCursor, isCursorRebuildNeeded, type MailboxCursorCheck, type MailboxCursorGuardDeps, MailboxCursorPausedError, } from "./mailbox-cursor.js";
33
33
  export { type CursorRebuildMatch, type CursorRebuildMatchResult, type CursorRebuildRow, type CursorRebuildSnapshot, matchCursorRebuild, } from "./mailbox-cursor-rebuild.js";
@@ -59,5 +59,5 @@ export { extractSearchText, extractSnippetFromEmail, generateSnippet, normalizeS
59
59
  export { MoveNotSettledError, NoJunkMailboxError, type SpamReportConfig, type SpamReportLogger, type SpamReportParams, SpamReportService, } from "./spam-report.js";
60
60
  export { type ServerUids, type SyncWarningContext, type SyncWarningRound, SyncWarningService, } from "./sync-warning.js";
61
61
  export { buildThreadMessageMoveRevert, buildThreadMessageTrashUpdate, buildThreadMessageUndelete, type ThreadMessageRowState, } from "./thread-message-rows.js";
62
- export type { FlatMailboxInfo, IImapConnection, ImapBoxStatus, ImapConnectionConfig, ImapConnectionState, ImapNamespace, ImapNamespaces, MailboxSyncResult, MailConnectionErrorKind, MailCredentials, } from "./types.js";
62
+ export type { FlatMailboxInfo, IImapConnection, ImapBoxStatus, ImapConnectionConfig, ImapConnectionState, ImapNamespace, ImapNamespaces, ImapTransport, MailboxSyncResult, MailConnectionErrorKind, MailCredentials, } from "./types.js";
63
63
  export { MailConnectionError } from "./types.js";
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ export { isMessageBodySyncBroken, resolveExhaustedBodySyncFailures, } from "./bo
12
12
  export { backfillClassifications, } from "./classification-backfill.js";
13
13
  export { classifyParsedMessage, extractPrimaryFromEmail, } from "./classify-message.js";
14
14
  // Connection factory
15
- export { createConnection, createConnectionFromAccount, createConnectionWithCredentials, createManagedConnectionFactory, } from "./connection-factory.js";
15
+ export { connectionScope, createConnection, createConnectionWithCredentials, } from "./connection-factory.js";
16
16
  // Connection testing
17
17
  export { testImapConnection, testSmtpConnection, } from "./connection-test.js";
18
18
  export { createFilter, listFilters, resolveFilterUpdate, updateFilter, } from "./filter-operations.js";
@@ -30,7 +30,7 @@ export { classifyDisplayNameCorrespondence, extractOffDomainLinkDomains, extract
30
30
  export { SOCIAL_DOMAINS } from "./heuristics/socialDomains.js";
31
31
  export { TRANSACTIONAL_DOMAINS } from "./heuristics/transactionalDomains.js";
32
32
  // IMAP connection (ImapFlow-based)
33
- export { createImapFlowConnectionFromAccount, createImapFlowConnectionWithCredentials, ImapFlowConnection, } from "./imapflow-connection.js";
33
+ export { ImapFlowConnection } from "./imapflow-connection.js";
34
34
  export { backfillListIds, } from "./list-id-backfill.js";
35
35
  export { guardConnectionCursor, guardMailboxCursor, isCursorRebuildNeeded, MailboxCursorPausedError, } from "./mailbox-cursor.js";
36
36
  export { matchCursorRebuild, } from "./mailbox-cursor-rebuild.js";
@@ -1,9 +1,8 @@
1
1
  import type { IAddressRepository, IEnvelopeRepository, IMailboxRepository, IMailboxSpecialUseRepository, IMessageFlagPushRepository, IMessageFlagRepository, IMessageRepository, IThreadMessageRepository } from "@remit/data-ports";
2
2
  import type { JunkRoleMailboxes } from "@remit/data-ports/folder-role";
3
- import type { ManagedConnectionFactory } from "./connection-factory.js";
4
3
  import type { QuarantineService } from "./quarantine.js";
5
4
  import type { SyncWarningService } from "./sync-warning.js";
6
- import type { ImapAddress } from "./types.js";
5
+ import type { IImapConnection, ImapAddress } from "./types.js";
7
6
  /**
8
7
  * A From address is only usable when it looks like a real mailbox: both parts
9
8
  * present, neither is a known "could not parse" sentinel, and the host carries
@@ -63,7 +62,7 @@ export declare const parseHeaderDate: (raw: string | undefined, fallbackMs: numb
63
62
  usedFallback: boolean;
64
63
  };
65
64
  /**
66
- * @deprecated Use ManagedConnectionFactory instead
65
+ * @deprecated Pass the IImapConnection to MessageSyncService instead
67
66
  */
68
67
  export type ImapConnectionFactory = () => {
69
68
  connect(): Promise<void>;
@@ -101,7 +100,7 @@ export interface SyncMessagesResult {
101
100
  */
102
101
  export declare const selectUidsToSync: (allUids: number[], lastSyncUid: number, highWaterMarkUid: number) => number[];
103
102
  export declare class MessageSyncService {
104
- private connectionFactory;
103
+ private connection;
105
104
  private mailboxService;
106
105
  /**
107
106
  * The account’s folder-role map. Required, not optional: which folder
@@ -135,7 +134,7 @@ export declare class MessageSyncService {
135
134
  */
136
135
  private quarantineService?;
137
136
  private log;
138
- constructor(connectionFactory: ManagedConnectionFactory, mailboxService: IMailboxRepository,
137
+ constructor(connection: IImapConnection, mailboxService: IMailboxRepository,
139
138
  /**
140
139
  * The account’s folder-role map. Required, not optional: which folder
141
140
  * holds the Junk role decides whether a sighting withholds its sender
@@ -147,7 +147,7 @@ const messageIdForSighting = (accountId, mailboxId, msg) => deriveMessageIdFromS
147
147
  fromHost: msg.envelope.from?.[0]?.host,
148
148
  });
149
149
  export class MessageSyncService {
150
- connectionFactory;
150
+ connection;
151
151
  mailboxService;
152
152
  mailboxSpecialUseService;
153
153
  messageService;
@@ -159,7 +159,7 @@ export class MessageSyncService {
159
159
  messageFlagService;
160
160
  quarantineService;
161
161
  log;
162
- constructor(connectionFactory, mailboxService,
162
+ constructor(connection, mailboxService,
163
163
  /**
164
164
  * The account’s folder-role map. Required, not optional: which folder
165
165
  * holds the Junk role decides whether a sighting withholds its sender
@@ -186,7 +186,7 @@ export class MessageSyncService {
186
186
  * not re-fetched on every round.
187
187
  */
188
188
  quarantineService) {
189
- this.connectionFactory = connectionFactory;
189
+ this.connection = connection;
190
190
  this.mailboxService = mailboxService;
191
191
  this.mailboxSpecialUseService = mailboxSpecialUseService;
192
192
  this.messageService = messageService;
@@ -227,7 +227,7 @@ export class MessageSyncService {
227
227
  }
228
228
  const lastSyncUid = mailbox.lastSyncUid || 0;
229
229
  const highWaterMarkUid = mailbox.highWaterMarkUid || 0;
230
- const connection = this.connectionFactory.getConnection();
230
+ const connection = this.connection;
231
231
  const box = await connection.openBox(mailboxPath);
232
232
  const status = await connection.getMailboxStatus(mailboxPath);
233
233
  // Detection: the served UIDVALIDITY may have changed since it was last
@@ -453,7 +453,7 @@ export class MessageSyncService {
453
453
  await this.mailboxService.update(accountId, mailboxId, {
454
454
  cursorState: MailboxCursorState.rebuilding,
455
455
  });
456
- const connection = this.connectionFactory.getConnection();
456
+ const connection = this.connection;
457
457
  const box = await connection.openBox(mailboxPath);
458
458
  // Read before the pass, never after: a message arriving while the
459
459
  // rebuild runs is absent from the snapshot below, and a HIGHESTMODSEQ
@@ -652,7 +652,7 @@ export class MessageSyncService {
652
652
  const { mailbox, accountId, accountConfigId, cursor, box, status, batchSize, } = params;
653
653
  const mailboxId = mailbox.mailboxId;
654
654
  const mailboxPath = mailbox.fullPath;
655
- const connection = this.connectionFactory.getConnection();
655
+ const connection = this.connection;
656
656
  // Ask from the last COMPLETE mod-sequence, so a group left part-applied
657
657
  // by an earlier round is served again in full; its applied members are
658
658
  // then dropped by position, without a lookup.
@@ -963,7 +963,7 @@ export class MessageSyncService {
963
963
  * Assumes mailbox is already open from fetchUidsToSync.
964
964
  */
965
965
  async fetchMessageBatch(uids) {
966
- const connection = this.connectionFactory.getConnection();
966
+ const connection = this.connection;
967
967
  return await connection.fetchMessages(uids);
968
968
  }
969
969
  /**
@@ -1004,7 +1004,7 @@ export class MessageSyncService {
1004
1004
  */
1005
1005
  async probeDepartures(mailbox, accountId, applicable) {
1006
1006
  return confirmDepartures({
1007
- connection: this.connectionFactory.getConnection(),
1007
+ connection: this.connection,
1008
1008
  mailboxService: this.mailboxService,
1009
1009
  messageService: this.messageService,
1010
1010
  log: this.log,
@@ -0,0 +1,2 @@
1
+ import type { ImapTransport } from "imapflow";
2
+ export declare const nodeTransport: () => ImapTransport;
@@ -0,0 +1,8 @@
1
+ import { connect as netConnect } from "node:net";
2
+ import { connect as tlsConnect } from "node:tls";
3
+ export const nodeTransport = () => ({
4
+ connect: ({ host, port, servername, tls, secure }, onConnect) => secure
5
+ ? tlsConnect({ host, port, servername, ...tls }, onConnect)
6
+ : netConnect({ host, port }, onConnect),
7
+ upgrade: (socket, { host, port, servername, tls }, onSecure) => tlsConnect({ socket, host, port, servername, ...tls }, onSecure),
8
+ });
@@ -91,8 +91,10 @@ export declare class FakeImapConnection implements IImapConnection {
91
91
  readonly state: ImapConnectionState;
92
92
  readonly isConnected = true;
93
93
  private selected;
94
+ private selectedReadOnly;
94
95
  constructor(server: FakeImapServer);
95
96
  private open;
97
+ private writable;
96
98
  private uidList;
97
99
  connect: () => Promise<void>;
98
100
  disconnect: () => Promise<void>;
@@ -216,6 +216,7 @@ export class FakeImapConnection {
216
216
  state = "authenticated";
217
217
  isConnected = true;
218
218
  selected = null;
219
+ selectedReadOnly = true;
219
220
  constructor(server) {
220
221
  this.server = server;
221
222
  }
@@ -224,6 +225,13 @@ export class FakeImapConnection {
224
225
  throw new Error("No mailbox selected");
225
226
  return this.server.folder(this.selected);
226
227
  }
228
+ writable() {
229
+ const folder = this.open();
230
+ if (this.selectedReadOnly) {
231
+ throw imapError("READ-ONLY", `Mailbox is read-only: ${this.selected}`);
232
+ }
233
+ return folder;
234
+ }
227
235
  uidList = (uids) => uids.join(",");
228
236
  connect = async () => { };
229
237
  disconnect = async () => { };
@@ -248,10 +256,11 @@ export class FakeImapConnection {
248
256
  };
249
257
  });
250
258
  };
251
- openBox = async (path, readOnly = false) => {
259
+ openBox = async (path, readOnly = true) => {
252
260
  this.server.run(readOnly ? "EXAMINE" : "SELECT", path);
253
261
  const folder = this.server.folder(path);
254
262
  this.selected = path;
263
+ this.selectedReadOnly = readOnly;
255
264
  return {
256
265
  name: path,
257
266
  readOnly,
@@ -389,7 +398,7 @@ export class FakeImapConnection {
389
398
  }
390
399
  touch = (detail, uids, change) => {
391
400
  this.server.run("STORE", `${this.selected} ${this.uidList(uids)} ${detail}`);
392
- const folder = this.open();
401
+ const folder = this.writable();
393
402
  const modseq = this.server.bumpModseq();
394
403
  for (const uid of uids) {
395
404
  const stored = folder.messages.get(uid);
@@ -446,7 +455,7 @@ export class FakeImapConnection {
446
455
  if (this.server.ignoreNext.delete(verb)) {
447
456
  return { destination, uidValidity: target.uidValidity, uidMap };
448
457
  }
449
- const source = this.open();
458
+ const source = verb === "MOVE" ? this.writable() : this.open();
450
459
  for (const uid of uids) {
451
460
  const stored = source.messages.get(uid);
452
461
  if (!stored)
@@ -468,7 +477,7 @@ export class FakeImapConnection {
468
477
  copyMessages = async (uids, destination) => this.transfer("COPY", uids, destination);
469
478
  deleteMessages = async (uids) => {
470
479
  this.server.run("EXPUNGE", `${this.selected} ${this.uidList(uids)}`);
471
- const folder = this.open();
480
+ const folder = this.writable();
472
481
  let deleted = 0;
473
482
  for (const uid of uids) {
474
483
  if (folder.messages.delete(uid))
@@ -146,7 +146,7 @@ export const silentLog = {
146
146
  };
147
147
  export const syncFolder = async (store, account, mailbox, imap) => {
148
148
  const connection = imap.connection();
149
- const service = new MessageSyncService({ getConnection: () => connection, close: async () => { } }, store.mailbox, store.mailboxSpecialUse, store.message, store.envelope, store.address, store.threadMessage, new SyncWarningService(store.syncWarning), silentLog, store.flagPush, store.messageFlag);
149
+ const service = new MessageSyncService(connection, store.mailbox, store.mailboxSpecialUse, store.message, store.envelope, store.address, store.threadMessage, new SyncWarningService(store.syncWarning), silentLog, store.flagPush, store.messageFlag);
150
150
  const synced = [];
151
151
  for (;;) {
152
152
  const result = await service.syncMessages(mailbox.mailboxId, account.accountId, account.accountConfigId);
package/dist/types.d.ts CHANGED
@@ -2,6 +2,8 @@
2
2
  * Types for the remit-mailbox-service
3
3
  */
4
4
  import type { Readable } from "node:stream";
5
+ import type { ImapTransport } from "imapflow";
6
+ export type { ImapTransport };
5
7
  /**
6
8
  * Discriminated union of mail authentication credentials.
7
9
  *
@@ -40,6 +42,7 @@ export interface ImapConnectionConfig {
40
42
  user: string;
41
43
  credentials: MailCredentials;
42
44
  tls: boolean;
45
+ transport: ImapTransport;
43
46
  tlsOptions?: {
44
47
  rejectUnauthorized?: boolean;
45
48
  servername?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/mailbox-service",
3
- "version": "0.0.114",
3
+ "version": "0.0.115",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -21,6 +21,10 @@
21
21
  "types": "./dist/imapflow-connection.d.ts",
22
22
  "default": "./dist/imapflow-connection.js"
23
23
  },
24
+ "./node-transport": {
25
+ "types": "./dist/node-transport.d.ts",
26
+ "default": "./dist/node-transport.js"
27
+ },
24
28
  "./account-credentials": {
25
29
  "types": "./dist/account-credentials.d.ts",
26
30
  "default": "./dist/account-credentials.js"
@@ -55,7 +59,7 @@
55
59
  "better-sqlite3": "^12.11.1"
56
60
  },
57
61
  "dependencies": {
58
- "imapflow": "^1.3.3",
62
+ "imapflow": "^2.9.1",
59
63
  "mailparser": "3.12.2",
60
64
  "nodemailer": "^10.0.6",
61
65
  "p-map": "^7.0.4",