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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@dao3fun/live-sdk

v0.2.0

Published

支持赛程定位、队伍/玩家查询、赛程进度/倒计时,队员信息上传、以及事件订阅。

Readme

神岛赛事直播工具

支持赛程定位、队伍/玩家查询、赛程进度/倒计时、队员信息上传,以及事件订阅。 注意:本 SDK 不提供 KDA/分数/排行榜/MVP 等统计计算;LiveEvent 仅管理“配置的比赛信息”,LiveUpdater 负责把你的实时数据(比分、玩家扩展等)上报到平台。

安装

npm install @dao3fun/live-sdk

快速开始

import { LiveEvent } from '@dao3fun/live-sdk';

async function main() {
  const live = LiveEvent.getInstance();

  // 选择数据源:测试 or 生产(异步,内部会立即拉一次)
  await live.useTestData(true); // 测试源(完整版,不随直播变动,便于联调)
  // await live.useTestData(false); // 生产源(上线前请切回)

  // 可选:固定“当前时间”做回放/测试,秒级时间戳,在两个队伍比赛时内有效
  live.setNowSecOverride(1755498600);
  // live.useRealTime(); // 取消覆盖,恢复实时

  // 订阅数据更新
  const off = live.onUpdate((payload) => {
    console.log('Updated:', payload.title);
  });

  // 立即拉一次(可选;useTestData 已经会拉一次)
  await live.refreshNow();

  // 读取当前比赛与地图、赛程进度与倒计时
  const curMatch = live.getCurrentMatch();
  const curMap = live.getCurrentMatchMap();
  const progress = live.getCurrentMatchProgress();
  const countdown = live.getCountdownToNextMatch();

  console.log({ curMatch, curMap, progress, countdown });
  off();
}

main().catch(console.error);

LiveUpdater 最简上报(当前场次)

import {
  LiveEvent,
  LiveUpdater,
  type LiveUpdatePlayer,
} from '@dao3fun/live-sdk';

// 使用 LiveUpdater 上报
const updater = new LiveUpdater({ key: 'demo_player_extras' });

async function pushNow() {
  // 获取 SDK 实例
  const live = LiveEvent.getInstance();
  await live.useTestData(true); // 示例:测试源

  // 获取当前进行中的比赛
  const cur = live.getCurrentMatch();
  if (!cur) return;

  // 覆盖“当前场次”的玩家扩展字段(按玩家 id)
  const extras: Record<number, Partial<LiveUpdatePlayer>> = {
    101: { score: 12, hp: 88, kda: '2/1/3' },
    102: { score: 7 },
  };

  // 上报 当前比赛信息
  // 他会自动记录对战历史,你只需要传入当前比赛的信息即可。
  // 注意,当前比赛和队员的信息一定要匹配,否则无效。
  const ok = await updater.pushFromMatch(cur, { teamA: 10, teamB: 9 }, extras);
  // 返回结果
  console.log('push ok?', ok);
}

// 执行
pushNow().catch(console.error);

数据源:生产/测试切换

  • await live.useTestData(enable: boolean): Promise<void>
    • 功能:切换数据源为测试或生产环境,并立即异步拉取一次最新数据。
    • 参数enable: boolean - true 表示切换到测试数据源,false 表示切换到生产数据源。
    • 用途:方便在开发/联调时使用固定的测试数据,上线前再切换回实时生产数据。
  • live.isUsingTestData(): boolean
    • 功能:检查当前是否正在使用测试数据源。
  • await live.useCustomUrl(url: string): Promise<void>
    • 功能: 切换到自定义的数据源 URL,并立即异步拉取一次最新数据。
    • 参数: url: string - 自定义数据源的 URL。
    • 用途: 用于连接到非官方或代理的数据接口。
  • 语义:
    • 测试源:完整静态数据,便于测试/联调(DEFAULT_LIVE_URL_TEST
    • 生产源:线上实时数据(DEFAULT_LIVE_URL,上线前务必切回)
  • 示例:
const live = LiveEvent.getInstance();

// 测试环境联调
await live.useTestData(true);
console.log('Using test?', live.isUsingTestData()); // true

// 发布到正式前
await live.useTestData(false);

刷新与轮询

  • refreshNow(): Promise<void>
    • 功能:立即异步拉取一次最新数据。
    • 用途:在需要确保获取到最新信息时(如执行统计前)手动调用。如果拉取失败,会触发 onError 事件,并且外层调用可以 catch 捕获错误。
  • stop(): void
    • 功能:停止内部的自动轮询。
    • 用途:在不需要实时更新数据时(如页面不可见或组件销毁时)调用,以节省资源。
  • resume(): void
    • 功能:恢复内部的自动轮询。
    • 用途:在需要时重新开启自动数据更新。
  • setFetcher(fetcher): void
    • 功能:设置自定义的数据请求器。
    • 参数fetcher - 一个函数,接收 url 作为参数,并返回一个包含 json() 方法的对象,如 (url) => fetch(url)
    • 用途:用于高级定制,例如在请求中添加自定义的鉴权头、日志记录或使用 mock 数据。
  • 默认行为:SDK 默认已开启短周期轮询,自动获取最新数据。
live.setFetcher(async (url) => {
  const res = await fetch(url, { headers: { 'x-token': '...' } });
  return { json: () => res.json() };
});

全局时间覆盖(用于回放/对齐)

  • setNowSecOverride(sec?: number): void
    • 功能:覆盖 SDK 内部使用的“当前时间”,单位为 Unix 时间戳(秒)。
    • 参数sec?: number - 可选的时间戳。如果传入 undefined 或不传,则清除覆盖,等同于 useRealTime()
    • 用途:用于测试和回放。通过固定一个时间点,可以稳定地测试依赖于当前时间的功能(如 getCurrentMatch())。
  • useRealTime(): void
    • 功能:取消时间覆盖,让 SDK 恢复使用真实的系统时间。
    • 用途:在测试或回放结束后,调用此方法以返回正常模式。
  • 影响范围:所有接受可选 nowSec 参数的方法,在未显式传入该参数时,都会优先使用这里设置的覆盖时间。
live.setNowSecOverride(1755498600);
const cur = live.getCurrentMatch(); // 将基于覆盖时间判断
live.useRealTime();

事件订阅

  • onUpdate(cb): () => void
    • 功能:订阅数据更新事件。当拉取到的数据与上一次相比发生变化时,回调函数 cb 会被调用。
    • 参数cb: (payload: LivePayload) => void - 数据更新时的回调函数。
    • 返回:一个函数,调用该函数可以取消此次订阅。
  • waitForNextUpdate(timeoutMs?): Promise<LivePayload>
    • 功能:返回一个 Promise,它会在下一次数据更新时 resolve,或在超时后 reject。
    • 参数timeoutMs?: number - 可选的超时时间(毫秒)。
    • 用途:用于需要等待特定更新才能继续执行的场景。
  • onError(cb): () => void
    • 功能:订阅数据拉取失败事件。
    • 参数cb: (error: Error) => void - 发生错误时的回调函数。
    • 返回:一个函数,调用该函数可以取消此次订阅。
  • onPlayerUpdate(playerId, cb): () => void
    • 功能:监听特定玩家在“当前比赛”中的引用变化(是否在场等)。
    • 参数
      • playerId: number - 要监听的玩家 ID。
      • cb: (playerRef: PlayerRef | undefined) => void - 当玩家在场状态变化时触发;不在当前比赛则为 undefined
    • 返回:取消订阅函数。
const offAll = live.onUpdate(console.log);
const offErr = live.onError(console.error);
const offP = live.onPlayerUpdate(123, (ref) => console.log('P123:', ref));

await live.waitForNextUpdate(10000); // 最多等 10s
offAll();
offErr();
offP();

顶层信息读取

  • getType(): 'Game' | 'Live':获取赛事类型,例如是普通游戏还是直播活动。
  • getEnv(): string:获取当前数据源的环境标识。
  • getTitle(): string:获取赛事的完整标题。
  • getDescription(): string:获取赛事的描述信息。
  • getRoomId(): number:获取直播或比赛房间的 ID。
  • getCover(): string:获取赛事的封面图片 URL。
  • getStartTime(): number / getEndTime(): number:以 Unix 时间戳(秒)格式,获取赛事的开始和结束时间。
  • getStartDate(): Date / getEndDate(): Date:以 JavaScript Date 对象格式,获取赛事的开始和结束时间。
  • isLive(nowSec?): boolean:判断当前是否处于赛事直播时间段内。可以提供一个可选的 nowSec 参数来基于特定时间点进行判断。

赛事配置与地图

  • getGameCfg(): GameCfg | undefined:获取游戏的核心配置信息,如果存在的话。
  • getLogo(): string | undefined:获取赛事的 Logo 图片 URL。
  • getTeams(): Team[]:获取在赛事配置中定义的所有参赛队伍的列表。
  • getMatchMaps(): MatchMap[]:获取本次赛事所有比赛地图的列表。
  • getCommentators(): number[]:获取解说员的用户 ID 列表。
  • getOperators(): number[]:获取中控台成员的用户 ID 列表。
  • getBracket(): Bracket | null:获取晋级图信息,如果不存在则返回 null

赛程/比赛

  • getMatches(): Match[]:获取原始的比赛列表。
  • getMatchesSortedByStart(): Match[]:获取按开始时间排序的比赛列表。
  • getFirstMatch() / getLastMatch():获取第一场和最后一场比赛。
  • getMatchAt(unixSec: number):获取在指定时间点(Unix 秒)正在进行的比赛。
  • getCurrentMatch(nowSec?):获取当前时间正在进行的比赛。
  • getNextMatch(nowSec?):获取当前时间之后的下一场比赛。
  • getCurrentMatchIndex(nowSec?):获取当前比赛在赛程中的索引。
  • getMatchMapByIndex(index: number):根据赛程索引获取比赛地图。
  • getCurrentMatchMap(nowSec?):获取当前比赛的地图信息。
  • getCurrentMatchDurationSec(nowSec?):获取当前比赛已经进行的时长(秒)。
  • getTimeToNextMatchSec(nowSec?):获取距离下一场比赛开始还有多少秒。
  • getCurrentMatchProgress(nowSec?):以 0-1 的小数形式,估算当前比赛的进度(基于下一场比赛的开始时间)。
  • getCountdownToNextMatch(nowSec?):获取距离下一场比赛开始的倒计时(秒)。

队伍/玩家便捷

  • getTeamById(id) / getTeamByName(name):通过队伍 ID 或名称查找并返回队伍对象。
  • getTeamPlayers(teamId) / getTeamPlayerIds(teamId):获取指定队伍的所有队员(PlayerRef[])或队员 ID 列表(number[])。
  • getAllPlayerIdsFromTeams() / getAllPlayerRefsFromTeams() / getAllPlayers():获取所有队伍的所有队员 ID 列表、队员引用对象列表或队员引用对象列表(getAllPlayersgetAllPlayerRefsFromTeams 的别名)。
  • getMatchTeams(match) / getMatchPlayers(match):获取指定比赛的参赛队伍或所有参赛队员。
  • getCurrentTeams(nowSec?):获取当前比赛的两个参赛队伍(按队伍名称匹配 Team 对象,可能返回 undefined)。
  • getCurrentMatchPlayerRefs(nowSec?):获取当前比赛所有在场玩家的 PlayerRef 列表。
  • getCurrentMatchTeamsRefs(nowSec?):获取当前比赛按阵营(teamA, teamB)划分的在场玩家 PlayerRef 列表。
  • isPlayerInCurrentMatch(playerId, nowSec?):判断指定 ID 的玩家当前是否在场比赛。
  • getTeamByPlayerId(playerId) / getPlayerTeamId(playerId):根据玩家 ID 查找其所在的队伍对象或队伍 ID。
  • isPlayerInTeam(teamId, playerId):判断指定玩家是否属于指定队伍。
  • getLineupDiffForTeam(teamId, nowSec?):比较指定队伍的配置阵容与当前实际上场阵容的差异。
    • onStageButNotInTeam: 返回在场但未在队伍配置中的玩家。
    • inTeamButNotOnStage: 返回在队伍配置中但未上场的玩家。

客户端/视图辅助

  • getScreenType(commentatorsEntity: GamePlayerEntity): 'live_mini' | 'live' | undefined

    • 功能:从解说员实体的 URL 参数中,解析并获取当前客户端的界面类型。
    • 参数commentatorsEntity: GamePlayerEntity - 解说员的玩家实体对象。
    • 返回
      • 'live_mini': 小窗副屏直播模式。
      • 'live': 大窗主屏直播模式。
      • undefined: 未指定或无法识别。
    • 用途:用于根据启动参数,在客户端展现不同的 UI 布局。
  • getPlayerUiHidden(entity: GamePlayerEntity): boolean

    • 功能:从玩家实体的 URL 参数中,判断是否需要隐藏游戏内 UI。
    • 参数entity: GamePlayerEntity - 玩家实体对象。
    • 返回true 表示需要隐藏 UI,false 表示正常显示。
    • 用途:用于实现“导演”或“观察者”模式,提供一个干净的无 UI 画面。

玩家引用

  • getPlayerRefById(playerId, nowSec?): 根据玩家 ID 获取其在当前比赛中的引用对象(PlayerRef)。若玩家不在当前比赛,返回 undefined

历史与即将开始

  • getHistory() / getUpcoming(): 获取原始的“历史活动”和“即将开始”的活动列表。
  • getHistorySortedByStart() / getUpcomingSortedByStart(): 获取按开始时间排序的“历史活动”和“即将开始”的活动列表。

序列化

  • toJSON(): LivePayload: 返回最新的原始数据负载对象(LivePayload),方便进行序列化(如 JSON.stringify)或传递。

类型(来自 server/src/lib/types.ts

  • PlayerRef: { id: number }
  • Team: { id; name; logo; players: PlayerRef[] }
  • MatchTeam: { name; logo; players }
  • Match: { startTime; teamA: MatchTeam; teamB: MatchTeam }
  • MatchMap: { contentId; name; preview }
  • BracketTeam: { name; logo }
  • BracketRound: { title; teams: BracketTeam[] }
  • Bracket: { stages: string[]; rounds: BracketRound[] }
  • HistoryItem/UpcomingItem: { title; cover; startTime; url }
  • GameCfg: 包含 logo, teams, matches, matchMaps, commentators, operators, bracket
  • LivePayload:
    • 核心:type, env, title, description, roomId, startTime, endTime, cover
    • 扩展:gameCfg?: GameCfg, history?: HistoryItem[], upcoming?: UpcomingItem[]

LiveUpdater 相关类型

  • LiveUpdatePlayer: { id; score?; hp?; kda? } 可选实时字段由数据上报方自行决定。
  • LiveUpdateSide: { score; players: LiveUpdatePlayer[] }
  • LiveUpdatePayload: { startTime; teamA; teamB; highlight?; danmaku? }
  • LiveUpdatePayloadArray: LiveUpdatePayload[] 这些类型已集中在 server/src/lib/types.ts 并由 server/src/App.ts 统一导出。

高光/弹幕类型要点:

  • LiveUpdateHighlight
    • reason: string 高光原因/提示
    • teamNames?: string[] 关联队伍名称(与 MatchTeam.name 对齐)
    • playerIds?: number[] 关联选手
    • immediate?: boolean 平台是否立刻放大 live_mini
  • LiveDanmaku
    • text: string 文本内容

LiveUpdater API(上报器)

  • new LiveUpdater(opts)
    • opts.key: string(必填)用于后端存储键名
    • opts.endpoint?: string 默认 https://api.box3lab.com/set_redis_value
    • opts.headers?: Record<string, string>
    • opts.maxRetries?: number 默认 0
    • opts.retryDelayMs?: number 默认 500
    • 内置提交节流:两次提交至少间隔 10ms
  • static buildPayloadFromMatch(match, scores?, playerExtras?) => LiveUpdatePayload
    • Match 构建可提交的载荷(不发送)
  • pushFromMatch(match, scores, playerExtras?) => Promise<boolean>
    • 将单场加入暂存并提交。若之前已提交过历史场次,会自动与历史合并后一起提交

高光与弹幕(即时接口)

  • pushHighlightNow(match, highlight) => Promise<boolean>
    • 立刻上报“单一高光”。平台可据此放大 live_mini 等。
    • 参数 highlight: LiveUpdateHighlight,示例见下。
  • stopHighlightNow(match) => Promise<boolean>
    • 立刻取消当前高光(置空并上报)。
  • pushDanmakuNow(match, danmaku) => Promise<boolean>
    • 立刻发送一条弹幕,平台侧会展示该弹幕。

示例:

// 触发高光
await updater.pushHighlightNow(match, {
  reason: 'ACE',
  teamNames: ['Team A'],
  playerIds: [12345],
  immediate: true,
});

// 立刻取消高光
await updater.stopHighlightNow(match);

// 发送弹幕
await updater.pushDanmakuNow(match, {
  text: '加油!稳住别浪!',
});

实用建议

  • 做快照或统计前建议 await refreshNow() 确保数据最新
  • 本地调试可 await useTestData(true) + setNowSecOverride()
  • 上线前请确保 await useTestData(false)

示例代码

  • server/src/examples/pushCurrentMatchDemo.ts
    • 最简示例:获取当前场次并上报比分
  • server/src/examples/pushPlayerExtrasDemo.ts
    • 为当前场次上报玩家扩展字段(例如 score/hp/kda)
  • server/src/examples/pushMultipleMatchesDemo.ts
    • 连续覆盖多场(历史 + 当前),第二次提交自动把两场一起提交
  • server/src/examples/pushDryRunDemo.ts
    • Dry-Run:仅构造并打印 payload,便于联调校验
  • server/src/examples/highlightDanmakuDemo.ts
    • 触发高光/弹幕

获取已上报的数据(查询接口)

  • 平台查询接口(GET):https://api.box3lab.com/get_redis_value?key=<你的key>
  • 例如本仓库示例使用的 key:demo_player_extras