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

@giime/crx

v1.0.1

Published

面向基于 giime 的 Chrome 扩展(MV3)插件:axios 与登录协调器。popup / options / contentScript / background 共用同一套代码;contentScript 经 background 自动桥接。

Downloads

1,671

Readme

@giime/crx

Chrome 扩展(MV3)专用的 axios + 登录协调封装。让 popup / options / contentScript / background 同一份业务代码调 API,自动桥接 contentScript 经 background 发请求;统一处理 401 / 登录失效 / 自动重发;登录窗口去重 + 冷却期保护。

跟 giime 主库的 createAxios 是平级封装——giime 那套面向 web 项目(document.cookie、iframe LoginDialog),这套面向 CRX(chrome.cookies、chrome.windows.create)。

安装

pnpm add @giime/crx

依赖 vue@^3 / axios@^1 / element-plus / giime(都是 peerDependencies)。

接入清单(8 步)

1. 配 .env

# 必须:业务方在自己的 .env 配
VITE_CRX_PLUGIN_ID=your-plugin-id

# 可选:90% 情况下不需要配;不配时自动 fallback 到 Vite 自带 import.meta.env.MODE。
# 只有跑非默认 mode(比如 `vite --mode release` / `--mode test`)才需要显式配
# VITE_GIIME_MODE=develop

四套 mode 对应的 host(包内硬编码,跟着包走,业务不用关心):

| mode | host | | --- | --- | | production | api.giikin.com | | release | api-pre.giikin.com | | test | api-test.giikin.cn | | develop | api-dev.giikin.cn |

2. 配 src/env.d.ts

/// <reference types="@giime/crx/client" />

import.meta.env.VITE_CRX_PLUGIN_ID 有类型提示。

3. 配 Vite 插件

// vite.config.ts
import { defineConfig } from 'vite';
import { crxAxiosIdPlugin } from '@giime/crx/vite-plugin';

export default defineConfig({
  plugins: [crxAxiosIdPlugin()],
});

插件做的事:编译期给每个 createCrxAxios({...}) 调用注入 __id__: "<源文件相对路径>",让 contentScript 桥接到 background 时能找到对应的 axios 实例。

4. 写一个 service 文件

文件名必须是 service.ts / serviceNext.ts / request.ts 之一(background 通过 glob 加载,文件名定死才能匹配)。

// src/api/basic/request.ts
import { createCrxAxios, currentCrxEnv } from '@giime/crx';

const { service } = createCrxAxios({
  // baseURL 推荐用 currentCrxEnv.apiHost 派生,自动跟随 mode 切换
  baseURL: `https://${currentCrxEnv.apiHost}/guard`,
  successCode: 200,
});

export default service;

5. controller 用同一个 service

// src/api/basic/controller/postBasicV1OrgList.ts
import request from '@/api/basic/request';

export const postBasicV1OrgList = (config?: AxiosRequestConfig) =>
  request.post(`/basic/v1/org/list`, config);

6. background entry

// src/background/main.ts
import { registerAxiosBridge, registerLoginCoordinator } from '@giime/crx';

// 让所有 service 文件在 background bundle 里被加载,自动注册到桥接 registry
import.meta.glob('@/api/**/{service,serviceNext,request}.ts', { eager: true });

registerAxiosBridge();        // background 收 contentScript 桥接消息
registerLoginCoordinator();   // background 协调登录窗 / cookie 监听 / 广播

7. contentScript entry

// src/contentScripts/index.ts
import { createRouter, createWebHashHistory } from 'vue-router';
import { configureCrx } from '@giime/crx';
import Giime from 'giime';

// 创建 shadow DOM root
const root = document.createElement('div');
const shadowDOM = container.attachShadow({ mode: 'open' });
shadowDOM.appendChild(root);

// 必传:让 LoginPrompt / 错误 toast 挂到 shadow root,避免被宿主页面样式干扰
configureCrx({ contentScriptRoot: root });

const app = createApp(App);
const router = createRouter({ history: createWebHashHistory(), routes: [] });

// router 既要传给 Giime(让 giime 内部 useGlobalConfig 拿到),
// 也要 app.use(router) 给 vue-router 自己——否则 GmButton 等组件
// 会报 injection "Symbol(route location)" not found。
//
// disabledVersionAttr: 关掉 giime 写 document.body[data-giime-version]——
// content script 里这个 body 是宿主页的,会跟宿主页自己的 giime 版本号来回打架。
app.use(Giime, {
  env: import.meta.env,
  router,
  disabledLoginDialog: true,
  disabledVersionAttr: true,
});
app.use(router);
app.mount(root);

8. popup / options entry

业务直接 import { postBasicV1OrgList } from '@/api/basic/controller' 调即可。

挂 Vue 应用时遵循跟 contentScript 同样的规则:app.use(Giime, { router }) + app.use(router) 必须同时调,缺一个 <GmButton> / 任何用 vue-router 注入的 gm 组件就会运行时报错。


行为细节

401 / 登录失效

| 入口 | 行为 | | --- | --- | | popup | 跳过 LoginPrompt 直接让 background 开登录标签页(popup 视口太窄 + 失焦即关闭,dialog 体验差)| | options | LoginPrompt 弹窗 → 用户点立即登录 → 开登录标签页 → 等 cookie 变化广播 → 重发原请求 | | contentScript | 同 options | | background 自己调 | 直接 reject(无 UI)|

background 侧的双重保护

  1. 登录标签页去重(P1):已有登录标签页 + URL 仍在登录页 prefix → focus 它;否则关掉重开。不会出现 2 个登录标签页。
  2. 登录后冷却期(P2):cookie 变化后 12 秒内再来的 401 视作"慢响应",告诉客户端 token 已更新直接重发,不再开窗。

自动关登录窗

cookie 检测到 token 写入 → background 广播 __crxLoginCompleted__ + chrome.windows.remove(loginWindowId)。loginWindowId 用 chrome.storage.session 持久化跨 SW 重启。

dk-plugin-id 请求头

所有 axios 请求自动带 dk-plugin-id: ${VITE_CRX_PLUGIN_ID},让后端识别请求来源。

contentScript 里的 ElDialog / GmDialog 必须显式 append-to

ElementPlus 的 ElDialog / ElOverlay / ElMessage 默认 <teleport to="body">——这里的 body宿主页document.body,shadow DOM 隔离不住 teleport,DOM 会跨出 shadow root 渲染到宿主页里,被宿主页样式污染、定位错乱。

修法:每个 dialog 显式传 append-to,指向 shadow root 内的容器元素(也就是你 configureCrx({ contentScriptRoot }) 传进来的那个):

<template>
  <gm-dialog v-model="visible" :append-to="appendTo">...</gm-dialog>
</template>

<script setup lang="ts">
import { getCrxConfig } from '@giime/crx';
const appendTo = getCrxConfig().contentScriptRoot as HTMLElement;
</script>

外层 dialog 加上 append-to 后,里面套的 GmTableCtx 自带 search dialog 等子级 portal 会继承同一个 teleport context,跟着一起进 shadow root,不用每个都加。

append-to-body 是 ElDialog 的旧 prop(已废弃且默认 true),效果跟 teleport 到宿主 body 一样,shadow DOM 场景别用

contentScript 桥接 cfg 会被 sanitize

signal / cancelToken / adapter / transformRequest / transformResponse / paramsSerializer / validateStatus / onUploadProgress / onDownloadProgress / httpAgent / httpsAgent + 任何函数字段,在 chrome.runtime.sendMessage 前会被剥掉——它们要么过不了 structured clone,要么跨进程没意义(如 AbortSignal)。详见 DESIGN.md 三

401 不进 chrome://extensions 错误面板

桥接层 / 真实 axios 拦截器抛 401 时打 __crxHandled__ 标记,包内 unhandledrejection 全局过滤器 preventDefault() 掉。非 401 异常(5xx / 网络错 / 业务错)仍正常报,真 bug 不被埋。详见 DESIGN.md 七


公开 API 速查

// 工厂
createCrxAxios(config): { service: AxiosInstance, __id__: string }

// 注册(background entry 用)
registerAxiosBridge(): void
registerLoginCoordinator(): void

// 配置
configureCrx(cfg: Partial<CrxConfig>): void
getCrxConfig(): Readonly<CrxConfig>

// 环境
currentCrxEnv: { apiHost: string, cookieDomain: string }
crxPluginId: string
isBackground() / isContentScript() / isExtensionPage() / isPopup(): boolean

// 登录
crxLoginUrl: string
isOnLoginPage(): boolean
showLoginPrompt(opts?): Promise<boolean>  // imperative 弹窗,业务一般用不上

设计文档

详细架构、__id__ 注入原理、跨 entry 桥接协议、为什么不用 iframe 等讨论见同目录的 DESIGN.md