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

@tni/cache

v2.0.2

Published

浏览器端模块化缓存与存储抽象(localStorage / sessionStorage / Cookie / IndexedDB)。

Downloads

59

Readme

@tni/cache

浏览器端模块化缓存与存储抽象:在 localStoragesessionStorageCookieIndexedDB 上提供统一的 StorageAdapter 异步接口,便于按场景切换后端而少改业务代码。

设计目标

  • 统一契约set / get / remove / getAll / clear / getSize 等行为在各后端一致,均为 Promise。
  • 可扩展:Web Storage 场景可通过 createWebStorageAdapter + createCachedStorageResolver 接入自定义 Storage 实现(含测试替身)。
  • 可维护:序列化、过期(ttl / expires)、键前缀、配额相关工具集中在内部管线,各后端只处理字符串载荷。
  • 无内置加解密:若要对落盘字符串加密,由业务实现 encrypt / decryptcipher 并传入配置;本包不提供算法实现。
  • 零运行时依赖:发布产物不捆绑第三方库;需在支持 window / document / indexedDB 的浏览器(或等价 polyfill)中使用。

包结构(源码)

src/
  index.ts                 # 对外统一导出
  shared/
    types.ts               # StorageAdapter、BaseStorageOptions、错误类型等
    base.ts                # 序列化/过期/配额辅助(含 estimateQuota 等)
  modules/
    browser/               # localStorage / sessionStorage / 通用 Web Storage 适配器
    cookie/                # document.cookie 封装
    indexed-db/            # 单库单 object store 的 KV 封装

安装

pnpm add @tni/cache

(Monorepo 内也可通过 workspace 协议引用本包。)

快速开始

localStorage / sessionStorage

import { createLocalStorage, createSessionStorage } from "@tni/cache";

const local = createLocalStorage<{ token: string }>({ prefix: "myapp:" });
await local.set("token", "xxx", { ttl: 3600 });
const token = await local.get("token");

const session = createSessionStorage<number>({ prefix: "myapp:" });
await session.set("step", 2);

包内也导出默认实例 localStoragesessionStorage(与全局同名,建议按需 as 别名 导入,避免与 window.localStorage 混淆):

import { localStorage as appLocal } from "@tni/cache";
await appLocal.set("k", "v");

Cookie

import { createCookieStorage } from "@tni/cache";

const cookies = createCookieStorage<string>({
  prefix: "app_",
  defaultAttributes: { path: "/", sameSite: "lax" },
});

await cookies.set("sid", "abc123", { maxAge: 86400 });
const sid = await cookies.get("sid");

set 支持第三个参数覆盖单次写入的 Cookie 属性(pathdomainsecuresameSitemaxAgeexpiresttl 等)。

IndexedDB

import { createIndexedDBStorage } from "@tni/cache";

const idb = createIndexedDBStorage<Record<string, unknown>>({
  dbName: "my_app_kv",
  storeName: "entries",
  version: 1,
  prefix: "cache:",
});

await idb.set("prefs", { theme: "dark" });
const prefs = await idb.get("prefs");

默认库名为 __tni_cache__,仓库名为 kv;同一站点多业务建议显式设置 dbName / prefix 隔离。

统一接口 StorageAdapter

| 方法 | 说明 | | ------------------------ | ----------------------------------------------------------------------------------- | | set(key, value, opts?) | 写入;opts 可含 ttl(秒)或 expiresDate 或毫秒时间戳) | | get(key) | 读取;无键或已过期返回 null(Cookie / Web Storage 等对「无值」可能表现为 null) | | remove(key) | 删除单键 | | getAll() | 当前前缀下键值对(已过期条目会在读取时尽量清理) | | clear() | 清空当前前缀(无前缀的 Web Storage 会 storage.clear()) | | getSize() | 当前命名空间占用估算(UTF-16 字符长度 ×2);IndexedDB 上为异步 | | isAvailable() | 当前环境是否可使用该后端 | | isQuotaExceeded() | 最近一次写入是否因配额失败(具体行为因后端略有差异) |

通用配置 BaseStorageOptions

| 字段 | 说明 | | -------------------------------- | -------------------------------------------------------------- | | prefix | 键前缀,隔离命名空间 | | defaultTtl | 默认存活时间(秒),可被单次 set 的选项覆盖 | | serialize / deserialize | 自定义序列化,默认 JSON.stringify / JSON.parse | | cipher / encrypt / decrypt | 对「序列化后的字符串」再加工;需自行实现,本包无内置加解密 | | isAvailable | 额外开关(例如 SSR 下返回 false) |

错误与配额

  • 写入时若判定为存储配额不足,可能抛出 StorageQuotaExceededErrorstorage 字段标识后端,如 localStorageindexedDB)。
  • 工具函数 isQuotaExceededError(error) 用于统一识别原生 QuotaExceededError 与上述包装错误。
  • estimateQuota() / formatBytes() 基于 navigator.storage?.estimate(),用于展示或监控(不支持时返回 null)。

扩展 Web Storage

若需在测试或非浏览器环境挂载自定义 Storage

import { createCachedStorageResolver, createWebStorageAdapter } from "@tni/cache";

const getStorage = createCachedStorageResolver("localStorage");

const adapter = createWebStorageAdapter({
  storageName: "memory",
  getStorage: () => myFakeStorage,
  prefix: "t:",
});

开发与构建

本仓库使用 Vite+vp)。

vp install          # 在仓库根目录安装依赖
cd packages/cache
vp test             # 单元测试
vp check            # 格式、Lint、类型检查
vp pack             # 构建 dist(ESM + CJS + 类型声明),对应 package.json 中 build 脚本

库包推荐使用 vp pack 作为发布构建;vp build 面向带 index.html 的应用入口,在未单独配置库模式时可能报错。

许可证

MIT