- 开发工具
- 后端
【免费下载链接】firebase-js-sdk
Firebase Javascript SDK
导读
@firebase/util是 Firebase JavaScript SDK 的公共基础工具包,为 auth、database、firestore、storage、analytics 等所有上层模块提供环境检测、错误工厂、JWT 解码、Base64 编解码、订阅模型、深拷贝、指数退避、参数校验等公共能力。本文以仓库中的 API 报告 common/api-review/util.api.md 为骨架,逐一解读该包的完整公开 API 签名、源码实现原理与典型使用场景,帮助读者理解 Firebase SDK 底层通用逻辑,并能在自己的项目中直接复用这些久经考验的工具函数。
包定位:Firebase 全家桶的"地基模块"
在 packages/util/package.json 中可以看到,@firebase/util当前版本为1.15.3,是整个 SDK 中几乎零依赖的公共层(运行时仅依赖tslib)。它通过 rollup 构建出 Node CJS、浏览器 ESM 等多种产物,并对外暴露dist/util-public.d.ts类型声明。
从源码目录 packages/util/src 的结构可以清晰看出它按职责划分的 26 个模块:errors.ts(错误体系)、deferred.ts(异步)、subscribe.ts(观察者模型)、jwt.ts(令牌解析)、environment.ts(环境检测)、crypt.ts(Base64)、sha1.ts/sha256.ts(哈希)、exponential_backoff.ts(退避算法)、defaults.ts/emulator.ts(默认配置与模拟器)等。该包自身的单元测试位于 packages/util/test,覆盖errors、subscribe、defaults、emulator、base64、deepCopy、environments、exponential_backoff、object、compat等主题,是验证下述 API 行为的最佳参考。
统一错误体系:FirebaseError 与 ErrorFactory
Firebase SDK 的所有模块共享一套标准化错误格式,这是 errors.ts 的核心职责。
FirebaseError 类
export class FirebaseError extends Error { readonly name: string; // 恒为 "FirebaseError" readonly code: string; // 形如 "service/code" customData?: Record<string, unknown>; constructor(code: string, message: string, customData?: Record<string, unknown>); }它做了两件 ES5 兼容性处理:通过Object.setPrototypeOf(this, FirebaseError.prototype)修复内建类继承问题,并利用Error.captureStackTrace保留抛出点的完整堆栈(V8 环境)。
ErrorFactory 与错误模板
ErrorFactory<ErrorCode, ErrorParams>是生产错误的工厂,构造参数为(service, serviceName, errors):
const factory = new ErrorFactory<Err>('storage', 'Storage', { 'object-not-found': 'Object {$name} not found.' }); throw factory.create('object-not-found', { name: 'avatar.png' }); // Storage: Object avatar.png not found. (storage/object-not-found).其create(code, ...data)方法的核心逻辑(errors.ts):
- 将错误码拼装为
service/code全限定格式; - 用模板字符串中的
{$key}占位符替换为data[key](缺失时渲染为<key?>,见replaceTemplate实现); - 最终消息格式为
ServiceName: message (service/code).。
配套类型ErrorMap<ErrorCode>是"错误码 → 模板串"的只读映射,ErrorData是{[key: string]: unknown}的任意扩展数据。这也是各产品包(如 auth、storage)在catch中通过error.code做分支处理的标准依据。
assert / assertionError
export const assert: (assertion: unknown, message: string) => void; export const assertionError: (message: string) => Error;实现位于 assert.ts:assert在断言为假时抛出由assertionError生成的错误,错误消息格式为Firebase Database (SDK_VERSION) INTERNAL ASSERT FAILED: message——其中 SDK 版本来自 constants.ts 中的CONSTANTS.SDK_VERSION编译期注入值。该断言主要供 SDK 内部逻辑使用,用于捕获"不应发生"的编程错误。
异步与订阅模型:Deferred、Observer 与 createSubscribe
Deferred 延迟对象
deferred.ts 提供Deferred<R>,暴露promise、resolve、reject三个成员,以及一个特色方法:
deferred.wrapCallback(callback?) // 返回一个 Node 风格回调 (error, value?) => voidwrapCallback将内部非 Promise 化的回调 API 桥接到 Deferred 上:有 error 则 reject,否则 resolve,并支持只接收单参数的旧式回调(按callback.length === 1判断)。这正是 SDK 内部"回调 API 与 Promise 并存"过渡期的关键粘合层。
Observer 订阅模型
subscribe.ts 定义了一整套迷你观察者模式类型:
type NextFn<T> = (value: T) => void; type ErrorFn = (error: Error) => void; type CompleteFn = () => void; interface Observer<T> { next: NextFn<T>; error: ErrorFn; complete: CompleteFn; } type PartialObserver<T> = Partial<Observer<T>>; type Unsubscribe = () => void;createSubscribe<T>(executor, onNoObservers?)返回一个Subscribe<T>函数,它同时支持两种调用形态(函数式回调或对象式观察者):
const subscribe = createSubscribe<number>(observer => { observer.next(1); observer.next(2); observer.complete(); }); const unsub = subscribe( v => console.log('next', v), e => console.error('error', e), () => console.log('done') ); // 等价写法: const unsub2 = subscribe({ next: v => {}, error: e => {}, complete: () => {} });从源码ObserverProxy类(subscribe.ts)可以看到其底层原理:
- 异步调度:executor 通过
Promise.resolve().then()微任务执行,保证"订阅后不会同步收到事件"; - 多观察者扇出:内部维护观察者数组,
forEachObserver逐个派发,且每个回调也经微任务异步执行并隔离异常; - 终止语义:
error或complete调用后进入finalized状态,后续值被丢弃;已终止流的新订阅者会在微任务中直接收到最终的 error/complete 事件; - 退订通知:
onNoObservers回调在观察者数量归零时触发。
async(fn, onError?)则把任意同步函数包装成异步调用(同样基于 Promise 微任务),并在失败时回调onError。
JWT 令牌工具:解码、时间窗校验与管理员识别
jwt.ts 提供针对 Firebase Auth ID Token 的解析与校验工具,全部建立在decode之上:
export const decode: (token: string) => DecodedToken; // DecodedToken = { header: object; claims: Claims; data: object; signature: string; }decode按.分割 JWT 三段,分别对 header 与 claims 做 Base64 解码后再jsonEval(JSON.parse),并提取 claims 中的d字段作为data。注意其注释明确说明:只解析、不校验签名与有效性,且无原生 Base64 支持时可能返回不完整结果。
基于它派生的四个校验函数:
| 函数 | 行为 |
|---|---|
isValidTimestamp(token) | 依据nbf/iat作为生效时间、exp作为过期时间(缺省exp时按 24 小时推算)判断当前时间是否落在窗口内 |
issuedAtTime(token) | 返回iat秒级时间戳,无iat时返回null |
isValidFormat(token) | 仅要求 claims 为对象且包含iat字段 |
isAdmin(token) | 检查 claims 中admin === true,用于识别 Admin SDK 签发的令牌 |
环境检测:isBrowser / isNode / isReactNative 全家桶
environment.ts 集中了所有运行环境判定,是各模块"按环境选实现"的决策基础:
isBrowser():typeof window !== 'undefined'或处于 Web Worker;isWebWorker():self instanceof WorkerGlobalScope;isNode():默认通过Object.prototype.toString.call(global.process) === '[object process]'判定,并支持forceEnvironment强制覆盖(见下文 FirebaseDefaults);isNodeSdk():依据CONSTANTS.NODE_CLIENT/CONSTANTS.NODE_ADMIN编译期常量判断是否为 Node SDK 构建;isReactNative():navigator.product === 'ReactNative';isMobileCordova():检测window.cordova/phonegap/PhoneGap且 UA 匹配移动端;isBrowserExtension():检测chrome.runtime/browser.runtime的存在与id;isCloudflareWorker():UA 等于Cloudflare-Workers;isElectron()/isIE()/isUWP():基于 UA 子串(Electron/、MSIE/Trident/、MSAppHost/);isSafari()/isSafariOrWebkit():UA 含 Safari/WebKit 且排除 Chrome、非 Node;isIndexedDBAvailable():检测typeof indexedDB === 'object';validateIndexedDBOpenable():实际打开一个名为validate-browser-context-for-indexeddb-analytics-module的临时库来探测 IndexedDB 是否可用(如 Safari iframe、Firefox 隐私模式下会失败),成功后删除不存在的临时库;areCookiesEnabled():读取navigator.cookieEnabled;getUA():返回navigator.userAgent,缺失时返回空串。
这些判定函数在 packages/util/test/environments.test.ts 中有对应测试覆盖。
默认配置与模拟器:FirebaseDefaults 体系
FIREBASE_DEFAULTS注入
defaults.ts 定义FirebaseDefaults接口,用于在应用启动前自动配置 Firebase app 与模拟器:
interface FirebaseDefaults { config?: Record<string, string>; // 默认 App 配置 emulatorHosts?: Record<string, string>; // 各产品的模拟器地址 _authTokenSyncURL?: string; // 实验性:auth token 同步 URL _authIdTokenMaxAge?: number; // 实验性:ID Token 最大有效期 forceEnvironment?: 'browser' | 'node'; // 强制运行环境 [key: string]: unknown; }getDefaults()的读取优先级(源码 defaults.ts):postinstall 写入的默认值 →globalThis.__FIREBASE_DEFAULTS__→ 环境变量__FIREBASE_DEFAULTS__(JSON 字符串)→ Cookie__FIREBASE_DEFAULTS__(Base64 编码的 JSON)。任一环节失败都会被捕获并记录 info 日志。
配套 getter:
getDefaultAppConfig():返回config;getDefaultEmulatorHost(productName):返回如127.0.0.1:9999或[::1]:4000;getDefaultEmulatorHostnameAndPort(productName):解析为[hostname, port]元组(对 IPv6 括号形式做了特判,见 defaults.ts);getExperimentalSetting(name):按_${name}前缀读取实验性配置,ExperimentalKey = 'authTokenSyncURL' | 'authIdTokenMaxAge'。
createMockUserToken:构造模拟用户令牌
emulator.ts 用于生成模拟器测试所需的未签名 JWT:
export type FirebaseSignInProvider = | 'custom' | 'email' | 'password' | 'phone' | 'anonymous' | 'google.com' | 'facebook.com' | 'github.com' | 'twitter.com' | 'microsoft.com' | 'apple.com'; export type EmulatorMockTokenOptions = ({ user_id: string } | { sub: string }) & Partial<FirebaseIdToken>; export function createMockUserToken(token: EmulatorMockTokenOptions, projectId?: string): string;源码实现要点(emulator.ts):
- 若传入
uid字段会直接抛错,提示改用sub; - 使用
alg: 'none'、type: 'JWT'的头部与空字符串签名(非安全用途,仅供模拟器验证身份); - 自动补齐
iss(https://securetoken.google.com/${project})、aud、iat、exp(iat + 3600)、auth_time、sub/user_id、firebase.sign_in_provider: 'custom'等默认字段,再与用户传入字段合并; projectId缺省为demo-project;- 用
base64urlEncodeWithoutPadding编码头部与载荷后以.拼接。
FirebaseIdToken接口还完整注释了email、email_verified、phone_number、name、picture、firebase.identities等标准 claim,并刻意声明uid?: never以在类型层面拦截常见误用。
数据操作:深拷贝、深合并与对象工具
deepCopy.ts 提供:
deepCopy<T>(value: T): T; deepExtend(target: unknown, source: unknown): unknown;deepExtend的实现要点:
Date按标量对待(new Date(source.getTime()));Object递归合并、Array整体覆盖;- 其他构造器(如函数、Map 等)视为标量直接返回;
- 跳过
__proto__键以防御原型污染(isValidKey守卫,源码 deepCopy.ts)。
obj.ts 提供对象操作工具:
contains(obj, key):安全的hasOwnProperty检查;safeGet(obj, key):有键返回值、无键返回undefined;isEmpty(obj):无自有属性时返回true(类型谓词obj is {});map(obj, fn, contextObj?):键值映射并保留键集合;deepEqual(a, b):递归比较两对象(支持嵌套对象与数组场景,先比较双方键集合,再递归比较值)。
字符串与编码:Base64、UTF-8 与字节转换
crypt.ts 是该包体量最大的模块之一,提供:
export const base64: Base64; // 底层编码器对象 export const base64Encode: (str: string) => string; // URL-safe 编码 export const base64Decode: (str: string) => string | null; // URL-safe 解码 export const base64urlEncodeWithoutPadding: (str: string) => string; // 去填充 export class DecodeBase64StringError extends Error { readonly name = "DecodeBase64StringError"; } export const stringToByteArray: (str: string) => number[]; export const stringLength: (str: string) => number; export interface StringLike { toString(): string; }实现细节值得注意:
- 自带 UTF-8 编码逻辑
stringToByteArray(支持 ASCII、多字节字符与代理对),因此base64Encode无需依赖btoa即可正确处理中文等非 ASCII 字符; base64.HAS_NATIVE_SUPPORT检测atob存在性,仅在非 URL-safe 且原生支持时走btoa/atob快路径,其余走自研查表实现;- URL-safe 字母表为
A-Za-z0-9-_(ENCODED_VALS_WEBSAFE),解码时对标准/URL-safe 两套字母表做了互相兼容(见init_中的双写映射); base64Decode失败时打印console.error并返回null,非法字符会抛出DecodeBase64StringError;base64urlEncodeWithoutPadding用于 JWT 分段编码,去掉末尾.(填充符)——createMockUserToken正是它的调用方。
哈希工具:Sha1 与 SHA-256
Sha1类(sha1.ts)实现 FIPS PUB 180-3 规定的 SHA-1 算法,提供update(bytes, length?)、digest()、reset()、compress_(buf, offset?)与只读的blockSize属性,按流式接口(先 update 再 digest)使用,文件头注释记录了 Chrome 23 / Firefox 16 时代的吞吐量参考。generateSHA256Hash(input)(sha256.ts)基于 Web Crypto 的crypto.subtle.digest('SHA-256', ...),将输入经TextEncoder编码后计算,返回十六进制小写字符串(Promise 形式),适用于需要标准 SHA-256 摘要的现代运行时。
指数退避:calculateBackoffMillis
exponential_backoff.ts 提供网络重试场景下的退避延时计算:
export const MAX_VALUE_MILLIS = 4 * 60 * 60 * 1000; // 4 小时上限 export const RANDOM_FACTOR = 0.5; export function calculateBackoffMillis( backoffCount: number, intervalMillis = 1000, backoffFactor = 2 ): number;算法要点(源码 exponential_backoff.ts):
- 基础值 =
intervalMillis * backoffFactor^backoffCount(默认即 1s、2s、4s、8s…); - 叠加 ±50%(
RANDOM_FACTOR)范围内的随机抖动randomWait,避免大量客户端同时重试造成流量尖峰; - 最终以
Math.min(MAX_VALUE_MILLIS, ...)封顶,防止退避时间"无限增长"(4 小时上限与 iOS/Android 客户端保持一致)。
参数校验:validateArgCount 与系列校验器
validation.ts 提供面向公共函数的参数检查:
validateArgCount(fnName, minCount, maxCount, argCount); // 越界即抛错 errorPrefix(fnName, argName); // 生成 "fnName failed: argName argument " validateNamespace(fnName, namespace, optional); validateCallback(fnName, argumentName, callback, optional); validateContextObject(fnName, argumentName, context, optional);validateArgCount的报错文案会区分单复数并说明期望:"fn failed: Was called with 0 arguments. Expects at least 1."。其余校验器遵循同一模式:optional为真且值缺失时直接放行,否则校验类型并在失败时抛出带errorPrefix前缀的错误。
查询串与 URL 工具
query.ts 提供三件套:
querystring(params): string; // {a: '1', b: '2'} -> "&a=1&b=2"(注意前置 &) querystringDecode(str): Record<string, string>; // "?a=1&b=2" -> {a: '1', b: '2'} extractQuerystring(url): string; // 提取 ? 到 # 之间的部分(含 ?)注意:querystring的输出以&开头,注释明确要求调用方在拼 URL 时自行补?;数组值会被展开为多个同名键值对。querystringDecode会自动剥离开头的?。extractQuerystring则正确处理了#锚点截断。
杂项工具:全局对象、JSON、格式器与网络
getGlobal()(global.ts):按self → window → global顺序返回全局对象,是globalThis的跨环境 polyfill。jsonEval(str)/stringify(data)(json.ts):薄封装JSON.parse/JSON.stringify,统一 SDK 内部的 JSON 入口。ordinal(i)(formatters.ts):生成英文序数后缀(1st / 2nd / 3rd / 11th…),对非有限数值原样返回。promiseWithTimeout(promise, timeInMS = 2000)(promise.ts):基于Deferred+setTimeout实现超时 reject,API 报告标注为@internal,主要供 SDK 内部探测类场景使用。pingServer(endpoint)与isCloudWorkstation(url)(url.ts):前者带credentials: 'include'请求端点并返回res.ok(多用于 Firebase Studio 的 Cookie 转发);后者判断主机名是否以.cloudworkstations.dev结尾,兼容带协议与不带协议两种入参。
兼容层:Compat 与 getModularInstance
compat.ts 服务于 SDK 的"旧版命名空间 API ↔ 新版模块化 API"过渡:
interface Compat<T> { _delegate: T; } function getModularInstance<ExpService>( service: Compat<ExpService> | ExpService ): ExpService;getModularInstance判断对象是否带有_delegate属性,有则解包返回模块化实现,否则原样返回——这是各产品模块统一接收"兼容实例或模块化实例"的标准入口。
如何安装与使用
@firebase/util随 Firebase JS SDK 一起发布。在实际项目中,它通常是其他 Firebase 包的传递依赖,一般无需直接安装;如需显式引入:
npm install @firebase/util # 或 yarn add @firebase/util模块化导入(ESM):
import { FirebaseError, isBrowser, deepCopy, calculateBackoffMillis } from '@firebase/util';Node 与浏览器均支持:package.json的exports字段分别指向 Node ESM/CJS 与浏览器 ESM/CJS 产物,类型声明位于dist/util-public.d.ts。
仓库内可运行该包的完整测试:
cd packages/util yarn test # lint + 全量单测(vitest run) yarn test:node # 仅 Node 项目 yarn test:browser # 仅浏览器项目(vitest --project=browser)各测试文件(如 packages/util/test/errors.test.ts、packages/util/test/subscribe.test.ts、packages/util/test/emulator.test.ts)展示了上述 API 的预期行为,可作为二次开发的契约参考。
小结
从 API 报告 common/api-review/util.api.md 可以看到,@firebase/util的公开面覆盖了错误工厂、延迟对象、订阅模型、JWT 工具、环境检测、默认配置、深拷贝、编码哈希、退避算法、参数校验与查询串处理等十余个领域。这些工具几乎全部被 auth、database、firestore、storage、analytics 等上层包直接复用,理解它们就等于掌握了 Firebase JS SDK 的公共底层契约——无论是排查 SDK 错误、编写模拟器测试,还是在自己的项目中借鉴其稳健实现,这份 API 清单与对应的源码注释都是可靠的参考依据。
- 开发工具
- 后端
【免费下载链接】firebase-js-sdk
Firebase Javascript SDK
相关推荐
Firebase JS SDK 的 @firebase/app 核心包:FirebaseApp 初始化、服务编排与公共 API 完全指南
Firebase JS SDK 的 @firebase/app 核心包:FirebaseApp 初始化、服务编排与公共 API 完全指南 @firebase/a
开发工具后端Firebase JS SDK 认证模块 `@firebase/auth` 公共 API 完全指南:从接口定义到源码实现
Firebase JS SDK 认证模块 @firebase/auth 公共 API 完全指南:从接口定义到源码实现 common/api review/aut
开发工具后端firebase-js-sdk 的 Firestore Lite(@firebase/firestore-lite)公共 API 全面解析与实战指南
firebase js sdk 的 Firestore Lite(@firebase/firestore lite)公共 API 全面解析与实战指南 导读 :本
开发工具后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考