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

zy-web-gate

v3.1.0

Published

Framework-agnostic, pure-frontend shared-password page gate with cross-subdomain login state via a parent-domain cookie. No backend auth, no accounts — verification is delegated to a check-password API.

Readme

zy-web-gate

纯前端「共享固定密码」页面访问门。框架无关,任何前端项目(Vue / React / 纯 HTML)都能用。

输入正确的共享密码后才能看到页面内容;验证一次后,同主域下所有子站自动放行,无需重复输入。新增子站只要接入本包即可纳入同一套门。

它是什么 / 不是什么

这是一道前端页面层的访问遮挡,属于「防君子不防小人」:

  • ✅ 没输对密码看不到页面、输对能进、同主域跨子域不用重复输。
  • ✅ 真密码只存在校验接口侧,前端 bundle 里没有任何密码信息(连 hash 都没有)。
  • ✅ 登录态 cookie 存的是后端签发的 JWT,每次进入调 /check 验签——能挡住「控制台伪造 cookie 直接进 UI」与「照搬旧 =1 绕过」。
  • ❌ 不防 DevTools、不防绕过 UI 直接扒静态资源 / 调业务 API。
  • ❌ 不是账号体系、不是 OAuth、不是邮箱/短信验证码,就是「一个共享固定密码」。

如果你需要真正的安全鉴权,请用后端鉴权 / 身份系统,本包不适用。

工作原理

先用一个固定 token 向「地址分发接口」换取真实校验地址,再去真实地址校验,避免真实接口地址被写死在 bundle 里。

  1. 进入任一子站时,先换真实地址、调 /site-status 问「当前网站受控吗」(后端按 Origin 判断)。不受控 → 直接放行,连密码框都不弹。
  2. 受控 → 查父域 cookie(Domain=example.com,值为 JWT)。
  3. cookie 存在 → 调 /check 验签;验过才放行,不弹任何 UI。
  4. cookie 不存在 / 验签不过 → 弹出密码输入页(原生 DOM,Shadow DOM 隔离样式)。
  5. 用户输入密码 → POST {url}/verify → 接口比对后返回「对 / 错」并在对时签发 JWT。
  6. 正确 → 把 JWT 写父域 cookie → 放行;之后所有同主域子站读到该 cookie 并验签通过即免输。

为什么用 cookie 而不是 localStoragelocalStorage 按 origin 隔离,子域之间读不到,无法实现「一次验证、全子域通行」。cookie 可通过 Domain 设置到父域被全部子域共享,这是纯前端实现跨子域登录态的唯一可靠机制。

安装

npm install zy-web-gate

1.1.0 起自带 TypeScript 类型声明(dist/index.d.ts),TS 项目无需手写 .d.ts

使用

挂载真实应用之前调用 ensureGate({ env })。它只在「已通过门禁」时 resolve,所以未通过时真实应用绝不会挂载(无内容闪现)。

env 必传(取值 dev / test):地址分发接口按 env 返回对应环境的真实校验地址。不传或传非法值会立即抛错,不做自动探测。

Vue3 + Vite

// main.js
import { createApp } from "vue";
import App from "./App.vue";
import { ensureGate } from "zy-web-gate";

// 用构建 mode 区分环境(dev 构建传 dev,test 构建传 test)
await ensureGate({ env: import.meta.env.MODE });

createApp(App).mount("#app");

顶层 await 需要入口是 ESM module(Vite 默认满足)。若环境不支持顶层 await,用 .then()

ensureGate({ env: import.meta.env.MODE }).then(() => {
  createApp(App).mount("#app");
});

React

import { ensureGate } from "zy-web-gate";

ensureGate({ env: import.meta.env.MODE }).then(() => {
  ReactDOM.createRoot(document.getElementById("root")).render(<App />);
});

纯 HTML(通过 CDN,无需打包工具)

用 UMD 产物,全局变量为 ZyWebGate。无构建工具时按部署环境手动指定 env

<script src="https://unpkg.com/zy-web-gate"></script>
<script>
  ZyWebGate.ensureGate({ env: "test" }).then(function () {
    document.getElementById("app").style.display = "";
  });
</script>

或用 ESM 方式:

<script type="module">
  import { ensureGate } from "https://unpkg.com/zy-web-gate?module";
  await ensureGate({ env: "test" });
  document.getElementById("app").style.display = "";
</script>

校验接口约定(地址分发 + 站点状态 + JWT)

地址分发接口写死在包内(src/discover.js),真实校验地址由分发接口动态返回。完整字段、示例与安全说明见 INTEGRATION.md

地址分发(换真实地址)

| | | |---|---| | 方法 | GET | | URL | src/discover.jsDISCOVER_URL(写死在包内) | | 必带请求头 | token + env(按 ensureGate({ env }) 取,dev/test 各一套,写死在包内;非安全凭证,仅提高扒取门槛) | | 成功 | HTTP 200,{ "code": 200, "data": { "url": "真实校验地址" } } |

分发接口按 env 头返回对应环境的真实校验地址。改真实地址:改分发接口(mock)的返回值即可,无需发新版本;改分发接口本身的地址、token 或环境才需改 src/discover.js 发新版本。本包每次校验都重新分发拉取,地址变更即时生效。

站点状态查询(弹框前先问:当前网站受控吗)

| | | |---|---| | 方法 | GET | | URL | {真实地址}/site-status | | 返回 | HTTP 200,{ "code": 0, "data": { "controlled": true \| false } } |

ensureGate() 在弹密码框之前先调一次:后端按请求 Origin 判断「当前网站」是否受访问门控制。只有明确返回 controlled === false 才直接放行(连密码框都不弹);返回 true、接口不存在或网络异常一律按受控处理,继续走下方门禁流程。

密码校验

| | | |---|---| | 方法 | POST | | URL | {真实地址}/verify | | 请求体 | { "password": "用户输入的密码" } | | 通过 | HTTP 200,{ "code": 0, "data": { "match": true, "token": "<JWT>" } } | | 密码错 | 任何 data.match !== true 的响应(HTTP 401 也可) | | 无权限 | 密码正确但对当前网站无授权:data.match !== true 且带 message(如 HTTP 403,{ "code": 1, "message": "当前访问密码无权限访问当前网站", "data": { "match": false, "reason": "no_permission" } }) |

判定规则:只有「HTTP ok 且 data.match === true」算通过,其余一律当未通过;通过时把 data.token 写入 cookie。未通过时,若响应体带 message 字段则原样展示给用户(用于「无权限访问」等场景),否则提示「密码错误」;网络/接口异常单独提示「网络异常」。

后端可按请求 Origin(浏览器跨域自动携带、前端不可伪造)识别「当前网站」,从而实现「某密码只能用于某些网站」「某网站不受门控直接放行」等策略。本包无需为此传任何站点参数。

token 验签(已有 cookie 时)

| | | |---|---| | 方法 | POST | | URL | {真实地址}/check | | token 位置 | 请求头 Authorization: Bearer <token>(同时也放进 body) | | 有效 | HTTP 200,{ "code": 0, "data": { "valid": true } } |

接口需放行 CORS(响应带 Access-Control-Allow-Origin,并正确处理 OPTIONS 预检),否则前端跨域调不通。建议接口侧加限流防暴力猜密码。

配置项

ensureGate(options):除 env 外其余均可选。

| 选项 | 默认 | 说明 | |---|---|---| | env | 必传 | 环境名,取值 dev / test,用于地址分发接口区分环境。缺失/非法立即抛错 | | cookieName | "zy_web_gate" | 登录态 cookie 名(值为后端签发的 JWT,不可配) | | cookieDomain | 自动推断 | 父域;不传则由当前 host 推断(如 a.example.comexample.com)。localhost / IP 自动不写 Domain | | maxAgeDays | 7 | 登录态有效天数 | | sameSite | "Lax" | cookie SameSite | | secure | 自动 | 按当前协议自动判断(https 为 true,本地 http 自动关闭) | | title | "访问验证" | 密码页标题 | | subtitle | "请输入访问密码后继续。" | 副标题 | | placeholder | "访问密码" | 输入框占位 | | buttonText | "进入" | 按钮文案 | | loadingText | "正在验证访问权限…" | 已有 cookie 验签时的全屏 loading 文案(避免慢网白屏) | | timeoutMs | 10000 | 接口超时(毫秒) |

登出

import { logoutGate } from "zy-web-gate";

logoutGate(); // 清除父域 cookie,下次进入任一子站会重新要求输入密码

ensureGate 用了自定义 cookieName / cookieDomainlogoutGate 要传相同的值才能删掉对应 cookie。

跨子域 / 父域说明

  • 父域自动推断为「去掉最左一段」:a.example.comexample.com。多级子域或想固定时,显式传 cookieDomain
  • 浏览器禁止把 cookie 设到 Public Suffix List 上的公共后缀(如 comeu.org 等),所以父域只会落到你自己的可注册域那一层——这正是我们要的,也避免 cookie 泄漏给同后缀下别人的站点。
  • 全站需 HTTPS(Cloudflare Pages 默认满足),Secure cookie 才能写入。

新增子站如何纳入

纯前端方案没有「零接入自动保护」——每个子站都要接入本包(装包 + 入口 await ensureGate({ env }) 几行)。但因登录态是全子域共享 cookie,新站只要接入了,已验证用户进去不会再被拦。

校验接口已内置在包内,新站接入只需装包 + 入口调一行 await ensureGate({ env }),可做成共享模板 / 脚手架复制即用。

本地验证

cd examples
npx serve .       # 或 python3 -m http.server
# 浏览器打开 demo.html

本地 localhost 下 cookie 不写 Domain、Secure 自动关闭,可验证「输对进、输错拦、刷新免输」;跨子域共享需部署到真实 *.example.com 才能验证。

构建与发布

源码在 src/,发布前用 Vite 库模式构建出 dist/(ESM + UMD):

npm run build      # 产出 dist/zy-web-gate.js (ESM) 和 dist/zy-web-gate.umd.cjs (UMD)
npm publish        # prepublishOnly 会自动先 build

package.jsonfiles 只包含 distREADME.md,源码与示例不进 npm 包。