@bernierllc/nevarmail

中文文档
0.30.0发布于 2026/11/05MITTypeScriptES Module零依赖近 30 天下载 2492

NevarMail v1 API的官方客户端. 已打入,回放429/5xx,将NevarMailError投向了每个非-2xx.

中文使用文档
文档目录14 节
  1. 安装
  2. 融合状况
  3. 使用量
  4. 租赁——一键是一角
  5. 键、表面和模式
  6. 错误
  7. 检索
  8. 案件使用和制止范围
  9. 方法
  10. sendEmailToList 返回卷起而非判决
  11. 日程安排需要实时
  12. 代理收件箱 - NevarMailInbox
  13. 选项
  14. 使用大小写组和继承的设置

@bernierllc/ nevarmail (英语)

NevarMail v1 API的官方客户端.

此所住之处. 此软件包发布于 bernierllc/tools (单位:千美元)packages/core/nevarmail),是客户端唯一的真相来源. 它所包裹的 NevarMail API 构建于 bernierllc/email_demo。 。 。 . 任何更改 NevarMail v1 API 表面需要匹配的更改, 在同一工作单位中 —— 说 看那个雷波的 CLAUDE.md - "NevarMail SDK(英语:@bernierllc/nevarmail) (中文(简体) ).

一个规则: 每一次非-2xx投出. 如果回电,API接受. 没有 "成功"物体内含故障 无法平息出错——审计 生产这个包 发现一个生产集成 吞噬每一个错误,所以 org可能已经邮寄到 403 几个月来,两边都没发现.

安装

npm install @bernierllc/nevarmail

仅服务器侧; 需要节点 20+( 使用全局) fetch 财务报告和审定财务报表 crypto.randomUUID) (中文(简体) ). 永远不要在浏览器代码中披露API证书. 零从属.

融合状况

一体化状态为什么
锁定器( L)@bernierllc/logger(中文(简体) )不适用零依赖性 API 客户端保持零依赖性. 每一次失败都像掷出 NevarMailError 携带 status, (中文(简体) ), code, (中文(简体) ), requestId 财务报告和审定财务报表 cause ——登录在消费者身上,加上消费者的伐木机.
Never Hub (没有@bernierllc/neverhub-adapter(中文(简体) )不适用核心级软件包没有运行时间生命周期可以注册; NeverHub 注册属于构成此客户端的服务或套件. 因此,此处不需要严重退化.

使用量

import { NevarMail } from "@bernierllc/nevarmail";

const nevarmail = new NevarMail({ apiKey: process.env.NEVARMAIL_API_KEY! });

const result = await nevarmail.sendEmail({
  to: "[email protected]",
  subject: "Your quote is ready",
  html: "<p>Here it is.</p>",
  use_case_id: process.env.NEVARMAIL_USE_CASE_TRANSACTIONAL,
});

console.log(result.id, result.status);

租赁——一键是一角

NevarMail API 密钥 实值 该组织。 任何请求机构都不存在Org id 服务器从密钥中解析并忽略您发送的任何东西。 有两个后果:

  • 每人一把钥匙 键不能被指向第二个键 和一个多租户应用程序 代表 NevarMail orgs 发送若干键.
  • 永远不要接受来自终端用户的Org标识符并经过. 无处可去 说吧,尝试是一个征兆 融合是建模租借错误.

键、表面和模式

钥匙看起来像 nvm_api_live_…, (中文(简体) ), nvm_api_test_…, (中文(简体) ), nvm_mcp_live_…, (中文(简体) ), nvm_mcp_test_…。 。 。 .

  • 表面 (单位:千美元)api 对 mcp)——这个包裹会说REST,所以它需要一个 api 键。 通过一个 mcp 键和构造器会立即扔出,而不是让服务器回答 403 KEY_TYPE_MISMATCH 在您的第一个发送, 它读起来像一个认证错误.
  • 模式 (单位:千美元)live 对 test)——测试密钥只发送到org测试上的地址 允许列表; 其他任何东西都是 422 TEST_MODE_RECIPIENT_NOT_ALLOWED模式为 属性,绝不是客户端选项: client.mode 报告关键是什么.
const nevarmail = new NevarMail({ apiKey: key });
if (nevarmail.mode === "test") console.warn("test key — sends are allow-list only");

错误

每一次失败都是 NevarMailError数字 :

import { NevarMail, NevarMailError } from "@bernierllc/nevarmail";

try {
  await nevarmail.sendEmail({ to, subject, html });
} catch (err) {
  if (err instanceof NevarMailError && err.code === "RECIPIENT_SUPPRESSED") {
    // Expected: this person opted out. Not an outage.
    return;
  }
  throw err; // everything else is worth waking someone for
}
外地含义
statusHTTP 状态 ; 0 当请求从未到达 API 时
code机器可读代码——这个分支,永远不要打开 message
requestId引用支持; 匹配 API x-request-id
retryAfter秒,从 Retry-After 在429号公路上 等待30多或更短的时间会为你重试;一个更长的时间(每天一顶帽子)会同时抛出
body解析的回复, 对于比您安装的版本更新的代码

值得明确处理的代码:

代码状态这是什么意思
RECIPIENT_SUPPRESSED422个被弹出,被抱怨,或未认购. 不要重试.
COMPLIANCE_ADDRESS_REQUIRED422个emailType: "marketing" (或用例收件人可以选择退出)从一个没有邮政地址的项目. 设定为 setProjectAddress。 。 。 .
TEST_MODE_RECIPIENT_NOT_ALLOWED422个测试密钥, 收件人不在允许列表中 .
CC_BCC_NOT_SUPPORTED_FOR_TEMPLATE400个按收件人制作模板邮件; 发送一个请求 .
VALIDATION_ERROR400个身体错. 复试无益.
SCOPE_DENIED403 (韩语)密钥缺乏这条路线需要的范围.
KEY_TYPE_MISMATCH403 (韩语)在 REST 路径上的 MCP 密钥( 此客户端在构建时拒绝) .
RATE_LIMITED429个自动复试;预算用完后浮出水面.
SCHEDULE_NOT_CANCELLABLE409个预定发送已发射或已取消 .
NETWORK_ERROR0 个完全没有反应——DNS,TLS,或超时.

检索

复试: 429 (荣誉) Retry-After, (中文(简体) ), 5xx,和网络故障——指数倒置 与 jitter , 默认的两次额外尝试(maxRetries) (中文(简体) ).

从未再审:任何其他 4xx它会永远失败 所以重试会变成一个坏的 请求为三。 发送不是同位素 5xx 可能交付两次.

案件使用和制止范围

每个项目都有禁忌. checkSuppression(email, useCaseId?, projectId?) 答案为 一个项目: projectId,否则项目 useCaseId,否则组织的唯一 项目。 一个有多个项目的组织 400 PROJECT_REQUIRED 当两者都不是 通过了。 对供应商账户的回弹或垃圾邮件投诉会阻碍每个项目;任何其他 镇压只能阻止它自己.

use_case_id 命名信件类别。 这是区别于 "这个人不订阅你的通讯"和"这个人不能再被电子邮件":

  • 说吧,并选择退出 不同 类别列表不阻碍发送, 并且标记为不可压制的类别可免于选择不适用.
  • 别管了,并且对该项目的每一个积极抑制都适用——严格解读. 仅a 项目范围密钥 可能会漏掉 sendEmail 编号 : scheduleEmail: 发送为 那个项目的默认使用大小写。 一个 粗略键 省略它会得到 422 USE_CASE_REQUIRED· ; listUseCases() 显示你的.

弹跳、垃圾邮件投诉和手动压制 阻止了每个类别 仅a use_case_id 您通过自身可以缩小一个块;默认服务器解析不能.

sendEmail 也用"弹"来表示 useCase: "receipts" - 而不是其编号(其中之一 两个,不是两个。 弹簧必须存在于键可以打开的完全一个工程中:未知是一个 404, 几个项目中的同一颗子弹是: 400 USE_CASE_AMBIGUOUS (通过) use_case_id ) (中文(简体) ). listUseCases 显示弹壳.

方法

方法路线范围
sendEmailPOST /api/v1/email/sendemail:send
sendEmailToListPOST /api/v1/email/send-to-listemail:send
scheduleEmailPOST /api/v1/email/scheduleemail:send
listScheduledEmailsGET /api/v1/email/scheduleemail:send
cancelScheduledEmailDELETE /api/v1/email/schedule/{id}email:send
checkSuppressionGET /api/v1/suppressions/checkorg:read
getPreferences(email, projectId?) - 一个项目中的一个地址名单; projectId 当鼠标有多个时需要GET /api/v1/preferencescontacts:read
setPreferences(email, { subscribedListIds, unsubscribeAll, projectId? }) ——保留完整的一组名单PUT /api/v1/preferencescontacts:write
listTemplates 编号 : createTemplate 编号 : getTemplate 编号 : updateTemplate 编号 : deleteTemplate/api/v1/templatestemplates:read 编号 : templates:write
renderTemplatePOST /api/v1/templates/{id}/rendertemplates:read
listCampaigns({ projectId?, page?, perPage? }) 编号 : getCampaign(id, { projectId? }) ——只读GET /api/v1/campaigns, (中文(简体) ), GET /api/v1/campaigns/{id}campaigns:read
getCampaignAnalytics(id, { projectId? }) ——计数加交付/打开/点击/弹出/不认购率(0比1)GET /api/v1/campaigns/{id}/analyticsanalytics:read
getAnalyticsSummary({ period? } | { start, end }) - 总体计算和跟踪率 7d编号 :30d编号 :90d (默认) 30d)或包容性世界协调时 YYYY-MM-DD 范围GET /api/v1/analytics/summaryanalytics:read
getUsage({ start, end }) ——用于包容性世界协调世界时的计量吸入器外送电子邮件计数(可填单和测试模式分开) YYYY-MM-DD 最多366天; 仅计数,不计费用GET /api/v1/usageusage:read
listLists 编号 : createList 编号 : getList/api/v1/listscontacts:read 编号 : contacts:write
addListContactsPOST /api/v1/lists/{id}/contactscontacts:write
removeListContactDELETE /api/v1/lists/{id}/contacts?email= (只列出一个清单;保持联系)contacts:write
listContacts 编号 : upsertContacts 编号 : updateContact 编号 : deleteContact/api/v1/contactscontacts:read 编号 : contacts:write
listBusinesses({ projectId?, page?, perPage? }) 编号 : getBusiness(id, { projectId? })GET /api/v1/businesses, (中文(简体) ), GET /api/v1/businesses/{id}contacts:read
createBusiness(input) 编号 : updateBusiness(id, input) 编号 : deleteBusiness(id, { projectId? }) - 更新替换 domains合并 attributes; 删除保留联系人POST /api/v1/businesses, (中文(简体) ), PATCH 编号 : DELETE /api/v1/businesses/{id}contacts:write
upsertBusinessByExternalRef(system, ref, input) ——同位素;返回 { business, created }PUT /api/v1/businesses/by-ref/{system}/{ref}contacts:write
listBusinessMembers(id, { projectId?, page?, perPage? })GET /api/v1/businesses/{id}/memberscontacts:read
addBusinessMembers(id, members, { addedBy?, projectId? }) (最多100个)/ removeBusinessMembers(id, { contactIds?, emails?, projectId? }) 仅限成员POST 编号 : DELETE /api/v1/businesses/{id}/memberscontacts:write
addBusinessMembersByDomain(id, { projectId? }) - 在企业领域增加联系人POST /api/v1/businesses/{id}/members/by-domaincontacts:write
listSenders 编号 : createSender(input) —— 说 input.projectId? 当鼠标有多个时, 目标为项目/api/v1/senderssenders:read 编号 : senders:write
getSender 编号 : updateSender 编号 : deleteSender/api/v1/senders/{id}senders:read 编号 : senders:write
listUseCases({ projectId?, includeArchived? }) 编号 : createUseCase(input) —— 说 input.projectId? 关于 createSender/api/v1/use-casesuse-cases:read 编号 : use-cases:write
getUseCase 编号 : updateUseCase 编号 : archiveUseCase 编号 : restoreUseCase ——删除档案;恢复撤销/api/v1/use-cases/{id}use-cases:read 编号 : use-cases:write
importSenders({ projectId? }) ——采用项目供应商账户身份;再运行以获取供应商核查POST /api/v1/senders/importsenders:write
listDomains 编号 : createDomain(domain, { projectId }) 编号 : getDomain 编号 : deleteDomain/api/v1/domainsdomains:read 编号 : domains:write
getDomainDnsRecords 编号 : verifyDomain/api/v1/domains/{id}/…domains:read 编号 : domains:write
getDomainBrandedTracking 编号 : enableDomainBrandedTrackingGET 编号 : POST /api/v1/domains/{id}/branded-trackingdomains:read 编号 : domains:write
listInboxes 编号 : getInbox 编号 : updateInbox(id, { displayName?, forwardTo?, storeMessages?, openTracking?, status? }) —— 说 openTracking: true 需要一个活跃的 HTTPS 品牌主机/api/v1/inboxes, (中文(简体) ), /api/v1/inboxes/{id}inboxes:read 编号 : inboxes:write
listSuppressions 编号 : addSuppressions/api/v1/suppressionsorg:read 编号 : org:write
getProjectAddress(projectId) 编号 : setProjectAddress(projectId, { physicalAddress, addressComponents? })GET 编号 : PUT /api/v1/projects/{id}/addressorg:read 编号 : org:write
getInboxDbStatusGET /api/v1/inbox-db/statusorg:read
connectInboxDb({ connectionString, provider? }) (202,启动背景连接) / testInboxDbConnectionPOST /api/v1/inbox-db/connection 编号 : …/connection/testorg:write
getInboxDbConnectAttempt (投弹直到 status 没有 running(中文(简体) )GET /api/v1/inbox-db/connectionorg:read

应用程序级别的细节, 在您击中它们之前需要知道 :

  • 列表方法保持pagination. listTemplates 和朋友回来 { data, pagination: { page, perPage, total } } - 信封的标注是 调用列表路由, 因此它不会被取消 。 单资源方法返回有效载荷 直接,与发送方法相同.
  • 企业. client.businesses 组合十种商业方法: list, (中文(简体) ), get, (中文(简体) ), create, (中文(简体) ), update, (中文(简体) ), delete, (中文(简体) ), upsertByExternalRef, (中文(简体) ), addByDomain 财务报告和审定财务报表 members.{list, add, remove},每个 授权给上面的平面方法。 租赁: projectId 查询中 get, (中文(简体) ), delete 财务报告和审定财务报表 addByDomain,在体内为 update, (中文(简体) ), createBusiness 而会员写作。 别管了 仅当 org 有一个项目时.
  • 更新为 PATCH和部分. 只发送您正在更改的字段 .
  • 运动读取选择 projectId。 。 。 . 只有在雄兽有一个项目时才开放; 否则服务器会回答 400 PROJECT_REQUIRED. 另一个项目中的运动是404 NOT_FOUND和失踪的一样 无工程的Org得到422 NO_PROJECT; 无 销售商得到404 NO_MARKETING_PROVIDER。 。 。 . listCampaigns 在提供者的页面后过滤, 所以是一个页面 可持有少于 perPage 作行相会. 分析路径需要 analytics:read没有 campaigns:read。 。 。 .
  • renderTemplate(id, variables) 将变量映射为整个正文 - 不包着 (单位:千美元) variables 键.
  • deleteTemplate 从来没有404s: 未知的 id 解析 { deleted: false } 两百块 每个 其他删除将丢入缺失的资源.
  • 列表没有更新或删除 ——API不曝光;SDK不发明.
  • 禁止范围是跨战地的数字 : scope: "use_case" 需求 use_case_id,以及 use_case_id 与 scope: "account" 是一个 400。 。 。 . projectId 命名工程所在区域 输入;省略后,它们会降落在使用项目中,否则每个项目中钥匙的所有人都可以打开.
  • 管理中的账户无法删除域 —— 说 deleteDomain 掷出 403 MANAGED_ACCOUNT。 。 。 .
  • 收件箱- db 连接字符串发送一次, 从未重复 . connectInboxDb 存储加密; InboxDbStatus 省却. schemaName 您数据库中的Postgres schema NevarMail 拥有 .

任何尚未覆盖的东西——一个网点——都经过 request() (或 requestPaginated() 用于列表路由),它应用相同的认证,信封 打开、丢弃和重试 :

const endpoints = await nevarmail.request<unknown>("GET", "/api/v1/webhook-endpoints");

到达 request() 而不是写第二个 HTTP 客户端 。 就是这样 第一次发生了吞咽-过敏症.

sendEmailToList 返回卷起而非判决

发送到一些收件人的列表是 200 与 status: "partial" 和一个非零 failedCount。检查字段;2xx并不意味着每个人都收到.

const run = await nevarmail.sendEmailToList({ listId, subject, htmlContent });
if (run.status !== "sent") console.error(run.failedCount, run.failures);

日程安排需要实时

scheduled_for 必须带有明确的抵消—— 2026-09-01T14:00:00Z 或者说 2026-09-01T10:00:00-04:00赤裸的当地时间是四个不同的时刻,取决于谁 输入,所以API拒绝而不是猜测. 在你的一侧转换区域 其实已经知道。 模板的调度发送被拒绝: 处理器不渲染 模板,所以接受一个会排队一个空体.

代理收件箱 - NevarMailInbox

一个代理收件箱有自己的API(/api/v1/inbox/*)及其本人的证书:客户身份 (单位:千美元)nvm_inbox_…从收件箱的代理标签里找到的秘密 API 密钥在那里被拒绝, 并且 a 收件箱的证书在自己的收件箱之外没有任何内容,所以它是一个单独的客户端:

import { NevarMailInbox } from "@bernierllc/nevarmail";

const inbox = new NevarMailInbox({
  clientId: process.env.NEVARMAIL_INBOX_CLIENT_ID!,
  clientSecret: process.env.NEVARMAIL_INBOX_CLIENT_SECRET!,
  // scopes: ["inbox:read"], // narrow the token; defaults to inbox:read + inbox:send
});

const { data } = await inbox.listEmails({ perPage: 10 });
for (const summary of data.filter((e) => !e.isRead)) {
  const email = await inbox.getEmail(summary.id);
  await inbox.send({
    to: email.from,
    subject: `Re: ${email.subject ?? ""}`,
    text: "Got it.",
    reply_to_email_id: email.id,
  });
  await inbox.markEmailRead(email.id);
}
方法路线范围
listEmails({ page, perPage, threadId })GET /api/v1/inbox/emailsinbox:read
getEmail(id)GET /api/v1/inbox/emails/{id}inbox:read
markEmailRead(id)POST /api/v1/inbox/emails/{id}/readinbox:read
deleteEmail(id)DELETE /api/v1/inbox/emails/{id}inbox:read
send(input)POST /api/v1/inbox/sendinbox:send
rotateCredentials()POST /api/v1/inbox/credentials/rotateinbox:read
  • 托肯斯为你处理. 客户将证书交换为短命 符号( OAuth) client_credentials 时间 /api/oauth/token在第一次使用, 更新一分钟 期满前,以及 401 得到一个新鲜的标志 并重复一次。 被拒绝的证书 掷出 code: "INVALID_CLIENT"。 。 。 .
  • send 是故意缩小. 总是收件箱的地址,最多5个 整个接收者 to 财务报告和审定财务报表 cc; 没有模板或列表。 击中每天的限制是 429 inbox_daily_limit_reached {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}马上抛出 retryAfter 设置到 直到协调世界时午夜.
  • rotateCredentials() 返回新密钥一次 并切换客户端到它. 贮之. 老秘密停止运作.

相同的选项( O)baseUrl, (中文(简体) ), timeoutMs, (中文(简体) ), maxRetries, (中文(简体) ), fetch)和与 NevarMail。 。 。 .

选项

new NevarMail({
  apiKey: process.env.NEVARMAIL_API_KEY!,
  baseUrl: "https://app.nevarmail.com", // default
  timeoutMs: 30_000,                    // default
  maxRetries: 2,                        // default
  fetch: undefined,                     // inject one for tests or a custom runtime
});

使用大小写组和继承的设置

创建可重复使用的项目默认值 createUseCaseGroup,然后创建只使用其名称和组ID的用例。 组合允许可压制的,交易的,或混合使用的案例;混合组合每个类型都有默认的种类和单独的跟踪默认.

const group = await mail.createUseCaseGroup({
  name: "Account emails", projectId,
  allowedKind: "transactional", defaultSuppressable: false,
  defaults: {
    shared: { fromName: { value: "Accounts" } },
    suppressable: {}, transactional: { clickTracking: { value: false } },
  },
});
const login = await mail.createUseCase({ name: "Login", groupId: group.id });
await mail.updateUseCase(login.id, {
  expectedRevision: login.settingsRevision,
  settings: { set: { replyToEmail: null }, inherit: ["fromName"] },
});

省略的覆盖继承; 明示 false 禁用; 明确 null 清除一个无效的字段. settings 关于答复的内容包括有效/已配置的值、源和覆被状态。 与组值等同的覆被仍为覆被. 集团默认为继承人保留活性.

使用 listUseCaseGroups, (中文(简体) ), getUseCaseGroup,以及 deleteUseCaseGroup 管理团体。 删除返回 {id, deleted: true} 并拒绝包含任何成员的团体,包括存档成员。 要编辑默认,请调用 previewUseCaseGroupUpdate(id, {changes})然后审查受影响的成员,然后 updateUseCaseGroup 这些修改,预览的修改和符号.

const target = { groupId: destinationId }; // null means Ungrouped
const preview = await mail.previewUseCaseGroupMove(login.id, target);
// Show preview.rows and both outcomes to the user before applying.
const idempotencyKey = globalThis.crypto.randomUUID();
const moved = await mail.moveUseCaseToGroup(login.id, {
  ...target, previewToken: preview.previewToken,
  policy: "inherit_destination", // or "preserve_effective"
  idempotencyKey,
});

移动保留已有的覆盖 . preserve_effective 附加快照 。 一种变换用品 suppressable 通过相同的预览/应用流,而不是普通的元数据 PATCH. Tokens在15分钟后过期并绑定在演员/镜像上;在预览时使用相同的证书并应用. 一个 stale 预览返回 409, 并且没有重试 : 获取另一个比较并再次获得确认 .

移动键是请求正文中的UUID. 每一次SDK呼叫都会产生一次出局,保存在自动回放中. 在网络结果模糊后重新测试整个呼叫时,保留和再利用一个明确的关键。 承诺的重试即使在符后返回其原始结果;为不同的动作重用密钥是冲突.

生产过渡需要 confirmation: {productionSending: true} 和服务器权限/准备状态。 对交易邮件进行跟踪 transactionalTracking: "transactional-tracking-v1" 在用户看到披露后。 永远不要自动确认这些选择。 跟踪是解决的服务器侧; SendEmailInput 没有追踪功能。 保留 API 证书服务器侧:应用浏览器组件使用会话路由.

版本与发布记录

@bernierllc/nevarmail 共发布 31 个版本,首次发布于 2026/09/11;过去 12 个月发布了 31 个版本。

版本发布日期距今
0.30.0latest2026/11/053 天前
0.29.02026/10/2316 天前
0.28.02026/10/2118 天前
0.27.02026/10/2019 天前
0.26.02026/10/2019 天前
0.25.02026/10/2019 天前

关于 @bernierllc/nevarmail 的常见问题

@bernierllc/nevarmail 是什么?

NevarMail v1 API的官方客户端. 已打入,回放429/5xx,将NevarMailError投向了每个非-2xx.

如何安装 @bernierllc/nevarmail?

在项目目录运行 npm install @bernierllc/nevarmail,或使用 pnpm add / yarn add / bun add。

@bernierllc/nevarmail 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@bernierllc/nevarmail 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bernierllc/nevarmail 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.0.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@bernierllc/nevarmail 可以商用吗?

@bernierllc/nevarmail 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bernierllc/nevarmail 的下载量有多少?

根据 npm 官方下载统计,@bernierllc/nevarmail 在 2026/10/08 – 2026/11/06 这 30 天内被下载 2,492 次,日均约 83 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bernierllc/nevarmail 还在维护吗?

最新版本 0.30.0 发布于 2026/11/05(3 天前);至今共发布 31 个版本,其中过去 12 个月发布了 31 个;首次发布于 2026/09/11。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。