@unireq/imap 0.0.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Olivier Orabona
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ var c=class{options;client=null;capabilities={imap:true,xoauth2:true,idle:true,append:true,search:true,move:true,flags:true,expunge:true};constructor(i={}){this.options=i;}async connect(i){let e=new URL(i),{ImapFlow:s}=await import('imapflow'),l=e.protocol==="imaps:",t=e.port?parseInt(e.port,10):l?993:143,o={user:decodeURIComponent(e.username)||"anonymous",pass:decodeURIComponent(e.password)||""},a={host:e.hostname,port:t,secure:l,auth:o,logger:this.options.debug?console:false,tls:this.options.tls},n=new s(a);return this.client=n,await n.connect(),{connected:true,host:e.hostname,user:o.user,usable:n.usable??true,secure:l}}async request(i,e){if(!this.client||!i.connected)throw new Error("IMAP session not connected");let s=this.client,l=e.operation;try{let t;switch(l){case "fetch":case "select":{let o=e.mailbox,a=e.range||"1:*",n=await s.getMailboxLock(o);try{let r=[];for await(let g of s.fetch(a,{envelope:!0,flags:!0,size:!0}))r.push({seq:g.seq,uid:g.uid,envelope:g.envelope,flags:g.flags,size:g.size});t=r;}finally{n.release();}break}case "append":{let o=e.mailbox,a=typeof e.body=="string"?e.body:JSON.stringify(e.body),n=e.flags;t=await s.append(o,a,n);break}case "idle":{await s.idle(),t={status:"idle"};break}case "search":{let o=e.mailbox,a=e.criteria,n=e.useUid??!0,r=await s.getMailboxLock(o);try{t=await s.search(a,{uid:n});}finally{r.release();}break}case "move":{let o=e.mailbox,a=e.destination,n=e.range||"1:*",r=await s.getMailboxLock(o);try{await s.messageMove(n,a),t={moved:!0,destination:a};}finally{r.release();}break}case "addFlags":case "setFlags":{let o=e.mailbox,a=e.range||"1:*",n=e.flags,r=await s.getMailboxLock(o);try{await s.messageFlagsAdd(a,n),t={flagsAdded:n};}finally{r.release();}break}case "removeFlags":{let o=e.mailbox,a=e.range||"1:*",n=e.flags,r=await s.getMailboxLock(o);try{await s.messageFlagsRemove(a,n),t={flagsRemoved:n};}finally{r.release();}break}case "replaceFlags":{let o=e.mailbox,a=e.range||"1:*",n=e.flags,r=await s.getMailboxLock(o);try{await s.messageFlagsSet(a,n),t={flagsSet:n};}finally{r.release();}break}case "expunge":{let o=e.mailbox,a=e.range,n=await s.getMailboxLock(o);try{await s.expunge(a),t={expunged:!0};}finally{n.release();}break}default:throw new Error(`Unsupported IMAP operation: ${l}`)}return {status:200,statusText:"OK",headers:{},data:t,ok:!0}}catch(t){return {status:500,statusText:"Error",headers:{},data:{error:t instanceof Error?t.message:String(t)},ok:false}}}async disconnect(i){this.client&&i.connected&&(await this.client.logout(),this.client=null);}};function m(u){return new c(u)}export{c as a,m as b};//# sourceMappingURL=chunk-2FERZ343.js.map
2
+ //# sourceMappingURL=chunk-2FERZ343.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/connectors/imapflow.ts"],"names":["ImapFlowConnector","options","uri","url","ImapFlow","secure","port","auth","config","client","session","ctx","operation","data","mailbox","range","lock","messages","msg","content","flags","criteria","useUid","destination","error","createDefaultImapConnector"],"mappings":"AAoCO,IAAMA,CAAAA,CAAN,KAAiD,CACrC,OAAA,CACT,OAAgC,IAAA,CAE/B,YAAA,CAAiC,CACxC,IAAA,CAAM,IAAA,CACN,OAAA,CAAS,IAAA,CACT,IAAA,CAAM,KACN,MAAA,CAAQ,IAAA,CACR,MAAA,CAAQ,IAAA,CACR,IAAA,CAAM,IAAA,CACN,KAAA,CAAO,IAAA,CACP,QAAS,IACX,CAAA,CAEA,WAAA,CAAYC,CAAAA,CAAgC,EAAC,CAAG,CAC9C,IAAA,CAAK,OAAA,CAAUA,EACjB,CAEA,MAAM,OAAA,CAAQC,CAAAA,CAAmC,CAC/C,IAAMC,CAAAA,CAAM,IAAI,IAAID,CAAG,CAAA,CAGjB,CAAE,QAAA,CAAAE,CAAS,CAAA,CAAI,MAAM,OAAO,UAAU,EAEtCC,CAAAA,CAASF,CAAAA,CAAI,QAAA,GAAa,QAAA,CAC1BG,CAAAA,CAAOH,CAAAA,CAAI,IAAA,CAAO,QAAA,CAASA,EAAI,IAAA,CAAM,EAAE,CAAA,CAAIE,CAAAA,CAAS,IAAM,GAAA,CAG1DE,CAAAA,CAAqB,CACzB,IAAA,CAAM,mBAAmBJ,CAAAA,CAAI,QAAQ,CAAA,EAAK,WAAA,CAC1C,IAAA,CAAM,kBAAA,CAAmBA,CAAAA,CAAI,QAAQ,GAAK,EAC5C,CAAA,CAGMK,CAAAA,CAAyB,CAC7B,KAAML,CAAAA,CAAI,QAAA,CACV,IAAA,CAAAG,CAAAA,CACA,OAAAD,CAAAA,CACA,IAAA,CAAAE,CAAAA,CACA,MAAA,CAAQ,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAQ,OAAA,CAAU,MACvC,GAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,GACpB,EAGME,CAAAA,CAAS,IAAKL,CAAAA,CAA4CI,CAAM,EAEtE,OAAA,IAAA,CAAK,MAAA,CAASC,CAAAA,CAEd,MAAMA,CAAAA,CAAO,OAAA,EAAQ,CAEd,CACL,UAAW,IAAA,CACX,IAAA,CAAMN,CAAAA,CAAI,QAAA,CACV,KAAMI,CAAAA,CAAK,IAAA,CACX,MAAA,CAAQE,CAAAA,CAAO,QAAU,IAAA,CACzB,MAAA,CAAAJ,CACF,CACF,CAEA,MAAM,OAAA,CAAQK,CAAAA,CAAsBC,EAAwC,CAC1E,GAAI,CAAC,IAAA,CAAK,QAAU,CAACD,CAAAA,CAAQ,SAAA,CAC3B,MAAM,IAAI,KAAA,CAAM,4BAA4B,CAAA,CAG9C,IAAMD,CAAAA,CAAS,IAAA,CAAK,MAAA,CACdG,CAAAA,CAAaD,EAAgC,SAAA,CAEnD,GAAI,CACF,IAAIE,CAAAA,CAEJ,OAAQD,CAAAA,EACN,KAAK,OAAA,CACL,KAAK,QAAA,CAAU,CACb,IAAME,CAAAA,CAAWH,CAAAA,CAAgC,OAAA,CAC3CI,EAAUJ,CAAAA,CAAgC,KAAA,EAAuB,KAAA,CACjEK,CAAAA,CAAO,MAAMP,CAAAA,CAAO,cAAA,CAAeK,CAAO,CAAA,CAChD,GAAI,CACF,IAAMG,CAAAA,CAA0B,EAAC,CACjC,UAAA,IAAiBC,CAAAA,IAAOT,CAAAA,CAAO,MAAMM,CAAAA,CAAO,CAAE,QAAA,CAAU,CAAA,CAAA,CAAM,MAAO,CAAA,CAAA,CAAM,IAAA,CAAM,CAAA,CAAK,CAAC,EACrFE,CAAAA,CAAS,IAAA,CAAK,CACZ,GAAA,CAAKC,CAAAA,CAAI,GAAA,CACT,GAAA,CAAKA,CAAAA,CAAI,IACT,QAAA,CAAUA,CAAAA,CAAI,QAAA,CACd,KAAA,CAAOA,EAAI,KAAA,CACX,IAAA,CAAMA,CAAAA,CAAI,IACZ,CAAC,CAAA,CAEHL,CAAAA,CAAOI,EACT,CAAA,OAAE,CACAD,CAAAA,CAAK,OAAA,GACP,CACA,KACF,CAEA,KAAK,QAAA,CAAU,CACb,IAAMF,CAAAA,CAAWH,CAAAA,CAAgC,OAAA,CAC3CQ,EAAU,OAAOR,CAAAA,CAAI,IAAA,EAAS,QAAA,CAAWA,CAAAA,CAAI,IAAA,CAAO,IAAA,CAAK,SAAA,CAAUA,EAAI,IAAI,CAAA,CAC3ES,CAAAA,CAAST,CAAAA,CAAgC,MAC/CE,CAAAA,CAAO,MAAMJ,CAAAA,CAAO,MAAA,CAAOK,EAASK,CAAAA,CAASC,CAAK,CAAA,CAClD,KACF,CAEA,KAAK,MAAA,CAAQ,CACX,MAAMX,CAAAA,CAAO,IAAA,EAAK,CAClBI,CAAAA,CAAO,CAAE,MAAA,CAAQ,MAAO,CAAA,CACxB,KACF,CAEA,KAAK,QAAA,CAAU,CACb,IAAMC,CAAAA,CAAWH,CAAAA,CAAgC,OAAA,CAC3CU,CAAAA,CAAYV,EAAgC,QAAA,CAC5CW,CAAAA,CAAWX,CAAAA,CAAgC,MAAA,EAAyB,GACpEK,CAAAA,CAAO,MAAMP,CAAAA,CAAO,cAAA,CAAeK,CAAO,CAAA,CAChD,GAAI,CACFD,CAAAA,CAAO,MAAMJ,CAAAA,CAAO,MAAA,CAAOY,CAAAA,CAAU,CAAE,GAAA,CAAKC,CAAO,CAAC,EACtD,CAAA,OAAE,CACAN,CAAAA,CAAK,OAAA,GACP,CACA,KACF,CAEA,KAAK,MAAA,CAAQ,CACX,IAAMF,CAAAA,CAAWH,EAAgC,OAAA,CAC3CY,CAAAA,CAAeZ,CAAAA,CAAgC,WAAA,CAC/CI,EAAUJ,CAAAA,CAAgC,KAAA,EAAkC,KAAA,CAC5EK,CAAAA,CAAO,MAAMP,CAAAA,CAAO,cAAA,CAAeK,CAAO,CAAA,CAChD,GAAI,CACF,MAAML,CAAAA,CAAO,YAAYM,CAAAA,CAAOQ,CAAW,CAAA,CAC3CV,CAAAA,CAAO,CAAE,KAAA,CAAO,CAAA,CAAA,CAAM,WAAA,CAAAU,CAAY,EACpC,CAAA,OAAE,CACAP,CAAAA,CAAK,OAAA,GACP,CACA,KACF,CAEA,KAAK,UAAA,CACL,KAAK,UAAA,CAAY,CACf,IAAMF,CAAAA,CAAWH,CAAAA,CAAgC,OAAA,CAC3CI,CAAAA,CAAUJ,EAAgC,KAAA,EAAkC,KAAA,CAC5ES,CAAAA,CAAST,CAAAA,CAAgC,KAAA,CACzCK,CAAAA,CAAO,MAAMP,CAAAA,CAAO,eAAeK,CAAO,CAAA,CAChD,GAAI,CACF,MAAML,CAAAA,CAAO,eAAA,CAAgBM,CAAAA,CAAOK,CAAK,EACzCP,CAAAA,CAAO,CAAE,UAAA,CAAYO,CAAM,EAC7B,CAAA,OAAE,CACAJ,CAAAA,CAAK,UACP,CACA,KACF,CAEA,KAAK,aAAA,CAAe,CAClB,IAAMF,CAAAA,CAAWH,EAAgC,OAAA,CAC3CI,CAAAA,CAAUJ,CAAAA,CAAgC,KAAA,EAAkC,KAAA,CAC5ES,CAAAA,CAAST,CAAAA,CAAgC,KAAA,CACzCK,EAAO,MAAMP,CAAAA,CAAO,cAAA,CAAeK,CAAO,EAChD,GAAI,CACF,MAAML,CAAAA,CAAO,mBAAmBM,CAAAA,CAAOK,CAAK,CAAA,CAC5CP,CAAAA,CAAO,CAAE,YAAA,CAAcO,CAAM,EAC/B,QAAE,CACAJ,CAAAA,CAAK,OAAA,GACP,CACA,KACF,CAEA,KAAK,cAAA,CAAgB,CACnB,IAAMF,CAAAA,CAAWH,CAAAA,CAAgC,OAAA,CAC3CI,CAAAA,CAAUJ,CAAAA,CAAgC,KAAA,EAAkC,KAAA,CAC5ES,EAAST,CAAAA,CAAgC,KAAA,CACzCK,CAAAA,CAAO,MAAMP,CAAAA,CAAO,cAAA,CAAeK,CAAO,CAAA,CAChD,GAAI,CACF,MAAML,CAAAA,CAAO,eAAA,CAAgBM,CAAAA,CAAOK,CAAK,CAAA,CACzCP,CAAAA,CAAO,CAAE,QAAA,CAAUO,CAAM,EAC3B,CAAA,OAAE,CACAJ,CAAAA,CAAK,OAAA,GACP,CACA,KACF,CAEA,KAAK,SAAA,CAAW,CACd,IAAMF,CAAAA,CAAWH,CAAAA,CAAgC,OAAA,CAC3CI,EAASJ,CAAAA,CAAgC,KAAA,CACzCK,CAAAA,CAAO,MAAMP,EAAO,cAAA,CAAeK,CAAO,CAAA,CAChD,GAAI,CACF,MAAML,CAAAA,CAAO,OAAA,CAAQM,CAAK,CAAA,CAC1BF,CAAAA,CAAO,CAAE,QAAA,CAAU,EAAK,EAC1B,CAAA,OAAE,CACAG,CAAAA,CAAK,UACP,CACA,KACF,CAEA,QACE,MAAM,IAAI,KAAA,CAAM,CAAA,4BAAA,EAA+BJ,CAAS,CAAA,CAAE,CAC9D,CAEA,OAAO,CACL,MAAA,CAAQ,GAAA,CACR,UAAA,CAAY,KACZ,OAAA,CAAS,EAAC,CACV,IAAA,CAAAC,EACA,EAAA,CAAI,CAAA,CACN,CACF,CAAA,MAASW,CAAAA,CAAO,CACd,OAAO,CACL,OAAQ,GAAA,CACR,UAAA,CAAY,OAAA,CACZ,OAAA,CAAS,EAAC,CACV,IAAA,CAAM,CAAE,KAAA,CAAOA,aAAiB,KAAA,CAAQA,CAAAA,CAAM,OAAA,CAAU,MAAA,CAAOA,CAAK,CAAE,CAAA,CACtE,EAAA,CAAI,KACN,CACF,CACF,CAEA,MAAM,WAAWd,CAAAA,CAAqC,CAChD,IAAA,CAAK,MAAA,EAAUA,EAAQ,SAAA,GACzB,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA,EAAO,CACzB,IAAA,CAAK,MAAA,CAAS,MAElB,CACF,EA8DO,SAASe,CAAAA,CAA2BxB,EAA+C,CACxF,OAAO,IAAID,CAAAA,CAAkBC,CAAO,CACtC","file":"chunk-2FERZ343.js","sourcesContent":["/**\n * ImapFlowConnector - Default IMAP connector using imapflow library\n *\n * This connector implements the IMAPConnector interface using the imapflow library.\n * It is the default connector used when no custom connector is provided.\n *\n * @requires imapflow - Install with: npm install imapflow\n */\n\nimport type { RequestContext, Response } from '@unireq/core';\nimport type {\n IMAPCapabilities,\n IMAPConnector,\n IMAPConnectorOptions,\n IMAPMessage,\n IMAPOperation,\n IMAPSession,\n SearchCriteria,\n} from '../connector.js';\n\n/**\n * ImapFlowConnector using imapflow library\n *\n * @example\n * ```ts\n * import { imap } from '@unireq/imap';\n * import { ImapFlowConnector } from '@unireq/imap/connectors/imapflow';\n *\n * // With default options\n * const { transport } = imap('imap://mail.server.com');\n *\n * // With custom options\n * const connector = new ImapFlowConnector({ timeout: 30000 });\n * const { transport } = imap('imap://mail.server.com', connector);\n * ```\n */\nexport class ImapFlowConnector implements IMAPConnector {\n private readonly options: IMAPConnectorOptions;\n private client: ImapFlowClient | null = null;\n\n readonly capabilities: IMAPCapabilities = {\n imap: true,\n xoauth2: true,\n idle: true,\n append: true,\n search: true,\n move: true,\n flags: true,\n expunge: true,\n };\n\n constructor(options: IMAPConnectorOptions = {}) {\n this.options = options;\n }\n\n async connect(uri: string): Promise<IMAPSession> {\n const url = new URL(uri);\n\n // Dynamically import imapflow\n const { ImapFlow } = await import('imapflow');\n\n const secure = url.protocol === 'imaps:';\n const port = url.port ? parseInt(url.port, 10) : secure ? 993 : 143;\n\n // Build auth config\n const auth: ImapFlowAuth = {\n user: decodeURIComponent(url.username) || 'anonymous',\n pass: decodeURIComponent(url.password) || '',\n };\n\n // Build config object\n const config: ImapFlowConfig = {\n host: url.hostname,\n port,\n secure,\n auth,\n logger: this.options.debug ? console : false,\n tls: this.options.tls,\n };\n\n // Cast to our minimal interface to avoid type conflicts\n const client = new (ImapFlow as unknown as ImapFlowConstructor)(config) as ImapFlowClient;\n\n this.client = client;\n\n await client.connect();\n\n return {\n connected: true,\n host: url.hostname,\n user: auth.user,\n usable: client.usable ?? true,\n secure,\n };\n }\n\n async request(session: IMAPSession, ctx: RequestContext): Promise<Response> {\n if (!this.client || !session.connected) {\n throw new Error('IMAP session not connected');\n }\n\n const client = this.client;\n const operation = (ctx as Record<string, unknown>)['operation'] as IMAPOperation | undefined;\n\n try {\n let data: unknown;\n\n switch (operation) {\n case 'fetch':\n case 'select': {\n const mailbox = (ctx as Record<string, unknown>)['mailbox'] as string;\n const range = ((ctx as Record<string, unknown>)['range'] as string) || '1:*';\n const lock = await client.getMailboxLock(mailbox);\n try {\n const messages: IMAPMessage[] = [];\n for await (const msg of client.fetch(range, { envelope: true, flags: true, size: true })) {\n messages.push({\n seq: msg.seq,\n uid: msg.uid,\n envelope: msg.envelope as IMAPMessage['envelope'],\n flags: msg.flags,\n size: msg.size,\n });\n }\n data = messages;\n } finally {\n lock.release();\n }\n break;\n }\n\n case 'append': {\n const mailbox = (ctx as Record<string, unknown>)['mailbox'] as string;\n const content = typeof ctx.body === 'string' ? ctx.body : JSON.stringify(ctx.body);\n const flags = (ctx as Record<string, unknown>)['flags'] as string[] | undefined;\n data = await client.append(mailbox, content, flags);\n break;\n }\n\n case 'idle': {\n await client.idle();\n data = { status: 'idle' };\n break;\n }\n\n case 'search': {\n const mailbox = (ctx as Record<string, unknown>)['mailbox'] as string;\n const criteria = (ctx as Record<string, unknown>)['criteria'] as SearchCriteria;\n const useUid = ((ctx as Record<string, unknown>)['useUid'] as boolean) ?? true;\n const lock = await client.getMailboxLock(mailbox);\n try {\n data = await client.search(criteria, { uid: useUid });\n } finally {\n lock.release();\n }\n break;\n }\n\n case 'move': {\n const mailbox = (ctx as Record<string, unknown>)['mailbox'] as string;\n const destination = (ctx as Record<string, unknown>)['destination'] as string;\n const range = ((ctx as Record<string, unknown>)['range'] as string | number[]) || '1:*';\n const lock = await client.getMailboxLock(mailbox);\n try {\n await client.messageMove(range, destination);\n data = { moved: true, destination };\n } finally {\n lock.release();\n }\n break;\n }\n\n case 'addFlags':\n case 'setFlags': {\n const mailbox = (ctx as Record<string, unknown>)['mailbox'] as string;\n const range = ((ctx as Record<string, unknown>)['range'] as string | number[]) || '1:*';\n const flags = (ctx as Record<string, unknown>)['flags'] as string[];\n const lock = await client.getMailboxLock(mailbox);\n try {\n await client.messageFlagsAdd(range, flags);\n data = { flagsAdded: flags };\n } finally {\n lock.release();\n }\n break;\n }\n\n case 'removeFlags': {\n const mailbox = (ctx as Record<string, unknown>)['mailbox'] as string;\n const range = ((ctx as Record<string, unknown>)['range'] as string | number[]) || '1:*';\n const flags = (ctx as Record<string, unknown>)['flags'] as string[];\n const lock = await client.getMailboxLock(mailbox);\n try {\n await client.messageFlagsRemove(range, flags);\n data = { flagsRemoved: flags };\n } finally {\n lock.release();\n }\n break;\n }\n\n case 'replaceFlags': {\n const mailbox = (ctx as Record<string, unknown>)['mailbox'] as string;\n const range = ((ctx as Record<string, unknown>)['range'] as string | number[]) || '1:*';\n const flags = (ctx as Record<string, unknown>)['flags'] as string[];\n const lock = await client.getMailboxLock(mailbox);\n try {\n await client.messageFlagsSet(range, flags);\n data = { flagsSet: flags };\n } finally {\n lock.release();\n }\n break;\n }\n\n case 'expunge': {\n const mailbox = (ctx as Record<string, unknown>)['mailbox'] as string;\n const range = (ctx as Record<string, unknown>)['range'] as string | number[] | undefined;\n const lock = await client.getMailboxLock(mailbox);\n try {\n await client.expunge(range);\n data = { expunged: true };\n } finally {\n lock.release();\n }\n break;\n }\n\n default:\n throw new Error(`Unsupported IMAP operation: ${operation}`);\n }\n\n return {\n status: 200,\n statusText: 'OK',\n headers: {},\n data,\n ok: true,\n };\n } catch (error) {\n return {\n status: 500,\n statusText: 'Error',\n headers: {},\n data: { error: error instanceof Error ? error.message : String(error) },\n ok: false,\n };\n }\n }\n\n async disconnect(session: IMAPSession): Promise<void> {\n if (this.client && session.connected) {\n await this.client.logout();\n this.client = null;\n }\n }\n}\n\n/**\n * imapflow Client interface (minimal subset used by connector)\n * This allows the connector to work without requiring imapflow types at compile time\n */\ninterface ImapFlowClient {\n usable?: boolean;\n connect(): Promise<void>;\n logout(): Promise<void>;\n getMailboxLock(path: string): Promise<{ release: () => void }>;\n fetch(\n range: string,\n options: { envelope?: boolean; flags?: boolean; size?: boolean },\n ): AsyncIterableIterator<ImapFlowMessage>;\n append(path: string, content: string | Buffer, flags?: string[]): Promise<unknown>;\n idle(): Promise<void>;\n search(criteria: SearchCriteria, options?: { uid?: boolean }): Promise<number[]>;\n messageMove(range: string | number[], destination: string): Promise<void>;\n messageFlagsAdd(range: string | number[], flags: string[]): Promise<void>;\n messageFlagsRemove(range: string | number[], flags: string[]): Promise<void>;\n messageFlagsSet(range: string | number[], flags: string[]): Promise<void>;\n expunge(range?: string | number[]): Promise<void>;\n}\n\ninterface ImapFlowMessage {\n seq: number;\n uid: number;\n envelope?: unknown;\n flags?: Set<string>;\n size?: number;\n}\n\ninterface ImapFlowAuth {\n user: string;\n pass?: string;\n accessToken?: string;\n}\n\n/**\n * ImapFlow configuration (minimal subset)\n */\ninterface ImapFlowConfig {\n host: string;\n port: number;\n secure: boolean;\n auth: ImapFlowAuth;\n logger?: unknown;\n tls?: unknown;\n}\n\n/**\n * ImapFlow constructor type\n */\ninterface ImapFlowConstructor {\n new (config: ImapFlowConfig): ImapFlowClient;\n}\n\n/**\n * Creates a default ImapFlowConnector instance\n * Used internally by the imap() transport function\n */\nexport function createDefaultImapConnector(options?: IMAPConnectorOptions): IMAPConnector {\n return new ImapFlowConnector(options);\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export{a as ImapFlowConnector,b as createDefaultImapConnector}from'./chunk-2FERZ343.js';//# sourceMappingURL=imapflow-RVSIPLH2.js.map
2
+ //# sourceMappingURL=imapflow-RVSIPLH2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"imapflow-RVSIPLH2.js"}
@@ -0,0 +1,474 @@
1
+ import { Connector, RequestContext, Response, TransportWithCapabilities, Policy } from '@unireq/core';
2
+ export { Connector, RequestContext, Response, TransportWithCapabilities, policy } from '@unireq/core';
3
+
4
+ /**
5
+ * IMAP Connector interface for BYOC (Bring Your Own Connector) pattern
6
+ *
7
+ * This interface extends the core Connector with IMAP-specific capabilities.
8
+ * Implement this interface to create custom IMAP connectors.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { imap, type IMAPConnector } from '@unireq/imap';
13
+ *
14
+ * class MyImapConnector implements IMAPConnector {
15
+ * // ... implementation
16
+ * }
17
+ *
18
+ * const { transport } = imap('imap://mail.server.com', new MyImapConnector());
19
+ * ```
20
+ */
21
+
22
+ /**
23
+ * IMAP session representing an active connection
24
+ */
25
+ interface IMAPSession {
26
+ /** Whether the session is currently connected */
27
+ readonly connected: boolean;
28
+ /** IMAP server hostname */
29
+ readonly host: string;
30
+ /** Authenticated username */
31
+ readonly user: string;
32
+ /** Whether the connection is usable for operations */
33
+ readonly usable: boolean;
34
+ /** Whether the connection uses TLS */
35
+ readonly secure: boolean;
36
+ }
37
+ /**
38
+ * IMAP connector capabilities
39
+ */
40
+ interface IMAPCapabilities {
41
+ /** Supports IMAP protocol */
42
+ readonly imap: boolean;
43
+ /** Supports XOAUTH2 authentication */
44
+ readonly xoauth2: boolean;
45
+ /** Supports IDLE command for push notifications */
46
+ readonly idle: boolean;
47
+ /** Supports APPEND command */
48
+ readonly append: boolean;
49
+ /** Supports SEARCH command */
50
+ readonly search: boolean;
51
+ /** Supports MOVE command */
52
+ readonly move: boolean;
53
+ /** Supports flag operations */
54
+ readonly flags: boolean;
55
+ /** Supports EXPUNGE command */
56
+ readonly expunge: boolean;
57
+ /** Index signature for TransportCapabilities compatibility */
58
+ readonly [key: string]: boolean | undefined;
59
+ }
60
+ /**
61
+ * IMAP connector options for connection configuration
62
+ */
63
+ interface IMAPConnectorOptions {
64
+ /** Connection timeout in milliseconds */
65
+ readonly timeout?: number;
66
+ /** TLS options for secure connections */
67
+ readonly tls?: {
68
+ /** Minimum TLS version */
69
+ readonly minVersion?: string;
70
+ /** Reject unauthorized certificates */
71
+ readonly rejectUnauthorized?: boolean;
72
+ };
73
+ /** Enable debug logging */
74
+ readonly debug?: boolean;
75
+ }
76
+ /**
77
+ * IMAP operation types extracted from request context
78
+ */
79
+ type IMAPOperation = 'fetch' | 'select' | 'append' | 'idle' | 'search' | 'move' | 'addFlags' | 'removeFlags' | 'setFlags' | 'replaceFlags' | 'expunge';
80
+ /**
81
+ * Extended request context for IMAP operations
82
+ */
83
+ interface IMAPRequestContext extends RequestContext {
84
+ /** IMAP operation to perform */
85
+ readonly operation: IMAPOperation;
86
+ /** Target mailbox for operations */
87
+ readonly mailbox?: string;
88
+ /** Message range for operations (e.g., '1:*', '1,2,3') */
89
+ readonly range?: string | number[];
90
+ /** Search criteria for search operation */
91
+ readonly criteria?: SearchCriteria;
92
+ /** Destination mailbox for move operation */
93
+ readonly destination?: string;
94
+ /** Flags for flag operations */
95
+ readonly flags?: string[];
96
+ /** Whether to use UID instead of sequence numbers */
97
+ readonly useUid?: boolean;
98
+ /** XOAUTH2 token for authentication */
99
+ readonly xoauth2Token?: string;
100
+ }
101
+ /**
102
+ * IMAP search criteria
103
+ * @see https://www.rfc-editor.org/rfc/rfc3501#section-6.4.4
104
+ */
105
+ interface SearchCriteria {
106
+ /** Messages that have the \Seen flag */
107
+ seen?: boolean;
108
+ /** Messages that have the \Answered flag */
109
+ answered?: boolean;
110
+ /** Messages that have the \Deleted flag */
111
+ deleted?: boolean;
112
+ /** Messages that have the \Flagged flag */
113
+ flagged?: boolean;
114
+ /** Messages that have the \Draft flag */
115
+ draft?: boolean;
116
+ /** Messages that contain the specified string in the header or body */
117
+ keyword?: string;
118
+ /** Messages that do not contain the specified string */
119
+ unkeyword?: string;
120
+ /** Messages from the specified sender */
121
+ from?: string;
122
+ /** Messages to the specified recipient */
123
+ to?: string;
124
+ /** Messages with the specified subject */
125
+ subject?: string;
126
+ /** Messages sent before the specified date */
127
+ before?: Date;
128
+ /** Messages sent on the specified date */
129
+ on?: Date;
130
+ /** Messages sent after the specified date */
131
+ since?: Date;
132
+ /** Messages larger than the specified size in bytes */
133
+ larger?: number;
134
+ /** Messages smaller than the specified size in bytes */
135
+ smaller?: number;
136
+ /** Messages with all of the specified criteria (AND) */
137
+ and?: SearchCriteria[];
138
+ /** Messages with any of the specified criteria (OR) */
139
+ or?: SearchCriteria[];
140
+ /** Messages not matching the specified criteria (NOT) */
141
+ not?: SearchCriteria;
142
+ /** UID range (e.g., '1:100', '*') */
143
+ uid?: string;
144
+ /** Sequence number range */
145
+ seq?: string;
146
+ }
147
+ /**
148
+ * IMAP Connector interface
149
+ *
150
+ * Extends the core Connector interface with IMAP-specific capabilities.
151
+ * The connector is responsible for:
152
+ * - Establishing and managing IMAP connections
153
+ * - Translating RequestContext to IMAP operations
154
+ * - Returning standardized Response objects
155
+ */
156
+ interface IMAPConnector extends Connector<IMAPSession> {
157
+ /**
158
+ * Connect to an IMAP server
159
+ * @param uri - IMAP URI (e.g., 'imap://user:pass@host:port' or 'imaps://...')
160
+ * @returns IMAP session
161
+ */
162
+ connect(uri: string): Promise<IMAPSession>;
163
+ /**
164
+ * Execute an IMAP request
165
+ * @param session - Active IMAP session
166
+ * @param ctx - Request context with IMAP operation
167
+ * @returns Response with operation result
168
+ */
169
+ request(session: IMAPSession, ctx: RequestContext): Promise<Response>;
170
+ /**
171
+ * Disconnect from IMAP server
172
+ * @param session - Active IMAP session
173
+ */
174
+ disconnect(session: IMAPSession): Promise<void> | void;
175
+ /**
176
+ * Connector capabilities
177
+ */
178
+ readonly capabilities: IMAPCapabilities;
179
+ }
180
+ /**
181
+ * IMAP message envelope (simplified)
182
+ */
183
+ interface IMAPEnvelope {
184
+ /** Message date */
185
+ readonly date?: Date;
186
+ /** Subject line */
187
+ readonly subject?: string;
188
+ /** From addresses */
189
+ readonly from?: IMAPAddress[];
190
+ /** Sender addresses */
191
+ readonly sender?: IMAPAddress[];
192
+ /** Reply-To addresses */
193
+ readonly replyTo?: IMAPAddress[];
194
+ /** To addresses */
195
+ readonly to?: IMAPAddress[];
196
+ /** CC addresses */
197
+ readonly cc?: IMAPAddress[];
198
+ /** BCC addresses */
199
+ readonly bcc?: IMAPAddress[];
200
+ /** In-Reply-To header */
201
+ readonly inReplyTo?: string;
202
+ /** Message-ID header */
203
+ readonly messageId?: string;
204
+ }
205
+ /**
206
+ * IMAP address
207
+ */
208
+ interface IMAPAddress {
209
+ /** Display name */
210
+ readonly name?: string;
211
+ /** Email address */
212
+ readonly address?: string;
213
+ }
214
+ /**
215
+ * IMAP message (simplified)
216
+ */
217
+ interface IMAPMessage {
218
+ /** Sequence number */
219
+ readonly seq: number;
220
+ /** UID */
221
+ readonly uid: number;
222
+ /** Message envelope */
223
+ readonly envelope?: IMAPEnvelope;
224
+ /** Message flags */
225
+ readonly flags?: Set<string>;
226
+ /** Message size in bytes */
227
+ readonly size?: number;
228
+ }
229
+
230
+ /**
231
+ * ImapFlowConnector - Default IMAP connector using imapflow library
232
+ *
233
+ * This connector implements the IMAPConnector interface using the imapflow library.
234
+ * It is the default connector used when no custom connector is provided.
235
+ *
236
+ * @requires imapflow - Install with: npm install imapflow
237
+ */
238
+
239
+ /**
240
+ * ImapFlowConnector using imapflow library
241
+ *
242
+ * @example
243
+ * ```ts
244
+ * import { imap } from '@unireq/imap';
245
+ * import { ImapFlowConnector } from '@unireq/imap/connectors/imapflow';
246
+ *
247
+ * // With default options
248
+ * const { transport } = imap('imap://mail.server.com');
249
+ *
250
+ * // With custom options
251
+ * const connector = new ImapFlowConnector({ timeout: 30000 });
252
+ * const { transport } = imap('imap://mail.server.com', connector);
253
+ * ```
254
+ */
255
+ declare class ImapFlowConnector implements IMAPConnector {
256
+ private readonly options;
257
+ private client;
258
+ readonly capabilities: IMAPCapabilities;
259
+ constructor(options?: IMAPConnectorOptions);
260
+ connect(uri: string): Promise<IMAPSession>;
261
+ request(session: IMAPSession, ctx: RequestContext): Promise<Response>;
262
+ disconnect(session: IMAPSession): Promise<void>;
263
+ }
264
+ /**
265
+ * Creates a default ImapFlowConnector instance
266
+ * Used internally by the imap() transport function
267
+ */
268
+ declare function createDefaultImapConnector(options?: IMAPConnectorOptions): IMAPConnector;
269
+
270
+ /**
271
+ * IMAP Transport using BYOC (Bring Your Own Connector) pattern
272
+ *
273
+ * The imap() function creates an IMAP transport that can use:
274
+ * - The default ImapFlowConnector (requires imapflow package)
275
+ * - A custom connector implementing IMAPConnector interface
276
+ *
277
+ * @example
278
+ * ```ts
279
+ * import { imap } from '@unireq/imap';
280
+ * import { client } from '@unireq/core';
281
+ *
282
+ * // Using default connector (requires: npm install imapflow)
283
+ * const { transport } = imap('imap://user:pass@mail.server.com');
284
+ * const api = client(transport);
285
+ *
286
+ * // Using custom connector (BYOC)
287
+ * import { imap, type IMAPConnector } from '@unireq/imap';
288
+ * const { transport } = imap('imap://server.com', myCustomConnector);
289
+ * ```
290
+ */
291
+
292
+ /**
293
+ * IMAP transport options
294
+ */
295
+ interface IMAPTransportOptions {
296
+ /** Connection timeout in milliseconds */
297
+ readonly timeout?: number;
298
+ /** TLS options for secure connections */
299
+ readonly tls?: {
300
+ readonly minVersion?: string;
301
+ readonly rejectUnauthorized?: boolean;
302
+ };
303
+ /** Enable debug logging */
304
+ readonly debug?: boolean;
305
+ }
306
+ /**
307
+ * Creates an IMAP transport
308
+ *
309
+ * Supports BYOC (Bring Your Own Connector) pattern:
310
+ * - Without connector: Uses default ImapFlowConnector (requires imapflow)
311
+ * - With connector: Uses the provided IMAPConnector implementation
312
+ *
313
+ * @param uri - IMAP URI (e.g., 'imap://user:pass@server.com' or 'imaps://...')
314
+ * @param connectorOrOptions - Custom connector or transport options
315
+ * @returns Transport with IMAP capabilities
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * // Simple usage with default connector
320
+ * const { transport } = imap('imap://mail.server.com');
321
+ *
322
+ * // With credentials in URI
323
+ * const { transport } = imap('imap://user:password@mail.server.com');
324
+ *
325
+ * // With IMAPS (secure)
326
+ * const { transport } = imap('imaps://mail.server.com');
327
+ *
328
+ * // With custom connector (BYOC)
329
+ * const { transport } = imap('imap://server.com', myConnector);
330
+ *
331
+ * // With options (uses default connector)
332
+ * const { transport } = imap('imap://server.com', { timeout: 30000 });
333
+ * ```
334
+ */
335
+ declare function imap(uri?: string, connectorOrOptions?: IMAPConnector | IMAPTransportOptions): TransportWithCapabilities;
336
+
337
+ /**
338
+ * @unireq/imap - IMAP transport with BYOC (Bring Your Own Connector) pattern
339
+ *
340
+ * This package provides an IMAP transport that can use:
341
+ * - The default ImapFlowConnector (requires imapflow package as peer dependency)
342
+ * - A custom connector implementing IMAPConnector interface (BYOC)
343
+ *
344
+ * @example Default usage (requires: npm install imapflow)
345
+ * ```ts
346
+ * import { imap, imapOperation } from '@unireq/imap';
347
+ * import { client } from '@unireq/core';
348
+ *
349
+ * const { transport } = imap('imap://user:pass@mail.server.com');
350
+ * const api = client(transport);
351
+ *
352
+ * // Fetch messages
353
+ * const messages = await api.get('/', imapOperation('fetch', { mailbox: 'INBOX' }));
354
+ *
355
+ * // Search messages
356
+ * const ids = await api.get('/', imapOperation('search', {
357
+ * mailbox: 'INBOX',
358
+ * criteria: { from: 'sender@example.com' }
359
+ * }));
360
+ * ```
361
+ *
362
+ * @example BYOC (Bring Your Own Connector)
363
+ * ```ts
364
+ * import { imap, type IMAPConnector } from '@unireq/imap';
365
+ *
366
+ * class MyImapConnector implements IMAPConnector {
367
+ * // Custom implementation...
368
+ * }
369
+ *
370
+ * const { transport } = imap('imap://mail.server.com', new MyImapConnector());
371
+ * ```
372
+ *
373
+ * @packageDocumentation
374
+ */
375
+
376
+ /**
377
+ * IMAP operation options for imapOperation policy
378
+ */
379
+ interface ImapOperationOptions {
380
+ /** Target mailbox for operations */
381
+ readonly mailbox?: string;
382
+ /** Message range for operations (e.g., '1:*', '1,2,3') */
383
+ readonly range?: string | number[];
384
+ /** Search criteria for search operation */
385
+ readonly criteria?: SearchCriteria;
386
+ /** Destination mailbox for move operation */
387
+ readonly destination?: string;
388
+ /** Flags for flag operations */
389
+ readonly flags?: string[];
390
+ /** Whether to use UID instead of sequence numbers */
391
+ readonly useUid?: boolean;
392
+ }
393
+ /**
394
+ * IMAP operation policy factory
395
+ * Creates a policy that injects the IMAP operation into the request context
396
+ *
397
+ * @param operation - IMAP operation to perform
398
+ * @param options - Operation-specific options
399
+ * @returns Policy that injects operation into context
400
+ *
401
+ * @example
402
+ * ```ts
403
+ * import { imap, imapOperation } from '@unireq/imap';
404
+ * import { client } from '@unireq/core';
405
+ *
406
+ * const { transport } = imap('imap://mail.server.com');
407
+ * const api = client(transport);
408
+ *
409
+ * // Fetch messages from INBOX
410
+ * await api.get('/', imapOperation('fetch', { mailbox: 'INBOX' }));
411
+ *
412
+ * // Search for unread messages
413
+ * await api.get('/', imapOperation('search', {
414
+ * mailbox: 'INBOX',
415
+ * criteria: { seen: false }
416
+ * }));
417
+ *
418
+ * // Move messages to Trash
419
+ * await api.get('/', imapOperation('move', {
420
+ * mailbox: 'INBOX',
421
+ * range: '1:5',
422
+ * destination: 'Trash'
423
+ * }));
424
+ *
425
+ * // Add flags to messages
426
+ * await api.get('/', imapOperation('addFlags', {
427
+ * mailbox: 'INBOX',
428
+ * range: '1:*',
429
+ * flags: ['\\Seen']
430
+ * }));
431
+ *
432
+ * // Append a message to a mailbox
433
+ * await api.post('/INBOX', messageContent, imapOperation('append'));
434
+ *
435
+ * // Wait for new messages (IDLE)
436
+ * await api.get('/', imapOperation('idle'));
437
+ *
438
+ * // Expunge deleted messages
439
+ * await api.get('/', imapOperation('expunge', { mailbox: 'INBOX' }));
440
+ * ```
441
+ */
442
+ declare function imapOperation(operation: IMAPOperation, options?: ImapOperationOptions): Policy;
443
+ /**
444
+ * XOAUTH2 authentication options
445
+ */
446
+ interface XOAuth2Options {
447
+ /** Function that returns the OAuth2 access token */
448
+ readonly tokenSupplier: () => string | Promise<string>;
449
+ }
450
+ /**
451
+ * Creates an XOAUTH2 authentication policy for IMAP
452
+ *
453
+ * This policy adds OAuth2 token to the request context for authentication.
454
+ *
455
+ * @param options - XOAUTH2 options with token supplier
456
+ * @returns Policy that adds XOAUTH2 token
457
+ *
458
+ * @example
459
+ * ```ts
460
+ * import { imap, imapOperation, xoauth2 } from '@unireq/imap';
461
+ * import { client, compose } from '@unireq/core';
462
+ *
463
+ * const { transport } = imap('imap://mail.server.com');
464
+ * const api = client(
465
+ * compose(
466
+ * transport,
467
+ * xoauth2({ tokenSupplier: async () => await getToken() })
468
+ * )
469
+ * );
470
+ * ```
471
+ */
472
+ declare function xoauth2(options: XOAuth2Options): Policy;
473
+
474
+ export { type IMAPAddress, type IMAPCapabilities, type IMAPConnector, type IMAPConnectorOptions, type IMAPEnvelope, type IMAPMessage, type IMAPOperation, type IMAPRequestContext, type IMAPSession, type IMAPTransportOptions, ImapFlowConnector, type ImapOperationOptions, type SearchCriteria, type XOAuth2Options, createDefaultImapConnector, imap, imapOperation, xoauth2 };
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export{a as ImapFlowConnector,b as createDefaultImapConnector}from'./chunk-2FERZ343.js';import {policy}from'@unireq/core';export{policy}from'@unireq/core';async function P(e){try{let{createDefaultImapConnector:n}=await import('./imapflow-RVSIPLH2.js');return n(e)}catch(n){let o=n instanceof Error?n.message:String(n);throw new Error(`Default IMAP connector not available. Install imapflow or provide a custom connector.
2
+ Install: npm install imapflow
3
+ Or use BYOC: imap('imap://server.com', myConnector)
4
+ Cause: ${o}`)}}function d(e,n){let o=null,r=null,a=t=>t!==null&&typeof t=="object"&&"connect"in t&&"request"in t&&"disconnect"in t,p=a(n)?n:null,c=a(n)?void 0:n,u=policy(async t=>{r||(r=p??await P(c)),!o&&e&&(o=await r.connect(e));let s=t.url;if(e&&t.url.startsWith("/")){let i=new URL(e);i.pathname=t.url,s=i.toString();}else if(!t.url.startsWith("imap://")&&!t.url.startsWith("imaps://")&&e){let i=new URL(e);i.pathname=t.url.startsWith("/")?t.url:`/${t.url}`,s=i.toString();}return r.request(o,{...t,url:s})},{name:"imap",kind:"transport",options:e?{uri:e}:void 0}),m={imap:true,xoauth2:true,idle:true,append:true,search:true,move:true,flags:true,expunge:true};return {transport:u,capabilities:p?.capabilities??m}}function h(e,n={}){return policy(async(o,r)=>r({...o,operation:e,...n}),{name:`imap:${e}`,kind:"other"})}function x(e){let{tokenSupplier:n}=e;return policy(async(o,r)=>{let a=await Promise.resolve(n());return r({...o,xoauth2Token:a})},{name:"imap:xoauth2",kind:"auth"})}
5
+ export{d as imap,h as imapOperation,x as xoauth2};//# sourceMappingURL=index.js.map
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transport.ts","../src/index.ts"],"names":["getDefaultConnector","options","createDefaultImapConnector","error","cause","imap","uri","connectorOrOptions","session","actualConnector","isConnector","arg","providedConnector","transport","policy","ctx","finalUrl","baseUrl","defaultCapabilities","imapOperation","operation","policyFn","next","xoauth2","tokenSupplier","token"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * IMAP Transport using BYOC (Bring Your Own Connector) pattern\n *\n * The imap() function creates an IMAP transport that can use:\n * - The default ImapFlowConnector (requires imapflow package)\n * - A custom connector implementing IMAPConnector interface\n *\n * @example\n * ```ts\n * import { imap } from '@unireq/imap';\n * import { client } from '@unireq/core';\n *\n * // Using default connector (requires: npm install imapflow)\n * const { transport } = imap('imap://user:pass@mail.server.com');\n * const api = client(transport);\n *\n * // Using custom connector (BYOC)\n * import { imap, type IMAPConnector } from '@unireq/imap';\n * const { transport } = imap('imap://server.com', myCustomConnector);\n * ```\n */\n\nimport type { RequestContext, Response, TransportWithCapabilities } from '@unireq/core';\nimport { policy } from '@unireq/core';\nimport type { IMAPConnector, IMAPConnectorOptions, IMAPSession } from './connector.js';\n\n/**\n * Lazily loads the default IMAP connector\n * Throws a helpful error if imapflow is not installed\n */\nasync function getDefaultConnector(options?: IMAPConnectorOptions): Promise<IMAPConnector> {\n try {\n const { createDefaultImapConnector } = await import('./connectors/imapflow.js');\n return createDefaultImapConnector(options);\n } catch (error) {\n const cause = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Default IMAP connector not available. Install imapflow or provide a custom connector.\\n` +\n `Install: npm install imapflow\\n` +\n `Or use BYOC: imap('imap://server.com', myConnector)\\n` +\n `Cause: ${cause}`,\n );\n }\n}\n\n/**\n * IMAP transport options\n */\nexport interface IMAPTransportOptions {\n /** Connection timeout in milliseconds */\n readonly timeout?: number;\n /** TLS options for secure connections */\n readonly tls?: {\n readonly minVersion?: string;\n readonly rejectUnauthorized?: boolean;\n };\n /** Enable debug logging */\n readonly debug?: boolean;\n}\n\n/**\n * Creates an IMAP transport\n *\n * Supports BYOC (Bring Your Own Connector) pattern:\n * - Without connector: Uses default ImapFlowConnector (requires imapflow)\n * - With connector: Uses the provided IMAPConnector implementation\n *\n * @param uri - IMAP URI (e.g., 'imap://user:pass@server.com' or 'imaps://...')\n * @param connectorOrOptions - Custom connector or transport options\n * @returns Transport with IMAP capabilities\n *\n * @example\n * ```ts\n * // Simple usage with default connector\n * const { transport } = imap('imap://mail.server.com');\n *\n * // With credentials in URI\n * const { transport } = imap('imap://user:password@mail.server.com');\n *\n * // With IMAPS (secure)\n * const { transport } = imap('imaps://mail.server.com');\n *\n * // With custom connector (BYOC)\n * const { transport } = imap('imap://server.com', myConnector);\n *\n * // With options (uses default connector)\n * const { transport } = imap('imap://server.com', { timeout: 30000 });\n * ```\n */\nexport function imap(\n uri?: string,\n connectorOrOptions?: IMAPConnector | IMAPTransportOptions,\n): TransportWithCapabilities {\n let session: IMAPSession | null = null;\n let actualConnector: IMAPConnector | null = null;\n\n // Determine if second argument is a connector or options\n const isConnector = (arg: unknown): arg is IMAPConnector =>\n arg !== null && typeof arg === 'object' && 'connect' in arg && 'request' in arg && 'disconnect' in arg;\n\n const providedConnector = isConnector(connectorOrOptions) ? connectorOrOptions : null;\n const options = !isConnector(connectorOrOptions) ? connectorOrOptions : undefined;\n\n const transport = policy(\n async (ctx: RequestContext): Promise<Response> => {\n // Lazy load connector\n if (!actualConnector) {\n actualConnector = providedConnector ?? (await getDefaultConnector(options));\n }\n\n // Connect if not connected and URI provided\n if (!session && uri) {\n session = await actualConnector.connect(uri);\n }\n\n // Build final URL: combine base URI with relative path\n let finalUrl = ctx.url;\n if (uri && ctx.url.startsWith('/')) {\n const baseUrl = new URL(uri);\n baseUrl.pathname = ctx.url;\n finalUrl = baseUrl.toString();\n } else if (!ctx.url.startsWith('imap://') && !ctx.url.startsWith('imaps://')) {\n // If no scheme, prepend the base URI\n if (uri) {\n const baseUrl = new URL(uri);\n baseUrl.pathname = ctx.url.startsWith('/') ? ctx.url : `/${ctx.url}`;\n finalUrl = baseUrl.toString();\n }\n }\n\n // biome-ignore lint/style/noNonNullAssertion: session is guaranteed by connect() or existing connection above\n return actualConnector.request(session!, { ...ctx, url: finalUrl });\n },\n {\n name: 'imap',\n kind: 'transport',\n options: uri ? { uri } : undefined,\n },\n );\n\n // Get capabilities from connector if available, otherwise use defaults\n const defaultCapabilities = {\n imap: true,\n xoauth2: true,\n idle: true,\n append: true,\n search: true,\n move: true,\n flags: true,\n expunge: true,\n };\n\n return {\n transport,\n capabilities: providedConnector?.capabilities ?? defaultCapabilities,\n };\n}\n","/**\n * @unireq/imap - IMAP transport with BYOC (Bring Your Own Connector) pattern\n *\n * This package provides an IMAP transport that can use:\n * - The default ImapFlowConnector (requires imapflow package as peer dependency)\n * - A custom connector implementing IMAPConnector interface (BYOC)\n *\n * @example Default usage (requires: npm install imapflow)\n * ```ts\n * import { imap, imapOperation } from '@unireq/imap';\n * import { client } from '@unireq/core';\n *\n * const { transport } = imap('imap://user:pass@mail.server.com');\n * const api = client(transport);\n *\n * // Fetch messages\n * const messages = await api.get('/', imapOperation('fetch', { mailbox: 'INBOX' }));\n *\n * // Search messages\n * const ids = await api.get('/', imapOperation('search', {\n * mailbox: 'INBOX',\n * criteria: { from: 'sender@example.com' }\n * }));\n * ```\n *\n * @example BYOC (Bring Your Own Connector)\n * ```ts\n * import { imap, type IMAPConnector } from '@unireq/imap';\n *\n * class MyImapConnector implements IMAPConnector {\n * // Custom implementation...\n * }\n *\n * const { transport } = imap('imap://mail.server.com', new MyImapConnector());\n * ```\n *\n * @packageDocumentation\n */\n\n// Re-export core types for convenience\nexport type { Connector, RequestContext, Response, TransportWithCapabilities } from '@unireq/core';\nexport { policy } from '@unireq/core';\n// Connector interface for BYOC\nexport type {\n IMAPAddress,\n IMAPCapabilities,\n IMAPConnector,\n IMAPConnectorOptions,\n IMAPEnvelope,\n IMAPMessage,\n IMAPOperation,\n IMAPRequestContext,\n IMAPSession,\n SearchCriteria,\n} from './connector.js';\n// Default connector (requires imapflow peer dependency)\nexport { createDefaultImapConnector, ImapFlowConnector } from './connectors/imapflow.js';\n// Main transport function\nexport { type IMAPTransportOptions, imap } from './transport.js';\n\n// Import policy for internal use\nimport { type RequestContext as Ctx, type Policy, policy as policyFn, type Response as Res } from '@unireq/core';\nimport type { IMAPOperation, SearchCriteria } from './connector.js';\n\n/**\n * IMAP operation options for imapOperation policy\n */\nexport interface ImapOperationOptions {\n /** Target mailbox for operations */\n readonly mailbox?: string;\n /** Message range for operations (e.g., '1:*', '1,2,3') */\n readonly range?: string | number[];\n /** Search criteria for search operation */\n readonly criteria?: SearchCriteria;\n /** Destination mailbox for move operation */\n readonly destination?: string;\n /** Flags for flag operations */\n readonly flags?: string[];\n /** Whether to use UID instead of sequence numbers */\n readonly useUid?: boolean;\n}\n\n/**\n * IMAP operation policy factory\n * Creates a policy that injects the IMAP operation into the request context\n *\n * @param operation - IMAP operation to perform\n * @param options - Operation-specific options\n * @returns Policy that injects operation into context\n *\n * @example\n * ```ts\n * import { imap, imapOperation } from '@unireq/imap';\n * import { client } from '@unireq/core';\n *\n * const { transport } = imap('imap://mail.server.com');\n * const api = client(transport);\n *\n * // Fetch messages from INBOX\n * await api.get('/', imapOperation('fetch', { mailbox: 'INBOX' }));\n *\n * // Search for unread messages\n * await api.get('/', imapOperation('search', {\n * mailbox: 'INBOX',\n * criteria: { seen: false }\n * }));\n *\n * // Move messages to Trash\n * await api.get('/', imapOperation('move', {\n * mailbox: 'INBOX',\n * range: '1:5',\n * destination: 'Trash'\n * }));\n *\n * // Add flags to messages\n * await api.get('/', imapOperation('addFlags', {\n * mailbox: 'INBOX',\n * range: '1:*',\n * flags: ['\\\\Seen']\n * }));\n *\n * // Append a message to a mailbox\n * await api.post('/INBOX', messageContent, imapOperation('append'));\n *\n * // Wait for new messages (IDLE)\n * await api.get('/', imapOperation('idle'));\n *\n * // Expunge deleted messages\n * await api.get('/', imapOperation('expunge', { mailbox: 'INBOX' }));\n * ```\n */\nexport function imapOperation(operation: IMAPOperation, options: ImapOperationOptions = {}): Policy {\n return policyFn(\n async (ctx: Ctx, next: (ctx: Ctx) => Promise<Res>): Promise<Res> => next({ ...ctx, operation, ...options }),\n { name: `imap:${operation}`, kind: 'other' },\n );\n}\n\n/**\n * XOAUTH2 authentication options\n */\nexport interface XOAuth2Options {\n /** Function that returns the OAuth2 access token */\n readonly tokenSupplier: () => string | Promise<string>;\n}\n\n/**\n * Creates an XOAUTH2 authentication policy for IMAP\n *\n * This policy adds OAuth2 token to the request context for authentication.\n *\n * @param options - XOAUTH2 options with token supplier\n * @returns Policy that adds XOAUTH2 token\n *\n * @example\n * ```ts\n * import { imap, imapOperation, xoauth2 } from '@unireq/imap';\n * import { client, compose } from '@unireq/core';\n *\n * const { transport } = imap('imap://mail.server.com');\n * const api = client(\n * compose(\n * transport,\n * xoauth2({ tokenSupplier: async () => await getToken() })\n * )\n * );\n * ```\n */\nexport function xoauth2(options: XOAuth2Options): Policy {\n const { tokenSupplier } = options;\n\n return policyFn(\n async (ctx: Ctx, next: (ctx: Ctx) => Promise<Res>): Promise<Res> => {\n const token = await Promise.resolve(tokenSupplier());\n\n return next({\n ...ctx,\n xoauth2Token: token,\n } as Ctx);\n },\n { name: 'imap:xoauth2', kind: 'auth' },\n );\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@unireq/imap",
3
+ "version": "0.0.1",
4
+ "description": "IMAP transport for unireq using ImapFlow",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "author": "Olivier Orabona",
18
+ "license": "MIT",
19
+ "dependencies": {
20
+ "@unireq/core": "0.0.1"
21
+ },
22
+ "peerDependencies": {
23
+ "imapflow": "^1.2.3"
24
+ },
25
+ "peerDependenciesMeta": {
26
+ "imapflow": {
27
+ "optional": true
28
+ }
29
+ },
30
+ "devDependencies": {
31
+ "typescript": "^5.9.3",
32
+ "tsup": "^8.5.1"
33
+ },
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ },
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/oorabona/unireq",
40
+ "directory": "packages/imap"
41
+ },
42
+ "bugs": {
43
+ "url": "https://github.com/oorabona/unireq/issues"
44
+ },
45
+ "homepage": "https://github.com/oorabona/unireq/tree/main/packages/imap",
46
+ "scripts": {
47
+ "build": "tsup",
48
+ "type-check": "tsc --noEmit",
49
+ "test": "vitest run",
50
+ "clean": "rm -rf dist *.tsbuildinfo"
51
+ }
52
+ }