@twin.org/mailbox-connector-imap 0.9.3-next.1 → 0.9.3-next.3

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 @@
1
+ {"version":3,"file":"imapEmailConnectorConfigSchema.js","sourceRoot":"","sources":["../../../src/connectorSchema/imapEmailConnectorConfigSchema.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAG5D;;GAEG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,8BAA8B,GAA0B;IACpE;QACC,QAAQ,EAAE,qCAAqC;QAC/C,WAAW,EAAE,MAAM;QACnB,IAAI,EAAE,wBAAwB,CAAC,MAAM;KACrC;IACD;QACC,QAAQ,EAAE,qCAAqC;QAC/C,WAAW,EAAE,MAAM;QACnB,IAAI,EAAE,wBAAwB,CAAC,OAAO;QACtC,YAAY,EAAE,GAAG;KACjB;IACD;QACC,QAAQ,EAAE,uCAAuC;QACjD,WAAW,EAAE,QAAQ;QACrB,IAAI,EAAE,wBAAwB,CAAC,OAAO;QACtC,YAAY,EAAE,IAAI;KAClB;IACD;QACC,QAAQ,EAAE,yCAAyC;QACnD,WAAW,EAAE,UAAU;QACvB,IAAI,EAAE,wBAAwB,CAAC,MAAM;KACrC;IACD;QACC,QAAQ,EAAE,yCAAyC;QACnD,WAAW,EAAE,UAAU;QACvB,IAAI,EAAE,wBAAwB,CAAC,MAAM;QACrC,QAAQ,EAAE,IAAI;KACd;IACD;QACC,QAAQ,EAAE,wCAAwC;QAClD,WAAW,EAAE,SAAS;QACtB,IAAI,EAAE,wBAAwB,CAAC,KAAK;QACpC,QAAQ,EAAE,wBAAwB,CAAC,MAAM;QACzC,YAAY,EAAE,CAAC,OAAO,CAAC;KACvB;IACD;QACC,QAAQ,EAAE,uDAAuD;QACjE,WAAW,EAAE,wBAAwB;QACrC,IAAI,EAAE,wBAAwB,CAAC,OAAO;QACtC,YAAY,EAAE,CAAC;KACf;CACD,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { EntitySchemaPropertyType } from \"@twin.org/entity\";\nimport type { IMailboxConfigField } from \"@twin.org/mailbox-models\";\n\n/**\n * The configuration field schema for IMAP connectors.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const ImapEmailConnectorConfigSchema: IMailboxConfigField[] = [\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.host\",\n\t\tpropertyKey: \"host\",\n\t\ttype: EntitySchemaPropertyType.String\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.port\",\n\t\tpropertyKey: \"port\",\n\t\ttype: EntitySchemaPropertyType.Integer,\n\t\tdefaultValue: 993\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.secure\",\n\t\tpropertyKey: \"secure\",\n\t\ttype: EntitySchemaPropertyType.Boolean,\n\t\tdefaultValue: true\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.username\",\n\t\tpropertyKey: \"username\",\n\t\ttype: EntitySchemaPropertyType.String\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.password\",\n\t\tpropertyKey: \"password\",\n\t\ttype: EntitySchemaPropertyType.String,\n\t\tisSecure: true\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.folders\",\n\t\tpropertyKey: \"folders\",\n\t\ttype: EntitySchemaPropertyType.Array,\n\t\titemType: EntitySchemaPropertyType.String,\n\t\tdefaultValue: [\"INBOX\"]\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.pollingIntervalMinutes\",\n\t\tpropertyKey: \"pollingIntervalMinutes\",\n\t\ttype: EntitySchemaPropertyType.Integer,\n\t\tdefaultValue: 2\n\t}\n];\n"]}
@@ -0,0 +1,16 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { EntitySchemaPropertyType } from "@twin.org/entity";
4
+ /**
5
+ * The runtime state field schema for IMAP connectors.
6
+ * The connector authenticates with its configured credentials, so no state property is secure.
7
+ */
8
+ // eslint-disable-next-line @typescript-eslint/naming-convention
9
+ export const ImapEmailConnectorStateSchema = [
10
+ {
11
+ labelKey: "imapEmailConnectorStateSchema.folders",
12
+ propertyKey: "folders",
13
+ type: EntitySchemaPropertyType.Object
14
+ }
15
+ ];
16
+ //# sourceMappingURL=imapEmailConnectorStateSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imapEmailConnectorStateSchema.js","sourceRoot":"","sources":["../../../src/connectorSchema/imapEmailConnectorStateSchema.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAG5D;;;GAGG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,6BAA6B,GAA0B;IACnE;QACC,QAAQ,EAAE,uCAAuC;QACjD,WAAW,EAAE,SAAS;QACtB,IAAI,EAAE,wBAAwB,CAAC,MAAM;KACrC;CACD,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { EntitySchemaPropertyType } from \"@twin.org/entity\";\nimport type { IMailboxConfigField } from \"@twin.org/mailbox-models\";\n\n/**\n * The runtime state field schema for IMAP connectors.\n * The connector authenticates with its configured credentials, so no state property is secure.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const ImapEmailConnectorStateSchema: IMailboxConfigField[] = [\n\t{\n\t\tlabelKey: \"imapEmailConnectorStateSchema.folders\",\n\t\tpropertyKey: \"folders\",\n\t\ttype: EntitySchemaPropertyType.Object\n\t}\n];\n"]}
@@ -1,6 +1,7 @@
1
1
  import { BaseError, ComponentFactory, GeneralError, Guards, Is, Mutex } from "@twin.org/core";
2
2
  import { MailHelper } from "@twin.org/mailbox-models";
3
3
  import { ImapFlow } from "imapflow";
4
+ import { initSchema } from "./schema.js";
4
5
  /**
5
6
  * IMAP email protocol connector. Polls configured folders for new messages using imapflow.
6
7
  */
@@ -67,6 +68,9 @@ export class ImapEmailConnector {
67
68
  mutexTimeoutMs: options.config.mutexTimeoutMs
68
69
  };
69
70
  this._retrieving = false;
71
+ // A connector registers its own schemas so the owning component can recognise the
72
+ // secure properties of the configuration and state it persists.
73
+ initSchema();
70
74
  }
71
75
  /**
72
76
  * Get the class name.
@@ -81,9 +85,11 @@ export class ImapEmailConnector {
81
85
  * @param state The current connector state for the instance.
82
86
  * @param authCallback Callback invoked when authentication fails during a poll cycle.
83
87
  * @param retrievalCallback Callback invoked with retrieved messages after each poll cycle.
88
+ * @param options Options supplied by the owning component, unused by this protocol which
89
+ * authenticates with the stored credentials rather than an external flow.
84
90
  * @returns A promise that resolves when the polling loop has been started.
85
91
  */
86
- async retrieve(instanceId, state, authCallback, retrievalCallback) {
92
+ async retrieve(instanceId, state, authCallback, retrievalCallback, options) {
87
93
  await this.retrieveStop();
88
94
  this._retrieving = true;
89
95
  this._instanceId = instanceId;
@@ -1 +1 @@
1
- {"version":3,"file":"imapEmailConnector.js","sourceRoot":"","sources":["../../src/imapEmailConnector.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAO9F,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAMpC;;GAEG;AACH,MAAM,OAAO,kBAAkB;IAC9B;;OAEG;IACI,MAAM,CAAU,UAAU,wBAAwC;IAEzE;;OAEG;IACI,MAAM,CAAU,SAAS,GAAW,MAAM,CAAC;IAElD;;;OAGG;IACc,QAAQ,CAAqB;IAE9C;;;OAGG;IACc,cAAc,CAA0B;IAEzD;;;OAGG;IACc,OAAO,CAM2B;IAEnD;;;OAGG;IACK,WAAW,CAAU;IAE7B;;;OAGG;IACK,WAAW,CAAU;IAE7B;;;OAGG;IACK,MAAM,CAA4B;IAE1C;;;OAGG;IACH,YAAY,OAA8C;QACzD,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QACvE,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,UAAU,oBAA0B,OAAO,CAAC,MAAM,CAAC,CAAC;QACrF,MAAM,CAAC,WAAW,CACjB,kBAAkB,CAAC,UAAU,yBAE7B,OAAO,CAAC,MAAM,CAAC,IAAI,CACnB,CAAC;QACF,MAAM,CAAC,WAAW,CACjB,kBAAkB,CAAC,UAAU,6BAE7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CACvB,CAAC;QACF,MAAM,CAAC,WAAW,CACjB,kBAAkB,CAAC,UAAU,6BAE7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CACvB,CAAC;QAEF,IAAI,CAAC,QAAQ,GAAG,gBAAgB,CAAC,WAAW,CAAoB,OAAO,CAAC,oBAAoB,CAAC,CAAC;QAE9F,IAAI,CAAC,cAAc,GAAG,gBAAgB,CAAC,GAAG,CACzC,OAAO,CAAC,0BAA0B,IAAI,gBAAgB,CACtD,CAAC;QAEF,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC;QAC7C,IAAI,CAAC,OAAO,GAAG;YACd,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI;YACzB,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YACjD,MAAM;YACN,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ;YACjC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ;YACjC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC;YAC5C,sBAAsB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,sBAAsB,IAAI,CAAC,CAAC;YAC/E,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC,cAAc;SAC7C,CAAC;QACF,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;IAC1B,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,kBAAkB,CAAC,UAAU,CAAC;IACtC,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,QAAQ,CACpB,UAAkB,EAClB,KAA+B,EAC/B,YAAiD,EACjD,iBAA2D;QAE3D,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;QAE1B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QAEpB,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAChC,UAAU,EACV,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,CAAC,EAC1D,KAAK,IAAI,EAAE;YACV,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,iBAAiB,CAAC,CAAC;QAClD,CAAC,CACD,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,YAAY;QACxB,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;YACzB,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACvD,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;YAC7B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACzB,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,IAAI,CACjB,YAAiD,EACjD,iBAA2D;QAE3D,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAChE,OAAO;QACR,CAAC;QAED,oFAAoF;QACpF,mFAAmF;QACnF,oCAAoC;QACpC,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;QAE1B,MAAM,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE;YAC5B,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,cAAc;YACtC,cAAc,EAAE,IAAI;SACpB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,IAAI,QAAQ,CAAC;YAC3B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YACvB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YACvB,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;YAC3B,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;YAClE,MAAM,EAAE,KAAK;SACb,CAAC,CAAC;QAEH,IAAI,CAAC;YACJ,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;YAEvB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBAC3C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC;gBAC3E,IAAI,CAAC,SAAS,EAAE,CAAC;oBAChB,MAAM;gBACP,CAAC;YACF,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,SAAS,GAAG,IAAI,YAAY,CACjC,kBAAkB,CAAC,UAAU,EAC7B,YAAY,EACZ,SAAS,EACT,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAC1B,CAAC;YAEF,MAAM,QAAQ,GAAG,EAAE,CAAC,MAAM,CAAyD,KAAK,CAAC;gBACxF,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,SAAS,CAAC;YACb,MAAM,aAAa,GAAG,QAAQ,EAAE,oBAAoB,KAAK,IAAI,CAAC;YAC9D,IAAI,aAAa,EAAE,CAAC;gBACnB,MAAM,YAAY,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;YACnE,CAAC;iBAAM,CAAC;gBACP,MAAM,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;YAClE,CAAC;QACF,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC;gBACJ,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;gBACtB,MAAM,CAAC,KAAK,EAAE,CAAC;YAChB,CAAC;YAAC,MAAM,CAAC;gBACR,iEAAiE;gBACjE,qFAAqF;YACtF,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1B,CAAC;IACF,CAAC;IAED;;;;;;;;OAQG;IACK,KAAK,CAAC,UAAU,CACvB,MAAgB,EAChB,MAAc,EACd,iBAA2D;QAE3D,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAChE,OAAO,IAAI,CAAC;QACb,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,OAAO,KAAK,EAAE,CAAC;QAC3B,MAAM,WAAW,GAAmC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACtF,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,WAAW,CAAC;QAE1C,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACjD,IAAI,CAAC;YACJ,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACrB,OAAO,IAAI,CAAC;YACb,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;YAE1D,IAAI,WAAW,CAAC,WAAW,KAAK,WAAW,EAAE,CAAC;gBAC7C,WAAW,CAAC,WAAW,GAAG,WAAW,CAAC;gBACtC,WAAW,CAAC,OAAO,GAAG,CAAC,CAAC;YACzB,CAAC;YAED,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,IAAI,CAAC,CAAC;YACzC,MAAM,WAAW,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;YAC9E,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YAE7D,IAAI,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAC7B,IAAI,EACJ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EACxC,EAAE,GAAG,EAAE,IAAI,EAAE,CACb,CAAC;gBACF,4EAA4E;gBAC5E,8EAA8E;gBAC9E,0EAA0E;gBAC1E,wEAAwE;gBACxE,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;oBACnC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;wBACvB,OAAO,KAAK,CAAC;oBACd,CAAC;oBACD,MAAM,cAAc,GAAG,WAAW,CAAC,OAAO,IAAI,CAAC,CAAC;oBAChD,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,GAAG,GAAG,cAAc,EAAE,CAAC;wBACtD,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;4BAC7B,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,UAAU,CACxC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,EACrB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CACtC,CAAC;4BACF,IAAI,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gCACxB,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;oCACxB,KAAK,EAAE,MAAM;oCACb,MAAM,EAAE,kBAAkB,CAAC,UAAU;oCACrC,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;oCACd,OAAO,EAAE,eAAe;oCACxB,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;iCAChC,CAAC,CAAC;gCACH,WAAW,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC;gCAC9B,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAC7C,IAAI,CAAC,WAAW,EAChB,KAAK,EACL,IAAI,CAAC,MAAM,CACX,CAAC;gCACF,IAAI,CAAC,cAAc,EAAE,CAAC;oCACrB,WAAW,CAAC,OAAO,GAAG,cAAc,CAAC;oCACrC,OAAO,KAAK,CAAC;gCACd,CAAC;4BACF,CAAC;wBACF,CAAC;oBACF,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,OAAO,EAAE,CAAC;QAChB,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITaskSchedulerComponent } from \"@twin.org/background-task-models\";\nimport { BaseError, ComponentFactory, GeneralError, Guards, Is, Mutex } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport type {\n\tIEmailProtocolConnector,\n\tIEmailProtocolConnectorAuthCallback,\n\tIEmailProtocolConnectorRetrievalCallback\n} from \"@twin.org/mailbox-models\";\nimport { MailHelper } from \"@twin.org/mailbox-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { ImapFlow } from \"imapflow\";\nimport type { IImapEmailConnectorConfig } from \"./models/IImapEmailConnectorConfig.js\";\nimport type { IImapEmailConnectorConstructorOptions } from \"./models/IImapEmailConnectorConstructorOptions.js\";\nimport type { IImapEmailConnectorFolderState } from \"./models/IImapEmailConnectorFolderState.js\";\nimport type { IImapEmailConnectorState } from \"./models/IImapEmailConnectorState.js\";\n\n/**\n * IMAP email protocol connector. Polls configured folders for new messages using imapflow.\n */\nexport class ImapEmailConnector implements IEmailProtocolConnector<IImapEmailConnectorState> {\n\t/**\n\t * The class name.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<ImapEmailConnector>();\n\n\t/**\n\t * The protocol namespace identifier.\n\t */\n\tpublic static readonly NAMESPACE: string = \"imap\";\n\n\t/**\n\t * The logging component.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The task scheduler component used for polling.\n\t * @internal\n\t */\n\tprivate readonly _taskScheduler: ITaskSchedulerComponent;\n\n\t/**\n\t * The resolved configuration for this connector instance.\n\t * @internal\n\t */\n\tprivate readonly _config: Required<\n\t\tPick<\n\t\t\tIImapEmailConnectorConfig,\n\t\t\t\"host\" | \"port\" | \"secure\" | \"username\" | \"password\" | \"folders\" | \"pollingIntervalMinutes\"\n\t\t>\n\t> &\n\t\tPick<IImapEmailConnectorConfig, \"mutexTimeoutMs\">;\n\n\t/**\n\t * The identifier of the currently active instance being polled.\n\t * @internal\n\t */\n\tprivate _instanceId?: string;\n\n\t/**\n\t * Set when retrieve has been called and cleared when retrieveStop is called.\n\t * @internal\n\t */\n\tprivate _retrieving: boolean;\n\n\t/**\n\t * The last successfully persisted connector state, updated after each successful callback.\n\t * @internal\n\t */\n\tprivate _state?: IImapEmailConnectorState;\n\n\t/**\n\t * Create a new instance of ImapEmailConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: IImapEmailConnectorConstructorOptions) {\n\t\tGuards.object(ImapEmailConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.object(ImapEmailConnector.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.stringValue(\n\t\t\tImapEmailConnector.CLASS_NAME,\n\t\t\tnameof(options.config.host),\n\t\t\toptions.config.host\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tImapEmailConnector.CLASS_NAME,\n\t\t\tnameof(options.config.username),\n\t\t\toptions.config.username\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tImapEmailConnector.CLASS_NAME,\n\t\t\tnameof(options.config.password),\n\t\t\toptions.config.password\n\t\t);\n\n\t\tthis._logging = ComponentFactory.getIfExists<ILoggingComponent>(options.loggingComponentType);\n\n\t\tthis._taskScheduler = ComponentFactory.get<ITaskSchedulerComponent>(\n\t\t\toptions.taskSchedulerComponentType ?? \"task-scheduler\"\n\t\t);\n\n\t\tconst secure = options.config.secure ?? true;\n\t\tthis._config = {\n\t\t\thost: options.config.host,\n\t\t\tport: options.config.port ?? (secure ? 993 : 143),\n\t\t\tsecure,\n\t\t\tusername: options.config.username,\n\t\t\tpassword: options.config.password,\n\t\t\tfolders: options.config.folders ?? [\"INBOX\"],\n\t\t\tpollingIntervalMinutes: Math.max(1, options.config.pollingIntervalMinutes ?? 2),\n\t\t\tmutexTimeoutMs: options.config.mutexTimeoutMs\n\t\t};\n\t\tthis._retrieving = false;\n\t}\n\n\t/**\n\t * Get the class name.\n\t * @returns The class name.\n\t */\n\tpublic className(): string {\n\t\treturn ImapEmailConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the internal polling loop for the given instance.\n\t * @param instanceId The identifier of the mailbox instance being polled.\n\t * @param state The current connector state for the instance.\n\t * @param authCallback Callback invoked when authentication fails during a poll cycle.\n\t * @param retrievalCallback Callback invoked with retrieved messages after each poll cycle.\n\t * @returns A promise that resolves when the polling loop has been started.\n\t */\n\tpublic async retrieve(\n\t\tinstanceId: string,\n\t\tstate: IImapEmailConnectorState,\n\t\tauthCallback: IEmailProtocolConnectorAuthCallback,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback\n\t): Promise<void> {\n\t\tawait this.retrieveStop();\n\n\t\tthis._retrieving = true;\n\t\tthis._instanceId = instanceId;\n\t\tthis._state = state;\n\n\t\tawait this._taskScheduler.addTask(\n\t\t\tinstanceId,\n\t\t\t[{ intervalMinutes: this._config.pollingIntervalMinutes }],\n\t\t\tasync () => {\n\t\t\t\tawait this.poll(authCallback, retrievalCallback);\n\t\t\t}\n\t\t);\n\t}\n\n\t/**\n\t * Stop the polling loop for this connector.\n\t * @returns A promise that resolves when the polling loop has been stopped.\n\t */\n\tpublic async retrieveStop(): Promise<void> {\n\t\tif (Is.stringValue(this._instanceId)) {\n\t\t\tthis._retrieving = false;\n\t\t\tawait this._taskScheduler.removeTask(this._instanceId);\n\t\t\tthis._instanceId = undefined;\n\t\t\tthis._state = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Poll each configured folder for new messages.\n\t * @param authCallback Callback invoked when authentication fails.\n\t * @param retrievalCallback Callback invoked with retrieved messages.\n\t * @internal\n\t */\n\tprivate async poll(\n\t\tauthCallback: IEmailProtocolConnectorAuthCallback,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback\n\t): Promise<void> {\n\t\tif (Is.empty(this._state) || !Is.stringValue(this._instanceId)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Capture the instance id and state for this poll cycle, retrieveStop can clear the\n\t\t// instance fields while a poll is in progress and the mutex must still be released\n\t\t// under the key it was locked with.\n\t\tconst instanceId = this._instanceId;\n\t\tconst state = this._state;\n\n\t\tawait Mutex.lock(instanceId, {\n\t\t\ttimeoutMs: this._config.mutexTimeoutMs,\n\t\t\tthrowOnTimeout: true\n\t\t});\n\n\t\tconst client = new ImapFlow({\n\t\t\thost: this._config.host,\n\t\t\tport: this._config.port,\n\t\t\tsecure: this._config.secure,\n\t\t\tauth: { user: this._config.username, pass: this._config.password },\n\t\t\tlogger: false\n\t\t});\n\n\t\ttry {\n\t\t\tawait client.connect();\n\n\t\t\tfor (const folder of this._config.folders) {\n\t\t\t\tconst continued = await this.pollFolder(client, folder, retrievalCallback);\n\t\t\t\tif (!continued) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst pollError = new GeneralError(\n\t\t\t\tImapEmailConnector.CLASS_NAME,\n\t\t\t\t\"pollFailed\",\n\t\t\t\tundefined,\n\t\t\t\tBaseError.fromError(error)\n\t\t\t);\n\n\t\t\tconst rawError = Is.object<{ authenticationFailed?: unknown; response?: unknown }>(error)\n\t\t\t\t? error\n\t\t\t\t: undefined;\n\t\t\tconst isAuthFailure = rawError?.authenticationFailed === true;\n\t\t\tif (isAuthFailure) {\n\t\t\t\tawait authCallback(instanceId, state, true, undefined, pollError);\n\t\t\t} else {\n\t\t\t\tawait retrievalCallback(instanceId, undefined, state, pollError);\n\t\t\t}\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tawait client.logout();\n\t\t\t\tclient.close();\n\t\t\t} catch {\n\t\t\t\t// ignore, not much we can do if the connection is already closed\n\t\t\t\t// but don't want to prevent the mutex from being unlocked in the finally block below\n\t\t\t}\n\t\t\tMutex.unlock(instanceId);\n\t\t}\n\t}\n\n\t/**\n\t * Poll a single folder for messages with UIDs beyond the last seen UID.\n\t * Each parsed message is forwarded to the retrieval callback; returning false aborts processing.\n\t * @param client The connected ImapFlow client.\n\t * @param folder The folder path to poll.\n\t * @param retrievalCallback Callback invoked for each parsed email; return false to abort.\n\t * @returns True if all messages were processed, false if processing was aborted.\n\t * @internal\n\t */\n\tprivate async pollFolder(\n\t\tclient: ImapFlow,\n\t\tfolder: string,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback\n\t): Promise<boolean> {\n\t\tif (Is.empty(this._state) || !Is.stringValue(this._instanceId)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tthis._state.folders ??= {};\n\t\tconst folderState: IImapEmailConnectorFolderState = this._state.folders[folder] ?? {};\n\t\tthis._state.folders[folder] = folderState;\n\n\t\tconst lock = await client.getMailboxLock(folder);\n\t\ttry {\n\t\t\tif (!client.mailbox) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst uidValidity = client.mailbox.uidValidity.toString();\n\n\t\t\tif (folderState.uidValidity !== uidValidity) {\n\t\t\t\tfolderState.uidValidity = uidValidity;\n\t\t\t\tfolderState.lastUid = 0;\n\t\t\t}\n\n\t\t\tconst lastUid = folderState.lastUid ?? 0;\n\t\t\tconst searchQuery = lastUid > 0 ? { uid: `${lastUid + 1}:*` } : { all: true };\n\t\t\tconst uids = await client.search(searchQuery, { uid: true });\n\n\t\t\tif (uids !== false && Is.arrayValue(uids)) {\n\t\t\t\tconst msgStream = client.fetch(\n\t\t\t\t\tuids,\n\t\t\t\t\t{ uid: true, source: true, flags: true },\n\t\t\t\t\t{ uid: true }\n\t\t\t\t);\n\t\t\t\t// Any early exit from this loop (return, break or throw) must terminate the\n\t\t\t\t// fetch iterator, otherwise imapflow leaves the FETCH command pending and the\n\t\t\t\t// LOGOUT queued behind it in poll() never resolves, so the mutex is never\n\t\t\t\t// released. for await calls the iterator's return() on every exit path.\n\t\t\t\tfor await (const msg of msgStream) {\n\t\t\t\t\tif (!this._retrieving) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tconst currentLastUid = folderState.lastUid ?? 0;\n\t\t\t\t\tif (Is.notEmpty(msg.uid) && msg.uid > currentLastUid) {\n\t\t\t\t\t\tif (Is.notEmpty(msg.source)) {\n\t\t\t\t\t\t\tconst email = await MailHelper.parseEmail(\n\t\t\t\t\t\t\t\tmsg.source.toString(),\n\t\t\t\t\t\t\t\tmsg.flags ? [...msg.flags] : undefined\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (Is.notEmpty(email)) {\n\t\t\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\t\t\t\tsource: ImapEmailConnector.CLASS_NAME,\n\t\t\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t\t\t\tmessage: \"emailReceived\",\n\t\t\t\t\t\t\t\t\tdata: { subject: email.subject }\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tfolderState.lastUid = msg.uid;\n\t\t\t\t\t\t\t\tconst shouldContinue = await retrievalCallback(\n\t\t\t\t\t\t\t\t\tthis._instanceId,\n\t\t\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\t\t\tthis._state\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (!shouldContinue) {\n\t\t\t\t\t\t\t\t\tfolderState.lastUid = currentLastUid;\n\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tlock.release();\n\t\t}\n\t\treturn true;\n\t}\n}\n"]}
1
+ {"version":3,"file":"imapEmailConnector.js","sourceRoot":"","sources":["../../src/imapEmailConnector.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITaskSchedulerComponent } from \"@twin.org/background-task-models\";\nimport { BaseError, ComponentFactory, GeneralError, Guards, Is, Mutex } from \"@twin.org/core\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport type {\n\tIEmailProtocolConnector,\n\tIEmailProtocolConnectorAuthCallback,\n\tIEmailProtocolConnectorOptions,\n\tIEmailProtocolConnectorRetrievalCallback\n} from \"@twin.org/mailbox-models\";\nimport { MailHelper } from \"@twin.org/mailbox-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport { ImapFlow } from \"imapflow\";\nimport type { IImapEmailConnectorConfig } from \"./models/IImapEmailConnectorConfig.js\";\nimport type { IImapEmailConnectorConstructorOptions } from \"./models/IImapEmailConnectorConstructorOptions.js\";\nimport type { IImapEmailConnectorFolderState } from \"./models/IImapEmailConnectorFolderState.js\";\nimport type { IImapEmailConnectorState } from \"./models/IImapEmailConnectorState.js\";\nimport { initSchema } from \"./schema.js\";\n\n/**\n * IMAP email protocol connector. Polls configured folders for new messages using imapflow.\n */\nexport class ImapEmailConnector implements IEmailProtocolConnector<IImapEmailConnectorState> {\n\t/**\n\t * The class name.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<ImapEmailConnector>();\n\n\t/**\n\t * The protocol namespace identifier.\n\t */\n\tpublic static readonly NAMESPACE: string = \"imap\";\n\n\t/**\n\t * The logging component.\n\t * @internal\n\t */\n\tprivate readonly _logging?: ILoggingComponent;\n\n\t/**\n\t * The task scheduler component used for polling.\n\t * @internal\n\t */\n\tprivate readonly _taskScheduler: ITaskSchedulerComponent;\n\n\t/**\n\t * The resolved configuration for this connector instance.\n\t * @internal\n\t */\n\tprivate readonly _config: Required<\n\t\tPick<\n\t\t\tIImapEmailConnectorConfig,\n\t\t\t\"host\" | \"port\" | \"secure\" | \"username\" | \"password\" | \"folders\" | \"pollingIntervalMinutes\"\n\t\t>\n\t> &\n\t\tPick<IImapEmailConnectorConfig, \"mutexTimeoutMs\">;\n\n\t/**\n\t * The identifier of the currently active instance being polled.\n\t * @internal\n\t */\n\tprivate _instanceId?: string;\n\n\t/**\n\t * Set when retrieve has been called and cleared when retrieveStop is called.\n\t * @internal\n\t */\n\tprivate _retrieving: boolean;\n\n\t/**\n\t * The last successfully persisted connector state, updated after each successful callback.\n\t * @internal\n\t */\n\tprivate _state?: IImapEmailConnectorState;\n\n\t/**\n\t * Create a new instance of ImapEmailConnector.\n\t * @param options The options for the connector.\n\t */\n\tconstructor(options: IImapEmailConnectorConstructorOptions) {\n\t\tGuards.object(ImapEmailConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.object(ImapEmailConnector.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.stringValue(\n\t\t\tImapEmailConnector.CLASS_NAME,\n\t\t\tnameof(options.config.host),\n\t\t\toptions.config.host\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tImapEmailConnector.CLASS_NAME,\n\t\t\tnameof(options.config.username),\n\t\t\toptions.config.username\n\t\t);\n\t\tGuards.stringValue(\n\t\t\tImapEmailConnector.CLASS_NAME,\n\t\t\tnameof(options.config.password),\n\t\t\toptions.config.password\n\t\t);\n\n\t\tthis._logging = ComponentFactory.getIfExists<ILoggingComponent>(options.loggingComponentType);\n\n\t\tthis._taskScheduler = ComponentFactory.get<ITaskSchedulerComponent>(\n\t\t\toptions.taskSchedulerComponentType ?? \"task-scheduler\"\n\t\t);\n\n\t\tconst secure = options.config.secure ?? true;\n\t\tthis._config = {\n\t\t\thost: options.config.host,\n\t\t\tport: options.config.port ?? (secure ? 993 : 143),\n\t\t\tsecure,\n\t\t\tusername: options.config.username,\n\t\t\tpassword: options.config.password,\n\t\t\tfolders: options.config.folders ?? [\"INBOX\"],\n\t\t\tpollingIntervalMinutes: Math.max(1, options.config.pollingIntervalMinutes ?? 2),\n\t\t\tmutexTimeoutMs: options.config.mutexTimeoutMs\n\t\t};\n\t\tthis._retrieving = false;\n\n\t\t// A connector registers its own schemas so the owning component can recognise the\n\t\t// secure properties of the configuration and state it persists.\n\t\tinitSchema();\n\t}\n\n\t/**\n\t * Get the class name.\n\t * @returns The class name.\n\t */\n\tpublic className(): string {\n\t\treturn ImapEmailConnector.CLASS_NAME;\n\t}\n\n\t/**\n\t * Start the internal polling loop for the given instance.\n\t * @param instanceId The identifier of the mailbox instance being polled.\n\t * @param state The current connector state for the instance.\n\t * @param authCallback Callback invoked when authentication fails during a poll cycle.\n\t * @param retrievalCallback Callback invoked with retrieved messages after each poll cycle.\n\t * @param options Options supplied by the owning component, unused by this protocol which\n\t * authenticates with the stored credentials rather than an external flow.\n\t * @returns A promise that resolves when the polling loop has been started.\n\t */\n\tpublic async retrieve(\n\t\tinstanceId: string,\n\t\tstate: IImapEmailConnectorState,\n\t\tauthCallback: IEmailProtocolConnectorAuthCallback,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback,\n\t\toptions: IEmailProtocolConnectorOptions\n\t): Promise<void> {\n\t\tawait this.retrieveStop();\n\n\t\tthis._retrieving = true;\n\t\tthis._instanceId = instanceId;\n\t\tthis._state = state;\n\n\t\tawait this._taskScheduler.addTask(\n\t\t\tinstanceId,\n\t\t\t[{ intervalMinutes: this._config.pollingIntervalMinutes }],\n\t\t\tasync () => {\n\t\t\t\tawait this.poll(authCallback, retrievalCallback);\n\t\t\t}\n\t\t);\n\t}\n\n\t/**\n\t * Stop the polling loop for this connector.\n\t * @returns A promise that resolves when the polling loop has been stopped.\n\t */\n\tpublic async retrieveStop(): Promise<void> {\n\t\tif (Is.stringValue(this._instanceId)) {\n\t\t\tthis._retrieving = false;\n\t\t\tawait this._taskScheduler.removeTask(this._instanceId);\n\t\t\tthis._instanceId = undefined;\n\t\t\tthis._state = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Poll each configured folder for new messages.\n\t * @param authCallback Callback invoked when authentication fails.\n\t * @param retrievalCallback Callback invoked with retrieved messages.\n\t * @internal\n\t */\n\tprivate async poll(\n\t\tauthCallback: IEmailProtocolConnectorAuthCallback,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback\n\t): Promise<void> {\n\t\tif (Is.empty(this._state) || !Is.stringValue(this._instanceId)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Capture the instance id and state for this poll cycle, retrieveStop can clear the\n\t\t// instance fields while a poll is in progress and the mutex must still be released\n\t\t// under the key it was locked with.\n\t\tconst instanceId = this._instanceId;\n\t\tconst state = this._state;\n\n\t\tawait Mutex.lock(instanceId, {\n\t\t\ttimeoutMs: this._config.mutexTimeoutMs,\n\t\t\tthrowOnTimeout: true\n\t\t});\n\n\t\tconst client = new ImapFlow({\n\t\t\thost: this._config.host,\n\t\t\tport: this._config.port,\n\t\t\tsecure: this._config.secure,\n\t\t\tauth: { user: this._config.username, pass: this._config.password },\n\t\t\tlogger: false\n\t\t});\n\n\t\ttry {\n\t\t\tawait client.connect();\n\n\t\t\tfor (const folder of this._config.folders) {\n\t\t\t\tconst continued = await this.pollFolder(client, folder, retrievalCallback);\n\t\t\t\tif (!continued) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst pollError = new GeneralError(\n\t\t\t\tImapEmailConnector.CLASS_NAME,\n\t\t\t\t\"pollFailed\",\n\t\t\t\tundefined,\n\t\t\t\tBaseError.fromError(error)\n\t\t\t);\n\n\t\t\tconst rawError = Is.object<{ authenticationFailed?: unknown; response?: unknown }>(error)\n\t\t\t\t? error\n\t\t\t\t: undefined;\n\t\t\tconst isAuthFailure = rawError?.authenticationFailed === true;\n\t\t\tif (isAuthFailure) {\n\t\t\t\tawait authCallback(instanceId, state, true, undefined, pollError);\n\t\t\t} else {\n\t\t\t\tawait retrievalCallback(instanceId, undefined, state, pollError);\n\t\t\t}\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tawait client.logout();\n\t\t\t\tclient.close();\n\t\t\t} catch {\n\t\t\t\t// ignore, not much we can do if the connection is already closed\n\t\t\t\t// but don't want to prevent the mutex from being unlocked in the finally block below\n\t\t\t}\n\t\t\tMutex.unlock(instanceId);\n\t\t}\n\t}\n\n\t/**\n\t * Poll a single folder for messages with UIDs beyond the last seen UID.\n\t * Each parsed message is forwarded to the retrieval callback; returning false aborts processing.\n\t * @param client The connected ImapFlow client.\n\t * @param folder The folder path to poll.\n\t * @param retrievalCallback Callback invoked for each parsed email; return false to abort.\n\t * @returns True if all messages were processed, false if processing was aborted.\n\t * @internal\n\t */\n\tprivate async pollFolder(\n\t\tclient: ImapFlow,\n\t\tfolder: string,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback\n\t): Promise<boolean> {\n\t\tif (Is.empty(this._state) || !Is.stringValue(this._instanceId)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tthis._state.folders ??= {};\n\t\tconst folderState: IImapEmailConnectorFolderState = this._state.folders[folder] ?? {};\n\t\tthis._state.folders[folder] = folderState;\n\n\t\tconst lock = await client.getMailboxLock(folder);\n\t\ttry {\n\t\t\tif (!client.mailbox) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst uidValidity = client.mailbox.uidValidity.toString();\n\n\t\t\tif (folderState.uidValidity !== uidValidity) {\n\t\t\t\tfolderState.uidValidity = uidValidity;\n\t\t\t\tfolderState.lastUid = 0;\n\t\t\t}\n\n\t\t\tconst lastUid = folderState.lastUid ?? 0;\n\t\t\tconst searchQuery = lastUid > 0 ? { uid: `${lastUid + 1}:*` } : { all: true };\n\t\t\tconst uids = await client.search(searchQuery, { uid: true });\n\n\t\t\tif (uids !== false && Is.arrayValue(uids)) {\n\t\t\t\tconst msgStream = client.fetch(\n\t\t\t\t\tuids,\n\t\t\t\t\t{ uid: true, source: true, flags: true },\n\t\t\t\t\t{ uid: true }\n\t\t\t\t);\n\t\t\t\t// Any early exit from this loop (return, break or throw) must terminate the\n\t\t\t\t// fetch iterator, otherwise imapflow leaves the FETCH command pending and the\n\t\t\t\t// LOGOUT queued behind it in poll() never resolves, so the mutex is never\n\t\t\t\t// released. for await calls the iterator's return() on every exit path.\n\t\t\t\tfor await (const msg of msgStream) {\n\t\t\t\t\tif (!this._retrieving) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\tconst currentLastUid = folderState.lastUid ?? 0;\n\t\t\t\t\tif (Is.notEmpty(msg.uid) && msg.uid > currentLastUid) {\n\t\t\t\t\t\tif (Is.notEmpty(msg.source)) {\n\t\t\t\t\t\t\tconst email = await MailHelper.parseEmail(\n\t\t\t\t\t\t\t\tmsg.source.toString(),\n\t\t\t\t\t\t\t\tmsg.flags ? [...msg.flags] : undefined\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (Is.notEmpty(email)) {\n\t\t\t\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\t\t\t\tsource: ImapEmailConnector.CLASS_NAME,\n\t\t\t\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\t\t\t\tmessage: \"emailReceived\",\n\t\t\t\t\t\t\t\t\tdata: { subject: email.subject }\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\tfolderState.lastUid = msg.uid;\n\t\t\t\t\t\t\t\tconst shouldContinue = await retrievalCallback(\n\t\t\t\t\t\t\t\t\tthis._instanceId,\n\t\t\t\t\t\t\t\t\temail,\n\t\t\t\t\t\t\t\t\tthis._state\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (!shouldContinue) {\n\t\t\t\t\t\t\t\t\tfolderState.lastUid = currentLastUid;\n\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tlock.release();\n\t\t}\n\t\treturn true;\n\t}\n}\n"]}
package/dist/es/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  // Copyright 2026 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
- export * from "./configSchema/imapEmailConnectorConfigSchema.js";
3
+ export * from "./connectorSchema/imapEmailConnectorConfigSchema.js";
4
+ export * from "./connectorSchema/imapEmailConnectorStateSchema.js";
4
5
  export * from "./imapEmailConnector.js";
5
6
  export * from "./models/IImapEmailConnectorConfig.js";
6
7
  export * from "./models/IImapEmailConnectorConstructorOptions.js";
7
8
  export * from "./models/IImapEmailConnectorFolderState.js";
8
9
  export * from "./models/IImapEmailConnectorState.js";
10
+ export * from "./schema.js";
9
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,kDAAkD,CAAC;AACjE,cAAc,yBAAyB,CAAC;AACxC,cAAc,uCAAuC,CAAC;AACtD,cAAc,mDAAmD,CAAC;AAClE,cAAc,4CAA4C,CAAC;AAC3D,cAAc,sCAAsC,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./configSchema/imapEmailConnectorConfigSchema.js\";\nexport * from \"./imapEmailConnector.js\";\nexport * from \"./models/IImapEmailConnectorConfig.js\";\nexport * from \"./models/IImapEmailConnectorConstructorOptions.js\";\nexport * from \"./models/IImapEmailConnectorFolderState.js\";\nexport * from \"./models/IImapEmailConnectorState.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,qDAAqD,CAAC;AACpE,cAAc,oDAAoD,CAAC;AACnE,cAAc,yBAAyB,CAAC;AACxC,cAAc,uCAAuC,CAAC;AACtD,cAAc,mDAAmD,CAAC;AAClE,cAAc,4CAA4C,CAAC;AAC3D,cAAc,sCAAsC,CAAC;AACrD,cAAc,aAAa,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./connectorSchema/imapEmailConnectorConfigSchema.js\";\nexport * from \"./connectorSchema/imapEmailConnectorStateSchema.js\";\nexport * from \"./imapEmailConnector.js\";\nexport * from \"./models/IImapEmailConnectorConfig.js\";\nexport * from \"./models/IImapEmailConnectorConstructorOptions.js\";\nexport * from \"./models/IImapEmailConnectorFolderState.js\";\nexport * from \"./models/IImapEmailConnectorState.js\";\nexport * from \"./schema.js\";\n"]}
@@ -0,0 +1,17 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { EmailProtocolConnectorConfigSchemaFactory, EmailProtocolConnectorStateSchemaFactory } from "@twin.org/mailbox-models";
4
+ import { ImapEmailConnectorConfigSchema } from "./connectorSchema/imapEmailConnectorConfigSchema.js";
5
+ import { ImapEmailConnectorStateSchema } from "./connectorSchema/imapEmailConnectorStateSchema.js";
6
+ import { ImapEmailConnector } from "./imapEmailConnector.js";
7
+ /**
8
+ * Initialise the connector schemas for the IMAP connector.
9
+ * The connector constructor calls this as well, so registering here is only needed to make the
10
+ * schemas available before any connector instance has been built, such as for a mailbox which
11
+ * is stored but not currently polled.
12
+ */
13
+ export function initSchema() {
14
+ EmailProtocolConnectorConfigSchemaFactory.register(ImapEmailConnector.NAMESPACE, () => ImapEmailConnectorConfigSchema);
15
+ EmailProtocolConnectorStateSchemaFactory.register(ImapEmailConnector.NAMESPACE, () => ImapEmailConnectorStateSchema);
16
+ }
17
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EACN,yCAAyC,EACzC,wCAAwC,EACxC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,8BAA8B,EAAE,MAAM,qDAAqD,CAAC;AACrG,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,UAAU,UAAU;IACzB,yCAAyC,CAAC,QAAQ,CACjD,kBAAkB,CAAC,SAAS,EAC5B,GAAG,EAAE,CAAC,8BAA8B,CACpC,CAAC;IACF,wCAAwC,CAAC,QAAQ,CAChD,kBAAkB,CAAC,SAAS,EAC5B,GAAG,EAAE,CAAC,6BAA6B,CACnC,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tEmailProtocolConnectorConfigSchemaFactory,\n\tEmailProtocolConnectorStateSchemaFactory\n} from \"@twin.org/mailbox-models\";\nimport { ImapEmailConnectorConfigSchema } from \"./connectorSchema/imapEmailConnectorConfigSchema.js\";\nimport { ImapEmailConnectorStateSchema } from \"./connectorSchema/imapEmailConnectorStateSchema.js\";\nimport { ImapEmailConnector } from \"./imapEmailConnector.js\";\n\n/**\n * Initialise the connector schemas for the IMAP connector.\n * The connector constructor calls this as well, so registering here is only needed to make the\n * schemas available before any connector instance has been built, such as for a mailbox which\n * is stored but not currently polled.\n */\nexport function initSchema(): void {\n\tEmailProtocolConnectorConfigSchemaFactory.register(\n\t\tImapEmailConnector.NAMESPACE,\n\t\t() => ImapEmailConnectorConfigSchema\n\t);\n\tEmailProtocolConnectorStateSchemaFactory.register(\n\t\tImapEmailConnector.NAMESPACE,\n\t\t() => ImapEmailConnectorStateSchema\n\t);\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { IMailboxConfigField } from "@twin.org/mailbox-models";
2
+ /**
3
+ * The runtime state field schema for IMAP connectors.
4
+ * The connector authenticates with its configured credentials, so no state property is secure.
5
+ */
6
+ export declare const ImapEmailConnectorStateSchema: IMailboxConfigField[];
@@ -1,4 +1,4 @@
1
- import type { IEmailProtocolConnector, IEmailProtocolConnectorAuthCallback, IEmailProtocolConnectorRetrievalCallback } from "@twin.org/mailbox-models";
1
+ import type { IEmailProtocolConnector, IEmailProtocolConnectorAuthCallback, IEmailProtocolConnectorOptions, IEmailProtocolConnectorRetrievalCallback } from "@twin.org/mailbox-models";
2
2
  import type { IImapEmailConnectorConstructorOptions } from "./models/IImapEmailConnectorConstructorOptions.js";
3
3
  import type { IImapEmailConnectorState } from "./models/IImapEmailConnectorState.js";
4
4
  /**
@@ -29,9 +29,11 @@ export declare class ImapEmailConnector implements IEmailProtocolConnector<IImap
29
29
  * @param state The current connector state for the instance.
30
30
  * @param authCallback Callback invoked when authentication fails during a poll cycle.
31
31
  * @param retrievalCallback Callback invoked with retrieved messages after each poll cycle.
32
+ * @param options Options supplied by the owning component, unused by this protocol which
33
+ * authenticates with the stored credentials rather than an external flow.
32
34
  * @returns A promise that resolves when the polling loop has been started.
33
35
  */
34
- retrieve(instanceId: string, state: IImapEmailConnectorState, authCallback: IEmailProtocolConnectorAuthCallback, retrievalCallback: IEmailProtocolConnectorRetrievalCallback): Promise<void>;
36
+ retrieve(instanceId: string, state: IImapEmailConnectorState, authCallback: IEmailProtocolConnectorAuthCallback, retrievalCallback: IEmailProtocolConnectorRetrievalCallback, options: IEmailProtocolConnectorOptions): Promise<void>;
35
37
  /**
36
38
  * Stop the polling loop for this connector.
37
39
  * @returns A promise that resolves when the polling loop has been stopped.
@@ -1,6 +1,8 @@
1
- export * from "./configSchema/imapEmailConnectorConfigSchema.js";
1
+ export * from "./connectorSchema/imapEmailConnectorConfigSchema.js";
2
+ export * from "./connectorSchema/imapEmailConnectorStateSchema.js";
2
3
  export * from "./imapEmailConnector.js";
3
4
  export * from "./models/IImapEmailConnectorConfig.js";
4
5
  export * from "./models/IImapEmailConnectorConstructorOptions.js";
5
6
  export * from "./models/IImapEmailConnectorFolderState.js";
6
7
  export * from "./models/IImapEmailConnectorState.js";
8
+ export * from "./schema.js";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Initialise the connector schemas for the IMAP connector.
3
+ * The connector constructor calls this as well, so registering here is only needed to make the
4
+ * schemas available before any connector instance has been built, such as for a mailbox which
5
+ * is stored but not currently polled.
6
+ */
7
+ export declare function initSchema(): void;
package/docs/changelog.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.3-next.3](https://github.com/iotaledger/twin-messaging/compare/mailbox-connector-imap-v0.9.3-next.2...mailbox-connector-imap-v0.9.3-next.3) (2026-09-10)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * **mailbox-connector-imap:** Synchronize repo versions
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/mailbox-models bumped from 0.9.3-next.2 to 0.9.3-next.3
16
+
17
+ ## [0.9.3-next.2](https://github.com/iotaledger/twin-messaging/compare/mailbox-connector-imap-v0.9.3-next.1...mailbox-connector-imap-v0.9.3-next.2) (2026-09-10)
18
+
19
+
20
+ ### Features
21
+
22
+ * add gmail with oauth2 workflow ([#61](https://github.com/iotaledger/twin-messaging/issues/61)) ([16b4eaf](https://github.com/iotaledger/twin-messaging/commit/16b4eaf40071444bb656e807d80579d98e21046a))
23
+
24
+
25
+ ### Dependencies
26
+
27
+ * The following workspace dependencies were updated
28
+ * dependencies
29
+ * @twin.org/mailbox-models bumped from 0.9.3-next.1 to 0.9.3-next.2
30
+
3
31
  ## [0.9.3-next.1](https://github.com/iotaledger/twin-messaging/compare/mailbox-connector-imap-v0.9.3-next.0...mailbox-connector-imap-v0.9.3-next.1) (2026-09-04)
4
32
 
5
33
 
package/docs/examples.md CHANGED
@@ -5,16 +5,14 @@ A polling connector for email ingestion via the IMAP protocol, with per-folder U
5
5
  ## ImapEmailConnector
6
6
 
7
7
  ```typescript
8
- import {
9
- ImapEmailConnector,
10
- ImapEmailConnectorConfigSchema
11
- } from '@twin.org/mailbox-connector-imap';
8
+ import { ImapEmailConnector, initSchema } from '@twin.org/mailbox-connector-imap';
12
9
  import {
13
10
  EmailProtocolConnectorFactory,
14
- EmailProtocolConnectorSchemaFactory
11
+ EmailProtocolConnectorConfigSchemaFactory,
12
+ EmailProtocolConnectorStateSchemaFactory
15
13
  } from '@twin.org/mailbox-models';
16
14
 
17
- // Register the connector and its config schema with the shared factories
15
+ // Register the connector with the shared factory
18
16
  EmailProtocolConnectorFactory.register(
19
17
  ImapEmailConnector.NAMESPACE,
20
18
  () =>
@@ -27,13 +25,18 @@ EmailProtocolConnectorFactory.register(
27
25
  })
28
26
  );
29
27
 
30
- EmailProtocolConnectorSchemaFactory.register(
31
- ImapEmailConnector.NAMESPACE,
32
- () => ImapEmailConnectorConfigSchema
33
- );
28
+ // The connector registers its config and state schemas when it is constructed. Call initSchema
29
+ // at startup as well, so a mailbox which is stored but not currently polled still has its
30
+ // secure properties recognised
31
+ initSchema();
34
32
 
35
- const schema = EmailProtocolConnectorSchemaFactory.get(ImapEmailConnector.NAMESPACE);
33
+ const schema = EmailProtocolConnectorConfigSchemaFactory.get(ImapEmailConnector.NAMESPACE);
36
34
  console.log(schema.find(f => f.isSecure)?.propertyKey); // "password"
35
+
36
+ const stateSchema = EmailProtocolConnectorStateSchemaFactory.get(ImapEmailConnector.NAMESPACE);
37
+ console.log(stateSchema.map(f => f.propertyKey)); // ["folders"]
38
+ // The credentials are configured, so no state property needs vaulting
39
+ console.log(stateSchema.filter(f => f.isSecure)); // []
37
40
  ```
38
41
 
39
42
  ```typescript
@@ -64,7 +64,7 @@ The class name.
64
64
 
65
65
  ### retrieve() {#retrieve}
66
66
 
67
- > **retrieve**(`instanceId`, `state`, `authCallback`, `retrievalCallback`): `Promise`\<`void`\>
67
+ > **retrieve**(`instanceId`, `state`, `authCallback`, `retrievalCallback`, `options`): `Promise`\<`void`\>
68
68
 
69
69
  Start the internal polling loop for the given instance.
70
70
 
@@ -94,6 +94,13 @@ Callback invoked when authentication fails during a poll cycle.
94
94
 
95
95
  Callback invoked with retrieved messages after each poll cycle.
96
96
 
97
+ ##### options
98
+
99
+ `IEmailProtocolConnectorOptions`
100
+
101
+ Options supplied by the owning component, unused by this protocol which
102
+ authenticates with the stored credentials rather than an external flow.
103
+
97
104
  #### Returns
98
105
 
99
106
  `Promise`\<`void`\>
@@ -0,0 +1,12 @@
1
+ # Function: initSchema()
2
+
3
+ > **initSchema**(): `void`
4
+
5
+ Initialise the connector schemas for the IMAP connector.
6
+ The connector constructor calls this as well, so registering here is only needed to make the
7
+ schemas available before any connector instance has been built, such as for a mailbox which
8
+ is stored but not currently polled.
9
+
10
+ ## Returns
11
+
12
+ `void`
@@ -14,3 +14,8 @@
14
14
  ## Variables
15
15
 
16
16
  - [ImapEmailConnectorConfigSchema](variables/ImapEmailConnectorConfigSchema.md)
17
+ - [ImapEmailConnectorStateSchema](variables/ImapEmailConnectorStateSchema.md)
18
+
19
+ ## Functions
20
+
21
+ - [initSchema](functions/initSchema.md)
@@ -0,0 +1,6 @@
1
+ # Variable: ImapEmailConnectorStateSchema
2
+
3
+ > `const` **ImapEmailConnectorStateSchema**: `IMailboxConfigField`[]
4
+
5
+ The runtime state field schema for IMAP connectors.
6
+ The connector authenticates with its configured credentials, so no state property is secure.
package/locales/en.json CHANGED
@@ -17,5 +17,8 @@
17
17
  "password": "Password",
18
18
  "folders": "Folders",
19
19
  "pollingIntervalMinutes": "Polling Interval (Minutes)"
20
+ },
21
+ "imapEmailConnectorStateSchema": {
22
+ "folders": "Folder State"
20
23
  }
21
24
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/mailbox-connector-imap",
3
- "version": "0.9.3-next.1",
3
+ "version": "0.9.3-next.3",
4
4
  "description": "IMAP connector for email mailbox ingestion.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -18,7 +18,7 @@
18
18
  "@twin.org/core": "next",
19
19
  "@twin.org/entity": "next",
20
20
  "@twin.org/logging-models": "next",
21
- "@twin.org/mailbox-models": "0.9.3-next.1",
21
+ "@twin.org/mailbox-models": "0.9.3-next.3",
22
22
  "@twin.org/nameof": "next",
23
23
  "imapflow": "1.7.7"
24
24
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"imapEmailConnectorConfigSchema.js","sourceRoot":"","sources":["../../../src/configSchema/imapEmailConnectorConfigSchema.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAG5D;;GAEG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,8BAA8B,GAA0B;IACpE;QACC,QAAQ,EAAE,qCAAqC;QAC/C,WAAW,EAAE,MAAM;QACnB,IAAI,EAAE,wBAAwB,CAAC,MAAM;KACrC;IACD;QACC,QAAQ,EAAE,qCAAqC;QAC/C,WAAW,EAAE,MAAM;QACnB,IAAI,EAAE,wBAAwB,CAAC,OAAO;QACtC,YAAY,EAAE,GAAG;KACjB;IACD;QACC,QAAQ,EAAE,uCAAuC;QACjD,WAAW,EAAE,QAAQ;QACrB,IAAI,EAAE,wBAAwB,CAAC,OAAO;QACtC,YAAY,EAAE,IAAI;KAClB;IACD;QACC,QAAQ,EAAE,yCAAyC;QACnD,WAAW,EAAE,UAAU;QACvB,IAAI,EAAE,wBAAwB,CAAC,MAAM;KACrC;IACD;QACC,QAAQ,EAAE,yCAAyC;QACnD,WAAW,EAAE,UAAU;QACvB,IAAI,EAAE,wBAAwB,CAAC,MAAM;QACrC,QAAQ,EAAE,IAAI;KACd;IACD;QACC,QAAQ,EAAE,wCAAwC;QAClD,WAAW,EAAE,SAAS;QACtB,IAAI,EAAE,wBAAwB,CAAC,KAAK;QACpC,QAAQ,EAAE,wBAAwB,CAAC,MAAM;QACzC,YAAY,EAAE,CAAC,OAAO,CAAC;KACvB;IACD;QACC,QAAQ,EAAE,uDAAuD;QACjE,WAAW,EAAE,wBAAwB;QACrC,IAAI,EAAE,wBAAwB,CAAC,OAAO;QACtC,YAAY,EAAE,CAAC;KACf;CACD,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { EntitySchemaPropertyType } from \"@twin.org/entity\";\nimport type { IMailboxConfigField } from \"@twin.org/mailbox-models\";\n\n/**\n * The configuration field schema for IMAP connectors.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const ImapEmailConnectorConfigSchema: IMailboxConfigField[] = [\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.host\",\n\t\tpropertyKey: \"host\",\n\t\ttype: EntitySchemaPropertyType.String\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.port\",\n\t\tpropertyKey: \"port\",\n\t\ttype: EntitySchemaPropertyType.Integer,\n\t\tdefaultValue: 993\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.secure\",\n\t\tpropertyKey: \"secure\",\n\t\ttype: EntitySchemaPropertyType.Boolean,\n\t\tdefaultValue: true\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.username\",\n\t\tpropertyKey: \"username\",\n\t\ttype: EntitySchemaPropertyType.String\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.password\",\n\t\tpropertyKey: \"password\",\n\t\ttype: EntitySchemaPropertyType.String,\n\t\tisSecure: true\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.folders\",\n\t\tpropertyKey: \"folders\",\n\t\ttype: EntitySchemaPropertyType.Array,\n\t\titemType: EntitySchemaPropertyType.String,\n\t\tdefaultValue: [\"INBOX\"]\n\t},\n\t{\n\t\tlabelKey: \"imapEmailConnectorConfigSchema.pollingIntervalMinutes\",\n\t\tpropertyKey: \"pollingIntervalMinutes\",\n\t\ttype: EntitySchemaPropertyType.Integer,\n\t\tdefaultValue: 2\n\t}\n];\n"]}