@zhin.js/adapter-email 5.0.0 → 5.0.2
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/CHANGELOG.md +23 -0
- package/README.md +2 -2
- package/lib/endpoint.d.ts +2 -5
- package/lib/endpoint.js +6 -5
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/protocol.d.ts +6 -0
- package/lib/protocol.js +11 -0
- package/package.json +9 -8
- package/src/endpoint.ts +7 -5
- package/src/index.ts +1 -0
- package/src/protocol.ts +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @zhin.js/adapter-email
|
|
2
2
|
|
|
3
|
+
## 5.0.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [afc0e66]
|
|
8
|
+
- Updated dependencies [2e41ad5]
|
|
9
|
+
- Updated dependencies [9f57124]
|
|
10
|
+
- @zhin.js/core@1.5.2
|
|
11
|
+
- @zhin.js/adapter@1.1.5
|
|
12
|
+
- @zhin.js/im-contract@1.0.1
|
|
13
|
+
- @zhin.js/plugin-runtime@1.1.3
|
|
14
|
+
- zhin.js@6.0.2
|
|
15
|
+
|
|
16
|
+
## 5.0.1
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Updated dependencies [c8f4d45]
|
|
21
|
+
- @zhin.js/plugin-runtime@1.1.2
|
|
22
|
+
- @zhin.js/adapter@1.1.4
|
|
23
|
+
- @zhin.js/core@1.5.1
|
|
24
|
+
- zhin.js@6.0.1
|
|
25
|
+
|
|
3
26
|
## 5.0.0
|
|
4
27
|
|
|
5
28
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -23,8 +23,8 @@ pnpm add @zhin.js/adapter-email
|
|
|
23
23
|
- `@zhin.js/plugin-runtime` — `plugin.ts`(`definePlugin`)
|
|
24
24
|
- 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`(`smtp` / `imap`)
|
|
25
25
|
|
|
26
|
-
入站:`gateway.receive({
|
|
27
|
-
出站:`send({
|
|
26
|
+
入站:`gateway.receive({ conversation, message, content: text, sender, metadata })`(`conversation` 为 kind=private、id=发件人地址的 ConversationRef)
|
|
27
|
+
出站:`send({ conversation, payload })` → nodemailer(收件人取 `conversation.id`;payload 已由 gateway/core 渲染;无 segment-mapper)
|
|
28
28
|
|
|
29
29
|
## 前置条件
|
|
30
30
|
|
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EndpointInstance } from '@zhin.js/adapter';
|
|
1
|
+
import type { EndpointInstance, EndpointSendRequest } from '@zhin.js/adapter';
|
|
2
2
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
3
3
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
4
4
|
import { type EmailMessage, type ResolvedEmailConfig } from './protocol.js';
|
|
@@ -21,10 +21,7 @@ export declare class EmailEndpoint implements EndpointInstance {
|
|
|
21
21
|
open(): void;
|
|
22
22
|
close(): void;
|
|
23
23
|
stop(): Promise<void>;
|
|
24
|
-
send({
|
|
25
|
-
readonly target: string;
|
|
26
|
-
readonly payload: unknown;
|
|
27
|
-
}): Promise<string>;
|
|
24
|
+
send({ conversation, payload }: EndpointSendRequest): Promise<string>;
|
|
28
25
|
/** Test / internal: admit a parsed mail when the endpoint is open. */
|
|
29
26
|
admit(email: EmailMessage): void;
|
|
30
27
|
}
|
package/lib/endpoint.js
CHANGED
|
@@ -5,7 +5,7 @@ import { mkdir, writeFile } from 'node:fs/promises';
|
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import { simpleParser } from 'mailparser';
|
|
7
7
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
8
|
-
import { formatInboundContent, formatInboundSegments, formatOutboundMail, parseEmailMessage, senderDisplayName, } from './protocol.js';
|
|
8
|
+
import { emailInboundConversation, formatInboundContent, formatInboundSegments, formatOutboundMail, parseEmailMessage, senderDisplayName, } from './protocol.js';
|
|
9
9
|
import { defaultCreateImap, defaultCreateSmtp, } from './transport.js';
|
|
10
10
|
const logger = getLogger('email');
|
|
11
11
|
/**
|
|
@@ -89,9 +89,10 @@ export class EmailEndpoint {
|
|
|
89
89
|
}
|
|
90
90
|
logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
|
|
91
91
|
}
|
|
92
|
-
async send({
|
|
92
|
+
async send({ conversation, payload }) {
|
|
93
93
|
if (!this.#smtp)
|
|
94
94
|
throw new Error('SMTP transporter not initialized');
|
|
95
|
+
const target = conversation.id;
|
|
95
96
|
const mailOptions = formatOutboundMail(payload, {
|
|
96
97
|
from: this.#options.config.smtp.auth.user,
|
|
97
98
|
to: target,
|
|
@@ -116,13 +117,13 @@ export class EmailEndpoint {
|
|
|
116
117
|
const savedAttachments = await this.#downloadAttachments(email);
|
|
117
118
|
const content = formatInboundContent(email);
|
|
118
119
|
const sender = email.from;
|
|
120
|
+
const conversation = emailInboundConversation(String(this.#options.id), email);
|
|
119
121
|
await this.#options.gateway.receive({
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
conversation,
|
|
123
|
+
...(email.messageId ? { message: { conversation, id: email.messageId } } : {}),
|
|
122
124
|
content,
|
|
123
125
|
segments: formatInboundSegments(email, savedAttachments),
|
|
124
126
|
sender: senderDisplayName(sender),
|
|
125
|
-
id: email.messageId || undefined,
|
|
126
127
|
metadata: Object.freeze({
|
|
127
128
|
subject: email.subject,
|
|
128
129
|
to: email.to,
|
package/lib/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { addressListText, formatInboundContent, formatInboundSegments, formatOutboundMail, htmlToText, parseEmailMessage, resolveEmailConfig, senderDisplayName, type EmailAdapterConfig, type EmailAttachmentsConfig, type EmailMessage, type EmailWireSegment, type ImapConfig, type ResolvedEmailConfig, type SavedEmailAttachment, type SmtpConfig, } from './protocol.js';
|
|
1
|
+
export { addressListText, emailInboundConversation, formatInboundContent, formatInboundSegments, formatOutboundMail, htmlToText, parseEmailMessage, resolveEmailConfig, senderDisplayName, type EmailAdapterConfig, type EmailAttachmentsConfig, type EmailMessage, type EmailWireSegment, type ImapConfig, type ResolvedEmailConfig, type SavedEmailAttachment, type SmtpConfig, } from './protocol.js';
|
|
2
2
|
export { EmailEndpoint, type EmailEndpointOptions, } from './endpoint.js';
|
|
3
3
|
export { defaultCreateImap, defaultCreateSmtp, type EmailImapFetchMessage, type EmailImapTransport, type EmailSmtpTransport, } from './transport.js';
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { addressListText, formatInboundContent, formatInboundSegments, formatOutboundMail, htmlToText, parseEmailMessage, resolveEmailConfig, senderDisplayName, } from './protocol.js';
|
|
1
|
+
export { addressListText, emailInboundConversation, formatInboundContent, formatInboundSegments, formatOutboundMail, htmlToText, parseEmailMessage, resolveEmailConfig, senderDisplayName, } from './protocol.js';
|
|
2
2
|
export { EmailEndpoint, } from './endpoint.js';
|
|
3
3
|
export { defaultCreateImap, defaultCreateSmtp, } from './transport.js';
|
package/lib/protocol.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { Attachment } from 'mailparser';
|
|
6
6
|
import type { Segment } from '@zhin.js/core/runtime';
|
|
7
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
7
8
|
export interface SmtpConfig {
|
|
8
9
|
readonly host: string;
|
|
9
10
|
readonly port: number;
|
|
@@ -101,6 +102,11 @@ export interface SavedEmailAttachment {
|
|
|
101
102
|
* 未下载的附件(disabled / 被过滤)只保留 content 里的占位文本。
|
|
102
103
|
*/
|
|
103
104
|
export declare function formatInboundSegments(email: EmailMessage, savedAttachments?: readonly SavedEmailAttachment[]): Segment[];
|
|
105
|
+
/**
|
|
106
|
+
* 入站归一化 → ConversationRef:Email 无群/频道概念,所有入站邮件都是
|
|
107
|
+
* 与发件人地址的 private 会话(id = 发件人地址)。
|
|
108
|
+
*/
|
|
109
|
+
export declare function emailInboundConversation(endpointId: string, email: EmailMessage): ConversationRef;
|
|
104
110
|
export declare function senderDisplayName(from: string): string;
|
|
105
111
|
/**
|
|
106
112
|
* nodemailer 附件的最小形状:url/path 走 `path`(URL 由 nodemailer 拉流、
|
package/lib/protocol.js
CHANGED
|
@@ -120,6 +120,17 @@ export function formatInboundSegments(email, savedAttachments = []) {
|
|
|
120
120
|
}
|
|
121
121
|
return out;
|
|
122
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* 入站归一化 → ConversationRef:Email 无群/频道概念,所有入站邮件都是
|
|
125
|
+
* 与发件人地址的 private 会话(id = 发件人地址)。
|
|
126
|
+
*/
|
|
127
|
+
export function emailInboundConversation(endpointId, email) {
|
|
128
|
+
return {
|
|
129
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
130
|
+
kind: 'private',
|
|
131
|
+
id: email.from,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
123
134
|
export function senderDisplayName(from) {
|
|
124
135
|
const name = from.split('<')[0]?.trim();
|
|
125
136
|
return name || from;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-email",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Zhin.js Email adapter for Plugin Runtime (SMTP/IMAP)",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -9,16 +9,17 @@
|
|
|
9
9
|
"imap": "^0.8.19",
|
|
10
10
|
"mailparser": "^3.9.14",
|
|
11
11
|
"nodemailer": "^9.0.3",
|
|
12
|
-
"@zhin.js/adapter": "1.1.
|
|
13
|
-
"@zhin.js/core": "1.5.
|
|
12
|
+
"@zhin.js/adapter": "1.1.5",
|
|
13
|
+
"@zhin.js/core": "1.5.2",
|
|
14
|
+
"@zhin.js/im-contract": "1.0.1",
|
|
14
15
|
"@zhin.js/logger": "1.0.75",
|
|
15
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
16
|
+
"@zhin.js/plugin-runtime": "1.1.3"
|
|
16
17
|
},
|
|
17
18
|
"peerDependencies": {
|
|
18
|
-
"@zhin.js/adapter": "1.1.
|
|
19
|
-
"@zhin.js/core": "1.5.
|
|
20
|
-
"@zhin.js/plugin-runtime": "1.1.
|
|
21
|
-
"zhin.js": "6.0.
|
|
19
|
+
"@zhin.js/adapter": "1.1.5",
|
|
20
|
+
"@zhin.js/core": "1.5.2",
|
|
21
|
+
"@zhin.js/plugin-runtime": "1.1.3",
|
|
22
|
+
"zhin.js": "6.0.2"
|
|
22
23
|
},
|
|
23
24
|
"peerDependenciesMeta": {
|
|
24
25
|
"zhin.js": {
|
package/src/endpoint.ts
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import { simpleParser } from 'mailparser';
|
|
7
|
-
import type { EndpointInstance } from '@zhin.js/adapter';
|
|
7
|
+
import type { EndpointInstance, EndpointSendRequest } from '@zhin.js/adapter';
|
|
8
8
|
import type { MessageGateway } from '@zhin.js/core/runtime';
|
|
9
9
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
10
10
|
import type { CapabilityId } from '@zhin.js/plugin-runtime';
|
|
11
11
|
import {
|
|
12
|
+
emailInboundConversation,
|
|
12
13
|
formatInboundContent,
|
|
13
14
|
formatInboundSegments,
|
|
14
15
|
formatOutboundMail,
|
|
@@ -120,8 +121,9 @@ export class EmailEndpoint implements EndpointInstance {
|
|
|
120
121
|
logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
|
|
121
122
|
}
|
|
122
123
|
|
|
123
|
-
async send({
|
|
124
|
+
async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
|
|
124
125
|
if (!this.#smtp) throw new Error('SMTP transporter not initialized');
|
|
126
|
+
const target = conversation.id;
|
|
125
127
|
const mailOptions = formatOutboundMail(payload, {
|
|
126
128
|
from: this.#options.config.smtp.auth.user,
|
|
127
129
|
to: target,
|
|
@@ -147,13 +149,13 @@ export class EmailEndpoint implements EndpointInstance {
|
|
|
147
149
|
const savedAttachments = await this.#downloadAttachments(email);
|
|
148
150
|
const content = formatInboundContent(email);
|
|
149
151
|
const sender = email.from;
|
|
152
|
+
const conversation = emailInboundConversation(String(this.#options.id), email);
|
|
150
153
|
await this.#options.gateway.receive({
|
|
151
|
-
|
|
152
|
-
|
|
154
|
+
conversation,
|
|
155
|
+
...(email.messageId ? { message: { conversation, id: email.messageId } } : {}),
|
|
153
156
|
content,
|
|
154
157
|
segments: formatInboundSegments(email, savedAttachments),
|
|
155
158
|
sender: senderDisplayName(sender),
|
|
156
|
-
id: email.messageId || undefined,
|
|
157
159
|
metadata: Object.freeze({
|
|
158
160
|
subject: email.subject,
|
|
159
161
|
to: email.to,
|
package/src/index.ts
CHANGED
package/src/protocol.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { Attachment } from 'mailparser';
|
|
7
7
|
import { htmlToPlainTextWithBlockBreaks, isMediaRef, type MediaRef } from '@zhin.js/core';
|
|
8
8
|
import type { Segment } from '@zhin.js/core/runtime';
|
|
9
|
+
import type { ConversationRef } from '@zhin.js/im-contract';
|
|
9
10
|
import { formatCompact, getLogger } from '@zhin.js/logger';
|
|
10
11
|
|
|
11
12
|
const logger = getLogger('email');
|
|
@@ -227,6 +228,18 @@ export function formatInboundSegments(
|
|
|
227
228
|
return out;
|
|
228
229
|
}
|
|
229
230
|
|
|
231
|
+
/**
|
|
232
|
+
* 入站归一化 → ConversationRef:Email 无群/频道概念,所有入站邮件都是
|
|
233
|
+
* 与发件人地址的 private 会话(id = 发件人地址)。
|
|
234
|
+
*/
|
|
235
|
+
export function emailInboundConversation(endpointId: string, email: EmailMessage): ConversationRef {
|
|
236
|
+
return {
|
|
237
|
+
endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
|
|
238
|
+
kind: 'private',
|
|
239
|
+
id: email.from,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
230
243
|
export function senderDisplayName(from: string): string {
|
|
231
244
|
const name = from.split('<')[0]?.trim();
|
|
232
245
|
return name || from;
|