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

@tracer-kit/core-storage

v0.1.0

Published

Generic IndexedDB record storage with bounded capacity and stable pagination.

Readme

@tracer-kit/core-storage

npm install @tracer-kit/core-storage

模块角色

Core Storage 是 Tracer 的通用浏览器持久化层,位于 Domain 与 IndexedDB 之间。上游传入已经准备好的泛型记录,本模块负责存储、稳定分页、容量治理、淘汰和跨上下文变更通知。

本模块不知道日志消息、业务字段或加密算法。它只处理 StorageRecord<TStored>,因此也可被其他浏览器端记录场景复用。

flowchart LR
  Domain[上游调用方] -->|StorageRecord T| Storage[Core Storage]
  Storage --> Estimate[浏览器配额估算]
  Storage --> Records[(IndexedDB records)]
  Storage --> Meta[(IndexedDB capacity meta)]
  Storage -->|结构化变更事件| Channel[BroadcastChannel]
  Channel --> Consumer[其他页面或扩展]
  Estimate -->|有效上限| Storage
  Storage -->|超限时淘汰最旧记录| Records

公共导出

  • createStorage<T>(options?):异步创建并初始化可用的 IndexedDB 存储实例。
  • DEFAULT_STORAGE_OPTIONS:默认数据库名与容量配置。
  • createStorageNotifier(channel, options?):创建基于 BroadcastChannel 的结构化变更通知器;单个监听器异常会被隔离并交给可选 onError。
  • validateCapacityConfig(config):校验容量配置。
  • calculateEffectiveCapacity(config, totalBytes, estimate?):计算当前有效上限与低水位。
  • 类型:RecordStorage、StorageOptions、StorageInput、StorageRecord、PutResult、StorageQuery、StorageListOptions、StorageIterateOptions、StoragePage、CapacityConfig、CapacityState、CapacityMeta、StorageDatabase、StorageNotifier、StorageChange 等。

所有类型统一定义在 src/types.ts,并由 src/index.ts 使用 export * from './types' 全部公开。新增或删除类型时必须同步更新本节和 public-import 测试。

基本用法

import { createStorage } from '@tracer-kit/core-storage';

// TStored 由上游决定;Core Storage 不理解 value 内的业务字段。
const storage = await createStorage<MyValue>({
  // 可选:内部恢复运行期错误后向诊断系统报告,不会从 put() 抛出。
  onError: console.error,
});

// 上游不传 byteSize,Core Storage 使用统一策略计算完整记录大小。
const result = await storage.add(value);
// add() 自动生成 ID 和时间;结果与 put() 相同,不额外返回 ID 或时间。

// 普通读取返回有限的记录数组,默认 100 条,最多 500 条。
const records = await storage.list({ limit: 100 });

// 导入、恢复或幂等覆盖时使用高级写入接口。
await storage.put({ id: '1', occurredAt: Date.now(), value });

// 高级分页查询按 occurredAt + id 升序返回;下一页使用 page.nextCursor。
const page = await storage.query({ limit: 100 });

// 批量遍历由 iterate() 内部稳定翻页,不向调用方暴露 cursor。
for await (const record of storage.iterate({ pageSize: 100 })) {
  consume(record);
}

// 宿主销毁时关闭 IndexedDB 与 BroadcastChannel。
storage.close();

未传配置时使用数据库 tracer-storage,容量上限为 50 MiB,为同 Origin 的其他数据预留 10 MiB,淘汰低水位为有效容量的 80%。StorageOptions 支持逐项覆盖这些默认值,以及 estimate、notificationChannel、measureRecord 和 onError;测试或受控运行环境还可注入 idSource 与 clock,生产默认使用 crypto.randomUUID() 和 Date.now()。

内部实现

indexeddb-storage.ts 使用 Dexie 建立 records 和 meta 两个对象仓库。记录通过复合索引 [occurredAt+id] 排序,游标同时包含时间与 ID,从而在时间相同的情况下仍能稳定分页。

每次写入先读取浏览器配额估算,并取“配置上限”和“浏览器可用空间”中的较小值作为有效容量。超限时按最旧记录优先淘汰,直到低水位能容纳新记录。遇到 QuotaExceededError 时重新估算、淘汰并只重试一次。

flowchart TD
  Put[put StorageInput] --> Measure[内部计算完整记录 byteSize]
  Measure --> EstimateNow[读取配额并计算有效容量]
  EstimateNow --> Oversized{单条记录超过上限?}
  Oversized -->|是| Reject[拒绝并增加计数]
  Oversized -->|否| Fits{当前空间足够?}
  Fits -->|否| Evict[按 occurredAt + id 淘汰到低水位]
  Fits -->|是| Persist[事务写入记录和容量元数据]
  Evict --> Persist
  Persist --> Notify[发布 record-committed]
  Persist -. QuotaExceededError .-> Retry[重新估算并只重试一次]
  Retry --> Persist

写入成功后发布 { type: 'record-committed', id };删除或清空发布 { type: 'records-invalidated' }。通知只表示数据已变化,数据本身仍从 IndexedDB 查询。

本实例监听器逐个隔离,并允许返回 void | Promise<void>:同步抛错和异步拒绝都不会让已经提交的 put() 变成失败,也不会阻断后续监听器或 BroadcastChannel.postMessage()。发布过程不等待监听器 Promise,只挂拒绝处理以避免 unhandled rejection。监听器、频道和错误上报器自身的异常都由存储层吸收;配置的 onError 只用于留痕。

失败与生命周期

  • createStorage() 只在数据库打开、Schema 和容量元数据初始化完成后 resolve;初始化失败或容量配置非法时 reject,并关闭半初始化实例。
  • 单条记录大于有效容量时返回 { status: 'rejected' } 并增加拒绝计数。
  • 实例创建后的 IndexedDB、计量或配额重试失败返回 { status: 'failed' },并通过可选 onError 留痕。
  • 所有操作共享同一次并发安全的自动初始化,不要求上游严格安排初始化时序。
  • close() 关闭 BroadcastChannel 和数据库连接,调用方销毁时必须执行。
  • IndexedDB 不可用时不会回退到 localStorage,避免容量、事务和查询语义悄然改变。

测试与维护

  • test/capacity.test.ts:容量计算与配置边界。
  • test/indexeddb-storage.test.ts:读写、游标分页、淘汰、配额重试和通知。
  • test/public-import.test.ts:公共出口可用性。

修改本模块时必须同步维护本 README 和相关测试;通知协议变化还要同步 DevTools 扩展及 docs/modules/core-storage.md。