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

@emat.tw/connector

v0.7.0

Published

將 Next.js 站點連接至 emat 中央後台 — heartbeat、feature flags、遠端控制、事件回報。

Readme

@emat.tw/connector

將 Next.js 站點連接至 emat 中央後台。提供 heartbeatfeature flags遠端控制事件回報 四個能力,整個 SDK 目標 < 5 kB gzipped。

目前版本 0.7.0 — 完整 SDK 表面:satellite + Hub + React + CLI scaffold + onMetric。E2E 已涵蓋 connector ↔ hub 全流程。


安裝

pnpm add @emat.tw/connector

或一鍵 scaffold(互動式問 Site ID / Hub URL,產出 lib/site.ts + 補 .env.example):

npx @emat.tw/connector init

需求:Node ≥ 18、Next ≥ 15、React ≥ 18。


Quick start

// lib/site.ts
import { createConnector } from '@emat.tw/connector'
import { revalidateTag } from 'next/cache'

export const site = createConnector({
  flags: {
    new_editor:  { default: false, label: '新版編輯器' },
    maintenance: { default: false, label: '維護模式' },
  },
  controls: {
    'clear-cache': {
      label: '清除快取',
      run: () => revalidateTag('all'),
    },
  },
})

業務程式裡:

import { site } from '@/lib/site'

// 讀 flag(本地 cache,預設 TTL 30 秒,Hub 不通時回 default)
// inline 宣告或 satisfies 時,TS 會強制 key 必須在宣告裡,
// 並把回傳型別 narrow 成該 flag default 的型別。
if (await site.flag('new_editor')) {  // Promise<boolean>
  // ...
}

// 回報事件(fire-and-forget,永不 throw)
site.report('news.published', { id: article.id })

// 顯式 heartbeat(不必常用,回傳 BeatResult)
const res = await site.heartbeat()
if (!res.ok) console.warn('hub unreachable:', res.error)

// 內部狀態快照(debug / 健檢)
console.log(site.snapshot())
//   { siteId, version, lastBeatAt, flags, pendingEvents, pendingReceipts, lastSchemaHash }

環境變數

SITE_ID=my-site-slug          # 每個站點獨一無二
SITE_SECRET=…                 # Hub 簽發的 token
HUB_URL=https://hub.emat.tw   # Hub base URL

三個缺一時 Connector 會 console.warn 並進入 no-op 模式flag() 一律回 default、report() 直接忽略、heartbeat() 不送出。設計原則是「設定失誤絕對不讓站點壞掉」。


Config 參考

| 欄位 | 預設 | 說明 | | --- | --- | --- | | siteId | env.SITE_ID | 站點識別 | | secret | env.SITE_SECRET | 共享密鑰 | | hubUrl | env.HUB_URL | Hub base URL | | flags | {} | Record<string, FlagDefinition> | | controls | {} | Record<string, ControlDefinition> | | heartbeatCooldown | 60 | 每次 heartbeat 最小間隔(秒) | | flagCacheTTL | 30 | Flag 本地 cache TTL(秒) | | version | env / "unknown" | 回報給 Hub 的站點版本 | | selfHosted | false | 站點本身就是 Hub 時設 true,跳過 env warn 與 network beat | | logger | console.* | 注入 { warn, error? } 自訂 logger(傳空函式可完全靜音) | | onBeat | — | (result) => void 每次 heartbeat 後觸發,給 metrics 餵簡單訊號 | | onMetric | — | (metric) => void 結構化 metric 串流(beat / event.buffered / event.dropped / command.executed / schema.changed) |

完整型別:src/types.ts


Hub subpath(接 heartbeat 的那一端)

Hub 那邊裝同一個 package,從 /hub import:

// app/api/connector/heartbeat/route.ts (Next.js)
import { createHeartbeatHandler } from "@emat.tw/connector/hub"
import { hubStore } from "@/lib/hub-store"

export const POST = createHeartbeatHandler({ store: hubStore })

hubStore 實作 HubStore 介面 — 7 個方法接你選的儲存(D1 / KV / Postgres / Firebase…)。Demo 跟測試可以直接用內建的:

import { createMemoryStore } from "@emat.tw/connector/hub"
export const hubStore = createMemoryStore()
hubStore.registerSite("emat", process.env.SITE_SECRET!)

handler 回傳 Web 標準 Response,所以 Next route handler / Hono / raw Node 都裝得上。狀態碼 200 / 400 / 401 / 426 / 500。

React subpath(client 元件用)

當 satellite 有 client 元件需要讀 flag,不要每個元件 fetch——server 端讀完一次塞進 context:

// app/layout.tsx — server component
import { ConnectorProvider } from "@emat.tw/connector/react"
import { site } from "@/lib/site"

export default function Layout({ children }) {
  return (
    <ConnectorProvider flags={site.snapshot().flags}>
      {children}
    </ConnectorProvider>
  )
}
// 任何 client 元件
"use client"
import { useFlag } from "@emat.tw/connector/react"

export function NewEditorButton() {
  const enabled = useFlag("new_editor", false)
  if (!enabled) return null
  return <button>新版編輯器</button>
}

Server 讀 → context → client 同步取,零 hydration mismatch、零額外 HTTP request。

執行環境

支援:

  • Server Components
  • Server Actions
  • Route Handlers(runtime: 'nodejs'

不支援

  • Edge Runtime(runtime: 'edge'
  • Middleware

若在 Edge 路由需要讀 flag,請改由 Server Component / Server Action 包一層後呼叫。


Wire Protocol 概要

Connector 與 Hub 之間只有一個 endpoint:

POST {HUB_URL}/api/connector/heartbeat

Request headers:

| Header | 值 | | --- | --- | | x-site-id | 站點 ID | | x-site-secret | 共享密鑰 | | x-protocol-version | 1 |

送出(HeartbeatRequest):site 元資料、schema hash(hash 變動時才帶 full schema)、本批 events、前輪 commands 的 receipts。

收回(HeartbeatResponse):flags 現值、待執行 commands、schema ack。

完整型別:src/protocol.ts


執行流程

cold start
  └─▶ heartbeat   ─▶ Hub 回 flags + commands
                     ├─ 收到 commands → 跑 control.run() → 記錄 receipt
                     └─ cache flags (TTL 30s)

業務碼呼叫 flag() / report()
  ├─ 距上次 beat > 60s  ─▶ 背景再 beat 一次(不 block 呼叫者)
  └─ 距上次 beat ≤ 60s  ─▶ 讀 cache 即回

Hub 不通
  ├─ flag()    ─▶ 回宣告的 default
  ├─ report()  ─▶ 吞下(event 進 buffer 等下次 beat)
  └─ 站點完全不受影響

開發

pnpm build        # 編譯到 dist/
pnpm dev          # watch mode
pnpm typecheck    # 純型別檢查

Roadmap

  • 0.0.1:公開 API + 型別 + stub 實作 ✓
  • 0.1.0:HTTP 傳輸、schema diff、flag cache、command loop ✓
  • 0.2.0:BeatResult、snapshot()、自訂 logger、subpath exports ✓
  • 0.3.0:typed flag keys、onBeat hook ✓
  • 0.4.0@emat.tw/connector/react<ConnectorProvider> + useFlag) ✓
  • 0.4.1:vitest 單元測試(26 tests) ✓
  • 0.5.0@emat.tw/connector/hub(heartbeat handler + memory store) ✓
  • 0.6.0:E2E roundtrip 測試 + onMetric 結構化串流 ✓
  • 0.7.0 ← 目前:CLI scaffold(npx @emat.tw/connector init

授權

MIT © emat.tw