npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@honeeasy/notification-jssdk

v2.0.0-beta.1

Published

UI-free Honeeasy Notification browser SDK

Readme

Honeeasy Notification JSSDK 2.x

Honeeasy Notification JSSDK 2.x 是无 UI 的浏览器通知协议客户端。SDK 不创建 DOM、不注入 CSS 或字体, 不包含 iframe、Widget 或 Honeeasy 静态资源地址。宿主应用收到 NotificationHandle 后自行决定 toast、 全屏、动画、样式和交互。

完整第三方接入说明见 docs/third-party-integration.md。面向编码助手的集成 Skill 见 skills/integrate-honeeasy-notifications/SKILL.md

安装与产物

npm install @honeeasy/[email protected]

发布包包含:

  • dist/index.mjs:npm ESM;
  • dist/index.cjs:CommonJS;
  • dist/honeeasy-notification-jssdk.umd.js:可部署到接入方自己服务器的 UMD;
  • dist/index.d.ts:TypeScript 声明。

SignalR 已打入三种 JavaScript 产物。接入项目不需要安装 Vue、React、jQuery 或 SignalR。已构建产物 使用 ES2017 语法,可被 Node 12/14/16 时代的构建器消费;构建本 SDK 源码需要 Node 20.19+。

基本用法

import { createHoneeasyNotification } from "@honeeasy/notification-jssdk";

const client = createHoneeasyNotification({
  apiBaseUrl: "https://api.example.com",
  ticketProvider: async () => {
    const response = await fetch("/api/my-app/notification-ticket", { credentials: "include" });
    const result = await response.json();
    return result.ticket;
  },
  onMessage: handle => {
    renderNotification(handle);
  },
  onError: error => reportToApplicationLogger(error),
});

await client.start();
// SPA 页面销毁时:await client.stop();

浏览器只能取得 60 秒一次性 Ticket,不能持有 ClientSecret、应用访问令牌或内部 Register.IdticketProvider 在启动、会话续期或 401 重建时会再次调用,每次必须返回新 Ticket。

NotificationHandle

interface NotificationHandle {
  readonly message: Readonly<NotificationMessage>;
  readonly expired: boolean;
  markReceived(): Promise<NotificationOperationResult>;
  markDisplayed(): Promise<NotificationOperationResult>;
  markClicked(): Promise<NotificationOperationResult>;
  markDismissed(): Promise<NotificationOperationResult>;
  createTrackedRedirect(): Promise<string>;
  track(name: string, properties?: Record<string, string | number | boolean | null>): Promise<NotificationOperationResult>;
}

RECEIVED 默认由 SDK 自动上报;设置 autoAckReceived: false 后由宿主调用 markReceived()DISPLAYED 应在真实 UI 完成渲染后调用,CLICKEDDISMISSED 应在真实交互后调用。它们是客户端声明, 服务端只验证身份、消息范围、幂等和状态规则,不能验证实际 DOM 或物理点击。

外链必须先调用 createTrackedRedirect()。该方法在后端落库 CLICKED 后返回短期跳转地址,但 SDK 不执行导航:

button.addEventListener("click", async () => {
  const redirectUrl = await handle.createTrackedRedirect();
  window.location.assign(redirectUrl);
});

自定义事件名必须匹配 ^[a-z][a-z0-9_.-]{0,63}$,不能使用 honeeasy.sdk. 或标准 ACK 名; 最多 16 个属性,属性值只能是字符串、数字、布尔或 null。自定义事件不会修改标准消息状态机。

Vue 2/3

// 组件 mounted/onMounted 后启动;以下 showNotification 由应用自己实现。
const client = createHoneeasyNotification({
  apiBaseUrl: window.NOTIFICATION_API_BASE_URL,
  ticketProvider: () => fetch("/notification-ticket").then(r => r.json()),
  onMessage(handle) {
    notifications.value.push(handle);
    nextTick(() => requestAnimationFrame(() => void handle.markDisplayed()));
  },
  onResolved({ messageId }) {
    notifications.value = notifications.value.filter(item => item.message.messageId !== messageId);
  },
});

onMounted(() => void client.start());
onBeforeUnmount(() => void client.stop());

React

function NotificationProvider() {
  const [notifications, setNotifications] = React.useState<NotificationHandle[]>([]);

  React.useEffect(() => {
    const client = createHoneeasyNotification({
      apiBaseUrl: process.env.REACT_APP_NOTIFICATION_API!,
      ticketProvider: () => fetch("/notification-ticket").then(r => r.json()),
      onMessage: handle => setNotifications(current => [...current, handle]),
      onResolved: ({ messageId }) => setNotifications(current =>
        current.filter(item => item.message.messageId !== messageId)),
    });
    void client.start();
    return () => { void client.stop(); };
  }, []);

  return <>{notifications.map(handle =>
    <NotificationView key={handle.message.messageId} handle={handle} />)}</>;
}

function NotificationView({ handle }: { handle: NotificationHandle }) {
  React.useLayoutEffect(() => { void handle.markDisplayed(); }, [handle]);
  return <button onClick={() => void handle.markClicked()}>{handle.message.title}</button>;
}

jQuery

var client = HoneeasyNotification.createHoneeasyNotification({
  apiBaseUrl: "https://api.example.com",
  ticketProvider: function () {
    return fetch("/notification-ticket").then(function (r) { return r.json(); });
  },
  onMessage: function (handle) {
    var $notice = $("<section>").addClass("my-notice").text(handle.message.title);
    $notice.on("click", function () { handle.markClicked(); });
    $("#notification-root").append($notice);
    window.requestAnimationFrame(function () { handle.markDisplayed(); });
  }
});
client.start();

UMD 文件可以从接入方自己的域名加载:

<script src="/assets/vendor/honeeasy-notification-jssdk.umd.js"></script>

也可以直接使用公共 CDN(生产环境建议固定具体版本):

<script src="https://cdn.jsdelivr.net/npm/@honeeasy/[email protected]/dist/honeeasy-notification-jssdk.umd.js"></script>

UMD 全局变量仍然是 HoneeasyNotification;npm scope 不会改变浏览器全局名称。

原生 JavaScript

var client = HoneeasyNotification.createHoneeasyNotification({
  apiBaseUrl: "https://api.example.com",
  autoAckReceived: false,
  ticketProvider: function () {
    return fetch("/notification-ticket").then(function (r) { return r.json(); });
  }
});

client.on("message", function (handle) {
  handle.markReceived();
  var notice = document.createElement("button");
  notice.textContent = handle.message.title;
  notice.onclick = function () { handle.markClicked(); };
  document.querySelector("#notification-root").appendChild(notice);
  requestAnimationFrame(function () { handle.markDisplayed(); });
});

client.start();

以上 DOM 都由示例宿主创建,不属于 SDK 行为。

第三方服务端

  1. 服务端用 clientId + clientSecret 调用 POST /api/notification/v1/oauth/token
  2. 服务端用 Bearer Token 调用 POST /api/notification/v1/tickets
{
  "activityId": 10001,
  "subject": { "scheme": "credential-code", "value": "证件码" },
  "origin": "https://receipt.partner.com"
}
  1. Honeeasy 后端按 activityId + credential-code 解析唯一 Register,并校验应用活动与 Origin 白名单。
  2. 第三方服务端只把一次性 Ticket 返回浏览器。

生产 Origin 必须是精确 HTTPS authority,不接受通配符、路径、查询、fragment 或 user-info。

运行时行为

SDK 负责 Session、SignalR 自动重连、提前五分钟续期、Pending 恢复、消息去重、页面可见性、 多标签页单可见宿主投递、过期清理,以及 IndexedDB(不可用时 localStorage/内存)ACK 重试。 重试沿用同一个操作 ID;网络错误、超时、429 和 5xx 使用指数退避。SDK 不清洗或渲染富文本,宿主在 v-htmldangerouslySetInnerHTMLinnerHTML 前必须使用 DOMPurify 等白名单清洗器二次清洗。

旧环境按实际浏览器补充 PromiseURLfetchHeadersWebSocketcrypto.getRandomValuesIndexedDB polyfill。SDK 不覆盖宿主全局对象。