☰
Firebase JS SDK 核心工具包 @firebase/util 完全指南:公开 API、源码实现与实战用法
2026/10/15 11:59:35 网站建设 项目流程
  • 开发工具
  • 后端

【免费下载链接】firebase-js-sdk

Firebase Javascript SDK

项目地址:https://gitcode.com/gh_mirrors/fi/firebase-js-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?) => void

wrapCallback将内部非 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

项目地址:https://gitcode.com/gh_mirrors/fi/firebase-js-sdk
点击查看免费下载
上一篇:Pinpoint OTLP Trace 接入指南:OpenTelemetry 数据接入 Pinpoint Collector 的完整实战
下一篇:JointJS + Angular 集成实战:使用 createComponent() 在 Element View 中渲染 Angular 组件

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询