npm.io
2.0.2 • Published 5 months ago

@tni/cache

Licence
MIT
Version
2.0.2
Deps
0
Size
37 kB
Vulns
0
Weekly
0

@tni/cache

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

设计目标

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

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

import { localStorage as appLocal } from "@tni/cache";
await appLocal.set("k", "v");
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 属性(path、domain、secure、sameSite、maxAge、expires、ttl 等)。

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(秒)或 expires(Date 或毫秒时间戳)
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)

错误与配额

  • 写入时若判定为存储配额不足,可能抛出 StorageQuotaExceededError(storage 字段标识后端,如 localStorage、indexedDB)。
  • 工具函数 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