tempmail-sdk 1.1.3 → 1.1.4

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/README.md CHANGED
@@ -22,11 +22,11 @@ npm install @XxxXTeam/tempmail-sdk --registry=https://npm.pkg.github.com
22
22
  | `tempmail` | tempmail.ing | - | 支持自定义有效期 |
23
23
  | `linshi-email` | linshi-email.com | - | |
24
24
  | `tempmail-lol` | tempmail.lol | ✅ | 支持指定域名 |
25
- | `chatgpt-org-uk` | mail.chatgpt.org.uk | - | |
26
- | `tempmail-la` | tempmail.la | - | 支持分页 |
25
+ | `chatgpt-org-uk` | mail.chatgpt.org.uk | | 首页注入 `__BROWSER_AUTH`,创建邮箱时须带 `X-Inbox-Token` + `gm_sid`(已自动处理) |
27
26
  | `temp-mail-io` | temp-mail.io | - | |
28
27
  | `awamail` | awamail.com | ✅ | Session Cookie 自动管理 |
29
- | `mail-tm` | mail.tm | ✅ | 自动注册账号获取 Bearer Token |
28
+ | `mail-tm` | mail.tm / api.mail.tm | ✅ | 自动注册账号;请求与 **Internxt** 等站点前端一致(`GET /domains?page=1`、`GET /messages?page=1` 及常见浏览器头) |
29
+ | `smail-pw` | smail.pw | ✅ | `POST/GET https://smail.pw/_root.data`,`__session` Cookie;解析 RSC/Flight 中的 **D1 邮件行对象**(`subject`/`time` 等) |
30
30
  | `dropmail` | dropmail.me | ✅ | GraphQL API |
31
31
  | `guerrillamail` | guerrillamail.com | ✅ | 公开 JSON API |
32
32
  | `maildrop` | maildrop.cc | ✅ | GraphQL API,自带反垃圾 |
@@ -76,13 +76,13 @@ console.log(channels);
76
76
  // { channel: 'linshi-email', name: '临时邮箱', website: 'linshi-email.com' },
77
77
  // { channel: 'tempmail-lol', name: 'TempMail LOL', website: 'tempmail.lol' },
78
78
  // { channel: 'chatgpt-org-uk', name: 'ChatGPT Mail', website: 'mail.chatgpt.org.uk' },
79
- // { channel: 'tempmail-la', name: 'TempMail LA', website: 'tempmail.la' },
80
79
  // { channel: 'temp-mail-io', name: 'Temp Mail IO', website: 'temp-mail.io' },
81
80
  // { channel: 'awamail', name: 'AwaMail', website: 'awamail.com' },
82
81
  // { channel: 'mail-tm', name: 'Mail.tm', website: 'mail.tm' },
83
82
  // { channel: 'dropmail', name: 'DropMail', website: 'dropmail.me' },
84
83
  // { channel: 'guerrillamail', name: 'Guerrilla Mail', website: 'guerrillamail.com' },
85
- // { channel: 'maildrop', name: 'Maildrop', website: 'maildrop.cc' }
84
+ // { channel: 'maildrop', name: 'Maildrop', website: 'maildrop.cc' },
85
+ // { channel: 'smail-pw', name: 'Smail.pw', website: 'smail.pw' }
86
86
  // ]
87
87
 
88
88
  const info = getChannelInfo('tempmail');
@@ -107,38 +107,29 @@ const emailInfo3 = await generateEmail({ channel: 'tempmail', duration: 60 });
107
107
 
108
108
  // tempmail-lol 渠道支持指定域名
109
109
  const emailInfo4 = await generateEmail({ channel: 'tempmail-lol', domain: 'example.com' });
110
+
111
+ // 只尝试指定渠道(探测可用性、写自动化时用)
112
+ const probe = await generateEmail({ channel: 'smail-pw', channelFallback: false });
110
113
  ```
111
114
 
112
115
  #### 获取邮件
113
116
 
114
- ```typescript
115
- import { getEmails } from 'tempmail-sdk';
116
-
117
- // 不需要 Token 的渠道
118
- const result = await getEmails({
119
- channel: 'tempmail',
120
- email: 'xxx@ibymail.com',
121
- });
122
- console.log(result.emails); // 标准化邮件数组
117
+ `getEmails` 的第一个参数必须是 **`generateEmail` 返回的 `EmailInfo`**(或与 `TempEmailClient` 缓存的相同结构)。Token / Session 由 SDK 内部绑定到该对象,**不要**再手动传入 `token` 字段。
123
118
 
124
- // 需要 Token 的渠道(token 由 generateEmail 返回)
125
- const result2 = await getEmails({
126
- channel: 'mail-tm',
127
- email: emailInfo.email,
128
- token: emailInfo.token, // Bearer Token
129
- });
130
-
131
- // 所有邮件使用统一格式,无需关心渠道差异
132
- for (const email of result2.emails) {
133
- console.log(email.id); // 邮件 ID
134
- console.log(email.from); // 发件人
135
- console.log(email.to); // 收件人
136
- console.log(email.subject); // 主题
137
- console.log(email.text); // 纯文本
138
- console.log(email.html); // HTML
139
- console.log(email.date); // ISO 日期
140
- console.log(email.isRead); // 是否已读
141
- console.log(email.attachments); // 附件列表
119
+ ```typescript
120
+ import { generateEmail, getEmails } from 'tempmail-sdk';
121
+
122
+ const emailInfo = await generateEmail({ channel: 'tempmail' });
123
+ if (!emailInfo) throw new Error('创建失败');
124
+ const result = await getEmails(emailInfo);
125
+ console.log(result.success, result.emails.length);
126
+
127
+ const mailTm = await generateEmail({ channel: 'mail-tm' });
128
+ if (mailTm) {
129
+ const r2 = await getEmails(mailTm, { retry: { maxRetries: 3, timeout: 20000 } });
130
+ for (const email of r2.emails) {
131
+ console.log(email.id, email.from, email.subject, email.date);
132
+ }
142
133
  }
143
134
  ```
144
135
 
@@ -171,8 +162,10 @@ for (const email of result2.emails) {
171
162
  | 字段 | 类型 | 说明 |
172
163
  |------|------|------|
173
164
  | `channel` | `Channel` | 指定渠道(可选,不指定则随机) |
165
+ | `channelFallback` | `boolean` | 默认 `true`:指定渠道失败会继续尝试其他渠道;设为 `false` 时仅尝试 `channel` |
174
166
  | `duration` | `number` | 有效期分钟数(仅 `tempmail` 渠道) |
175
167
  | `domain` | `string` | 指定域名(仅 `tempmail-lol` 渠道) |
168
+ | `retry` | `RetryConfig` | 创建邮箱时的重试(超时、5xx、网络错误等) |
176
169
 
177
170
  **返回值:** `EmailInfo`
178
171
 
@@ -184,17 +177,16 @@ for (const email of result2.emails) {
184
177
  | `expiresAt` | `string \| number?` | 过期时间 |
185
178
  | `createdAt` | `string?` | 创建时间 |
186
179
 
187
- ### getEmails(options)
180
+ ### getEmails(info, options?)
188
181
 
189
182
  获取邮件列表。
190
183
 
191
184
  **参数:**
192
185
 
193
- | 字段 | 类型 | 必填 | 说明 |
194
- |------|------|:----:|------|
195
- | `channel` | `Channel` | | 渠道标识 |
196
- | `email` | `string` | | 邮箱地址 |
197
- | `token` | `string` | 部分 | 访问令牌(`tempmail-lol`、`awamail`、`mail-tm`、`dropmail`、`guerrillamail`、`maildrop` 必填) |
186
+ | 参数 | 类型 | 说明 |
187
+ |------|------|------|
188
+ | `info` | `EmailInfo` | `generateEmail()` `TempEmailClient.generate()` 返回;SDK 从中读取 `channel`、`email` 及内部 Token |
189
+ | `options?.retry` | `RetryConfig` | 拉取邮件时的重试配置(可选) |
198
190
 
199
191
  **返回值:** `GetEmailsResult`
200
192
 
@@ -281,6 +273,19 @@ export TEMPMAIL_TIMEOUT=30000
281
273
 
282
274
  > **提示:** Node.js 原生 fetch 不支持代理,推荐通过 `customFetch` + `undici` 的 `ProxyAgent` 实现代理支持。
283
275
 
276
+ DropMail 自动令牌等更多配置见源码 `src/config.ts` 注释(`DROPMAIL_*` 环境变量)。
277
+
278
+ ## 示例脚本(本仓库)
279
+
280
+ 在 `sdk/npm` 目录执行:
281
+
282
+ | 命令 / 文件 | 说明 |
283
+ |-------------|------|
284
+ | `npx ts-node demo/poll-emails.ts` | 未配置 SMTP 时为交互式选渠道并轮询;配置 `SMTP_HOST` 等后可自动向各渠道发探针并轮询(`POLL_CHANNELS`、`POLL_INTERVAL_MS`、`POLL_MAX_ROUNDS`) |
285
+ | `npm run demo:internxt` | `demo/internxt-tempmail-probe.ts`:检查 Internxt 页面与 **Mail.tm(SDK `mail-tm`)** 全流程 |
286
+
287
+ `poll-emails` 使用 SMTP 时需安装:`npm install nodemailer @types/nodemailer`(若尚未加入 devDependencies)。
288
+
284
289
  ## 环境要求
285
290
 
286
291
  - Node.js 18+(需要原生 `fetch` 支持)
@@ -1,18 +1,66 @@
1
1
  /**
2
2
  * Demo: 获取指定渠道邮箱,轮询获取邮件
3
- *
3
+ *
4
4
  * 使用方法:
5
- * npx ts-node demo/poll-emails.ts
5
+ * npx ts-node demo/poll-emails.ts
6
+ *
7
+ * 行为:
8
+ * - 已设置 SMTP_HOST:自动对全部渠道(可用 POLL_CHANNELS 限定)创建临时邮箱、经 SMTP 发送探针邮件并轮询是否收到,用于验收渠道是否可用。
9
+ * - 未设置 SMTP_HOST:交互式选择单个渠道并轮询(原行为)。
10
+ * - 已设置 SMTP 时仍想交互:加参数 --interactive 或 -i
11
+ *
12
+ * SMTP 环境变量(自动模式必填 SMTP_HOST):
13
+ * SMTP_HOST 服务器地址
14
+ * SMTP_PORT 端口,默认 587
15
+ * SMTP_SECURE 含义随端口变化:
16
+ * - 465 / smtps / implicit:隐式 TLS(整条连接即 SSL,对应 nodemailer secure:true)
17
+ * - 587、2525、25:不使用 secure:true;1/true 表示「强制 STARTTLS」(requireTLS)
18
+ * - 其它端口:1/true 视为隐式 TLS(secure:true)
19
+ * SMTP_REQUIRE_TLS 是否强制 STARTTLS(仅非隐式 TLS 时有效)。未设时 587/2525 默认强制,25 默认不强制
20
+ * SMTP_IGNORE_TLS 1/true 则禁用 STARTTLS(明文,仅调试)
21
+ * SMTP_TLS_REJECT_UNAUTHORIZED 默认校验证书;0/false 允许自签名(仅调试)
22
+ * SMTP_USER 用户名(可选,无认证可留空)
23
+ * SMTP_PASS 密码(可选)
24
+ * SMTP_FROM 发件人地址,默认 SMTP_USER,再否则 noreply@localhost
25
+ *
26
+ * 可选:
27
+ * POLL_CHANNELS 逗号分隔的渠道 id,仅测这些;未设则测全部
28
+ * POLL_INTERVAL_MS 轮询间隔毫秒,默认 5000
29
+ * POLL_MAX_ROUNDS 每渠道最大轮询次数,默认 60
6
30
  */
7
31
 
32
+ import { randomBytes } from 'crypto';
8
33
  import * as readline from 'readline';
9
- import { generateEmail, getEmails, listChannels, getChannelInfo, Channel, EmailInfo, ChannelInfo, Email } from '../src';
34
+ import nodemailer from 'nodemailer';
35
+ import {
36
+ generateEmail,
37
+ getEmails,
38
+ listChannels,
39
+ getChannelInfo,
40
+ Channel,
41
+ EmailInfo,
42
+ ChannelInfo,
43
+ Email,
44
+ } from '../src';
10
45
 
11
- // 配置
12
- const POLL_INTERVAL = 5000; // 轮询间隔(毫秒)
13
- const MAX_POLL_COUNT = 60; // 最大轮询次数
46
+ const POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL_MS || '5000', 10);
47
+ const MAX_POLL_COUNT = parseInt(process.env.POLL_MAX_ROUNDS || '60', 10);
14
48
 
15
- function printJson(label: string, data: any): void {
49
+ interface SmtpConfig {
50
+ host: string;
51
+ port: number;
52
+ /** true = 隐式 TLS(SMTPS,典型端口 465) */
53
+ secure: boolean;
54
+ /** 明文连接后强制升级到 TLS(587 提交端口) */
55
+ requireTLS: boolean;
56
+ ignoreTLS: boolean;
57
+ tls?: { rejectUnauthorized?: boolean };
58
+ user?: string;
59
+ pass?: string;
60
+ from: string;
61
+ }
62
+
63
+ function printJson(label: string, data: unknown): void {
16
64
  console.log(`\n${label}:`);
17
65
  console.log(JSON.stringify(data, null, 2));
18
66
  }
@@ -34,16 +82,124 @@ function question(prompt: string): Promise<string> {
34
82
  });
35
83
  }
36
84
 
85
+ function envBool(v: string | undefined, defaultVal: boolean): boolean {
86
+ if (v === undefined || v.trim() === '') {
87
+ return defaultVal;
88
+ }
89
+ const s = v.trim().toLowerCase();
90
+ if (['1', 'true', 'yes'].includes(s)) {
91
+ return true;
92
+ }
93
+ if (['0', 'false', 'no'].includes(s)) {
94
+ return false;
95
+ }
96
+ return defaultVal;
97
+ }
98
+
99
+ function loadSmtpConfig(): SmtpConfig | null {
100
+ const host = process.env.SMTP_HOST?.trim();
101
+ if (!host) {
102
+ return null;
103
+ }
104
+ const port = parseInt(process.env.SMTP_PORT || '587', 10);
105
+ const sec = process.env.SMTP_SECURE?.trim().toLowerCase();
106
+
107
+ const submissionPorts = new Set([25, 587, 2525]);
108
+ const userWantsEncryption =
109
+ sec === '1' || sec === 'true' || sec === 'yes' || sec === 'implicit' || sec === 'smtps';
110
+
111
+ /** 465 或显式 smtps / implicit → 连接即 TLS */
112
+ const implicitTls =
113
+ port === 465 || sec === 'implicit' || sec === 'smtps';
114
+
115
+ let secure = false;
116
+ if (implicitTls) {
117
+ secure = true;
118
+ } else if (!submissionPorts.has(port) && userWantsEncryption) {
119
+ secure = true;
120
+ }
121
+ /*
122
+ * 587/2525/25 必须用 secure:false,由 STARTTLS 升级;此前把 SMTP_SECURE=1 当成 secure:true
123
+ * 会导致与提交端口不兼容(对方期待明文握手再 STARTTLS)。
124
+ */
125
+ const ignoreTLS = envBool(process.env.SMTP_IGNORE_TLS, false);
126
+
127
+ const defaultRequireStartTls =
128
+ !secure &&
129
+ !ignoreTLS &&
130
+ (port === 587 || port === 2525 || (port === 25 && userWantsEncryption));
131
+ const requireTLS = envBool(process.env.SMTP_REQUIRE_TLS, defaultRequireStartTls);
132
+
133
+ const rejectUnauthorized = envBool(process.env.SMTP_TLS_REJECT_UNAUTHORIZED, true);
134
+
135
+ const user = process.env.SMTP_USER?.trim() || undefined;
136
+ const pass = process.env.SMTP_PASS?.trim() || undefined;
137
+ const from =
138
+ process.env.SMTP_FROM?.trim() ||
139
+ user ||
140
+ 'noreply@localhost';
141
+ return {
142
+ host,
143
+ port,
144
+ secure,
145
+ requireTLS,
146
+ ignoreTLS,
147
+ tls: { rejectUnauthorized },
148
+ user,
149
+ pass,
150
+ from,
151
+ };
152
+ }
153
+
154
+ function channelsToTest(all: ChannelInfo[]): Channel[] {
155
+ const raw = process.env.POLL_CHANNELS?.trim();
156
+ if (!raw) {
157
+ return all.map((c) => c.channel);
158
+ }
159
+ const wanted = raw.split(',').map((s) => s.trim()).filter(Boolean);
160
+ const valid = new Set<Channel>(all.map((c) => c.channel));
161
+ const picked: Channel[] = [];
162
+ for (const id of wanted) {
163
+ if (valid.has(id as Channel)) {
164
+ picked.push(id as Channel);
165
+ } else {
166
+ console.warn(`未知渠道,已跳过: ${id}`);
167
+ }
168
+ }
169
+ return picked.length > 0 ? picked : all.map((c) => c.channel);
170
+ }
171
+
172
+ async function sendProbeMail(config: SmtpConfig, to: string, subject: string, text: string): Promise<void> {
173
+ const transporter = nodemailer.createTransport({
174
+ host: config.host,
175
+ port: config.port,
176
+ secure: config.secure,
177
+ requireTLS: config.requireTLS,
178
+ ignoreTLS: config.ignoreTLS,
179
+ tls: config.tls,
180
+ auth:
181
+ config.user !== undefined && config.pass !== undefined
182
+ ? { user: config.user, pass: config.pass }
183
+ : undefined,
184
+ });
185
+ await transporter.sendMail({
186
+ from: config.from,
187
+ to,
188
+ subject,
189
+ text,
190
+ });
191
+ }
192
+
37
193
  async function selectChannel(channels: ChannelInfo[]): Promise<Channel> {
38
194
  console.log('\n请选择渠道:');
39
195
  channels.forEach((ch, index) => {
40
196
  console.log(` [${index + 1}] ${ch.channel} - ${ch.name} (${ch.website})`);
41
197
  });
42
-
198
+
43
199
  while (true) {
44
200
  const input = await question('\n请输入渠道编号 (1-' + channels.length + '): ');
45
201
  const num = parseInt(input, 10);
46
-
202
+
47
203
  if (num >= 1 && num <= channels.length) {
48
204
  return channels[num - 1].channel;
49
205
  }
@@ -82,24 +238,20 @@ async function pollEmails(emailInfo: EmailInfo): Promise<void> {
82
238
 
83
239
  while (pollCount < MAX_POLL_COUNT) {
84
240
  pollCount++;
85
-
241
+
86
242
  try {
87
243
  const result = await getEmails(emailInfo);
88
244
 
89
245
  const timestamp = new Date().toLocaleTimeString();
90
-
246
+
91
247
  if (result.emails.length > 0) {
92
248
  console.log(`\n[${timestamp}] 🎉 收到 ${result.emails.length} 封邮件!\n`);
93
-
94
- // 打印原始返回数据
249
+
95
250
  printJson('返回数据 (GetEmailsResult)', result);
96
251
 
97
252
  for (let i = 0; i < result.emails.length; i++) {
98
253
  printEmail(result.emails[i], i);
99
254
  }
100
-
101
- // 收到邮件后可以选择继续轮询或退出
102
- // break;
103
255
  } else {
104
256
  process.stdout.write(`\r[${timestamp}] 第 ${pollCount}/${MAX_POLL_COUNT} 次检查,暂无新邮件...`);
105
257
  }
@@ -113,31 +265,130 @@ async function pollEmails(emailInfo: EmailInfo): Promise<void> {
113
265
  console.log('\n\n已达到最大轮询次数,停止轮询');
114
266
  }
115
267
 
116
- async function main(): Promise<void> {
268
+ type ProbeResult = 'ok' | 'fail_create' | 'fail_smtp' | 'fail_timeout';
269
+
270
+ async function probeChannel(channel: Channel, smtp: SmtpConfig): Promise<ProbeResult> {
271
+ const emailInfo = await generateEmail({ channel, channelFallback: false });
272
+ if (!emailInfo) {
273
+ console.log(' ❌ 创建临时邮箱失败');
274
+ return 'fail_create';
275
+ }
276
+
277
+ const to = String(emailInfo.email ?? '').trim();
278
+ if (!to || !to.includes('@')) {
279
+ console.log(' ❌ 无效收件地址(渠道未返回邮箱,例如 linshi-email 触发频率限制时 API 会返回空 data)');
280
+ return 'fail_create';
281
+ }
282
+
283
+ const marker = `tm-sdk-${Date.now()}-${randomBytes(4).toString('hex')}`;
284
+ const subject = `[tempmail-sdk] ${marker}`;
285
+ const text = `自动化探针邮件\nmarker: ${marker}\nchannel: ${channel}\n`;
286
+
287
+ try {
288
+ await sendProbeMail(smtp, to, subject, text);
289
+ } catch (err) {
290
+ console.log(` ❌ SMTP 发送失败: ${err}`);
291
+ return 'fail_smtp';
292
+ }
293
+
294
+ console.log(` 📤 已发送测试邮件 → ${to}`);
295
+
296
+ let probeMismatchLogged = false;
297
+ for (let i = 0; i < MAX_POLL_COUNT; i++) {
298
+ const result = await getEmails(emailInfo);
299
+ if (result.success && result.emails.length > 0) {
300
+ const hit = result.emails.some(
301
+ (e) =>
302
+ (e.subject && e.subject.includes(marker)) ||
303
+ (e.text && e.text.includes(marker)) ||
304
+ (e.html && e.html.includes(marker)),
305
+ );
306
+ if (hit) {
307
+ console.log(` ✅ 已收到匹配邮件(第 ${i + 1} 次轮询)`);
308
+ return 'ok';
309
+ }
310
+ const hintChannel = channel === 'smail-pw' || channel === 'chatgpt-org-uk';
311
+ if (hintChannel && !probeMismatchLogged) {
312
+ probeMismatchLogged = true;
313
+ const subs = result.emails.map((e) => e.subject || '(无主题)').join('; ');
314
+ console.log(`\n 提示: 已解析到 ${result.emails.length} 封邮件,但主题/正文/HTML 未含探针标记。主题摘要: ${subs.slice(0, 200)}`);
315
+ }
316
+ }
317
+ process.stdout.write(`\r 轮询 ${i + 1}/${MAX_POLL_COUNT}...`);
318
+ await sleep(POLL_INTERVAL);
319
+ }
320
+
321
+ console.log('\n ❌ 超时未收到带标记的邮件');
322
+ if (channel === 'smail-pw') {
323
+ console.log(
324
+ ' 提示: smail.pw 依赖 RSC 响应解析;若已修复解析仍超时,可能是外发 SMTP 被拒/延迟,可加大 POLL_MAX_ROUNDS、POLL_INTERVAL_MS 后重试。',
325
+ );
326
+ }
327
+ return 'fail_timeout';
328
+ }
329
+
330
+ async function runSmtpAutoSuite(smtp: SmtpConfig): Promise<void> {
331
+ console.log('═'.repeat(50));
332
+ console.log(' 临时邮箱 Demo — SMTP 自动探针(默认测全部渠道)');
333
+ console.log('═'.repeat(50));
334
+ console.log(
335
+ `\nSMTP: ${smtp.host}:${smtp.port} implicitTLS=${smtp.secure} requireTLS=${smtp.requireTLS} ignoreTLS=${smtp.ignoreTLS} tls.rejectUnauthorized=${smtp.tls?.rejectUnauthorized ?? true} from=${smtp.from}`,
336
+ );
337
+
338
+ const all = listChannels();
339
+ const targets = channelsToTest(all);
340
+ console.log(`\n待测渠道 (${targets.length}): ${targets.join(', ')}\n`);
341
+
342
+ const stats: Record<ProbeResult, number> = {
343
+ ok: 0,
344
+ fail_create: 0,
345
+ fail_smtp: 0,
346
+ fail_timeout: 0,
347
+ };
348
+
349
+ for (const channel of targets) {
350
+ const info = getChannelInfo(channel);
351
+ const label = info ? `${info.name} (${channel})` : channel;
352
+ console.log(`\n── ${label} ──`);
353
+ const r = await probeChannel(channel, smtp);
354
+ stats[r]++;
355
+ }
356
+
357
+ console.log('\n' + '═'.repeat(50));
358
+ console.log(' 汇总');
359
+ console.log('═'.repeat(50));
360
+ console.log(` 成功收到: ${stats.ok}`);
361
+ console.log(` 创建失败: ${stats.fail_create}`);
362
+ console.log(` SMTP 失败: ${stats.fail_smtp}`);
363
+ console.log(` 超时未收到: ${stats.fail_timeout}`);
364
+
365
+ const bad = stats.fail_create + stats.fail_smtp + stats.fail_timeout;
366
+ if (bad > 0) {
367
+ process.exitCode = 1;
368
+ }
369
+ }
370
+
371
+ async function runInteractive(): Promise<void> {
117
372
  console.log('═'.repeat(50));
118
373
  console.log(' 临时邮箱 Demo - 获取邮箱并轮询邮件');
119
374
  console.log('═'.repeat(50));
120
375
 
121
- // 1. 列出所有支持的渠道
122
376
  console.log('\n[1] 列出所有支持的渠道...');
123
377
  const channels = listChannels();
124
378
  printJson('支持的渠道列表', channels);
125
379
 
126
- // 2. 让用户选择渠道
127
380
  console.log('\n[2] 选择渠道...');
128
381
  const selectedChannel = await selectChannel(channels);
129
-
130
- // 3. 获取指定渠道信息
382
+
131
383
  console.log(`\n[3] 获取渠道 "${selectedChannel}" 信息...`);
132
384
  const channelInfo = getChannelInfo(selectedChannel);
133
385
  printJson('渠道信息', channelInfo);
134
386
 
135
- // 4. 从指定渠道获取邮箱
136
387
  console.log(`\n[4] 从 ${selectedChannel} 渠道获取临时邮箱...`);
137
-
388
+
138
389
  try {
139
390
  const emailInfo = await generateEmail({ channel: selectedChannel });
140
-
391
+
141
392
  if (!emailInfo) {
142
393
  console.error('\n❌ 所有渠道均不可用');
143
394
  process.exit(1);
@@ -146,7 +397,7 @@ async function main(): Promise<void> {
146
397
 
147
398
  console.log('\n✅ 获取邮箱成功!');
148
399
  printJson('返回数据 (EmailInfo)', emailInfo);
149
-
400
+
150
401
  console.log('\n📋 邮箱信息:');
151
402
  console.log(` 渠道: ${emailInfo.channel}`);
152
403
  console.log(` 邮箱: ${emailInfo.email}`);
@@ -158,14 +409,25 @@ async function main(): Promise<void> {
158
409
  }
159
410
 
160
411
  console.log('\n📧 请发送邮件到以上邮箱地址进行测试');
161
-
162
- // 5. 轮询获取邮件
163
- await pollEmails(emailInfo);
164
412
 
413
+ await pollEmails(emailInfo);
165
414
  } catch (error) {
166
415
  console.error(`\n❌ 获取邮箱失败: ${error}`);
167
416
  process.exit(1);
168
417
  }
169
418
  }
170
419
 
420
+ async function main(): Promise<void> {
421
+ const argv = new Set(process.argv.slice(2));
422
+ const forceInteractive = argv.has('--interactive') || argv.has('-i');
423
+ const smtp = loadSmtpConfig();
424
+
425
+ if (smtp && !forceInteractive) {
426
+ await runSmtpAutoSuite(smtp);
427
+ return;
428
+ }
429
+
430
+ await runInteractive();
431
+ }
432
+
171
433
  main().catch(console.error);
package/dist/config.d.ts CHANGED
@@ -6,6 +6,9 @@
6
6
  * TEMPMAIL_PROXY - 代理 URL
7
7
  * TEMPMAIL_TIMEOUT - 超时毫秒数
8
8
  * TEMPMAIL_INSECURE - 设为 "1" 或 "true" 跳过 SSL 验证
9
+ * DROPMAIL_AUTH_TOKEN / DROPMAIL_API_TOKEN - DropMail GraphQL 的 af_ 令牌(可选;未设置时 SDK 会 /api/token/generate 获取并在将过期时用 /api/token/renew 续期)
10
+ * DROPMAIL_NO_AUTO_TOKEN - 设为 "1" 或 "true" 则禁止自动拉取/续期令牌
11
+ * DROPMAIL_RENEW_LIFETIME - 自动续期请求的 lifetime,默认 1d(与官网 renew 接口一致)
9
12
  *
10
13
  * Node.js 环境下设置 insecure 会自动设置 NODE_TLS_REJECT_UNAUTHORIZED=0
11
14
  */
@@ -19,6 +22,19 @@ export interface SDKConfig {
19
22
  timeout?: number;
20
23
  /** 跳过 SSL 证书验证(调试用) */
21
24
  insecure?: boolean;
25
+ /**
26
+ * DropMail 渠道专用:GraphQL 端点 `https://dropmail.me/api/graphql/${token}` 中的 token(须为官网生成的 `af_` 前缀令牌)。
27
+ * 也可通过环境变量 DROPMAIL_AUTH_TOKEN 或 DROPMAIL_API_TOKEN 提供。
28
+ * 未配置且未设置 DROPMAIL_NO_AUTO_TOKEN 时,会向 https://dropmail.me/api/token/generate 申请 1h 令牌并内存缓存。
29
+ */
30
+ dropmailAuthToken?: string;
31
+ /** 为 true 时不自动请求 token(须配置 dropmailAuthToken 或环境变量) */
32
+ dropmailDisableAutoToken?: boolean;
33
+ /**
34
+ * 自动管理令牌即将过期时,调用 /api/token/renew 使用的 lifetime(如 1h、1d)。
35
+ * 也可通过环境变量 DROPMAIL_RENEW_LIFETIME 设置。
36
+ */
37
+ dropmailRenewLifetime?: string;
22
38
  /**
23
39
  * 自定义 fetch 函数,用于完全控制 HTTP 请求行为
24
40
  * 当设置了 proxy 但环境不支持 undici 时,可通过此选项传入支持代理的 fetch 实现
package/dist/config.js CHANGED
@@ -7,6 +7,9 @@
7
7
  * TEMPMAIL_PROXY - 代理 URL
8
8
  * TEMPMAIL_TIMEOUT - 超时毫秒数
9
9
  * TEMPMAIL_INSECURE - 设为 "1" 或 "true" 跳过 SSL 验证
10
+ * DROPMAIL_AUTH_TOKEN / DROPMAIL_API_TOKEN - DropMail GraphQL 的 af_ 令牌(可选;未设置时 SDK 会 /api/token/generate 获取并在将过期时用 /api/token/renew 续期)
11
+ * DROPMAIL_NO_AUTO_TOKEN - 设为 "1" 或 "true" 则禁止自动拉取/续期令牌
12
+ * DROPMAIL_RENEW_LIFETIME - 自动续期请求的 lifetime,默认 1d(与官网 renew 接口一致)
10
13
  *
11
14
  * Node.js 环境下设置 insecure 会自动设置 NODE_TLS_REJECT_UNAUTHORIZED=0
12
15
  */
@@ -65,4 +68,4 @@ function setConfig(config) {
65
68
  function getConfig() {
66
69
  return globalConfig;
67
70
  }
68
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7R0FVRzs7QUF3REgsOEJBVUM7QUFHRCw4QkFFQztBQXBERCxrQkFBa0I7QUFDbEIsU0FBUyxhQUFhO0lBQ3BCLE1BQU0sTUFBTSxHQUFjLEVBQUUsQ0FBQztJQUM3QixJQUFJLE9BQU8sT0FBTyxLQUFLLFdBQVcsSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDbEQsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQy9CLE1BQU0sQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUM7UUFDNUMsQ0FBQztRQUNELElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQ2pDLE1BQU0sQ0FBQyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGdCQUFnQixFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQ3JELElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUM7Z0JBQUUsTUFBTSxDQUFDLE9BQU8sR0FBRyxDQUFDLENBQUM7UUFDN0MsQ0FBQztRQUNELElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsS0FBSyxHQUFHLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsRUFBRSxXQUFXLEVBQUUsS0FBSyxNQUFNLEVBQUUsQ0FBQztZQUNyRyxNQUFNLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQztRQUN6QixDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUM7QUFFRCxJQUFJLFlBQVksR0FBYyxhQUFhLEVBQUUsQ0FBQztBQUU5Qzs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILFNBQWdCLFNBQVMsQ0FBQyxNQUFpQjtJQUN6QyxZQUFZLEdBQUcsRUFBRSxHQUFHLE1BQU0sRUFBRSxDQUFDO0lBQzdCLG1DQUFtQztJQUNuQyxJQUFJLE9BQU8sT0FBTyxLQUFLLFdBQVcsSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDbEQsSUFBSSxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDcEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyw0QkFBNEIsR0FBRyxHQUFHLENBQUM7UUFDakQsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUM7UUFDbEQsQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDO0FBRUQsb0JBQW9CO0FBQ3BCLFNBQWdCLFNBQVM7SUFDdkIsT0FBTyxZQUFZLENBQUM7QUFDdEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU0RLIOWFqOWxgOmFjee9rlxuICog5YyF5ZCr5Luj55CG44CB6LaF5pe244CBU1NMIOetieiuvue9ru+8jOS9nOeUqOS6juaJgOaciSBIVFRQIOivt+axglxuICpcbiAqIOaUr+aMgeeOr+Wig+WPmOmHj+iHquWKqOivu+WPlu+8iOS8mOWFiOe6p+S9juS6juS7o+eggeiuvue9ru+8ie+8mlxuICogICBURU1QTUFJTF9QUk9YWSAgICAtIOS7o+eQhiBVUkxcbiAqICAgVEVNUE1BSUxfVElNRU9VVCAgLSDotoXml7bmr6vnp5LmlbBcbiAqICAgVEVNUE1BSUxfSU5TRUNVUkUgLSDorr7kuLogXCIxXCIg5oiWIFwidHJ1ZVwiIOi3s+i/hyBTU0wg6aqM6K+BXG4gKlxuICogTm9kZS5qcyDnjq/looPkuIvorr7nva4gaW5zZWN1cmUg5Lya6Ieq5Yqo6K6+572uIE5PREVfVExTX1JFSkVDVF9VTkFVVEhPUklaRUQ9MFxuICovXG5cbi8qKlxuICogU0RLIOWFqOWxgOmFjee9ruaOpeWPo1xuICovXG5leHBvcnQgaW50ZXJmYWNlIFNES0NvbmZpZyB7XG4gIC8qKiDku6PnkIYgVVJM77yM5pSv5oyBIGh0dHAvaHR0cHMvc29ja3M177yM5aaCIFwiaHR0cDovLzEyNy4wLjAuMTo3ODkwXCIgKi9cbiAgcHJveHk/OiBzdHJpbmc7XG4gIC8qKiDlhajlsYDpu5jorqTotoXml7bvvIjmr6vnp5LvvInvvIzpu5jorqQgMTUwMDAgKi9cbiAgdGltZW91dD86IG51bWJlcjtcbiAgLyoqIOi3s+i/hyBTU0wg6K+B5Lmm6aqM6K+B77yI6LCD6K+V55So77yJICovXG4gIGluc2VjdXJlPzogYm9vbGVhbjtcbiAgLyoqXG4gICAqIOiHquWumuS5iSBmZXRjaCDlh73mlbDvvIznlKjkuo7lrozlhajmjqfliLYgSFRUUCDor7fmsYLooYzkuLpcbiAgICog5b2T6K6+572u5LqGIHByb3h5IOS9hueOr+Wig+S4jeaUr+aMgSB1bmRpY2kg5pe277yM5Y+v6YCa6L+H5q2k6YCJ6aG55Lyg5YWl5pSv5oyB5Luj55CG55qEIGZldGNoIOWunueOsFxuICAgKi9cbiAgY3VzdG9tRmV0Y2g/OiB0eXBlb2YgZmV0Y2g7XG59XG5cbi8qKiDku47njq/looPlj5jph4/or7vlj5bpu5jorqTphY3nva4gKi9cbmZ1bmN0aW9uIGxvYWRFbnZDb25maWcoKTogU0RLQ29uZmlnIHtcbiAgY29uc3QgY29uZmlnOiBTREtDb25maWcgPSB7fTtcbiAgaWYgKHR5cGVvZiBwcm9jZXNzICE9PSAndW5kZWZpbmVkJyAmJiBwcm9jZXNzLmVudikge1xuICAgIGlmIChwcm9jZXNzLmVudi5URU1QTUFJTF9QUk9YWSkge1xuICAgICAgY29uZmlnLnByb3h5ID0gcHJvY2Vzcy5lbnYuVEVNUE1BSUxfUFJPWFk7XG4gICAgfVxuICAgIGlmIChwcm9jZXNzLmVudi5URU1QTUFJTF9USU1FT1VUKSB7XG4gICAgICBjb25zdCB0ID0gcGFyc2VJbnQocHJvY2Vzcy5lbnYuVEVNUE1BSUxfVElNRU9VVCwgMTApO1xuICAgICAgaWYgKCFpc05hTih0KSAmJiB0ID4gMCkgY29uZmlnLnRpbWVvdXQgPSB0O1xuICAgIH1cbiAgICBpZiAocHJvY2Vzcy5lbnYuVEVNUE1BSUxfSU5TRUNVUkUgPT09ICcxJyB8fCBwcm9jZXNzLmVudi5URU1QTUFJTF9JTlNFQ1VSRT8udG9Mb3dlckNhc2UoKSA9PT0gJ3RydWUnKSB7XG4gICAgICBjb25maWcuaW5zZWN1cmUgPSB0cnVlO1xuICAgIH1cbiAgfVxuICByZXR1cm4gY29uZmlnO1xufVxuXG5sZXQgZ2xvYmFsQ29uZmlnOiBTREtDb25maWcgPSBsb2FkRW52Q29uZmlnKCk7XG5cbi8qKlxuICog6K6+572uIFNESyDlhajlsYDphY3nva5cbiAqXG4gKiBAZXhhbXBsZVxuICogYGBgdHNcbiAqIC8vIOS4gOihjOi3s+i/hyBTU0wg6aqM6K+BXG4gKiBzZXRDb25maWcoeyBpbnNlY3VyZTogdHJ1ZSB9KTtcbiAqXG4gKiAvLyDorr7nva7ku6PnkIblkozotoXml7ZcbiAqIHNldENvbmZpZyh7IHByb3h5OiAnaHR0cDovLzEyNy4wLjAuMTo3ODkwJywgdGltZW91dDogMzAwMDAgfSk7XG4gKlxuICogLy8g5L2/55So6Ieq5a6a5LmJIGZldGNo77yI5aaC5pSv5oyB5Luj55CG55qEIHVuZGljae+8iVxuICogaW1wb3J0IHsgUHJveHlBZ2VudCwgZmV0Y2ggYXMgdW5kaWNpRmV0Y2ggfSBmcm9tICd1bmRpY2knO1xuICogY29uc3QgYWdlbnQgPSBuZXcgUHJveHlBZ2VudCgnaHR0cDovLzEyNy4wLjAuMTo3ODkwJyk7XG4gKiBzZXRDb25maWcoeyBjdXN0b21GZXRjaDogKHVybCwgaW5pdCkgPT4gdW5kaWNpRmV0Y2godXJsLCB7IC4uLmluaXQsIGRpc3BhdGNoZXI6IGFnZW50IH0pIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZXRDb25maWcoY29uZmlnOiBTREtDb25maWcpOiB2b2lkIHtcbiAgZ2xvYmFsQ29uZmlnID0geyAuLi5jb25maWcgfTtcbiAgLyogTm9kZS5qcyDnjq/looPkuIsgaW5zZWN1cmUg6Ieq5Yqo6K6+572u546v5aKD5Y+Y6YePICovXG4gIGlmICh0eXBlb2YgcHJvY2VzcyAhPT0gJ3VuZGVmaW5lZCcgJiYgcHJvY2Vzcy5lbnYpIHtcbiAgICBpZiAoY29uZmlnLmluc2VjdXJlKSB7XG4gICAgICBwcm9jZXNzLmVudi5OT0RFX1RMU19SRUpFQ1RfVU5BVVRIT1JJWkVEID0gJzAnO1xuICAgIH0gZWxzZSB7XG4gICAgICBkZWxldGUgcHJvY2Vzcy5lbnYuTk9ERV9UTFNfUkVKRUNUX1VOQVVUSE9SSVpFRDtcbiAgICB9XG4gIH1cbn1cblxuLyoqIOiOt+WPluW9k+WJjSBTREsg5YWo5bGA6YWN572uICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q29uZmlnKCk6IFNES0NvbmZpZyB7XG4gIHJldHVybiBnbG9iYWxDb25maWc7XG59XG4iXX0=
71
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7R0FhRzs7QUFxRUgsOEJBVUM7QUFHRCw4QkFFQztBQXBERCxrQkFBa0I7QUFDbEIsU0FBUyxhQUFhO0lBQ3BCLE1BQU0sTUFBTSxHQUFjLEVBQUUsQ0FBQztJQUM3QixJQUFJLE9BQU8sT0FBTyxLQUFLLFdBQVcsSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDbEQsSUFBSSxPQUFPLENBQUMsR0FBRyxDQUFDLGNBQWMsRUFBRSxDQUFDO1lBQy9CLE1BQU0sQ0FBQyxLQUFLLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUM7UUFDNUMsQ0FBQztRQUNELElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQ2pDLE1BQU0sQ0FBQyxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGdCQUFnQixFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQ3JELElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUM7Z0JBQUUsTUFBTSxDQUFDLE9BQU8sR0FBRyxDQUFDLENBQUM7UUFDN0MsQ0FBQztRQUNELElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsS0FBSyxHQUFHLElBQUksT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsRUFBRSxXQUFXLEVBQUUsS0FBSyxNQUFNLEVBQUUsQ0FBQztZQUNyRyxNQUFNLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQztRQUN6QixDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUM7QUFFRCxJQUFJLFlBQVksR0FBYyxhQUFhLEVBQUUsQ0FBQztBQUU5Qzs7Ozs7Ozs7Ozs7Ozs7OztHQWdCRztBQUNILFNBQWdCLFNBQVMsQ0FBQyxNQUFpQjtJQUN6QyxZQUFZLEdBQUcsRUFBRSxHQUFHLE1BQU0sRUFBRSxDQUFDO0lBQzdCLG1DQUFtQztJQUNuQyxJQUFJLE9BQU8sT0FBTyxLQUFLLFdBQVcsSUFBSSxPQUFPLENBQUMsR0FBRyxFQUFFLENBQUM7UUFDbEQsSUFBSSxNQUFNLENBQUMsUUFBUSxFQUFFLENBQUM7WUFDcEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyw0QkFBNEIsR0FBRyxHQUFHLENBQUM7UUFDakQsQ0FBQzthQUFNLENBQUM7WUFDTixPQUFPLE9BQU8sQ0FBQyxHQUFHLENBQUMsNEJBQTRCLENBQUM7UUFDbEQsQ0FBQztJQUNILENBQUM7QUFDSCxDQUFDO0FBRUQsb0JBQW9CO0FBQ3BCLFNBQWdCLFNBQVM7SUFDdkIsT0FBTyxZQUFZLENBQUM7QUFDdEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogU0RLIOWFqOWxgOmFjee9rlxuICog5YyF5ZCr5Luj55CG44CB6LaF5pe244CBU1NMIOetieiuvue9ru+8jOS9nOeUqOS6juaJgOaciSBIVFRQIOivt+axglxuICpcbiAqIOaUr+aMgeeOr+Wig+WPmOmHj+iHquWKqOivu+WPlu+8iOS8mOWFiOe6p+S9juS6juS7o+eggeiuvue9ru+8ie+8mlxuICogICBURU1QTUFJTF9QUk9YWSAgICAtIOS7o+eQhiBVUkxcbiAqICAgVEVNUE1BSUxfVElNRU9VVCAgLSDotoXml7bmr6vnp5LmlbBcbiAqICAgVEVNUE1BSUxfSU5TRUNVUkUgLSDorr7kuLogXCIxXCIg5oiWIFwidHJ1ZVwiIOi3s+i/hyBTU0wg6aqM6K+BXG4gKiAgIERST1BNQUlMX0FVVEhfVE9LRU4gLyBEUk9QTUFJTF9BUElfVE9LRU4gLSBEcm9wTWFpbCBHcmFwaFFMIOeahCBhZl8g5Luk54mM77yI5Y+v6YCJ77yb5pyq6K6+572u5pe2IFNESyDkvJogL2FwaS90b2tlbi9nZW5lcmF0ZSDojrflj5blubblnKjlsIbov4fmnJ/ml7bnlKggL2FwaS90b2tlbi9yZW5ldyDnu63mnJ/vvIlcbiAqICAgRFJPUE1BSUxfTk9fQVVUT19UT0tFTiAtIOiuvuS4uiBcIjFcIiDmiJYgXCJ0cnVlXCIg5YiZ56aB5q2i6Ieq5Yqo5ouJ5Y+WL+e7reacn+S7pOeJjFxuICogICBEUk9QTUFJTF9SRU5FV19MSUZFVElNRSAtIOiHquWKqOe7reacn+ivt+axgueahCBsaWZldGltZe+8jOm7mOiupCAxZO+8iOS4juWumOe9kSByZW5ldyDmjqXlj6PkuIDoh7TvvIlcbiAqXG4gKiBOb2RlLmpzIOeOr+Wig+S4i+iuvue9riBpbnNlY3VyZSDkvJroh6rliqjorr7nva4gTk9ERV9UTFNfUkVKRUNUX1VOQVVUSE9SSVpFRD0wXG4gKi9cblxuLyoqXG4gKiBTREsg5YWo5bGA6YWN572u5o6l5Y+jXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU0RLQ29uZmlnIHtcbiAgLyoqIOS7o+eQhiBVUkzvvIzmlK/mjIEgaHR0cC9odHRwcy9zb2NrczXvvIzlpoIgXCJodHRwOi8vMTI3LjAuMC4xOjc4OTBcIiAqL1xuICBwcm94eT86IHN0cmluZztcbiAgLyoqIOWFqOWxgOm7mOiupOi2heaXtu+8iOavq+enku+8ie+8jOm7mOiupCAxNTAwMCAqL1xuICB0aW1lb3V0PzogbnVtYmVyO1xuICAvKiog6Lez6L+HIFNTTCDor4Hkuabpqozor4HvvIjosIPor5XnlKjvvIkgKi9cbiAgaW5zZWN1cmU/OiBib29sZWFuO1xuICAvKipcbiAgICogRHJvcE1haWwg5rig6YGT5LiT55So77yaR3JhcGhRTCDnq6/ngrkgYGh0dHBzOi8vZHJvcG1haWwubWUvYXBpL2dyYXBocWwvJHt0b2tlbn1gIOS4reeahCB0b2tlbu+8iOmhu+S4uuWumOe9keeUn+aIkOeahCBgYWZfYCDliY3nvIDku6TniYzvvInjgIJcbiAgICog5Lmf5Y+v6YCa6L+H546v5aKD5Y+Y6YePIERST1BNQUlMX0FVVEhfVE9LRU4g5oiWIERST1BNQUlMX0FQSV9UT0tFTiDmj5DkvpvjgIJcbiAgICog5pyq6YWN572u5LiU5pyq6K6+572uIERST1BNQUlMX05PX0FVVE9fVE9LRU4g5pe277yM5Lya5ZCRIGh0dHBzOi8vZHJvcG1haWwubWUvYXBpL3Rva2VuL2dlbmVyYXRlIOeUs+ivtyAxaCDku6TniYzlubblhoXlrZjnvJPlrZjjgIJcbiAgICovXG4gIGRyb3BtYWlsQXV0aFRva2VuPzogc3RyaW5nO1xuICAvKiog5Li6IHRydWUg5pe25LiN6Ieq5Yqo6K+35rGCIHRva2Vu77yI6aG76YWN572uIGRyb3BtYWlsQXV0aFRva2VuIOaIlueOr+Wig+WPmOmHj++8iSAqL1xuICBkcm9wbWFpbERpc2FibGVBdXRvVG9rZW4/OiBib29sZWFuO1xuICAvKipcbiAgICog6Ieq5Yqo566h55CG5Luk54mM5Y2z5bCG6L+H5pyf5pe277yM6LCD55SoIC9hcGkvdG9rZW4vcmVuZXcg5L2/55So55qEIGxpZmV0aW1l77yI5aaCIDFo44CBMWTvvInjgIJcbiAgICog5Lmf5Y+v6YCa6L+H546v5aKD5Y+Y6YePIERST1BNQUlMX1JFTkVXX0xJRkVUSU1FIOiuvue9ruOAglxuICAgKi9cbiAgZHJvcG1haWxSZW5ld0xpZmV0aW1lPzogc3RyaW5nO1xuICAvKipcbiAgICog6Ieq5a6a5LmJIGZldGNoIOWHveaVsO+8jOeUqOS6juWujOWFqOaOp+WItiBIVFRQIOivt+axguihjOS4ulxuICAgKiDlvZPorr7nva7kuoYgcHJveHkg5L2G546v5aKD5LiN5pSv5oyBIHVuZGljaSDml7bvvIzlj6/pgJrov4fmraTpgInpobnkvKDlhaXmlK/mjIHku6PnkIbnmoQgZmV0Y2gg5a6e546wXG4gICAqL1xuICBjdXN0b21GZXRjaD86IHR5cGVvZiBmZXRjaDtcbn1cblxuLyoqIOS7jueOr+Wig+WPmOmHj+ivu+WPlum7mOiupOmFjee9riAqL1xuZnVuY3Rpb24gbG9hZEVudkNvbmZpZygpOiBTREtDb25maWcge1xuICBjb25zdCBjb25maWc6IFNES0NvbmZpZyA9IHt9O1xuICBpZiAodHlwZW9mIHByb2Nlc3MgIT09ICd1bmRlZmluZWQnICYmIHByb2Nlc3MuZW52KSB7XG4gICAgaWYgKHByb2Nlc3MuZW52LlRFTVBNQUlMX1BST1hZKSB7XG4gICAgICBjb25maWcucHJveHkgPSBwcm9jZXNzLmVudi5URU1QTUFJTF9QUk9YWTtcbiAgICB9XG4gICAgaWYgKHByb2Nlc3MuZW52LlRFTVBNQUlMX1RJTUVPVVQpIHtcbiAgICAgIGNvbnN0IHQgPSBwYXJzZUludChwcm9jZXNzLmVudi5URU1QTUFJTF9USU1FT1VULCAxMCk7XG4gICAgICBpZiAoIWlzTmFOKHQpICYmIHQgPiAwKSBjb25maWcudGltZW91dCA9IHQ7XG4gICAgfVxuICAgIGlmIChwcm9jZXNzLmVudi5URU1QTUFJTF9JTlNFQ1VSRSA9PT0gJzEnIHx8IHByb2Nlc3MuZW52LlRFTVBNQUlMX0lOU0VDVVJFPy50b0xvd2VyQ2FzZSgpID09PSAndHJ1ZScpIHtcbiAgICAgIGNvbmZpZy5pbnNlY3VyZSA9IHRydWU7XG4gICAgfVxuICB9XG4gIHJldHVybiBjb25maWc7XG59XG5cbmxldCBnbG9iYWxDb25maWc6IFNES0NvbmZpZyA9IGxvYWRFbnZDb25maWcoKTtcblxuLyoqXG4gKiDorr7nva4gU0RLIOWFqOWxgOmFjee9rlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0c1xuICogLy8g5LiA6KGM6Lez6L+HIFNTTCDpqozor4FcbiAqIHNldENvbmZpZyh7IGluc2VjdXJlOiB0cnVlIH0pO1xuICpcbiAqIC8vIOiuvue9ruS7o+eQhuWSjOi2heaXtlxuICogc2V0Q29uZmlnKHsgcHJveHk6ICdodHRwOi8vMTI3LjAuMC4xOjc4OTAnLCB0aW1lb3V0OiAzMDAwMCB9KTtcbiAqXG4gKiAvLyDkvb/nlKjoh6rlrprkuYkgZmV0Y2jvvIjlpoLmlK/mjIHku6PnkIbnmoQgdW5kaWNp77yJXG4gKiBpbXBvcnQgeyBQcm94eUFnZW50LCBmZXRjaCBhcyB1bmRpY2lGZXRjaCB9IGZyb20gJ3VuZGljaSc7XG4gKiBjb25zdCBhZ2VudCA9IG5ldyBQcm94eUFnZW50KCdodHRwOi8vMTI3LjAuMC4xOjc4OTAnKTtcbiAqIHNldENvbmZpZyh7IGN1c3RvbUZldGNoOiAodXJsLCBpbml0KSA9PiB1bmRpY2lGZXRjaCh1cmwsIHsgLi4uaW5pdCwgZGlzcGF0Y2hlcjogYWdlbnQgfSkgfSk7XG4gKiBgYGBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNldENvbmZpZyhjb25maWc6IFNES0NvbmZpZyk6IHZvaWQge1xuICBnbG9iYWxDb25maWcgPSB7IC4uLmNvbmZpZyB9O1xuICAvKiBOb2RlLmpzIOeOr+Wig+S4iyBpbnNlY3VyZSDoh6rliqjorr7nva7njq/looPlj5jph48gKi9cbiAgaWYgKHR5cGVvZiBwcm9jZXNzICE9PSAndW5kZWZpbmVkJyAmJiBwcm9jZXNzLmVudikge1xuICAgIGlmIChjb25maWcuaW5zZWN1cmUpIHtcbiAgICAgIHByb2Nlc3MuZW52Lk5PREVfVExTX1JFSkVDVF9VTkFVVEhPUklaRUQgPSAnMCc7XG4gICAgfSBlbHNlIHtcbiAgICAgIGRlbGV0ZSBwcm9jZXNzLmVudi5OT0RFX1RMU19SRUpFQ1RfVU5BVVRIT1JJWkVEO1xuICAgIH1cbiAgfVxufVxuXG4vKiog6I635Y+W5b2T5YmNIFNESyDlhajlsYDphY3nva4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRDb25maWcoKTogU0RLQ29uZmlnIHtcbiAgcmV0dXJuIGdsb2JhbENvbmZpZztcbn1cbiJdfQ==
package/dist/index.d.ts CHANGED
@@ -5,6 +5,8 @@ export { normalizeEmail } from './normalize';
5
5
  export { withRetry, fetchWithTimeout, RetryOptions } from './retry';
6
6
  export { LogLevel, LogHandler, setLogLevel, getLogLevel, setLogger, logger } from './logger';
7
7
  export { SDKConfig, setConfig, getConfig } from './config';
8
+ export type { SyntheticBrowserProfile } from './providers/linshi-token';
9
+ export { deriveLinshiApiPathKey, randomSyntheticLinshiKey, randomBrowserLikeProfile, syntheticVisitorIdFromProfile, } from './providers/linshi-token';
8
10
  /**
9
11
  * 渠道信息,包含渠道标识、显示名称和对应网站
10
12
  */
@@ -39,7 +41,8 @@ export declare function getChannelInfo(channel: Channel): ChannelInfo | undefine
39
41
  * 创建临时邮箱
40
42
  *
41
43
  * 错误处理策略:
42
- * - 指定渠道失败时,自动尝试其他可用渠道(打乱顺序逐个尝试)
44
+ * - 指定渠道失败时,默认自动尝试其他可用渠道(打乱顺序逐个尝试)
45
+ * - `channelFallback: false` 且指定了 `channel` 时,仅尝试该渠道,失败即返回 null
43
46
  * - 未指定渠道时,打乱全部渠道逐个尝试,直到成功
44
47
  * - 所有渠道均不可用时返回 null(不抛出异常)
45
48
  *