@3sixty/mailbox-connector-gmail 0.0.0-stage → 0.20.0-next.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.
- package/LICENSE +201 -0
- package/README.md +76 -2
- package/dist/es/connectorSchema/gmailEmailConnectorConfigSchema.js +51 -0
- package/dist/es/connectorSchema/gmailEmailConnectorConfigSchema.js.map +1 -0
- package/dist/es/connectorSchema/gmailEmailConnectorStateSchema.js +45 -0
- package/dist/es/connectorSchema/gmailEmailConnectorStateSchema.js.map +1 -0
- package/dist/es/gmailEmailConnector.js +773 -0
- package/dist/es/gmailEmailConnector.js.map +1 -0
- package/dist/es/index.js +10 -0
- package/dist/es/index.js.map +1 -0
- package/dist/es/models/IGmailEmailConnectorConfig.js +4 -0
- package/dist/es/models/IGmailEmailConnectorConfig.js.map +1 -0
- package/dist/es/models/IGmailEmailConnectorConstructorOptions.js +2 -0
- package/dist/es/models/IGmailEmailConnectorConstructorOptions.js.map +1 -0
- package/dist/es/models/IGmailEmailConnectorState.js +4 -0
- package/dist/es/models/IGmailEmailConnectorState.js.map +1 -0
- package/dist/es/schema.js +17 -0
- package/dist/es/schema.js.map +1 -0
- package/dist/types/connectorSchema/gmailEmailConnectorConfigSchema.d.ts +5 -0
- package/dist/types/connectorSchema/gmailEmailConnectorStateSchema.d.ts +7 -0
- package/dist/types/gmailEmailConnector.d.ts +64 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/models/IGmailEmailConnectorConfig.d.ts +56 -0
- package/dist/types/models/IGmailEmailConnectorConstructorOptions.d.ts +19 -0
- package/dist/types/models/IGmailEmailConnectorState.d.ts +39 -0
- package/dist/types/schema.d.ts +7 -0
- package/docs/changelog.md +0 -0
- package/docs/examples.md +174 -0
- package/docs/reference/classes/GmailEmailConnector.md +218 -0
- package/docs/reference/functions/initSchema.md +12 -0
- package/docs/reference/index.md +20 -0
- package/docs/reference/interfaces/IGmailEmailConnectorConfig.md +110 -0
- package/docs/reference/interfaces/IGmailEmailConnectorConstructorOptions.md +33 -0
- package/docs/reference/interfaces/IGmailEmailConnectorState.md +63 -0
- package/docs/reference/variables/GmailEmailConnectorConfigSchema.md +5 -0
- package/docs/reference/variables/GmailEmailConnectorStateSchema.md +7 -0
- package/locales/en.json +41 -0
- package/package.json +57 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gmailEmailConnector.js","sourceRoot":"","sources":["../../src/gmailEmailConnector.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,SAAS,EACT,MAAM,EACN,gBAAgB,EAChB,SAAS,EACT,YAAY,EACZ,MAAM,EACN,EAAE,EACF,KAAK,EACL,MAAM,cAAc,CAAC;AAUtB,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAwC,MAAM,YAAY,CAAC;AAI1E,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;GAEG;AACH,MAAM,OAAO,mBAAmB;IAC/B;;OAEG;IACI,MAAM,CAAU,UAAU,yBAAyC;IAE1E;;OAEG;IACI,MAAM,CAAU,SAAS,GAAW,OAAO,CAAC;IAEnD;;;OAGG;IACK,MAAM,CAAU,OAAO,GAAa,CAAC,gDAAgD,CAAC,CAAC;IAE/F;;;OAGG;IACK,MAAM,CAAU,oBAAoB,GAAa;QACxD,eAAe;QACf,gBAAgB;QAChB,qBAAqB;KACrB,CAAC;IAEF;;;;;OAKG;IACK,MAAM,CAAU,0BAA0B,GAAW,eAAe,CAAC;IAE7E;;;;;OAKG;IACK,MAAM,CAAU,sBAAsB,GAAa;QAC1D,mBAAmB;QACnB,uBAAuB;QACvB,oBAAoB;QACpB,eAAe;QACf,cAAc;KACd,CAAC;IAEF;;;OAGG;IACK,MAAM,CAAU,kBAAkB,GAAG,eAAe,CAAC;IAE7D;;;OAGG;IACK,MAAM,CAAU,gBAAgB,GAAG,aAAa,CAAC;IAEzD;;;OAGG;IACK,MAAM,CAAU,eAAe,GAAG,YAAY,CAAC;IAEvD;;;OAGG;IACK,MAAM,CAAU,iBAAiB,GAAG,cAAc,CAAC;IAE3D;;;OAGG;IACc,QAAQ,CAAqB;IAE9C;;;OAGG;IACc,cAAc,CAA0B;IAEzD;;;OAGG;IACc,OAAO,CAarB;IAEH;;;OAGG;IACc,WAAW,CAAoB;IAEhD;;;OAGG;IACK,MAAM,CAAkB;IAEhC;;;OAGG;IACK,WAAW,CAAU;IAE7B;;;OAGG;IACK,WAAW,CAAU;IAE7B;;;OAGG;IACK,MAAM,CAA6B;IAE3C;;;OAGG;IACK,YAAY,CAAU;IAE9B;;;;OAIG;IACK,iBAAiB,CAAU;IAEnC;;;OAGG;IACK,aAAa,CAAuC;IAE5D;;;;OAIG;IACH,YAAY,OAA+C;QAC1D,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QACxE,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,oBAA0B,OAAO,CAAC,MAAM,CAAC,CAAC;QACtF,MAAM,CAAC,WAAW,CACjB,mBAAmB,CAAC,UAAU,iCAE9B,OAAO,CAAC,MAAM,CAAC,YAAY,CAC3B,CAAC;QAEF,yFAAyF;QACzF,yFAAyF;QACzF,IACC,CAAC,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC;YACjD,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,EACxF,CAAC;YACF,MAAM,IAAI,YAAY,CAAC,mBAAmB,CAAC,UAAU,EAAE,yBAAyB,CAAC,CAAC;QACnF,CAAC;QAED,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,IAAI,CAAC,OAAO,GAAG;YACd,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY;YACzC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ;YACjC,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY;YACzC,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,iBAAiB;YACnD,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACtF,kBAAkB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,IAAI,EAAE,CAAC;YACxE,qBAAqB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,qBAAqB,IAAI,GAAG,CAAC;YAC/E,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,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC3C,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QAEzB,kFAAkF;QAClF,gEAAgE;QAChE,UAAU,EAAE,CAAC;IACd,CAAC;IAED;;;OAGG;IACI,SAAS;QACf,OAAO,mBAAmB,CAAC,UAAU,CAAC;IACvC,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,QAAQ,CACpB,UAAkB,EAClB,KAAgC,EAChC,YAAiD,EACjD,iBAA2D,EAC3D,OAAuC;QAEvC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QAExE,uFAAuF;QACvF,6CAA6C;QAC7C,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;QACpB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;QACxC,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;QAClD,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAElC,wFAAwF;QACxF,2EAA2E;QAC3E,IAAI,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,EAAE,CAAC;YACzC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC;gBAC/B,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,EAAE,KAAK,CAAC,YAAY;aAC5D,CAAC,CAAC;QACJ,CAAC;QAED,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;;;;;;;;OAQG;IACI,KAAK,CAAC,YAAY,CACxB,UAAkB,EAClB,KAAgC,EAChC,OAAuC;QAEvC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QACxE,MAAM,CAAC,WAAW,CACjB,mBAAmB,CAAC,UAAU,yBAE9B,OAAO,CAAC,WAAW,CACnB,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC3E,CAAC;IAED;;;;;;;;;;OAUG;IACI,KAAK,CAAC,YAAY,CACxB,UAAkB,EAClB,WAAoB,EACpB,OAAuC;QAEvC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,aAAmB,OAAO,CAAC,CAAC;QACxE,MAAM,CAAC,WAAW,CACjB,mBAAmB,CAAC,UAAU,yBAE9B,OAAO,CAAC,WAAW,CACnB,CAAC;QAEF,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAAqB,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QACrF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC1C,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,UAAU,UAAgB,IAAI,CAAC,CAAC;QAEvE,qFAAqF;QACrF,wEAAwE;QACxE,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,YAAY,CAAC,mBAAmB,CAAC,UAAU,EAAE,oBAAoB,CAAC,CAAC;QAC9E,CAAC;QAED,IAAI,YAAgC,CAAC;QACrC,IAAI,CAAC;YACJ,uEAAuE;YACvE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;gBAChD,IAAI;gBACJ,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC,WAAW;aAC5D,CAAC,CAAC;YACH,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACjD,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QAC7D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,YAAY,CACrB,mBAAmB,CAAC,UAAU,EAC9B,wBAAwB,EACxB,SAAS,EACT,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAC1B,CAAC;QACH,CAAC;QAED,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,CAAC;YACnC,MAAM,IAAI,YAAY,CAAC,mBAAmB,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC;QAChF,CAAC;QAED,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAEnC,MAAM,KAAK,GAAG,IAAI,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC;QAEnD,0FAA0F;QAC1F,0FAA0F;QAC1F,4DAA4D;QAC5D,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACpD,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;YACxB,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;YACnC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;QAChC,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,gBAAgB;QACvB,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACpD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CACxB,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAC9B,CAAC;YAEF,MAAM,WAAW,GAAG,GAAG,EAAE,YAAY,CAAC;YACtC,MAAM,UAAU,GAAG,GAAG,EAAE,WAAW,CAAC;YACpC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjE,MAAM,IAAI,YAAY,CAAC,mBAAmB,CAAC,UAAU,EAAE,0BAA0B,CAAC,CAAC;YACpF,CAAC;YAED,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;gBAC1B,KAAK,EAAE,WAAW;gBAClB,GAAG,EAAE,UAAU;gBACf,MAAM,EAAE,mBAAmB,CAAC,OAAO;gBACnC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;aAClC,CAAC,CAAC;QACJ,CAAC;QAED,0FAA0F;QAC1F,kEAAkE;QAClE,yFAAyF;QACzF,4CAA4C;QAC5C,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;YAC7B,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ;YAC/B,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;SACvC,CAAC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACK,iBAAiB,CAAC,YAAoB;QAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;QAChC,KAAK,CAAC,YAAY,GAAG,YAAY,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QAEpB,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC;YAC/B,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,EAAE,YAAY;SACtD,CAAC,CAAC;QAEH,OAAO,KAAK,CAAC;IACd,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,qBAAqB;QAClC,sFAAsF;QACtF,uFAAuF;QACvF,yFAAyF;QACzF,MAAM,OAAO,GAAG,MAAM,MAAM;aAC1B,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;aAChD,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAErC,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAElE,IAAI,gBAAgB,EAAE,WAAW,EAAE,KAAK,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,EAAE,CAAC;YACjF,MAAM,IAAI,YAAY,CAAC,mBAAmB,CAAC,UAAU,EAAE,wBAAwB,EAAE;gBAChF,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;aACvC,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,YAAY,CAAC,KAA4C;QAChE,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAChG,CAAC;IAED;;;;;;;OAOG;IACK,cAAc,CACrB,WAA+B,EAC/B,gBAAoC;QAEpC,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACpD,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACvE,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,OAAO;YACN,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC;gBACzC,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,SAAS;gBACjD,CAAC,mBAAmB,CAAC,eAAe,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;gBAChE,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,EAAE,WAAW;gBACpD,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE,mBAAmB,CAAC,OAAO;gBAClC,iFAAiF;gBACjF,iFAAiF;gBACjF,KAAK,EAAE,gBAAgB;aACvB,CAAC;SACF,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,OAAO;QACpB,0FAA0F;QAC1F,sFAAsF;QACtF,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,EAAE,CAAC;QAExC,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;QACxE,OAAO,IAAI,CAAC,MAAM,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACK,aAAa,CAAC,KAAc;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAEvC,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACb,CAAC;QAED,yFAAyF;QACzF,mFAAmF;QACnF,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACjD,OAAO,CACN,EAAE,CAAC,WAAW,CAAC,WAAW,CAAC,IAAI,mBAAmB,CAAC,oBAAoB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAC7F,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,sBAAsB,CAAC,KAAc;QAC5C,OAAO,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,mBAAmB,CAAC,0BAA0B,CAAC;IACxF,CAAC;IAED;;;;;OAKG;IACK,cAAc,CAAC,KAAc;QACpC,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAGtB,KAAK,CAAC;YACR,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,SAAS,CAAC;QAEb,MAAM,IAAI,GAAG,EAAE,CAAC,MAAM,CAAmC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC;YAChF,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI;YACzB,CAAC,CAAC,SAAS,CAAC;QAEb,MAAM,OAAO,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC;aACpD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAuB,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aAC3E,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;aACzC,MAAM,CAAC,CAAC,MAAM,EAAoB,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;QAE/D,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,mBAAmB,CAAC,sBAAsB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5F,CAAC;IAED;;;;;OAKG;IACK,gBAAgB,CAAC,KAAc;QACtC,OAAO,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC;IACxC,CAAC;IAED;;;;;OAKG;IACK,WAAW,CAAC,KAAc;QACjC,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAItB,KAAK,CAAC;YACR,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,SAAS,CAAC;QAEb,OAAO,CACN,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC;YAC5B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,CACxC,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,gBAAgB,CAAC,KAAc;QACtC,MAAM,OAAO,GAAG,EAAE,CAAC,MAAM,CAAuD,KAAK,CAAC;YACrF,CAAC,CAAC,KAAK;YACP,CAAC,CAAC,SAAS,CAAC;QAEb,MAAM,IAAI,GAAG,EAAE,CAAC,MAAM,CAAsB,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC;YACnE,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI;YACzB,CAAC,CAAC,SAAS,CAAC;QACb,IAAI,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC,KAAK,CAAC;QACnB,CAAC;QAED,4FAA4F;QAC5F,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,OAAO,mBAAmB,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IACvF,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,IAAI,CAAC;YACJ,IAAI,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;oBACxB,KAAK,EAAE,MAAM;oBACb,MAAM,EAAE,mBAAmB,CAAC,UAAU;oBACtC,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;oBACd,OAAO,EAAE,iBAAiB;oBAC1B,IAAI,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;iBAC/B,CAAC,CAAC;gBAEH,+EAA+E;gBAC/E,iFAAiF;gBACjF,MAAM,YAAY,CACjB,UAAU,EACV,KAAK,EACL,IAAI,EACJ,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAC9D,CAAC;gBACF,OAAO;YACR,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YAEnC,MAAM,SAAS,GACd,KAAK,CAAC,mBAAmB,KAAK,IAAI;gBACjC,CAAC,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,iBAAiB,CAAC;gBACrE,CAAC,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC;YAE5E,IAAI,SAAS,EAAE,CAAC;gBACf,iFAAiF;gBACjF,4EAA4E;gBAC5E,MAAM,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;YACvD,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,SAAS,GAAG,IAAI,YAAY,CACjC,mBAAmB,CAAC,UAAU,EAC9B,YAAY,EACZ,SAAS,EACT,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAC1B,CAAC;YAEF,IAAI,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC/B,kFAAkF;gBAClF,oFAAoF;gBACpF,iEAAiE;gBACjE,IAAI,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxC,KAAK,CAAC,YAAY,GAAG,SAAS,CAAC;gBAChC,CAAC;gBAED,MAAM,YAAY,CACjB,UAAU,EACV,KAAK,EACL,IAAI,EACJ,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAC9D,SAAS,CACT,CAAC;YACH,CAAC;iBAAM,CAAC;gBACP,MAAM,iBAAiB,CAAC,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;YAClE,CAAC;QACF,CAAC;gBAAS,CAAC;YACV,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC1B,CAAC;IACF,CAAC;IAED;;;;;;;;OAQG;IACK,KAAK,CAAC,eAAe,CAC5B,KAAqB,EACrB,UAAkB,EAClB,KAAgC,EAChC,iBAA2D;QAE3D,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC;YACtC,kFAAkF;YAClF,mFAAmF;YACnF,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;YACpF,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzD,CAAC;QAED,KAAK,CAAC,cAAc,KAAK,EAAE,CAAC;QAC5B,KAAK,CAAC,qBAAqB,KAAK,EAAE,CAAC;QAEnC,sFAAsF;QACtF,mEAAmE;QACnE,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC7C,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBACpD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CACzC,KAAK,EACL,UAAU,EACV,OAAO,EACP,KAAK,EACL,iBAAiB,CACjB,CAAC;gBACF,IAAI,CAAC,SAAS,EAAE,CAAC;oBAChB,OAAO,KAAK,CAAC;gBACd,CAAC;YACF,CAAC;QACF,CAAC;QAED,IAAI,KAAK,CAAC,qBAAqB,CAAC,MAAM,KAAK,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACzE,KAAK,CAAC,mBAAmB,GAAG,IAAI,CAAC;YACjC,KAAK,CAAC,cAAc,GAAG,SAAS,CAAC;YACjC,KAAK,CAAC,qBAAqB,GAAG,SAAS,CAAC;QACzC,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;;OASG;IACK,KAAK,CAAC,aAAa,CAC1B,KAAqB,EACrB,UAAkB,EAClB,OAAe,EACf,KAAgC,EAChC,iBAA2D;QAE3D,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAC5C,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;YACjC,QAAQ,EAAE,CAAC,OAAO,CAAC;YACnB,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,kBAAkB;YAC3C,SAAS,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC,OAAO,CAAC;YAC1C,gBAAgB,EAAE,KAAK;SACvB,CAAC,CAAC;QAEH,iFAAiF;QACjF,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;aAC3C,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;aAC1B,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;aAChD,OAAO,EAAE,CAAC;QAEZ,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,eAAe,CAC3C,KAAK,EACL,UAAU,EACV,UAAU,EACV,KAAK,EACL,iBAAiB,CACjB,CAAC;QACF,IAAI,CAAC,SAAS,EAAE,CAAC;YAChB,OAAO,KAAK,CAAC;QACd,CAAC;QAED,sFAAsF;QACtF,sDAAsD;QACtD,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC7D,KAAK,CAAC,cAAc,KAAK,EAAE,CAAC;QAC5B,KAAK,CAAC,qBAAqB,KAAK,EAAE,CAAC;QAEnC,IAAI,EAAE,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,CAAC;YACnC,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC;QAC/C,CAAC;aAAM,CAAC;YACP,OAAO,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YACrC,KAAK,CAAC,qBAAqB,GAAG,CAAC,GAAG,KAAK,CAAC,qBAAqB,EAAE,OAAO,CAAC,CAAC;QACzE,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;OAQG;IACK,KAAK,CAAC,WAAW,CACxB,KAAqB,EACrB,UAAkB,EAClB,KAAgC,EAChC,iBAA2D;QAE3D,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,CAAC;QACvC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC;YACrC,iEAAiE;YACjE,KAAK,CAAC,mBAAmB,GAAG,KAAK,CAAC;YAClC,OAAO,IAAI,CAAC;QACb,CAAC;QAED,IAAI,SAA6B,CAAC;QAClC,IAAI,gBAAoC,CAAC;QACzC,IAAI,SAAS,GAAG,IAAI,CAAC;QAErB,OAAO,SAAS,EAAE,CAAC;YAClB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC;YACd,CAAC;YAED,IAAI,QAAQ,CAAC;YACb,IAAI,CAAC;gBACJ,mFAAmF;gBACnF,0EAA0E;gBAC1E,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;oBACzC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;oBACjC,cAAc;oBACd,YAAY,EAAE,CAAC,cAAc,CAAC;oBAC9B,SAAS;iBACT,CAAC,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;oBACnC,MAAM,KAAK,CAAC;gBACb,CAAC;gBAED,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;oBACxB,KAAK,EAAE,MAAM;oBACb,MAAM,EAAE,mBAAmB,CAAC,UAAU;oBACtC,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;oBACd,OAAO,EAAE,gBAAgB;iBACzB,CAAC,CAAC;gBAEH,iFAAiF;gBACjF,gFAAgF;gBAChF,KAAK,CAAC,SAAS,GAAG,SAAS,CAAC;gBAC5B,KAAK,CAAC,cAAc,GAAG,SAAS,CAAC;gBACjC,KAAK,CAAC,qBAAqB,GAAG,SAAS,CAAC;gBACxC,KAAK,CAAC,mBAAmB,GAAG,KAAK,CAAC;gBAClC,OAAO,IAAI,CAAC;YACb,CAAC;YAED,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;gBAClD,MAAM,UAAU,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC;qBAC7C,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;qBAC5D,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;qBAC/B,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,EAAE,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;gBAEnD,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,eAAe,CAC3C,KAAK,EACL,UAAU,EACV,UAAU,EACV,KAAK,EACL,iBAAiB,CACjB,CAAC;gBACF,IAAI,CAAC,SAAS,EAAE,CAAC;oBAChB,OAAO,KAAK,CAAC;gBACd,CAAC;gBAED,iFAAiF;gBACjF,sEAAsE;gBACtE,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC;YAC/D,CAAC;YAED,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACvD,gBAAgB,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC1D,SAAS,GAAG,EAAE,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;QACvC,CAAC;QAED,IAAI,EAAE,CAAC,WAAW,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACtC,oFAAoF;YACpF,wDAAwD;YACxD,KAAK,CAAC,SAAS,GAAG,gBAAgB,CAAC;QACpC,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;;;;OAUG;IACK,KAAK,CAAC,eAAe,CAC5B,KAAqB,EACrB,UAAkB,EAClB,UAAoB,EACpB,KAAgC,EAChC,iBAA2D;QAE3D,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC;YACd,CAAC;YAED,MAAM,gBAAgB,GAAG,KAAK,CAAC,mBAAmB,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC;YACjF,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACvB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;gBAEtD,IAAI,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxB,MAAM,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;wBACxB,KAAK,EAAE,MAAM;wBACb,MAAM,EAAE,mBAAmB,CAAC,UAAU;wBACtC,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE;wBACd,OAAO,EAAE,eAAe;wBACxB,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE;qBAChC,CAAC,CAAC;oBAEH,gFAAgF;oBAChF,gFAAgF;oBAChF,MAAM,iBAAiB,GAAG,KAAK,CAAC,mBAAmB,CAAC;oBACpD,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;oBAEvC,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;oBACzE,IAAI,CAAC,cAAc,EAAE,CAAC;wBACrB,KAAK,CAAC,mBAAmB,GAAG,iBAAiB,CAAC;wBAC9C,OAAO,KAAK,CAAC;oBACd,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAED,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;OAMG;IACK,KAAK,CAAC,UAAU,CAAC,KAAqB,EAAE,SAAiB;QAChE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;YAC/C,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY;YACjC,EAAE,EAAE,SAAS;YACb,MAAM,EAAE,KAAK;SACb,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,SAAS,CAAC;QAClB,CAAC;QAED,OAAO,UAAU,CAAC,UAAU,CAC3B,SAAS,CAAC,WAAW,CAAC,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,EACtD,QAAQ,CAAC,IAAI,CAAC,QAAQ,IAAI,SAAS,CACnC,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,aAAa,CAAC,QAAqC;QAC1D,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;IAClF,CAAC;IAED;;;;;OAKG;IACK,eAAe,CAAC,KAAgC,EAAE,SAAiB;QAC1E,0FAA0F;QAC1F,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,mBAAmB,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;QACpE,KAAK,CAAC,mBAAmB,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAClF,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { ITaskSchedulerComponent } from \"@3sixty/background-task-models\";\nimport {\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tMutex\n} from \"@3sixty/core\";\nimport type { ILoggingComponent } from \"@3sixty/logging-models\";\nimport type {\n\tIEmail,\n\tIEmailProtocolConnector,\n\tIEmailProtocolConnectorAuthCallback,\n\tIEmailProtocolConnectorAuthState,\n\tIEmailProtocolConnectorOptions,\n\tIEmailProtocolConnectorRetrievalCallback\n} from \"@3sixty/mailbox-models\";\nimport { MailHelper } from \"@3sixty/mailbox-models\";\nimport { nameof } from \"@3sixty/nameof\";\nimport { google, type Auth, type gmail_v1 as GmailApi } from \"googleapis\";\nimport type { IGmailEmailConnectorConfig } from \"./models/IGmailEmailConnectorConfig.js\";\nimport type { IGmailEmailConnectorConstructorOptions } from \"./models/IGmailEmailConnectorConstructorOptions.js\";\nimport type { IGmailEmailConnectorState } from \"./models/IGmailEmailConnectorState.js\";\nimport { initSchema } from \"./schema.js\";\n\n/**\n * Gmail email protocol connector. Monitors a mailbox for new messages using the Gmail API.\n */\nexport class GmailEmailConnector implements IEmailProtocolConnector<IGmailEmailConnectorState> {\n\t/**\n\t * The class name.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<GmailEmailConnector>();\n\n\t/**\n\t * The protocol namespace identifier.\n\t */\n\tpublic static readonly NAMESPACE: string = \"gmail\";\n\n\t/**\n\t * The OAuth 2.0 scopes the connector requires on the mailbox.\n\t * @internal\n\t */\n\tprivate static readonly _SCOPES: string[] = [\"https://www.googleapis.com/auth/gmail.readonly\"];\n\n\t/**\n\t * The OAuth 2.0 error codes which mean the mailbox cannot authenticate as it is configured.\n\t * @internal\n\t */\n\tprivate static readonly _OAUTH_FAILURE_CODES: string[] = [\n\t\t\"invalid_grant\",\n\t\t\"invalid_client\",\n\t\t\"unauthorized_client\"\n\t];\n\n\t/**\n\t * The OAuth 2.0 error code which means the stored refresh token itself is no longer valid.\n\t * The other failure codes describe the client rather than the token, so they must not cause\n\t * a working token to be discarded.\n\t * @internal\n\t */\n\tprivate static readonly _OAUTH_TOKEN_REJECTED_CODE: string = \"invalid_grant\";\n\n\t/**\n\t * The Gmail API failure reasons which report a quota or rate limit rather than a credential\n\t * problem. They share the 403 status with the permission failures, so they are named here to\n\t * keep a throttled poll from being treated as an authentication failure.\n\t * @internal\n\t */\n\tprivate static readonly _QUOTA_FAILURE_REASONS: string[] = [\n\t\t\"rateLimitExceeded\",\n\t\t\"userRateLimitExceeded\",\n\t\t\"dailyLimitExceeded\",\n\t\t\"quotaExceeded\",\n\t\t\"backendError\"\n\t];\n\n\t/**\n\t * The Google credentials property holding the refresh token.\n\t * @internal\n\t */\n\tprivate static readonly _REFRESH_TOKEN_KEY = \"refresh_token\";\n\n\t/**\n\t * The Google consent URL property requesting a refresh token.\n\t * @internal\n\t */\n\tprivate static readonly _ACCESS_TYPE_KEY = \"access_type\";\n\n\t/**\n\t * The Google consent URL property preselecting the mailbox account.\n\t * @internal\n\t */\n\tprivate static readonly _LOGIN_HINT_KEY = \"login_hint\";\n\n\t/**\n\t * The Google property naming where a consent flow returns to.\n\t * @internal\n\t */\n\tprivate static readonly _REDIRECT_URI_KEY = \"redirect_uri\";\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\tIGmailEmailConnectorConfig,\n\t\t\t| \"emailAddress\"\n\t\t\t| \"labelIds\"\n\t\t\t| \"maxMessagesPerPoll\"\n\t\t\t| \"maxDeliveredIdHistory\"\n\t\t\t| \"pollingIntervalMinutes\"\n\t\t>\n\t> &\n\t\tPick<\n\t\t\tIGmailEmailConnectorConfig,\n\t\t\t\"clientId\" | \"clientSecret\" | \"serviceAccountKey\" | \"mutexTimeoutMs\"\n\t\t>;\n\n\t/**\n\t * The OAuth 2.0 client used to authenticate Gmail API calls.\n\t * @internal\n\t */\n\tprivate readonly _authClient: Auth.OAuth2Client;\n\n\t/**\n\t * The Gmail API client, created on the first poll and reused afterwards.\n\t * @internal\n\t */\n\tprivate _gmail?: GmailApi.Gmail;\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 connector state for the instance currently being polled.\n\t * @internal\n\t */\n\tprivate _state?: IGmailEmailConnectorState;\n\n\t/**\n\t * The fixed URI the consent flow returns to, supplied by the owning component on retrieve.\n\t * @internal\n\t */\n\tprivate _callbackUri?: string;\n\n\t/**\n\t * The value the consent flow carries so the response correlates back to the mailbox,\n\t * supplied by the owning component on retrieve.\n\t * @internal\n\t */\n\tprivate _correlationState?: string;\n\n\t/**\n\t * The auth callback given by retrieve, used to report the outcome of the consent flow.\n\t * @internal\n\t */\n\tprivate _authCallback?: IEmailProtocolConnectorAuthCallback;\n\n\t/**\n\t * Create a new instance of GmailEmailConnector.\n\t * @param options The options for the connector.\n\t * @throws GeneralError if neither a service account key nor an OAuth client is supplied.\n\t */\n\tconstructor(options: IGmailEmailConnectorConstructorOptions) {\n\t\tGuards.object(GmailEmailConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.object(GmailEmailConnector.CLASS_NAME, nameof(options.config), options.config);\n\t\tGuards.stringValue(\n\t\t\tGmailEmailConnector.CLASS_NAME,\n\t\t\tnameof(options.config.emailAddress),\n\t\t\toptions.config.emailAddress\n\t\t);\n\n\t\t// The refresh token is deliberately not required, a mailbox can be created with just the\n\t\t// OAuth client and the connector then drives the consent flow through the auth callback.\n\t\tif (\n\t\t\t!Is.stringValue(options.config.serviceAccountKey) &&\n\t\t\t!(Is.stringValue(options.config.clientId) && Is.stringValue(options.config.clientSecret))\n\t\t) {\n\t\t\tthrow new GeneralError(GmailEmailConnector.CLASS_NAME, \"missingOAuthCredentials\");\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\tthis._config = {\n\t\t\temailAddress: options.config.emailAddress,\n\t\t\tclientId: options.config.clientId,\n\t\t\tclientSecret: options.config.clientSecret,\n\t\t\tserviceAccountKey: options.config.serviceAccountKey,\n\t\t\tlabelIds: Is.arrayValue(options.config.labelIds) ? options.config.labelIds : [\"INBOX\"],\n\t\t\tmaxMessagesPerPoll: Math.max(1, options.config.maxMessagesPerPoll ?? 50),\n\t\t\tmaxDeliveredIdHistory: Math.max(1, options.config.maxDeliveredIdHistory ?? 200),\n\t\t\tpollingIntervalMinutes: Math.max(1, options.config.pollingIntervalMinutes ?? 2),\n\t\t\tmutexTimeoutMs: options.config.mutexTimeoutMs\n\t\t};\n\t\tthis._authClient = this.createAuthClient();\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 GmailEmailConnector.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, carrying the consent callback URI.\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: IGmailEmailConnectorState,\n\t\tauthCallback: IEmailProtocolConnectorAuthCallback,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback,\n\t\toptions: IEmailProtocolConnectorOptions\n\t): Promise<void> {\n\t\tGuards.object(GmailEmailConnector.CLASS_NAME, nameof(options), options);\n\n\t\t// The callback URI is only needed to produce a consent URL, so a mailbox which already\n\t\t// holds credentials still polls without one.\n\t\tawait this.retrieveStop();\n\n\t\tthis._retrieving = true;\n\t\tthis._instanceId = instanceId;\n\t\tthis._state = state;\n\t\tthis._callbackUri = options.callbackUri;\n\t\tthis._correlationState = options.correlationState;\n\t\tthis._authCallback = authCallback;\n\n\t\t// The refresh token is issued by the consent flow rather than configured, so it arrives\n\t\t// with the state and has to be handed to the client the constructor built.\n\t\tif (Is.stringValue(state?.refreshToken)) {\n\t\t\tthis._authClient.setCredentials({\n\t\t\t\t[GmailEmailConnector._REFRESH_TOKEN_KEY]: state.refreshToken\n\t\t\t});\n\t\t}\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 * Start the consent flow for a mailbox which has no refresh token yet.\n\t * @param instanceId The identifier of the mailbox instance being authenticated.\n\t * @param state The current connector state for the instance.\n\t * @param options Options supplied by the owning component, carrying the consent callback URI\n\t * and the state which correlates the consent response back to the mailbox.\n\t * @returns The auth state carrying the consent URL to open, or undefined when the mailbox can\n\t * already authenticate itself from a service account key or a refresh token in its state.\n\t */\n\tpublic async initiateAuth(\n\t\tinstanceId: string,\n\t\tstate: IGmailEmailConnectorState,\n\t\toptions: IEmailProtocolConnectorOptions\n\t): Promise<IEmailProtocolConnectorAuthState | undefined> {\n\t\tGuards.object(GmailEmailConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tGmailEmailConnector.CLASS_NAME,\n\t\t\tnameof(options.callbackUri),\n\t\t\toptions.callbackUri\n\t\t);\n\n\t\tif (!this.needsConsent(state)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn this.buildAuthState(options.callbackUri, options.correlationState);\n\t}\n\n\t/**\n\t * Complete the consent flow with the authorisation code handed to the redirect URI.\n\t * The issued credentials are applied to this connector instance so the next poll uses them,\n\t * and the refresh token is placed in the state reported through the auth callback given by\n\t * retrieve, so the owning component vaults it and lifts the authentication halt.\n\t * @param instanceId The identifier of the mailbox instance being authenticated.\n\t * @param authPayload The redirect query, carrying the authorisation code as its code property.\n\t * @param options Options supplied by the owning component, carrying the consent callback URI.\n\t * @returns A promise that resolves when the flow has been completed.\n\t * @throws GeneralError if the authorisation code could not be exchanged for tokens.\n\t */\n\tpublic async completeAuth(\n\t\tinstanceId: string,\n\t\tauthPayload: unknown,\n\t\toptions: IEmailProtocolConnectorOptions\n\t): Promise<void> {\n\t\tGuards.object(GmailEmailConnector.CLASS_NAME, nameof(options), options);\n\t\tGuards.stringValue(\n\t\t\tGmailEmailConnector.CLASS_NAME,\n\t\t\tnameof(options.callbackUri),\n\t\t\toptions.callbackUri\n\t\t);\n\n\t\tconst payload = Is.object<{ code?: unknown }>(authPayload) ? authPayload : undefined;\n\t\tconst code = Coerce.string(payload?.code);\n\t\tGuards.stringValue(GmailEmailConnector.CLASS_NAME, nameof(code), code);\n\n\t\t// The authorisation code can only be exchanged once, so the connector refuses before\n\t\t// spending it if it has no callback to report the issued token through.\n\t\tif (Is.empty(this._authCallback)) {\n\t\t\tthrow new GeneralError(GmailEmailConnector.CLASS_NAME, \"authFlowNotStarted\");\n\t\t}\n\n\t\tlet refreshToken: string | undefined;\n\t\ttry {\n\t\t\t// Google requires the same redirect the consent URL was produced with.\n\t\t\tconst response = await this._authClient.getToken({\n\t\t\t\tcode,\n\t\t\t\t[GmailEmailConnector._REDIRECT_URI_KEY]: options.callbackUri\n\t\t\t});\n\t\t\tthis._authClient.setCredentials(response.tokens);\n\t\t\trefreshToken = Coerce.string(response.tokens.refresh_token);\n\t\t} catch (error) {\n\t\t\tthrow new GeneralError(\n\t\t\t\tGmailEmailConnector.CLASS_NAME,\n\t\t\t\t\"authCodeExchangeFailed\",\n\t\t\t\tundefined,\n\t\t\t\tBaseError.fromError(error)\n\t\t\t);\n\t\t}\n\n\t\tif (!Is.stringValue(refreshToken)) {\n\t\t\tthrow new GeneralError(GmailEmailConnector.CLASS_NAME, \"noRefreshTokenIssued\");\n\t\t}\n\n\t\tawait this.guardConsentedAccount();\n\n\t\tconst state = this.applyRefreshToken(refreshToken);\n\n\t\t// Continue the workflow through the auth callback, which clears the halt and persists the\n\t\t// state, where the state schema marks the token as a vaulted field. A restarted connector\n\t\t// is then handed the state with the token already in place.\n\t\tawait this._authCallback(instanceId, state, false);\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\tthis._correlationState = undefined;\n\t\t\tthis._authCallback = undefined;\n\t\t}\n\t}\n\n\t/**\n\t * Create the authentication client for the configured credentials.\n\t * @returns The authentication client.\n\t * @throws GeneralError if the service account key is not valid JSON containing a key pair.\n\t * @internal\n\t */\n\tprivate createAuthClient(): Auth.OAuth2Client {\n\t\tif (Is.stringValue(this._config.serviceAccountKey)) {\n\t\t\tconst key = Coerce.object<{ client_email?: string; private_key?: string }>(\n\t\t\t\tthis._config.serviceAccountKey\n\t\t\t);\n\n\t\t\tconst clientEmail = key?.client_email;\n\t\t\tconst privateKey = key?.private_key;\n\t\t\tif (!Is.stringValue(clientEmail) || !Is.stringValue(privateKey)) {\n\t\t\t\tthrow new GeneralError(GmailEmailConnector.CLASS_NAME, \"invalidServiceAccountKey\");\n\t\t\t}\n\n\t\t\treturn new google.auth.JWT({\n\t\t\t\temail: clientEmail,\n\t\t\t\tkey: privateKey,\n\t\t\t\tscopes: GmailEmailConnector._SCOPES,\n\t\t\t\tsubject: this._config.emailAddress\n\t\t\t});\n\t\t}\n\n\t\t// The redirect URI is not set here, it is passed explicitly on the calls which need it so\n\t\t// the value always matches the one the owning component supplied.\n\t\t// The refresh token is not set here, it lives in the connector state and is applied once\n\t\t// retrieve or the consent flow supplies it.\n\t\treturn new google.auth.OAuth2({\n\t\t\tclientId: this._config.clientId,\n\t\t\tclientSecret: this._config.clientSecret\n\t\t});\n\t}\n\n\t/**\n\t * Apply an issued refresh token to the state and the authentication client for later polls.\n\t * @param refreshToken The refresh token to apply.\n\t * @returns The state the token was applied to.\n\t * @internal\n\t */\n\tprivate applyRefreshToken(refreshToken: string): IGmailEmailConnectorState {\n\t\tconst state = this._state ?? {};\n\t\tstate.refreshToken = refreshToken;\n\t\tthis._state = state;\n\n\t\tthis._authClient.setCredentials({\n\t\t\t[GmailEmailConnector._REFRESH_TOKEN_KEY]: refreshToken\n\t\t});\n\n\t\treturn state;\n\t}\n\n\t/**\n\t * Verify the account which gave consent is the mailbox the connector is configured for.\n\t * @throws GeneralError if the consent was given for a different account.\n\t * @internal\n\t */\n\tprivate async guardConsentedAccount(): Promise<void> {\n\t\t// The correlating state travels through the browser, so consent can come back from an\n\t\t// account other than the one the mailbox names. Binding the mailbox to it would ingest\n\t\t// somebody else's email, so the issued credentials are checked before they are reported.\n\t\tconst profile = await google\n\t\t\t.gmail({ version: \"v1\", auth: this._authClient })\n\t\t\t.users.getProfile({ userId: \"me\" });\n\n\t\tconst consentedAddress = Coerce.string(profile.data.emailAddress);\n\n\t\tif (consentedAddress?.toLowerCase() !== this._config.emailAddress.toLowerCase()) {\n\t\t\tthrow new GeneralError(GmailEmailConnector.CLASS_NAME, \"consentAccountMismatch\", {\n\t\t\t\temailAddress: this._config.emailAddress\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Whether the connector is still waiting for the consent flow to supply a refresh token.\n\t * @param state The connector state holding any token the consent flow has already issued.\n\t * @returns True if consent is required before the mailbox can be polled.\n\t * @internal\n\t */\n\tprivate needsConsent(state: IGmailEmailConnectorState | undefined): boolean {\n\t\treturn !Is.stringValue(this._config.serviceAccountKey) && !Is.stringValue(state?.refreshToken);\n\t}\n\n\t/**\n\t * Build the auth state describing how the mailbox can be authenticated.\n\t * @param callbackUri The URI the consent flow returns to.\n\t * @param correlationState The value the consent flow carries back so the response correlates\n\t * to the mailbox it belongs to.\n\t * @returns The auth state, or undefined if no consent URL can be produced.\n\t * @internal\n\t */\n\tprivate buildAuthState(\n\t\tcallbackUri: string | undefined,\n\t\tcorrelationState: string | undefined\n\t): IEmailProtocolConnectorAuthState | undefined {\n\t\tif (Is.stringValue(this._config.serviceAccountKey)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tif (!Is.stringValue(callbackUri) || !Is.stringValue(correlationState)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tauthUrl: this._authClient.generateAuthUrl({\n\t\t\t\t[GmailEmailConnector._ACCESS_TYPE_KEY]: \"offline\",\n\t\t\t\t[GmailEmailConnector._LOGIN_HINT_KEY]: this._config.emailAddress,\n\t\t\t\t[GmailEmailConnector._REDIRECT_URI_KEY]: callbackUri,\n\t\t\t\tprompt: \"consent\",\n\t\t\t\tscope: GmailEmailConnector._SCOPES,\n\t\t\t\t// The callback URI is fixed for the deployment, so the owning component supplies\n\t\t\t\t// the value which correlates the response back to the mailbox and its partition.\n\t\t\t\tstate: correlationState\n\t\t\t})\n\t\t};\n\t}\n\n\t/**\n\t * Get the Gmail API client, refreshing the access token if it has expired.\n\t * @returns The Gmail API client.\n\t * @internal\n\t */\n\tprivate async connect(): Promise<GmailApi.Gmail> {\n\t\t// Asking for an access token exchanges the refresh token or the service account key for a\n\t\t// new one whenever the cached token has expired, so no manual intervention is needed.\n\t\tawait this._authClient.getAccessToken();\n\n\t\tthis._gmail ??= google.gmail({ version: \"v1\", auth: this._authClient });\n\t\treturn this._gmail;\n\t}\n\n\t/**\n\t * Determine whether an error reports that the stored credentials were rejected.\n\t * @param error The error thrown by the Gmail API or the token endpoint.\n\t * @returns True if the error requires the mailbox to be re-authenticated.\n\t * @internal\n\t */\n\tprivate isAuthFailure(error: unknown): boolean {\n\t\tconst status = this.errorStatus(error);\n\n\t\tif (status === 401) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Gmail returns 403 both for a mailbox the credentials may not read and for a poll which\n\t\t// exceeded a quota, so a throttled cycle is reported as a retrieval error instead.\n\t\tif (status === 403) {\n\t\t\treturn !this.isQuotaFailure(error);\n\t\t}\n\n\t\tconst failureCode = this.oauthFailureCode(error);\n\t\treturn (\n\t\t\tIs.stringValue(failureCode) && GmailEmailConnector._OAUTH_FAILURE_CODES.includes(failureCode)\n\t\t);\n\t}\n\n\t/**\n\t * Determine whether an error reports that the stored refresh token is no longer valid.\n\t * @param error The error thrown by the Gmail API or the token endpoint.\n\t * @returns True if the refresh token has expired or been revoked and has to be replaced.\n\t * @internal\n\t */\n\tprivate isRefreshTokenRejected(error: unknown): boolean {\n\t\treturn this.oauthFailureCode(error) === GmailEmailConnector._OAUTH_TOKEN_REJECTED_CODE;\n\t}\n\n\t/**\n\t * Determine whether an error reports a quota or rate limit rather than a credential problem.\n\t * @param error The error thrown by the Gmail API.\n\t * @returns True if the request was throttled.\n\t * @internal\n\t */\n\tprivate isQuotaFailure(error: unknown): boolean {\n\t\tconst details = Is.object<{\n\t\t\terrors?: unknown;\n\t\t\tresponse?: { data?: unknown };\n\t\t}>(error)\n\t\t\t? error\n\t\t\t: undefined;\n\n\t\tconst data = Is.object<{ error?: { errors?: unknown } }>(details?.response?.data)\n\t\t\t? details?.response?.data\n\t\t\t: undefined;\n\n\t\tconst reasons = [details?.errors, data?.error?.errors]\n\t\t\t.flatMap(entry => (Is.arrayValue<{ reason?: unknown }>(entry) ? entry : []))\n\t\t\t.map(entry => Coerce.string(entry.reason))\n\t\t\t.filter((reason): reason is string => Is.stringValue(reason));\n\n\t\treturn reasons.some(reason => GmailEmailConnector._QUOTA_FAILURE_REASONS.includes(reason));\n\t}\n\n\t/**\n\t * Determine whether an error reports that the stored history identifier is too old to use.\n\t * @param error The error thrown by the Gmail history API.\n\t * @returns True if the history identifier has aged out of the mailbox history.\n\t * @internal\n\t */\n\tprivate isHistoryExpired(error: unknown): boolean {\n\t\treturn this.errorStatus(error) === 404;\n\t}\n\n\t/**\n\t * Extract the HTTP status code from a Gmail API error.\n\t * @param error The error to inspect.\n\t * @returns The status code, or undefined if the error carries none.\n\t * @internal\n\t */\n\tprivate errorStatus(error: unknown): number | undefined {\n\t\tconst details = Is.object<{\n\t\t\tcode?: unknown;\n\t\t\tstatus?: unknown;\n\t\t\tresponse?: { status?: unknown };\n\t\t}>(error)\n\t\t\t? error\n\t\t\t: undefined;\n\n\t\treturn (\n\t\t\tCoerce.number(details?.status) ??\n\t\t\tCoerce.number(details?.code) ??\n\t\t\tCoerce.number(details?.response?.status)\n\t\t);\n\t}\n\n\t/**\n\t * Extract the OAuth 2.0 failure code from a token endpoint error.\n\t * @param error The error to inspect.\n\t * @returns The failure code, or undefined if the error carries none.\n\t * @internal\n\t */\n\tprivate oauthFailureCode(error: unknown): string | undefined {\n\t\tconst details = Is.object<{ message?: unknown; response?: { data?: unknown } }>(error)\n\t\t\t? error\n\t\t\t: undefined;\n\n\t\tconst data = Is.object<{ error?: unknown }>(details?.response?.data)\n\t\t\t? details?.response?.data\n\t\t\t: undefined;\n\t\tif (Is.stringValue(data?.error)) {\n\t\t\treturn data.error;\n\t\t}\n\n\t\t// When the refresh request itself is rejected the library surfaces the code in the message.\n\t\tconst message = Coerce.string(details?.message);\n\t\treturn GmailEmailConnector._OAUTH_FAILURE_CODES.find(code => message?.includes(code));\n\t}\n\n\t/**\n\t * Poll the mailbox 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\ttry {\n\t\t\tif (this.needsConsent(state)) {\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\tsource: GmailEmailConnector.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"consentRequired\",\n\t\t\t\t\tdata: { mailboxId: instanceId }\n\t\t\t\t});\n\n\t\t\t\t// Hand the consent URL back through the auth callback and wait for the flow to\n\t\t\t\t// complete, the mailbox has no refresh token to authenticate the Gmail API with.\n\t\t\t\tawait authCallback(\n\t\t\t\t\tinstanceId,\n\t\t\t\t\tstate,\n\t\t\t\t\ttrue,\n\t\t\t\t\tthis.buildAuthState(this._callbackUri, this._correlationState)\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst gmail = await this.connect();\n\n\t\t\tconst completed =\n\t\t\t\tstate.initialSyncComplete === true\n\t\t\t\t\t? await this.syncHistory(gmail, instanceId, state, retrievalCallback)\n\t\t\t\t\t: await this.syncMessageList(gmail, instanceId, state, retrievalCallback);\n\n\t\t\tif (completed) {\n\t\t\t\t// Flush the sync progress even when no message was delivered, a cycle which only\n\t\t\t\t// moved the page token or the history id on would otherwise repeat forever.\n\t\t\t\tawait retrievalCallback(instanceId, undefined, state);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconst pollError = new GeneralError(\n\t\t\t\tGmailEmailConnector.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\tif (this.isAuthFailure(error)) {\n\t\t\t\t// Only a token the provider rejected is cleared from the state. A client which is\n\t\t\t\t// misconfigured, or a mailbox the credentials may not read, still halts the mailbox\n\t\t\t\t// but must not discard a refresh token which is otherwise valid.\n\t\t\t\tif (this.isRefreshTokenRejected(error)) {\n\t\t\t\t\tstate.refreshToken = undefined;\n\t\t\t\t}\n\n\t\t\t\tawait authCallback(\n\t\t\t\t\tinstanceId,\n\t\t\t\t\tstate,\n\t\t\t\t\ttrue,\n\t\t\t\t\tthis.buildAuthState(this._callbackUri, this._correlationState),\n\t\t\t\t\tpollError\n\t\t\t\t);\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\tMutex.unlock(instanceId);\n\t\t}\n\t}\n\n\t/**\n\t * Walk the existing mailbox contents one page per poll cycle, oldest message first.\n\t * @param gmail The Gmail API client.\n\t * @param instanceId The identifier of the mailbox instance being polled.\n\t * @param state The connector state for the instance.\n\t * @param retrievalCallback Callback invoked for each parsed email.\n\t * @returns True if the page was fully processed, false if processing was aborted.\n\t * @internal\n\t */\n\tprivate async syncMessageList(\n\t\tgmail: GmailApi.Gmail,\n\t\tinstanceId: string,\n\t\tstate: IGmailEmailConnectorState,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback\n\t): Promise<boolean> {\n\t\tif (!Is.stringValue(state.historyId)) {\n\t\t\t// Record where the mailbox history stands before the first page is listed, so the\n\t\t\t// incremental sync which follows picks up everything that arrives in the meantime.\n\t\t\tconst profile = await gmail.users.getProfile({ userId: this._config.emailAddress });\n\t\t\tstate.historyId = Coerce.string(profile.data.historyId);\n\t\t}\n\n\t\tstate.syncPageTokens ??= {};\n\t\tstate.syncCompletedLabelIds ??= [];\n\n\t\t// The Gmail message list requires a message to carry every label it is given, so each\n\t\t// configured label is walked with its own call and its own cursor.\n\t\tfor (const labelId of this._config.labelIds) {\n\t\t\tif (!state.syncCompletedLabelIds.includes(labelId)) {\n\t\t\t\tconst continued = await this.syncLabelPage(\n\t\t\t\t\tgmail,\n\t\t\t\t\tinstanceId,\n\t\t\t\t\tlabelId,\n\t\t\t\t\tstate,\n\t\t\t\t\tretrievalCallback\n\t\t\t\t);\n\t\t\t\tif (!continued) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (state.syncCompletedLabelIds.length === this._config.labelIds.length) {\n\t\t\tstate.initialSyncComplete = true;\n\t\t\tstate.syncPageTokens = undefined;\n\t\t\tstate.syncCompletedLabelIds = undefined;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Walk a single page of one label's existing messages, oldest message first.\n\t * @param gmail The Gmail API client.\n\t * @param instanceId The identifier of the mailbox instance being polled.\n\t * @param labelId The label to list.\n\t * @param state The connector state for the instance.\n\t * @param retrievalCallback Callback invoked for each parsed email.\n\t * @returns True if the page was fully processed, false if processing was aborted.\n\t * @internal\n\t */\n\tprivate async syncLabelPage(\n\t\tgmail: GmailApi.Gmail,\n\t\tinstanceId: string,\n\t\tlabelId: string,\n\t\tstate: IGmailEmailConnectorState,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback\n\t): Promise<boolean> {\n\t\tconst list = await gmail.users.messages.list({\n\t\t\tuserId: this._config.emailAddress,\n\t\t\tlabelIds: [labelId],\n\t\t\tmaxResults: this._config.maxMessagesPerPoll,\n\t\t\tpageToken: state.syncPageTokens?.[labelId],\n\t\t\tincludeSpamTrash: false\n\t\t});\n\n\t\t// The Gmail API returns the newest message first, deliver them in arrival order.\n\t\tconst messageIds = (list.data.messages ?? [])\n\t\t\t.map(message => message.id)\n\t\t\t.filter((id): id is string => Is.stringValue(id))\n\t\t\t.reverse();\n\n\t\tconst delivered = await this.deliverMessages(\n\t\t\tgmail,\n\t\t\tinstanceId,\n\t\t\tmessageIds,\n\t\t\tstate,\n\t\t\tretrievalCallback\n\t\t);\n\t\tif (!delivered) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Only move the cursor on once the whole page has been delivered, a page which failed\n\t\t// part way through is listed again on the next cycle.\n\t\tconst nextPageToken = Coerce.string(list.data.nextPageToken);\n\t\tstate.syncPageTokens ??= {};\n\t\tstate.syncCompletedLabelIds ??= [];\n\n\t\tif (Is.stringValue(nextPageToken)) {\n\t\t\tstate.syncPageTokens[labelId] = nextPageToken;\n\t\t} else {\n\t\t\tdelete state.syncPageTokens[labelId];\n\t\t\tstate.syncCompletedLabelIds = [...state.syncCompletedLabelIds, labelId];\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Deliver the messages added to the mailbox since the last processed history record.\n\t * @param gmail The Gmail API client.\n\t * @param instanceId The identifier of the mailbox instance being polled.\n\t * @param state The connector state for the instance.\n\t * @param retrievalCallback Callback invoked for each parsed email.\n\t * @returns True if all history records were processed, false if processing was aborted.\n\t * @internal\n\t */\n\tprivate async syncHistory(\n\t\tgmail: GmailApi.Gmail,\n\t\tinstanceId: string,\n\t\tstate: IGmailEmailConnectorState,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback\n\t): Promise<boolean> {\n\t\tconst startHistoryId = state.historyId;\n\t\tif (!Is.stringValue(startHistoryId)) {\n\t\t\t// Nothing to resume from, fall back to walking the message list.\n\t\t\tstate.initialSyncComplete = false;\n\t\t\treturn true;\n\t\t}\n\n\t\tlet pageToken: string | undefined;\n\t\tlet mailboxHistoryId: string | undefined;\n\t\tlet morePages = true;\n\n\t\twhile (morePages) {\n\t\t\tif (!this._retrieving) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tlet response;\n\t\t\ttry {\n\t\t\t\t// The history is mailbox wide and only accepts one label, so it is read unfiltered\n\t\t\t\t// and the added messages are matched against the configured labels below.\n\t\t\t\tresponse = await gmail.users.history.list({\n\t\t\t\t\tuserId: this._config.emailAddress,\n\t\t\t\t\tstartHistoryId,\n\t\t\t\t\thistoryTypes: [\"messageAdded\"],\n\t\t\t\t\tpageToken\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tif (!this.isHistoryExpired(error)) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\n\t\t\t\tawait this._logging?.log({\n\t\t\t\t\tlevel: \"warn\",\n\t\t\t\t\tsource: GmailEmailConnector.CLASS_NAME,\n\t\t\t\t\tts: Date.now(),\n\t\t\t\t\tmessage: \"historyExpired\"\n\t\t\t\t});\n\n\t\t\t\t// The stored history id has aged out of the mailbox history, so walk the message\n\t\t\t\t// list again from the start. Already delivered identifiers stop it duplicating.\n\t\t\t\tstate.historyId = undefined;\n\t\t\t\tstate.syncPageTokens = undefined;\n\t\t\t\tstate.syncCompletedLabelIds = undefined;\n\t\t\t\tstate.initialSyncComplete = false;\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tfor (const record of response.data.history ?? []) {\n\t\t\t\tconst messageIds = (record.messagesAdded ?? [])\n\t\t\t\t\t.filter(added => this.matchesLabels(added.message?.labelIds))\n\t\t\t\t\t.map(added => added.message?.id)\n\t\t\t\t\t.filter((id): id is string => Is.stringValue(id));\n\n\t\t\t\tconst delivered = await this.deliverMessages(\n\t\t\t\t\tgmail,\n\t\t\t\t\tinstanceId,\n\t\t\t\t\tmessageIds,\n\t\t\t\t\tstate,\n\t\t\t\t\tretrievalCallback\n\t\t\t\t);\n\t\t\t\tif (!delivered) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t// Only move the cursor on once every message in the record has been delivered, a\n\t\t\t\t// record which failed part way through is replayed on the next cycle.\n\t\t\t\tstate.historyId = Coerce.string(record.id) ?? state.historyId;\n\t\t\t}\n\n\t\t\tpageToken = Coerce.string(response.data.nextPageToken);\n\t\t\tmailboxHistoryId = Coerce.string(response.data.historyId);\n\t\t\tmorePages = Is.stringValue(pageToken);\n\t\t}\n\n\t\tif (Is.stringValue(mailboxHistoryId)) {\n\t\t\t// Every record was processed, so move to the head of the mailbox history. This also\n\t\t\t// skips the change types the connector does not act on.\n\t\t\tstate.historyId = mailboxHistoryId;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Fetch and deliver each of the given messages in order.\n\t * Messages already recorded in the state are skipped.\n\t * @param gmail The Gmail API client.\n\t * @param instanceId The identifier of the mailbox instance being polled.\n\t * @param messageIds The identifiers of the messages to deliver, in arrival order.\n\t * @param state The connector state for the instance.\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 deliverMessages(\n\t\tgmail: GmailApi.Gmail,\n\t\tinstanceId: string,\n\t\tmessageIds: string[],\n\t\tstate: IGmailEmailConnectorState,\n\t\tretrievalCallback: IEmailProtocolConnectorRetrievalCallback\n\t): Promise<boolean> {\n\t\tfor (const messageId of messageIds) {\n\t\t\tif (!this._retrieving) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tconst alreadyDelivered = state.deliveredMessageIds?.includes(messageId) ?? false;\n\t\t\tif (!alreadyDelivered) {\n\t\t\t\tconst email = await this.fetchEmail(gmail, messageId);\n\n\t\t\t\tif (Is.notEmpty(email)) {\n\t\t\t\t\tawait this._logging?.log({\n\t\t\t\t\t\tlevel: \"info\",\n\t\t\t\t\t\tsource: GmailEmailConnector.CLASS_NAME,\n\t\t\t\t\t\tts: Date.now(),\n\t\t\t\t\t\tmessage: \"emailReceived\",\n\t\t\t\t\t\tdata: { subject: email.subject }\n\t\t\t\t\t});\n\n\t\t\t\t\t// Record the delivery before the callback runs so the identifier is part of the\n\t\t\t\t\t// state the caller persists, then wind it back if the caller reports a failure.\n\t\t\t\t\tconst previousDelivered = state.deliveredMessageIds;\n\t\t\t\t\tthis.recordDelivered(state, messageId);\n\n\t\t\t\t\tconst shouldContinue = await retrievalCallback(instanceId, email, state);\n\t\t\t\t\tif (!shouldContinue) {\n\t\t\t\t\t\tstate.deliveredMessageIds = previousDelivered;\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Fetch a single message and parse it into an email.\n\t * @param gmail The Gmail API client.\n\t * @param messageId The identifier of the message to fetch.\n\t * @returns The parsed email, or undefined if the message carried no content.\n\t * @internal\n\t */\n\tprivate async fetchEmail(gmail: GmailApi.Gmail, messageId: string): Promise<IEmail | undefined> {\n\t\tconst response = await gmail.users.messages.get({\n\t\t\tuserId: this._config.emailAddress,\n\t\t\tid: messageId,\n\t\t\tformat: \"raw\"\n\t\t});\n\n\t\tconst raw = Coerce.string(response.data.raw);\n\t\tif (!Is.stringValue(raw)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn MailHelper.parseEmail(\n\t\t\tConverter.bytesToUtf8(Converter.base64UrlToBytes(raw)),\n\t\t\tresponse.data.labelIds ?? undefined\n\t\t);\n\t}\n\n\t/**\n\t * Whether a message carries any of the configured labels.\n\t * @param labelIds The labels on the message.\n\t * @returns True if the message should be delivered.\n\t * @internal\n\t */\n\tprivate matchesLabels(labelIds: string[] | null | undefined): boolean {\n\t\treturn (labelIds ?? []).some(labelId => this._config.labelIds.includes(labelId));\n\t}\n\n\t/**\n\t * Record a message identifier as delivered, trimming the oldest entries.\n\t * @param state The connector state to update.\n\t * @param messageId The identifier of the delivered message.\n\t * @internal\n\t */\n\tprivate recordDelivered(state: IGmailEmailConnectorState, messageId: string): void {\n\t\t// Replace rather than mutate, the previous array is kept so the record can be wound back.\n\t\tconst delivered = [...(state.deliveredMessageIds ?? []), messageId];\n\t\tstate.deliveredMessageIds = delivered.slice(-this._config.maxDeliveredIdHistory);\n\t}\n}\n"]}
|
package/dist/es/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright 2026 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
export * from "./connectorSchema/gmailEmailConnectorConfigSchema.js";
|
|
4
|
+
export * from "./connectorSchema/gmailEmailConnectorStateSchema.js";
|
|
5
|
+
export * from "./gmailEmailConnector.js";
|
|
6
|
+
export * from "./models/IGmailEmailConnectorConfig.js";
|
|
7
|
+
export * from "./models/IGmailEmailConnectorConstructorOptions.js";
|
|
8
|
+
export * from "./models/IGmailEmailConnectorState.js";
|
|
9
|
+
export * from "./schema.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,sDAAsD,CAAC;AACrE,cAAc,qDAAqD,CAAC;AACpE,cAAc,0BAA0B,CAAC;AACzC,cAAc,wCAAwC,CAAC;AACvD,cAAc,oDAAoD,CAAC;AACnE,cAAc,uCAAuC,CAAC;AACtD,cAAc,aAAa,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./connectorSchema/gmailEmailConnectorConfigSchema.js\";\nexport * from \"./connectorSchema/gmailEmailConnectorStateSchema.js\";\nexport * from \"./gmailEmailConnector.js\";\nexport * from \"./models/IGmailEmailConnectorConfig.js\";\nexport * from \"./models/IGmailEmailConnectorConstructorOptions.js\";\nexport * from \"./models/IGmailEmailConnectorState.js\";\nexport * from \"./schema.js\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IGmailEmailConnectorConfig.js","sourceRoot":"","sources":["../../../src/models/IGmailEmailConnectorConfig.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Configuration for the Gmail email connector.\n */\nexport interface IGmailEmailConnectorConfig {\n\t/**\n\t * The address of the mailbox to monitor, for example \"user@example.com\".\n\t * Used as the Gmail API user identifier, and as the impersonated subject\n\t * when a service account key is supplied.\n\t */\n\temailAddress: string;\n\n\t/**\n\t * The OAuth 2.0 client identifier from the Google Cloud project.\n\t * Required unless serviceAccountKey is supplied.\n\t */\n\tclientId?: string;\n\n\t/**\n\t * The OAuth 2.0 client secret from the Google Cloud project. Stored securely in the vault.\n\t * Required unless serviceAccountKey is supplied.\n\t */\n\tclientSecret?: string;\n\n\t/**\n\t * The JSON service account key used for domain-wide delegation, as a string.\n\t * Stored securely in the vault. When supplied this takes precedence over the\n\t * authorisation code credentials and the mailbox is accessed by impersonating emailAddress.\n\t */\n\tserviceAccountKey?: string;\n\n\t/**\n\t * The identifiers of the labels to monitor for new messages.\n\t * A message carrying any of them is delivered, so the mailbox sync walks one label at a time\n\t * because the Gmail message list requires a message to carry every label it is given.\n\t * @default [\"INBOX\"]\n\t */\n\tlabelIds?: string[];\n\n\t/**\n\t * The maximum number of messages to retrieve during a single poll of the initial sync.\n\t * Later polls continue from where the previous one finished.\n\t * @default 50\n\t */\n\tmaxMessagesPerPoll?: number;\n\n\t/**\n\t * The number of delivered message identifiers retained in the connector state.\n\t * These guard against a message being delivered twice when a poll cycle is replayed.\n\t * @default 200\n\t */\n\tmaxDeliveredIdHistory?: number;\n\n\t/**\n\t * How often to poll for new messages, in minutes.\n\t * @default 2\n\t */\n\tpollingIntervalMinutes?: number;\n\n\t/**\n\t * Maximum time in milliseconds to wait for the poll mutex before skipping the tick.\n\t * When not set, the mutex waits based on the system default.\n\t */\n\tmutexTimeoutMs?: number;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IGmailEmailConnectorConstructorOptions.js","sourceRoot":"","sources":["../../../src/models/IGmailEmailConnectorConstructorOptions.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IGmailEmailConnectorConfig } from \"./IGmailEmailConnectorConfig.js\";\n\n/**\n * Constructor options for the Gmail email connector.\n */\nexport interface IGmailEmailConnectorConstructorOptions {\n\t/**\n\t * The component type for the task scheduler used to manage polling intervals.\n\t * @default \"task-scheduler\"\n\t */\n\ttaskSchedulerComponentType?: string;\n\n\t/**\n\t * The component type for logging.\n\t */\n\tloggingComponentType?: string;\n\n\t/**\n\t * The configuration for the Gmail connector.\n\t */\n\tconfig: IGmailEmailConnectorConfig;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IGmailEmailConnectorState.js","sourceRoot":"","sources":["../../../src/models/IGmailEmailConnectorState.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Runtime state persisted between poll cycles for a Gmail connector.\n */\nexport interface IGmailEmailConnectorState {\n\t/**\n\t * The OAuth 2.0 refresh token the consent flow issued for the mailbox, declared secure in the\n\t * connector state schema so the owning component vaults it rather than persisting it here.\n\t * Unset until consent has been given, while the connector supplies a consent URL instead of\n\t * polling, and never set for a mailbox authenticated by a service account key.\n\t */\n\trefreshToken?: string;\n\n\t/**\n\t * The mailbox history identifier processing has reached.\n\t * Later polls ask the Gmail history API for the changes which follow it.\n\t */\n\thistoryId?: string;\n\n\t/**\n\t * The page tokens for the next page of the initial mailbox sync, keyed by label identifier.\n\t * Only set while initialSyncComplete is false and more pages remain for that label.\n\t */\n\tsyncPageTokens?: { [labelId: string]: string };\n\n\t/**\n\t * The labels whose existing messages have all been walked during the initial sync.\n\t */\n\tsyncCompletedLabelIds?: string[];\n\n\t/**\n\t * Whether the initial sync of the existing mailbox contents has finished.\n\t * Until it has, polling walks the message list rather than the history API.\n\t */\n\tinitialSyncComplete?: boolean;\n\n\t/**\n\t * The identifiers of the most recently delivered messages, oldest first.\n\t * Gmail message identifiers are permanent, so this prevents a message being\n\t * delivered twice when a poll cycle is replayed after a persistence failure.\n\t */\n\tdeliveredMessageIds?: string[];\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Copyright 2026 IOTA Stiftung.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0.
|
|
3
|
+
import { EmailProtocolConnectorConfigSchemaFactory, EmailProtocolConnectorStateSchemaFactory } from "@3sixty/mailbox-models";
|
|
4
|
+
import { GmailEmailConnectorConfigSchema } from "./connectorSchema/gmailEmailConnectorConfigSchema.js";
|
|
5
|
+
import { GmailEmailConnectorStateSchema } from "./connectorSchema/gmailEmailConnectorStateSchema.js";
|
|
6
|
+
import { GmailEmailConnector } from "./gmailEmailConnector.js";
|
|
7
|
+
/**
|
|
8
|
+
* Initialise the connector schemas for the Gmail 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(GmailEmailConnector.NAMESPACE, () => GmailEmailConnectorConfigSchema);
|
|
15
|
+
EmailProtocolConnectorStateSchemaFactory.register(GmailEmailConnector.NAMESPACE, () => GmailEmailConnectorStateSchema);
|
|
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,wBAAwB,CAAC;AAChC,OAAO,EAAE,+BAA+B,EAAE,MAAM,sDAAsD,CAAC;AACvG,OAAO,EAAE,8BAA8B,EAAE,MAAM,qDAAqD,CAAC;AACrG,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,UAAU,UAAU;IACzB,yCAAyC,CAAC,QAAQ,CACjD,mBAAmB,CAAC,SAAS,EAC7B,GAAG,EAAE,CAAC,+BAA+B,CACrC,CAAC;IACF,wCAAwC,CAAC,QAAQ,CAChD,mBAAmB,CAAC,SAAS,EAC7B,GAAG,EAAE,CAAC,8BAA8B,CACpC,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport {\n\tEmailProtocolConnectorConfigSchemaFactory,\n\tEmailProtocolConnectorStateSchemaFactory\n} from \"@3sixty/mailbox-models\";\nimport { GmailEmailConnectorConfigSchema } from \"./connectorSchema/gmailEmailConnectorConfigSchema.js\";\nimport { GmailEmailConnectorStateSchema } from \"./connectorSchema/gmailEmailConnectorStateSchema.js\";\nimport { GmailEmailConnector } from \"./gmailEmailConnector.js\";\n\n/**\n * Initialise the connector schemas for the Gmail 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\tGmailEmailConnector.NAMESPACE,\n\t\t() => GmailEmailConnectorConfigSchema\n\t);\n\tEmailProtocolConnectorStateSchemaFactory.register(\n\t\tGmailEmailConnector.NAMESPACE,\n\t\t() => GmailEmailConnectorStateSchema\n\t);\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { IMailboxConfigField } from "@3sixty/mailbox-models";
|
|
2
|
+
/**
|
|
3
|
+
* The runtime state field schema for Gmail connectors.
|
|
4
|
+
* The refresh token is issued by the consent flow rather than configured, so it travels in the
|
|
5
|
+
* connector state and is marked secure to keep it in the vault.
|
|
6
|
+
*/
|
|
7
|
+
export declare const GmailEmailConnectorStateSchema: IMailboxConfigField[];
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { IEmailProtocolConnector, IEmailProtocolConnectorAuthCallback, IEmailProtocolConnectorAuthState, IEmailProtocolConnectorOptions, IEmailProtocolConnectorRetrievalCallback } from "@3sixty/mailbox-models";
|
|
2
|
+
import type { IGmailEmailConnectorConstructorOptions } from "./models/IGmailEmailConnectorConstructorOptions.js";
|
|
3
|
+
import type { IGmailEmailConnectorState } from "./models/IGmailEmailConnectorState.js";
|
|
4
|
+
/**
|
|
5
|
+
* Gmail email protocol connector. Monitors a mailbox for new messages using the Gmail API.
|
|
6
|
+
*/
|
|
7
|
+
export declare class GmailEmailConnector implements IEmailProtocolConnector<IGmailEmailConnectorState> {
|
|
8
|
+
/**
|
|
9
|
+
* The class name.
|
|
10
|
+
*/
|
|
11
|
+
static readonly CLASS_NAME: string;
|
|
12
|
+
/**
|
|
13
|
+
* The protocol namespace identifier.
|
|
14
|
+
*/
|
|
15
|
+
static readonly NAMESPACE: string;
|
|
16
|
+
/**
|
|
17
|
+
* Create a new instance of GmailEmailConnector.
|
|
18
|
+
* @param options The options for the connector.
|
|
19
|
+
* @throws GeneralError if neither a service account key nor an OAuth client is supplied.
|
|
20
|
+
*/
|
|
21
|
+
constructor(options: IGmailEmailConnectorConstructorOptions);
|
|
22
|
+
/**
|
|
23
|
+
* Get the class name.
|
|
24
|
+
* @returns The class name.
|
|
25
|
+
*/
|
|
26
|
+
className(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Start the internal polling loop for the given instance.
|
|
29
|
+
* @param instanceId The identifier of the mailbox instance being polled.
|
|
30
|
+
* @param state The current connector state for the instance.
|
|
31
|
+
* @param authCallback Callback invoked when authentication fails during a poll cycle.
|
|
32
|
+
* @param retrievalCallback Callback invoked with retrieved messages after each poll cycle.
|
|
33
|
+
* @param options Options supplied by the owning component, carrying the consent callback URI.
|
|
34
|
+
* @returns A promise that resolves when the polling loop has been started.
|
|
35
|
+
*/
|
|
36
|
+
retrieve(instanceId: string, state: IGmailEmailConnectorState, authCallback: IEmailProtocolConnectorAuthCallback, retrievalCallback: IEmailProtocolConnectorRetrievalCallback, options: IEmailProtocolConnectorOptions): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Start the consent flow for a mailbox which has no refresh token yet.
|
|
39
|
+
* @param instanceId The identifier of the mailbox instance being authenticated.
|
|
40
|
+
* @param state The current connector state for the instance.
|
|
41
|
+
* @param options Options supplied by the owning component, carrying the consent callback URI
|
|
42
|
+
* and the state which correlates the consent response back to the mailbox.
|
|
43
|
+
* @returns The auth state carrying the consent URL to open, or undefined when the mailbox can
|
|
44
|
+
* already authenticate itself from a service account key or a refresh token in its state.
|
|
45
|
+
*/
|
|
46
|
+
initiateAuth(instanceId: string, state: IGmailEmailConnectorState, options: IEmailProtocolConnectorOptions): Promise<IEmailProtocolConnectorAuthState | undefined>;
|
|
47
|
+
/**
|
|
48
|
+
* Complete the consent flow with the authorisation code handed to the redirect URI.
|
|
49
|
+
* The issued credentials are applied to this connector instance so the next poll uses them,
|
|
50
|
+
* and the refresh token is placed in the state reported through the auth callback given by
|
|
51
|
+
* retrieve, so the owning component vaults it and lifts the authentication halt.
|
|
52
|
+
* @param instanceId The identifier of the mailbox instance being authenticated.
|
|
53
|
+
* @param authPayload The redirect query, carrying the authorisation code as its code property.
|
|
54
|
+
* @param options Options supplied by the owning component, carrying the consent callback URI.
|
|
55
|
+
* @returns A promise that resolves when the flow has been completed.
|
|
56
|
+
* @throws GeneralError if the authorisation code could not be exchanged for tokens.
|
|
57
|
+
*/
|
|
58
|
+
completeAuth(instanceId: string, authPayload: unknown, options: IEmailProtocolConnectorOptions): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Stop the polling loop for this connector.
|
|
61
|
+
* @returns A promise that resolves when the polling loop has been stopped.
|
|
62
|
+
*/
|
|
63
|
+
retrieveStop(): Promise<void>;
|
|
64
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./connectorSchema/gmailEmailConnectorConfigSchema.js";
|
|
2
|
+
export * from "./connectorSchema/gmailEmailConnectorStateSchema.js";
|
|
3
|
+
export * from "./gmailEmailConnector.js";
|
|
4
|
+
export * from "./models/IGmailEmailConnectorConfig.js";
|
|
5
|
+
export * from "./models/IGmailEmailConnectorConstructorOptions.js";
|
|
6
|
+
export * from "./models/IGmailEmailConnectorState.js";
|
|
7
|
+
export * from "./schema.js";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for the Gmail email connector.
|
|
3
|
+
*/
|
|
4
|
+
export interface IGmailEmailConnectorConfig {
|
|
5
|
+
/**
|
|
6
|
+
* The address of the mailbox to monitor, for example "user@example.com".
|
|
7
|
+
* Used as the Gmail API user identifier, and as the impersonated subject
|
|
8
|
+
* when a service account key is supplied.
|
|
9
|
+
*/
|
|
10
|
+
emailAddress: string;
|
|
11
|
+
/**
|
|
12
|
+
* The OAuth 2.0 client identifier from the Google Cloud project.
|
|
13
|
+
* Required unless serviceAccountKey is supplied.
|
|
14
|
+
*/
|
|
15
|
+
clientId?: string;
|
|
16
|
+
/**
|
|
17
|
+
* The OAuth 2.0 client secret from the Google Cloud project. Stored securely in the vault.
|
|
18
|
+
* Required unless serviceAccountKey is supplied.
|
|
19
|
+
*/
|
|
20
|
+
clientSecret?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The JSON service account key used for domain-wide delegation, as a string.
|
|
23
|
+
* Stored securely in the vault. When supplied this takes precedence over the
|
|
24
|
+
* authorisation code credentials and the mailbox is accessed by impersonating emailAddress.
|
|
25
|
+
*/
|
|
26
|
+
serviceAccountKey?: string;
|
|
27
|
+
/**
|
|
28
|
+
* The identifiers of the labels to monitor for new messages.
|
|
29
|
+
* A message carrying any of them is delivered, so the mailbox sync walks one label at a time
|
|
30
|
+
* because the Gmail message list requires a message to carry every label it is given.
|
|
31
|
+
* @default ["INBOX"]
|
|
32
|
+
*/
|
|
33
|
+
labelIds?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* The maximum number of messages to retrieve during a single poll of the initial sync.
|
|
36
|
+
* Later polls continue from where the previous one finished.
|
|
37
|
+
* @default 50
|
|
38
|
+
*/
|
|
39
|
+
maxMessagesPerPoll?: number;
|
|
40
|
+
/**
|
|
41
|
+
* The number of delivered message identifiers retained in the connector state.
|
|
42
|
+
* These guard against a message being delivered twice when a poll cycle is replayed.
|
|
43
|
+
* @default 200
|
|
44
|
+
*/
|
|
45
|
+
maxDeliveredIdHistory?: number;
|
|
46
|
+
/**
|
|
47
|
+
* How often to poll for new messages, in minutes.
|
|
48
|
+
* @default 2
|
|
49
|
+
*/
|
|
50
|
+
pollingIntervalMinutes?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Maximum time in milliseconds to wait for the poll mutex before skipping the tick.
|
|
53
|
+
* When not set, the mutex waits based on the system default.
|
|
54
|
+
*/
|
|
55
|
+
mutexTimeoutMs?: number;
|
|
56
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { IGmailEmailConnectorConfig } from "./IGmailEmailConnectorConfig.js";
|
|
2
|
+
/**
|
|
3
|
+
* Constructor options for the Gmail email connector.
|
|
4
|
+
*/
|
|
5
|
+
export interface IGmailEmailConnectorConstructorOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The component type for the task scheduler used to manage polling intervals.
|
|
8
|
+
* @default "task-scheduler"
|
|
9
|
+
*/
|
|
10
|
+
taskSchedulerComponentType?: string;
|
|
11
|
+
/**
|
|
12
|
+
* The component type for logging.
|
|
13
|
+
*/
|
|
14
|
+
loggingComponentType?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The configuration for the Gmail connector.
|
|
17
|
+
*/
|
|
18
|
+
config: IGmailEmailConnectorConfig;
|
|
19
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime state persisted between poll cycles for a Gmail connector.
|
|
3
|
+
*/
|
|
4
|
+
export interface IGmailEmailConnectorState {
|
|
5
|
+
/**
|
|
6
|
+
* The OAuth 2.0 refresh token the consent flow issued for the mailbox, declared secure in the
|
|
7
|
+
* connector state schema so the owning component vaults it rather than persisting it here.
|
|
8
|
+
* Unset until consent has been given, while the connector supplies a consent URL instead of
|
|
9
|
+
* polling, and never set for a mailbox authenticated by a service account key.
|
|
10
|
+
*/
|
|
11
|
+
refreshToken?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The mailbox history identifier processing has reached.
|
|
14
|
+
* Later polls ask the Gmail history API for the changes which follow it.
|
|
15
|
+
*/
|
|
16
|
+
historyId?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The page tokens for the next page of the initial mailbox sync, keyed by label identifier.
|
|
19
|
+
* Only set while initialSyncComplete is false and more pages remain for that label.
|
|
20
|
+
*/
|
|
21
|
+
syncPageTokens?: {
|
|
22
|
+
[labelId: string]: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The labels whose existing messages have all been walked during the initial sync.
|
|
26
|
+
*/
|
|
27
|
+
syncCompletedLabelIds?: string[];
|
|
28
|
+
/**
|
|
29
|
+
* Whether the initial sync of the existing mailbox contents has finished.
|
|
30
|
+
* Until it has, polling walks the message list rather than the history API.
|
|
31
|
+
*/
|
|
32
|
+
initialSyncComplete?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* The identifiers of the most recently delivered messages, oldest first.
|
|
35
|
+
* Gmail message identifiers are permanent, so this prevents a message being
|
|
36
|
+
* delivered twice when a poll cycle is replayed after a persistence failure.
|
|
37
|
+
*/
|
|
38
|
+
deliveredMessageIds?: string[];
|
|
39
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialise the connector schemas for the Gmail 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;
|
|
File without changes
|
package/docs/examples.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Mailbox Connector Gmail Examples
|
|
2
|
+
|
|
3
|
+
A connector for email ingestion from a Gmail or Google Workspace mailbox, monitoring the mailbox
|
|
4
|
+
through the Gmail API with OAuth 2.0 credentials.
|
|
5
|
+
|
|
6
|
+
## GmailEmailConnector
|
|
7
|
+
|
|
8
|
+
```typescript
|
|
9
|
+
import { GmailEmailConnector, initSchema } from '@3sixty/mailbox-connector-gmail';
|
|
10
|
+
import {
|
|
11
|
+
EmailProtocolConnectorFactory,
|
|
12
|
+
EmailProtocolConnectorConfigSchemaFactory,
|
|
13
|
+
EmailProtocolConnectorStateSchemaFactory
|
|
14
|
+
} from '@3sixty/mailbox-models';
|
|
15
|
+
|
|
16
|
+
// Register the connector with the shared factory.
|
|
17
|
+
// Only the OAuth client is configured, the refresh token is issued by the consent flow
|
|
18
|
+
EmailProtocolConnectorFactory.register(
|
|
19
|
+
GmailEmailConnector.NAMESPACE,
|
|
20
|
+
() =>
|
|
21
|
+
new GmailEmailConnector({
|
|
22
|
+
config: {
|
|
23
|
+
emailAddress: 'ingest@example.com',
|
|
24
|
+
clientId: '1234567890-example.apps.googleusercontent.com',
|
|
25
|
+
clientSecret: 'client-secret'
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
// The connector registers its config and state schemas when it is constructed. Call initSchema
|
|
31
|
+
// at startup as well, so a mailbox which is stored but not currently polled still has its
|
|
32
|
+
// secure properties recognised
|
|
33
|
+
initSchema();
|
|
34
|
+
|
|
35
|
+
const schema = EmailProtocolConnectorConfigSchemaFactory.get(GmailEmailConnector.NAMESPACE);
|
|
36
|
+
console.log(schema.filter(f => f.isSecure).map(f => f.propertyKey));
|
|
37
|
+
// ["clientSecret", "serviceAccountKey"]
|
|
38
|
+
|
|
39
|
+
const stateSchema = EmailProtocolConnectorStateSchemaFactory.get(GmailEmailConnector.NAMESPACE);
|
|
40
|
+
console.log(stateSchema.map(f => f.propertyKey));
|
|
41
|
+
// ["refreshToken", "historyId", "syncPageTokens", "syncCompletedLabelIds",
|
|
42
|
+
// "initialSyncComplete", "deliveredMessageIds"]
|
|
43
|
+
console.log(stateSchema.filter(f => f.isSecure).map(f => f.propertyKey));
|
|
44
|
+
// ["refreshToken"]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Monitoring a mailbox
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { GmailEmailConnector } from '@3sixty/mailbox-connector-gmail';
|
|
51
|
+
import type { IGmailEmailConnectorState } from '@3sixty/mailbox-connector-gmail';
|
|
52
|
+
import type {
|
|
53
|
+
IEmailProtocolConnectorAuthCallback,
|
|
54
|
+
IEmailProtocolConnectorRetrievalCallback
|
|
55
|
+
} from '@3sixty/mailbox-models';
|
|
56
|
+
|
|
57
|
+
const connector = new GmailEmailConnector({
|
|
58
|
+
config: {
|
|
59
|
+
emailAddress: 'ingest@example.com',
|
|
60
|
+
clientId: '1234567890-example.apps.googleusercontent.com',
|
|
61
|
+
clientSecret: 'client-secret',
|
|
62
|
+
// Any message carrying one of these labels is delivered
|
|
63
|
+
labelIds: ['INBOX', 'Label_12'],
|
|
64
|
+
maxMessagesPerPoll: 50,
|
|
65
|
+
pollingIntervalMinutes: 2
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// State persisted between poll cycles; tracks the mailbox history position and carries the
|
|
70
|
+
// refresh token the consent flow issued
|
|
71
|
+
const state: IGmailEmailConnectorState = {};
|
|
72
|
+
|
|
73
|
+
const authCallback: IEmailProtocolConnectorAuthCallback = async (
|
|
74
|
+
mailboxId,
|
|
75
|
+
updatedState,
|
|
76
|
+
requiresAuth,
|
|
77
|
+
authState,
|
|
78
|
+
authError
|
|
79
|
+
) => {
|
|
80
|
+
if (requiresAuth) {
|
|
81
|
+
// Raised both before the first consent and after a refresh token is revoked
|
|
82
|
+
console.log(`Mailbox ${mailboxId} is awaiting consent`);
|
|
83
|
+
console.log(authState?.authUrl); // send the operator here to issue a refresh token
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const retrievalCallback: IEmailProtocolConnectorRetrievalCallback = async (
|
|
88
|
+
mailboxId,
|
|
89
|
+
message,
|
|
90
|
+
updatedState,
|
|
91
|
+
retrievalError
|
|
92
|
+
) => {
|
|
93
|
+
if (retrievalError !== undefined) {
|
|
94
|
+
console.log(retrievalError.message); // description of the retrieval problem
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (message !== undefined) {
|
|
98
|
+
console.log(message.subject); // e.g. "Project update"
|
|
99
|
+
console.log(message.flags); // Gmail label ids, e.g. ["INBOX", "UNREAD"]
|
|
100
|
+
console.log(message.htmlContent !== undefined); // true when an HTML part is present
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Persist updatedState so the next cycle resumes from the same mailbox history position,
|
|
104
|
+
// then return false if the message could not be stored so it is delivered again
|
|
105
|
+
return true;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Start the polling loop; the scheduler fires it every pollingIntervalMinutes.
|
|
109
|
+
// The callback URI is fixed for the deployment and owned by the mailbox service, which also
|
|
110
|
+
// supplies the correlation state carrying the partition and the mailbox through the flow
|
|
111
|
+
await connector.retrieve('mailbox-abc', state, authCallback, retrievalCallback, {
|
|
112
|
+
callbackUri: 'https://app.example.com/mailbox/authcallback',
|
|
113
|
+
correlationState: 'tenant-a/mailbox-abc'
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Stop polling when the mailbox is removed or disabled
|
|
117
|
+
await connector.retrieveStop();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Starting the consent flow
|
|
121
|
+
|
|
122
|
+
A mailbox which has no refresh token yet can be handed its consent URL straight away, without
|
|
123
|
+
waiting for the first poll to report it. `MailboxService.createMailbox` calls this when a mailbox is
|
|
124
|
+
created and returns the auth state to the caller, so this is only needed when driving a connector
|
|
125
|
+
directly.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const authState = await connector.initiateAuth('mailbox-abc', state, {
|
|
129
|
+
callbackUri: 'https://app.example.com/mailbox/authcallback',
|
|
130
|
+
correlationState: 'tenant-a/mailbox-abc'
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Undefined when a service account key is configured, or the state already holds a token
|
|
134
|
+
console.log(authState?.authUrl); // send the operator here to issue a refresh token
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Completing the consent flow
|
|
138
|
+
|
|
139
|
+
The redirect handler exchanges the authorisation code and reports the issued token through the
|
|
140
|
+
auth callback the connector was given by `retrieve`, in the state it hands back. Going through
|
|
141
|
+
`MailboxService.completeAuth` does the correlating, persisting, vaulting and connector restart, so
|
|
142
|
+
this is only needed when driving a connector directly.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// The redirect arrives at the fixed callback URI as ?code=...&state=<correlationState>
|
|
146
|
+
await connector.completeAuth(
|
|
147
|
+
mailboxId,
|
|
148
|
+
{ code: authorizationCode },
|
|
149
|
+
{
|
|
150
|
+
callbackUri: 'https://app.example.com/mailbox/authcallback',
|
|
151
|
+
correlationState: 'tenant-a/mailbox-abc'
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// The auth callback is invoked with requiresAuth false and a state carrying the issued
|
|
156
|
+
// refreshToken, which the state schema marks as secure so it belongs in the vault rather than
|
|
157
|
+
// in a log or alongside the rest of the persisted state
|
|
158
|
+
console.log(state.refreshToken !== undefined); // true
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Using a service account with domain-wide delegation
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { GmailEmailConnector } from '@3sixty/mailbox-connector-gmail';
|
|
165
|
+
|
|
166
|
+
const connector = new GmailEmailConnector({
|
|
167
|
+
config: {
|
|
168
|
+
emailAddress: 'ingest@example.com',
|
|
169
|
+
serviceAccountKey: process.env.GOOGLE_SERVICE_ACCOUNT_KEY
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
console.log(connector.className()); // "GmailEmailConnector"
|
|
174
|
+
```
|