@zhin.js/adapter-email 5.0.1 → 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 CHANGED
@@ -1,5 +1,18 @@
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
+
3
16
  ## 5.0.1
4
17
 
5
18
  ### 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({ adapter, target: fromEmail, content: text, sender, metadata })`
27
- 出站:`send({ target, payload })` → nodemailerpayload 已由 gateway/core 渲染;无 segment-mapper)
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({ target, payload }: {
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({ target, payload }) {
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
- adapter: this.#options.id,
121
- target: sender,
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.1",
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.4",
13
- "@zhin.js/core": "1.5.1",
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.2"
16
+ "@zhin.js/plugin-runtime": "1.1.3"
16
17
  },
17
18
  "peerDependencies": {
18
- "@zhin.js/adapter": "1.1.4",
19
- "@zhin.js/core": "1.5.1",
20
- "@zhin.js/plugin-runtime": "1.1.2",
21
- "zhin.js": "6.0.1"
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({ target, payload }: { readonly target: string; readonly payload: unknown }): Promise<string> {
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
- adapter: this.#options.id,
152
- target: sender,
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
@@ -1,5 +1,6 @@
1
1
  export {
2
2
  addressListText,
3
+ emailInboundConversation,
3
4
  formatInboundContent,
4
5
  formatInboundSegments,
5
6
  formatOutboundMail,
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;