@video-lab/protocol 3.1.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,40 +11,56 @@ Video Lab Player 的通信契约层。它定义 host 与 iframe 的消息格式
11
11
  | `version` | `CONTRACT_VERSION` + 版本兼容判定 |
12
12
  | `envelope` | 通信包裹:`command` / `event` / `response` / `error` |
13
13
  | `methods` | 17 条命令(play / pause / seek / load / setSubtitle / pushDanmaku / …) |
14
- | `events` | 27 个事件(sourceroute / ready / timeupdate / error / recovery / subtitlechange / stalled / …) |
15
- | `delivery` | `DeliveredPlayerEvent`:带生产端 session、稳定 ID、发生时间与序号的完整事件投递证据 |
16
- | `errors` | 29 个错误码 + 2 个警告码 + `PlayerError` |
14
+ | `events` | 28 个事件(sourceroute / ready / timeupdate / error / recovery / subtitlechange / stalled / …) |
15
+ | `delivery` | `PlayerEvent.delivery`:带生产端 session、稳定 ID、发生时间与序号的完整事件投递证据 |
16
+ | `errors` | 19 个错误码 + 3 个警告码 + `PlayerError` |
17
17
  | `configs` | `MediaSource`、`PlayerConfig`、poster / locale / danmaku / controls |
18
- | `presets` | 场景预设(如 `homepage-preview`)与 `resolvePreset` |
18
+ | `official-messages` | 默认简体中文、内置英文资源,文案 key 与语言标签归一;越南语从 `@video-lab/locales/vi-VN` 按需引入 |
19
+ | `presets` | 场景预设(如 `ambient-preview`)与 `resolvePreset` |
19
20
 
20
21
  类型一律从 Zod schema 用 `z.infer` 推导,不手写 interface——避免 schema 和类型变成两套真相。
21
22
 
23
+ ## 谁应该直接使用它
24
+
25
+ | 你的目标 | 是否直接安装 `protocol` | 先看什么 |
26
+ | --- | --- | --- |
27
+ | 在业务页面播放视频 | 通常不需要 | 选择 `react`、`vue`、`react-frame`、`vue-frame` 或静态 iframe;遥测另装 `telemetry` |
28
+ | 写自定义 iframe bridge、框架包装层或契约适配器 | 需要 | 本 README、导出 schema 和 [跨模式契约](../../docs/guides/PLAYER-CROSS-MODE-CONTRACT.md) |
29
+ | 只想消费播放事件 | 通常不需要 | 从所选接入包取得回调;不要在业务层手写 envelope |
30
+
31
+ 本包只定义数据形状和纯归一规则:不创建播放器、不建立网络连接,也不发送上报。对外数据进入 iframe
32
+ 边界时先用 schema 校验;业务侧应通过上层接入包获得已经路由好的命令与事件。
33
+
22
34
  ## 用法
23
35
 
24
36
  <!-- doc-snippet-preamble
25
37
  declare const rawMessage: unknown
26
38
  declare const originalError: unknown
39
+ declare function reportProtocolError(error: unknown): void
27
40
  -->
28
41
  ```ts
29
42
  import {
30
- CommandSchema,
31
43
  EnvelopeSchema,
32
- eventEnvelope,
33
44
  makePlayerError,
34
45
  resolvePreset,
35
46
  } from '@video-lab/protocol'
36
47
 
37
- // 收到消息:先 parse,再按 type 分支(TS 会自动收窄 payload)
38
- const envelope = EnvelopeSchema.parse(rawMessage)
39
- if (envelope.type === 'event' && envelope.payload.event === 'timeupdate') {
40
- console.log(envelope.payload.payload.time)
48
+ // 跨窗消息是不可信输入:先安全校验;成功后再按 type 分支。
49
+ const parsed = EnvelopeSchema.safeParse(rawMessage)
50
+ if (parsed.success) {
51
+ const envelope = parsed.data
52
+ if (envelope.type === 'event' && envelope.payload.event === 'timeupdate') {
53
+ console.log(envelope.payload.payload.time)
54
+ }
55
+ } else {
56
+ reportProtocolError(parsed.error)
41
57
  }
42
58
 
43
59
  // 构造错误:category / retryable 从 ERROR_META 推出,不接受调用方伪造
44
60
  const err = makePlayerError('E_NETWORK', '拉流失败', originalError)
45
61
 
46
62
  // 应用预设:显式传的字段永远覆盖预设默认值
47
- const config = resolvePreset('homepage-preview', { source: 'a.m3u8', autoplay: false })
63
+ const config = resolvePreset('ambient-preview', { source: 'a.m3u8', autoplay: false })
48
64
  // → autoplay: false(显式值赢),muted: true(来自预设)
49
65
  ```
50
66
 
@@ -52,11 +68,15 @@ const config = resolvePreset('homepage-preview', { source: 'a.m3u8', autoplay: f
52
68
 
53
69
  **事件名是全小写连写**(`timeupdate` / `autoplayblocked`),对齐 HTML5 媒体事件。这是通信层名称;Vue 映射为 `time-update`,React 映射为 `onTimeUpdate`。
54
70
 
55
- **完整事件流优先使用 `DeliveredPlayerEvent`**。它在兼容的新增 outlet 上提供 `producerSessionId`、`deliveryId`、`occurredAtMs`、`sequence`,适合跨重连排序与去重;旧 `PlayerEvent` 回调保持原样。旧版 iframe 可以省略该证据,宿主仍会收到 raw 事件。
71
+ **完整事件流统一使用 `PlayerEvent`**。它的可选顶层 `delivery` 提供 `producerSessionId`、`deliveryId`、`occurredAtMs`、`sequence`,适合跨重连排序与去重;新版 SDK 事件带完整证据,旧版 iframe 可以省略,宿主仍会收到事件。
72
+
73
+ **schema 是格式真相,不是上报实现。** `PlayerEvent.delivery` 解决的是事件身份、顺序和发生时间;
74
+ 队列、批量、重试和 HTTP 由宿主 transport 决定。使用 `@video-lab/telemetry` 时,将完整事件交给
75
+ 它的 `record`,不要在 `timeupdate` 等高频回调中逐条发请求。
56
76
 
57
77
  **`retryable` 是给自动重连看的信号**,不是“用户能不能点重试按钮”。判断标准是同一请求原样重发是否可能成功。因此 `E_AUTH_EXPIRED` 为 `false`:SDK 不刷新签名,原样重试必然再次失败。
58
78
 
59
- **`source.onBeforeRequest` 不在 wire 契约里**。函数不可序列化,过不了 iframe 边界;它是宿主侧的 hook,由 inline 模式的 player-core 直接消费。且它在 MP4 和 iOS Safari 播 HLS 时**不生效**。
79
+ **`source.onBeforeRequest`(`SourceRequestHook` / `RequestInfo`)已在 4.0.0 移除(ADR-057)**。它从来没有被任何消费面真正接线过,且过不了 cross-mode-parity——函数不可序列化,`postMessage` 的结构化克隆直接抛异常。认证一律走签名 URL + 长有效期(ADR-022)。
60
80
 
61
81
  **带 query 的 URL 推断不出类型**。`video.m3u8?token=xxx` 必须显式传 `type: 'hls'`,否则会走 MP4 路径。签名 URL 场景尤其注意。
62
82
 
@@ -64,12 +84,12 @@ const config = resolvePreset('homepage-preview', { source: 'a.m3u8', autoplay: f
64
84
 
65
85
  ## 版本与冻结
66
86
 
67
- 当前 `CONTRACT_VERSION = '1.0.0'`。现有 schema 不再进行破坏性兼容变更。
87
+ 当前 `CONTRACT_VERSION = '4.0.0'`。现有 schema 不再在同一 major 内进行破坏性兼容变更。
68
88
 
69
89
  兼容规则(host 和 iframe 用同一个 `isContractCompatible`):
70
90
 
71
91
  - `0.x` 阶段(已过):minor 变更即破坏性 → minor 必须相同
72
- - `>= 1.0.0`(当前):minor 是向后兼容的新增 → **major 相同即兼容**
92
+ - `>= 1.0.0`(当前规则):minor 是向后兼容的新增 → **major 相同即兼容**
73
93
  - patch 差异永远兼容
74
94
 
75
95
  后续新增 method、event 或可选字段走 **minor** 版本;破坏兼容的变更必须走 **major** 版本。