@bobfrankston/iflow 1.0.50 → 1.0.52

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.
@@ -1,15 +1,15 @@
1
1
  /**
2
- * Bridge transport for IMAP — uses mailxapi.tcp from the native shell.
3
- * For Android (MAUI) and potentially desktop WebView.
2
+ * Bridge transport for IMAP — uses msgapi.tcp from the native shell.
3
+ * Works in any msgapi host: msga (MAUI), msger (Rust/wry), msgview (Electron).
4
4
  *
5
5
  * The native shell provides:
6
- * mailxapi.tcp.connect(host, port, tls) → streamId
7
- * mailxapi.tcp.write(streamId, data)
8
- * mailxapi.tcp.onData(streamId, callback)
9
- * mailxapi.tcp.upgradeTLS(streamId, servername)
10
- * mailxapi.tcp.close(streamId)
6
+ * msgapi.tcp.connect(host, port, tls) → streamId
7
+ * msgapi.tcp.write(streamId, data)
8
+ * msgapi.tcp.onData(streamId, callback)
9
+ * msgapi.tcp.upgradeTLS(streamId, servername)
10
+ * msgapi.tcp.close(streamId)
11
11
  *
12
- * TLS is handled natively (C# SslStream) — JS never sees raw TLS handshake.
12
+ * TLS is handled natively — JS never sees raw TLS handshake.
13
13
  */
14
14
  import type { ImapTransport } from "./transport.js";
15
15
  export declare class BridgeTransport implements ImapTransport {
@@ -1,15 +1,15 @@
1
1
  /**
2
- * Bridge transport for IMAP — uses mailxapi.tcp from the native shell.
3
- * For Android (MAUI) and potentially desktop WebView.
2
+ * Bridge transport for IMAP — uses msgapi.tcp from the native shell.
3
+ * Works in any msgapi host: msga (MAUI), msger (Rust/wry), msgview (Electron).
4
4
  *
5
5
  * The native shell provides:
6
- * mailxapi.tcp.connect(host, port, tls) → streamId
7
- * mailxapi.tcp.write(streamId, data)
8
- * mailxapi.tcp.onData(streamId, callback)
9
- * mailxapi.tcp.upgradeTLS(streamId, servername)
10
- * mailxapi.tcp.close(streamId)
6
+ * msgapi.tcp.connect(host, port, tls) → streamId
7
+ * msgapi.tcp.write(streamId, data)
8
+ * msgapi.tcp.onData(streamId, callback)
9
+ * msgapi.tcp.upgradeTLS(streamId, servername)
10
+ * msgapi.tcp.close(streamId)
11
11
  *
12
- * TLS is handled natively (C# SslStream) — JS never sees raw TLS handshake.
12
+ * TLS is handled natively — JS never sees raw TLS handshake.
13
13
  */
14
14
  export class BridgeTransport {
15
15
  streamId = null;
@@ -19,18 +19,18 @@ export class BridgeTransport {
19
19
  _connected = false;
20
20
  get connected() { return this._connected; }
21
21
  async connect(host, port, tls, _servername) {
22
- this.streamId = await mailxapi.tcp.connect(host, port, tls);
22
+ this.streamId = await msgapi.tcp.connect(host, port, tls);
23
23
  this._connected = true;
24
- mailxapi.tcp.onData(this.streamId, (data) => {
24
+ msgapi.tcp.onData(this.streamId, (data) => {
25
25
  if (this.dataHandler)
26
26
  this.dataHandler(data);
27
27
  });
28
- mailxapi.tcp.onClose(this.streamId, (hadError) => {
28
+ msgapi.tcp.onClose(this.streamId, (hadError) => {
29
29
  this._connected = false;
30
30
  if (this.closeHandler)
31
31
  this.closeHandler(hadError);
32
32
  });
33
- mailxapi.tcp.onError(this.streamId, (message) => {
33
+ msgapi.tcp.onError(this.streamId, (message) => {
34
34
  if (this.errorHandler)
35
35
  this.errorHandler(new Error(message));
36
36
  });
@@ -38,13 +38,13 @@ export class BridgeTransport {
38
38
  async upgradeTLS(servername) {
39
39
  if (this.streamId == null)
40
40
  throw new Error("Not connected");
41
- await mailxapi.tcp.upgradeTLS(this.streamId, servername || "");
41
+ await msgapi.tcp.upgradeTLS(this.streamId, servername || "");
42
42
  }
43
43
  async write(data) {
44
44
  if (this.streamId == null)
45
45
  throw new Error("Not connected");
46
46
  const str = typeof data === "string" ? data : new TextDecoder().decode(data);
47
- await mailxapi.tcp.write(this.streamId, str);
47
+ await msgapi.tcp.write(this.streamId, str);
48
48
  }
49
49
  onData(handler) { this.dataHandler = handler; }
50
50
  onClose(handler) { this.closeHandler = handler; }
@@ -52,7 +52,7 @@ export class BridgeTransport {
52
52
  close() {
53
53
  this._connected = false;
54
54
  if (this.streamId != null) {
55
- mailxapi.tcp.close(this.streamId);
55
+ msgapi.tcp.close(this.streamId);
56
56
  this.streamId = null;
57
57
  }
58
58
  }
@@ -55,6 +55,8 @@ export declare class CompatImapClient {
55
55
  }): Promise<any[]>;
56
56
  /** Search messages in a mailbox */
57
57
  searchMessages(mailbox: string, criteria: any): Promise<number[]>;
58
+ /** Search by header value — returns matching UIDs */
59
+ searchByHeader(mailbox: string, headerName: string, headerValue: string): Promise<number[]>;
58
60
  /** Delete a message by UID */
59
61
  deleteMessageByUid(mailbox: string, uid: number): Promise<void>;
60
62
  /** Move a message between mailboxes (same server) */
@@ -162,6 +162,14 @@ export class CompatImapClient {
162
162
  await this.native.closeMailbox();
163
163
  return uids;
164
164
  }
165
+ /** Search by header value — returns matching UIDs */
166
+ async searchByHeader(mailbox, headerName, headerValue) {
167
+ await this.ensureConnected();
168
+ await this.native.select(mailbox);
169
+ const uids = await this.native.search(`HEADER ${headerName} "${headerValue}"`);
170
+ await this.native.closeMailbox();
171
+ return uids;
172
+ }
165
173
  /** Delete a message by UID */
166
174
  async deleteMessageByUid(mailbox, uid) {
167
175
  await this.ensureConnected();
@@ -58,6 +58,8 @@ export declare class NativeImapClient {
58
58
  private selectedMailbox;
59
59
  private mailboxInfo;
60
60
  private greetingResolve;
61
+ /** Callback for waitForContinuation — set when waiting for "+" response */
62
+ private continuationResolve;
61
63
  constructor(config: ImapClientConfig, transportFactory: TransportFactory);
62
64
  get connected(): boolean;
63
65
  connect(): Promise<void>;
@@ -21,6 +21,8 @@ export class NativeImapClient {
21
21
  selectedMailbox = null;
22
22
  mailboxInfo = { exists: 0, recent: 0, uidNext: 0, uidValidity: 0, flags: [], permanentFlags: [] };
23
23
  greetingResolve = null;
24
+ /** Callback for waitForContinuation — set when waiting for "+" response */
25
+ continuationResolve = null;
24
26
  constructor(config, transportFactory) {
25
27
  this.config = config;
26
28
  this.transportFactory = transportFactory;
@@ -445,30 +447,15 @@ export class NativeImapClient {
445
447
  }
446
448
  waitForContinuation(tag) {
447
449
  return new Promise((resolve) => {
448
- const timeout = setTimeout(() => resolve(null), 30000);
449
- const check = () => {
450
- const lineEnd = this.buffer.indexOf("\r\n");
451
- if (lineEnd >= 0) {
452
- const line = this.buffer.substring(0, lineEnd + 2);
453
- const resp = proto.parseResponseLine(line);
454
- if (resp.tag === "+") {
455
- this.buffer = this.buffer.substring(lineEnd + 2);
456
- clearTimeout(timeout);
457
- resolve(resp);
458
- return;
459
- }
460
- // Not continuation — process and keep waiting
461
- this.buffer = this.buffer.substring(lineEnd + 2);
462
- this.handleUntaggedResponse(resp);
463
- }
464
- if (this.transport.connected)
465
- setTimeout(check, 5);
466
- else {
467
- clearTimeout(timeout);
468
- resolve(null);
469
- }
450
+ const timeout = setTimeout(() => {
451
+ this.continuationResolve = null;
452
+ resolve(null);
453
+ }, 30000);
454
+ this.continuationResolve = (resp) => {
455
+ clearTimeout(timeout);
456
+ this.continuationResolve = null;
457
+ resolve(resp);
470
458
  };
471
- check();
472
459
  });
473
460
  }
474
461
  waitForTagged(tag) {
@@ -583,9 +570,12 @@ export class NativeImapClient {
583
570
  }
584
571
  // Continuation response
585
572
  if (resp.tag === "+") {
586
- // If not in IDLE and no one is waiting for continuation (APPEND),
587
- // this is likely an AUTHENTICATE challenge — send empty line to cancel
588
- if (!this.idleTag && this.pendingCommand) {
573
+ if (this.continuationResolve) {
574
+ // APPEND or other command waiting for continuation
575
+ this.continuationResolve(resp);
576
+ }
577
+ else if (!this.idleTag && this.pendingCommand) {
578
+ // Unexpected continuation (e.g. AUTHENTICATE challenge) — cancel
589
579
  this.transport.write("\r\n").catch(() => { });
590
580
  }
591
581
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/iflow",
3
- "version": "1.0.50",
3
+ "version": "1.0.52",
4
4
  "description": "IMAP client wrapper library",
5
5
  "main": "index.js",
6
6
  "types": "index.ts",