Skip to content
shishikamiPublic

About

Backend-agnostic, privacy-first browser analytics SDK for TypeScript with durable queues, consent controls, autocapture and replaceable transports.

Topics

Resources

Code of conduct

Contributing

Security policy

Accessibility

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

70 Commits

Folders and files

Tracelet

npm version npm downloads CI license TypeScript

A backend-agnostic, privacy-first analytics SDK for modern browsers. Tracelet gives product code one typed API while keeping queues, codecs, compression and delivery behind replaceable contracts.

Live playground · Quickstart · SDK evaluation guide · Examples · API reference · 简体中文

Why Tracelet?

  • Backend-agnostic: send the same event model to your own HTTP collector or any custom Transport.
  • Reliable in the browser: persist pending events in IndexedDB and coordinate shared queues across tabs.
  • Privacy-aware: check consent before identities or events are created; autocapture never reads DOM text or form values.
  • Type-safe: model event names and properties with a TypeScript event map.
  • Replaceable by design: swap storage, routing, codecs, compression and plugins without changing product instrumentation.

Install

npm install tracelet-browser

Quick example

import {
  HttpTransport,
  createAnalytics,
  createBrowserEventStorage,
} from "tracelet-browser";

interface Events {
  order_submitted: { orderId: string; amount: number };
}

const eventStorage = await createBrowserEventStorage({
  databaseName: "storefront-analytics",
  onFallback(error) {
    console.warn("IndexedDB unavailable; analytics will use memory", error);
  },
});

const analytics = createAnalytics<Events>({
  appId: "storefront",
  eventStorage,
  transport: new HttpTransport({
    endpoint: "https://events.example.com/v1/batch",
  }),
});

await analytics.track("order_submitted", {
  orderId: "o_123",
  amount: 199,
});

Try this lifecycle without creating a backend in the interactive playground. Tracelet is framework-independent and tested in Chromium, Firefox and WebKit on every change.

Runnable starters:

Planning: 0.4 release readiness · 1.0 readiness gates · 0.3 → 0.4 migration · EventBatch v1 · Browser support · Compatibility matrix

Privacy and security: Data inventory · Threat model · Failure semantics · Security policy

Community: Support · Contributing · Code of Conduct · Accessibility · Security · Issues

0.3.x remains the stable line. The published 0.4.0-beta.2 prerelease fixes Consent delivery and concurrent erasure issues found after beta.1. Install tracelet-browser@0.4.0-beta.2 for evaluation; the beta tag points to it while latest stays on 0.3.0. See the migration guide and changelog before upgrading.

简体中文

Tracelet 是一个后端无关、隐私优先的浏览器埋点 SDK。SDK 核心保持框架无关,React 官网作为独立 workspace 维护。

安装:

npm install tracelet-browser
pnpm add tracelet-browser
yarn add tracelet-browser

tracelet 已在 npm 被其他项目占用,因此本项目使用 tracelet-browser 作为 npm 包名。

项目状态与浏览器支持

当前版本见页面顶部的 npm badge,适合评估、原型和对投递行为有明确验证方案的早期项目。

Tracelet 的完整浏览器套件会在固定版本的 Chromium、Firefox 和 WebKit 中运行。精确引擎版本、必需能力、能力缺失时的降级行为,以及尚未单独验证的移动端/隐私模式,见浏览器支持策略;不要把“同一引擎”理解成未经测试的设备兼容承诺。

数据链路

track / page / identify
  -> standard event
  -> plugins
  -> event storage
  -> event route
  -> batch
  -> codec
  -> compression
  -> transport

客户端与 Collector 的稳定线协议见 EventBatch v1。协议把任意 2xx 定义为整批事件已持久接收(包括按 eventId 判定的重复事件),因此 Collector 不应在只提交部分事件时返回成功。

快速使用

import {
  BrowserLocalStorage,
  HttpTransport,
  createAnalytics,
  createBrowserEventStorage,
} from "tracelet-browser";

interface Events {
  order_submitted: {
    orderId: string;
    amount: number;
  };
}

const eventStorage = await createBrowserEventStorage({
  databaseName: "shop-analytics",
  onFallback(error) {
    console.warn("IndexedDB 不可用,埋点队列将退回内存", error);
  },
});

const analytics = createAnalytics<Events>({
  appId: "shop-web",
  identityStorage: new BrowserLocalStorage(),
  eventStorage,
  transport: new HttpTransport({
    endpoint: "https://events.example.com/v1/batch",
  }),
});

await analytics.track("order_submitted", {
  orderId: "o_123",
  amount: 199,
});

低侵入自动采集

自动采集是可选插件,不会扫描 DOM,也不会读取元素文本、表单值或 data-* 属性。它目前只处理 SPA 页面浏览和浏览器全局错误:

import {
  createAnalytics,
  createAutoCapturePlugin,
} from "tracelet-browser";

const analytics = createAnalytics({
  appId: "shop-web",
  transport,
  plugins: [
    createAutoCapturePlugin({
      pageViews: {
        initial: true,
        // Hash 可能包含敏感信息,仅 Hash Router 项目显式开启。
        includeHash: false,
      },
      errors: {
        // 堆栈可能包含文件路径或参数,因此默认关闭。
        includeStack: false,
      },
    }),
  ],
});

await analytics.init();

页面浏览通过 page() 事件进入统一管线。URL 上下文默认移除 query 和 hash;Hash Router 可以用 includeHash: true 开启 fragment 路由采集。window.error 生成 $error,未处理的 Promise 拒绝生成 $unhandled_rejection。它们都会遵守 Consent,调用 shutdown() 后会移除监听器并恢复被包装的 History 方法。

关键业务事实仍应使用显式 track(),例如支付成功或订单创建;自动采集适合补充页面访问和技术异常,而不是替代业务事件设计。

Consent

需要用户授权后才能采集时,将初始状态设为 pending:

const analytics = createAnalytics({
  appId: "shop-web",
  transport,
  consent: {
    initialState: "pending",
    clearQueueOnDenied: true,
  },
});

await analytics.track("ignored_before_consent", {});
await analytics.setConsent("granted");
await analytics.track("collected_after_consent", {});

// 撤回授权会清空未发送队列并重置身份。
await analytics.setConsent("denied");

默认值仍为 granted,便于服务端已统一处理授权的项目保持兼容。

可靠投递

  • IndexedDbEventStorage 按写入顺序持久化未发送事件。
  • 单事件默认限制为 64 KiB,编码后单批次默认限制为 256 KiB,可通过 maxEventBytes 和 maxBatchBytes 调整。
  • visibilitychange 和 pagehide 会触发最后一次 Flush。
  • Web Locks API 会协调同源多 Tab 的共享队列发送,避免并发 Flush 重复读取同一批事件。
  • Session 默认在连续 30 分钟无事件后自动续期,可通过 sessionTimeoutMs 调整。
  • 网络、HTTP 408、429 和 5xx 使用指数退避与随机抖动重试。
  • HTTP 4xx 等不可重试错误会从队列移除,防止永久阻塞。
  • 每个事件拥有稳定 eventId,服务端应按该字段幂等去重。

可通过构造参数 onDiagnostic 或运行时 subscribeDiagnostics() 观察 SDK 自身状态,不会生成新的业务埋点。投递事件使用 attemptId 关联同一次发送:

const analytics = createAnalytics({
  appId: "shop-web",
  transport,
  onDiagnostic(event) {
    console.debug("[analytics]", event);
  },
});

const unsubscribe = analytics.subscribeDiagnostics((event) => {
  if (event.type === "flush_failed") {
    console.warn(event.attemptId, event.status, event.retryable);
  }
});

const snapshot = await analytics.getQueueSnapshot();
console.info(snapshot.pendingEvents, snapshot.delivery.phase);

unsubscribe();

getQueueSnapshot() 返回只读的近似快照。pendingEvents 来自当前 EventStorage,oldestEventTimestamp 是事件时间而不是精确入队时间;delivery.phase 为 idle、sending 或 waiting-retry。诊断与快照都不会包含事件 Properties、用户 ID、请求体或鉴权头。

浏览器存储配额

使用 IndexedDbEventStorage 时,可以读取浏览器提供的近似容量信息:

const estimate = await analytics.getStorageEstimate();

if (estimate && (estimate.usageRatio ?? 0) >= 0.8) {
  console.warn("Origin storage usage is high", estimate);
}

usageBytes、quotaBytes、availableBytes 和 usageRatio 都是整个 Origin 的估算值,可能包含应用自己的 IndexedDB、Cache Storage 等数据,并非 Tracelet 独占空间;浏览器也可能为了隐私而舍入或省略字段。当 IndexedDB 写入实际触发配额错误时,SDK 不会让 track() 影响业务流程,而是丢弃当前事件并发出 event_dropped: storage-quota 与 storage_quota_exceeded 诊断。SDK 不会擅自删除其他应用数据。

完整示例见 examples/browser.ts;官网 Quickstart 来自 examples/quickstart.ts,并在 CI 中执行 TypeScript 类型检查。

框架适配

Tracelet 不要求框架 Wrapper。推荐在渲染过程之外创建一个应用级实例,并只把初始化连接到框架根生命周期:

  • React:根部渲染 TraceletBootstrap,避免在普通组件卸载时关闭全局单例。
  • Vue:在 app.mount() 前注册 traceletPlugin,组合式函数通过注入取得同一实例。
  • Next.js:从根布局渲染带 "use client" 的 TraceletClient,避免在 Server Component 中初始化浏览器能力。

可运行代码位于 examples/frameworks,详细边界见 docs/framework-integrations.md。这些示例会在 CI 中由真实 React/Vue 类型检查。

多 Collector 条件路由

EventRouter 会在 Codec 与 Compression 之前检查原始事件,并把每个事件交给第一个匹配的 Transport。未匹配事件必须进入 fallback,因此不会因规则遗漏永久阻塞队列:

import { EventRouter, HttpTransport, createAnalytics } from "tracelet-browser";

const analytics = createAnalytics({
  appId: "shop-web",
  transport: new EventRouter({
    routes: [
      {
        name: "technical-errors",
        match: (event) =>
          event.name === "$error" || event.name === "$unhandled_rejection",
        transport: new HttpTransport({
          name: "alert-collector",
          endpoint: "https://alerts.example.com/v1/batch",
        }),
      },
    ],
    fallback: new HttpTransport({
      name: "product-collector",
      endpoint: "https://events.example.com/v1/batch",
    }),
  }),
});

规则按声明顺序匹配。SDK 保持队列的 FIFO 顺序,并在目标 Transport 改变时切分批次;一次 flush() 仍只发送一个批次。flush_started、flush_succeeded 和 flush_failed 诊断事件会携带实际的 transport 名称,方便区分多个端点。完整示例见 examples/routing.ts。

接入 Protobuf

Core 不依赖具体 Protobuf Runtime。使用 protobufjs、ts-proto 或 Buf 生成编码器后,通过 ProtobufCodec 注入:

import { ProtobufCodec, createAnalytics } from "tracelet-browser";
import { EventBatch } from "./generated/analytics.js";

const codec = new ProtobufCodec((batch) => {
  return EventBatch.encode(EventBatch.fromJSON(batch)).finish();
});

const analytics = createAnalytics({
  appId: "shop-web",
  codec,
  transport,
});

示例协议位于 proto/analytics.proto。实际项目建议把生成代码放在独立包,例如 @tracelet/codec-protobuf。

当前限制

  • 默认队列仍是内存存储;浏览器生产环境应显式使用 createBrowserEventStorage(),优先选择 IndexedDB 并在启动失败时退回内存。
  • gzip 使用浏览器原生 CompressionStream,不支持时不会自动降级。
  • 自动采集暂不包含点击、输入和 DOM 内容采集;这是有意的隐私与稳定性边界。
  • properties 和 context 在示例 Protobuf 中以 JSON bytes 保存,优先保持通用性;稳定后可以逐步强类型化。

本地端到端测试

pnpm build
pnpm build:docs
pnpm local:server
pnpm test:browser

浏览器访问 http://127.0.0.1:4318 查看网页版使用手册;访问 http://127.0.0.1:4318/lab 打开完整的 SDK 验证台。页面支持:

  • 自定义事件名、Properties 和批量数量。
  • 仅入队或立即 Flush,并实时查看 IndexedDB 队列长度。
  • Identify、身份重置和 Consent 状态切换。
  • 注入 503、429、500、400 以及服务端响应延迟。
  • 观察指数退避、失败保留、不可重试丢弃等行为。
  • 查看服务端事件、批次统计、请求日志和 SDK 诊断日志。

Collector 同时提供以下调试接口:

http://127.0.0.1:4318/health
http://127.0.0.1:4318/v1/events
http://127.0.0.1:4318/v1/stats
方法 路径 用途
POST /v1/batch 接收 SDK 批量事件
POST /v1/batch/:channel 验证多个 Transport 的条件路由
GET /v1/events?limit=100 查询最近事件
DELETE /v1/events 清空事件和统计
GET /v1/stats 查询批次、失败、事件名和请求日志
POST /v1/control/failure 配置后续请求的失败状态与延迟

本地 Collector 仅用于开发验证:事件只保存在内存中,进程退出即清空,不包含生产鉴权、限流和持久化能力。

构建和部署手册站点

官网采用 React、Vite、Tailwind CSS 和 shadcn/ui 的源码组件模式;这些依赖只存在于私有的 website workspace,不会进入 SDK 的 npm 包:

pnpm dev:docs
pnpm build:docs

官网源码位于 website/,构建产物位于 out/。精简首页、Quickstart、Examples、API Reference 与两篇指南分别使用独立 URL,英文页面统一增加 /en 前缀。所有页面共享组件与中英文搜索,文档页提供粘性侧边目录,并具有各自的 canonical、hreflang、首个 HTML 响应摘要和搜索元数据。仓库附带 vercel.json,导入 Vercel 后会自动安装 workspace 依赖、运行构建并发布 out/。其他静态托管平台只需把构建命令设为 pnpm build:docs、输出目录设为 out。框架演进判断见 docs/website-framework.md。

仓库结构

src/       SDK 源码(不依赖 React)
tests/     SDK 行为测试
examples/  浏览器接入和本地验证示例
website/   React + Vite + Tailwind 官网
proto/     可选的 Protobuf 协议示例

发布 npm 包

项目通过 GitHub Actions 自动完成持续集成与 npm 发布:

  • Pull Request 和 main 分支推送会在 Node.js 20、24 上运行类型检查、测试、官网构建和 npm 包内容检查。
  • CI 会把 npm pack 生成的 tarball 安装进空白项目,验证 JavaScript 导入、TypeScript 声明和 Vite 构建,避免“源码能用但发布包不可用”。
  • Node.js 24 还会安装同一个 tarball,并完成 React/Vite、Vue/Vite、Next.js/Turbopack、Webpack 与 Rollup 的真实生产构建。
  • Node.js 检查通过后,会在 Chromium、Firefox 和 WebKit 上运行浏览器端到端测试。
  • 发布 GitHub Release 后,工作流会校验 Git tag 与 package.json 版本一致,再通过 npm Trusted Publishing 提交待审核版本;SemVer 预发布版本固定使用 beta dist-tag,正式版本使用 latest。
  • Vercel 保持 Git 集成:Pull Request 生成 Preview,main 更新生产站点,不在 Actions 中重复部署。

首次启用自动发布时,需要在 npm 包的 Settings → Trusted Publisher 中添加 GitHub Actions:

  • Organization or user:shishikami
  • Repository:Tracelet
  • Workflow filename:publish.yml
  • Environment:留空
  • Allowed action:保持直接 npm publish 关闭;npm stage publish 默认允许

日常发布步骤:

  1. 更新 package.json 版本和 CHANGELOG.md。
  2. 合并到 main,等待 CI 通过。
  3. 创建同版本 tag(例如版本 0.2.0 对应 v0.2.0)。
  4. 发布 GitHub Release;Publish npm package 工作流会把版本提交到 npm 暂存区。
  5. 在 npm 的 Staged Packages 中检查包内容,并通过 Passkey/2FA 批准,版本才会正式公开。

本地发布前检查仍可使用:

pnpm check
pnpm smoke:package
pnpm smoke:ecosystem
pnpm pack:check

完整发布检查表和故障处理见 RELEASING.md。

如果自动发布失败,请先修复 CI、Trusted Publisher 或 staged publishing 配置,再重新创建新的版本;不要绕过人工审批直接执行 npm publish。已经公开到 npm 的版本号不能覆盖。

正式发布前请更新 package.json 版本号。npm 包只包含 dist/、README、CHANGELOG、LICENSE 和 npm 必需的 package.json 清单,不包含官网与测试文件。

Roadmap

0.2.0 里程碑已完成:

  • 浏览器测试覆盖 pagehide、gzip 和异常恢复
  • 多 Transport 路由与条件投递
  • 存储配额估算与 IndexedDB 配额诊断
  • 文档搜索和框架适配示例

0.3.0 已完成。0.4.0 聚焦 1.0 前的契约硬化:

  • 可观测 API 设计稿:队列快照、投递状态和多订阅者语义。

  • 兼容性与韧性测试矩阵:明确自动化、部分覆盖、手工验证和待验证边界。

  • Vercel Collector starter:CORS、安全大小边界、gzip、鉴权扩展、PostgreSQL 迁移与跨实例幂等 Sink。

  • 实现并验证可观测 API:队列快照、运行时订阅、Attempt 关联与三浏览器覆盖。

  • 发布并验证 0.3.0 正式版。

  • 增加 CSP、离线恢复、隐藏页 Flush 与真实跨标签页锁的三浏览器夹具。

  • 增加 React、Vue、Next.js、Webpack 与 Rollup 的真实生产构建夹具。

  • 评估 CDN / IIFE 构建:0.3 保持原生 ESM-only,等待真实无打包器需求后再扩展。

  • 增加 IndexedDB 启动探测与显式内存后备队列,避免首个 track() 因持久存储不可用而影响业务。

  • 对完整 TypeScript 声明建立可审查的 API 快照,而不只检查运行时导出名称。

  • 冻结 EventBatch v1、重试、幂等与兼容性语义。

  • 明确页面退出、关闭后调用和适配器异常的生命周期边界。

  • 将 npm 暂存发布与人工批准后的公共 Registry 验证拆成独立成功门禁。

  • 增加 PostgreSQL 参考 Sink 与真实数据库重启幂等 CI。

  • 完成真实项目接入与持续运行验证,为 1.0 提供生产证据。

0.3 的公开命名与 ESM-only 分发范围已经冻结;历史发布证据见 0.3 release readiness。

版本变化见 CHANGELOG.md。

About

Backend-agnostic, privacy-first browser analytics SDK for TypeScript with durable queues, consent controls, autocapture and replaceable transports.

Topics

Resources

Code of conduct

Contributing

Security policy

Accessibility

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages