Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,27 @@

| 包 | 版本 | 用途 |
|---|---|---|
| [`@rei-standard/amsg-shared`](./packages/rei-standard-amsg/shared/README.md) | `0.1.0` | 三轴推送契约(`AmsgPush` 判别联合 + builders + 类型守卫) |
| [`@rei-standard/amsg-instant`](./packages/rei-standard-amsg/instant/README.md) | `0.8.0` | 一次性即时推送(无 DB、无 cron、无租户) |
| [`@rei-standard/amsg-server`](./packages/rei-standard-amsg/server/README.md) | `2.4.0` | 定时 / 周期消息,多租户 Blob 配置 + token 鉴权 |
| [`@rei-standard/amsg-client`](./packages/rei-standard-amsg/client/README.md) | `2.3.0` | 浏览器 SDK:加密、请求封装、Push 订阅 |
| [`@rei-standard/amsg-sw`](./packages/rei-standard-amsg/sw/README.md) | `2.1.0` | Service Worker:推送展示、离线队列 |
| [`@rei-standard/amsg-shared`](./packages/rei-standard-amsg/shared/README.md) | `0.2.0` | 三轴推送契约(`AmsgPush` 判别联合 + builders + 类型守卫) |
| [`@rei-standard/amsg-instant`](./packages/rei-standard-amsg/instant/README.md) | `0.9.0` | 一次性即时推送(SSE 默认传输、always-on Web Push backup) |
| [`@rei-standard/amsg-server`](./packages/rei-standard-amsg/server/README.md) | `2.5.0` | 定时 / 周期消息,多租户 Blob 配置 + token 鉴权 |
| [`@rei-standard/amsg-client`](./packages/rei-standard-amsg/client/README.md) | `2.4.0` | 浏览器 SDK:加密、请求封装、Push 订阅、SSE consumer |
| [`@rei-standard/amsg-sw`](./packages/rei-standard-amsg/sw/README.md) | `2.2.0` | Service Worker:推送展示、离线队列、delivery dedupe |

`amsg-shared` 是依赖图最底层:其他四个包都依赖它,反过来不行;它本身零运行时依赖。

**怎么挑服务端包**:只发"按钮点了就立刻推一条" → `amsg-instant`;要定时或周期任务 → `amsg-server`;两种都要就都装,共用同一套 VAPID 与 masterKey。

### 协调发布说明:稳定版发布(shared 0.1.0 / instant 0.8.0 / sw 2.1.0 / client 2.3.0 / server 2.4.0)
### 协调发布说明:稳定版发布(shared 0.2.0 / instant 0.9.0 / sw 2.2.0 / client 2.4.0 / server 2.5.0)

本轮是一次跨包协调的 minor 升级,统一 push wire shape 到 `@rei-standard/amsg-shared` 的 `AmsgPush` 判别联合(以 `messageKind` 为字面量类型判别器)。所有 amsg 子包同时上调一个 minor 并正式发布
本轮补上 SSE + Web Push backup 的同 key 去重链路,并将相关包作为稳定版发布。`amsg-server` 没有运行时行为改动,只做 shared 依赖协调发版

- `@rei-standard/amsg-shared` 新增 → `0.1.0`
- `@rei-standard/amsg-instant`:`0.7.0` → `0.8.0`
- `@rei-standard/amsg-server`:`2.3.2` → `2.4.0`
- `@rei-standard/amsg-sw`:`2.0.1` → `2.1.0`
- `@rei-standard/amsg-client`:`2.2.3` → `2.3.0`
- `@rei-standard/amsg-shared`:`0.1.0` → `0.2.0`
- `@rei-standard/amsg-instant`:`0.8.2` → `0.9.0`
- `@rei-standard/amsg-server`:`2.4.1` → `2.5.0`
- `@rei-standard/amsg-sw`:`2.1.1` → `2.2.0`
- `@rei-standard/amsg-client`:`2.3.0` → `2.4.0`

包间依赖一律使用**精确版本**(不带 `^`),所有 `dependencies` 字段都钉死在对应的版本,避免 npm 在生态系统里解析出混版本图。同时本轮移除了旧的 `{ type: 'error', code: '...' }` 错误信封——错误推送统一走 `ErrorPush`(`messageKind: 'error'`)
包间依赖一律使用**精确版本**(不带 `^`),避免 npm 在生态系统里解析出混版本图。本轮重点是:`amsg-instant` 默认 SSE 传输与 always-on Web Push backup、`amsg-client` 的 SSE consumer、`amsg-sw` 的 delivery dedupe / `REI_AMSG_DELIVER` bridge,以及 shared 的 `notification.silent` 类型补齐

**安装最新版(`latest` dist-tag)**:

Expand Down
10 changes: 5 additions & 5 deletions bump.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ function updatePkg(pkgPath, version, sharedDep) {
fs.writeFileSync(file, JSON.stringify(json, null, 2) + '\n');
}

updatePkg('packages/rei-standard-amsg/shared/package.json', '0.1.0', null);
updatePkg('packages/rei-standard-amsg/sw/package.json', '2.1.0', '0.1.0');
updatePkg('packages/rei-standard-amsg/instant/package.json', '0.8.0', '0.1.0');
updatePkg('packages/rei-standard-amsg/client/package.json', '2.3.0', '0.1.0');
updatePkg('packages/rei-standard-amsg/server/package.json', '2.4.0', '0.1.0');
updatePkg('packages/rei-standard-amsg/shared/package.json', '0.2.0', null);
updatePkg('packages/rei-standard-amsg/sw/package.json', '2.2.0', '0.2.0');
updatePkg('packages/rei-standard-amsg/instant/package.json', '0.9.0', '0.2.0');
updatePkg('packages/rei-standard-amsg/client/package.json', '2.4.0', '0.2.0');
updatePkg('packages/rei-standard-amsg/server/package.json', '2.5.0', '0.2.0');
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 6 additions & 5 deletions packages/rei-standard-amsg/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# ReiStandard AMSG SDK Workspace

主动消息能力的 SDK 工作区,4 个可发布的 npm 包。
主动消息能力的 SDK 工作区,5 个可发布的 npm 包。

| Package | 版本 | 用途 |
|---------|------|------|
| [`@rei-standard/amsg-instant`](./instant/README.md) | `0.6.1` | 一次性即时推送 handler(无 DB / 无 cron / 无租户) |
| [`@rei-standard/amsg-server`](./server/README.md) | `2.3.2` | 定时 + 周期消息:Blob 租户配置、token 鉴权、标准 handlers |
| [`@rei-standard/amsg-client`](./client/README.md) | `2.2.3` | 浏览器 SDK:加密、请求封装、Push 订阅 |
| [`@rei-standard/amsg-sw`](./sw/README.md) | `2.0.1` | Service Worker:推送展示、离线队列、后台重试 |
| [`@rei-standard/amsg-shared`](./shared/README.md) | `0.2.0` | 三轴推送契约、builders、类型守卫 |
| [`@rei-standard/amsg-instant`](./instant/README.md) | `0.9.0` | 一次性即时推送 handler(SSE 默认传输 / always-on Web Push backup) |
| [`@rei-standard/amsg-server`](./server/README.md) | `2.5.0` | 定时 + 周期消息:Blob 租户配置、token 鉴权、标准 handlers |
| [`@rei-standard/amsg-client`](./client/README.md) | `2.4.0` | 浏览器 SDK:加密、请求封装、Push 订阅、SSE consumer |
| [`@rei-standard/amsg-sw`](./sw/README.md) | `2.2.0` | Service Worker:推送展示、离线队列、delivery dedupe |

**服务端选哪个**:只发"按钮触发 → 立刻推" 用 `amsg-instant`;要定时 / 周期任务 用 `amsg-server`;两种都要就都装,共用同一套 VAPID + masterKey。

Expand Down
54 changes: 50 additions & 4 deletions packages/rei-standard-amsg/client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,58 @@
# Changelog — @rei-standard/amsg-client

## 2.4.0 — `consumeInstantStream()` SSE consumer

配套 `@rei-standard/amsg-instant@0.9.0` 的 SSE 默认模式;同时移除 client 默认请求体大小上限,避免本地误拦长上下文请求。

### New

- 新增 `consumeInstantStream(payload, endpointPath?, options)`,按 SSE frame 解析 `event: payload` / `event: error` / `event: done`,并分发到 `options.onPayload`。
- 新增构造器选项 `maxPayloadBytes?: number | null`。默认 `null`,不再由 client 对请求体大小做本地限制;显式配置后,超限请求仍抛 `PAYLOAD_TOO_LARGE_LOCAL`。
- `@rei-standard/amsg-shared` 精确依赖升级到 `0.2.0`,同步 `notification.silent` 类型/校验能力。

### Changed

- 移除默认请求体大小上限。Web Push 单条回复超限仍由 `amsg-instant` 的 BlobStore / multipart 输出链路处理;client 只保留 `avatarUrl` 软清空,避免 data URI 头像把最终 push 撑爆。

### Docs

- `consumeInstantStream` 章节校正:原文写 "SSE 写失败 / 断开才 fallback push",但 `amsg-instant 0.9.0` 起 Web Push backup 是 **always-on**——SSE 成功 enqueue 也照样发一份同 `messageId` 的 backup,由 SW / client dedupe 收敛。README 改成 "SSE 直送 + Web Push always-on backup + dedupe" 的双路语义;"fallback" 在文档里收窄回它本来该指代的含义(stream 不可用 / enqueue 抛错时的兜底)。仅文档,行为不变。

## 2.4.0-next.0 — `consumeInstantStream()` SSE consumer (pre-release)

发布在 `next` dist-tag。配套 `@rei-standard/amsg-instant@0.9.0-next.0+` 的 SSE 默认模式;老的 `sendInstant()` 字节级不变。

### 新增 `consumeInstantStream(payload, endpointPath?, options)`

POST 到 amsg-instant 的 `/instant` 或 `/continue` 端点,按 SSE frame 解析 `event: payload` / `event: error` / `event: done`,分发到 `options.onPayload` 回调;可被 `options.signal` 中止。

```js
await client.consumeInstantStream(payload, '/instant', {
onPayload: async (p) => routeToIDB(p), // 必填
onError: (err) => log(err), // 可选;通知用,不抑制 throw
onDone: () => stopSpinner(), // 可选
signal: abortController.signal, // 可选
});
```

错误语义:网络 / 协议 / abort / `onPayload` 抛错都会让返回的 Promise reject。`onError` 是**通知性 side-channel**(fire 后照常 throw),不是 try/catch 替代——总是 `await` + 外层 `try/catch` 处理。

加密 / 明文两种 transport 共享构造器配置(`instantEncryption` / `instantClientToken`),用法和 `sendInstant` 一致。请求体跟 `sendInstant` 完全一样——包括必须的 `pushSubscription`(SSE 写失败时框架会用它做 fallback push)。

### Spec 细节

- 多行 `data:` 按 SSE 规范用 `\n` 拼接(不是后写覆盖)
- 非 2xx / 非 `text/event-stream` 响应立即 throw,不进 parser
- 出错时 `reader.cancel(err)` 关闭底层连接,避免 fetch stream 残留至 GC
- AbortError 与其他错误一视同仁走 reject——caller 用 `signal` 主动取消时也能拿到 rejection

## 2.3.0 — Dependency bump

- 依赖更新:同步升级 `@rei-standard/amsg-shared` 至稳定版 `0.1.0`。

## 2.3.0-next.1 — avatarUrl 本地软清空 (pre-release)

Cherry-pick stable `2.2.4` 的本地 `avatarUrl` 软清空到 next 预发布线。`scheduleMessage` / `sendInstant` / `updateMessage` 不合法的 `avatarUrl`(`data:` URI / 长度 > 2048 / 非字符串)改为 `console.warn` + 在 payload 上置 `null`(`updateMessage` 路径走 `delete` 以保留服务端原头像),请求继续发送。`Error.code === 'INVALID_AVATAR_URL_LOCAL'` 已移除;`PAYLOAD_TOO_LARGE_LOCAL`(3KB 体积上限)保留不变。详见 `2.2.4` stable 条目;与 `@rei-standard/amsg-server` 2.4.0-next.1 / `@rei-standard/amsg-instant` 0.8.0-next.1 / `@rei-standard/amsg-sw` 2.1.0-next.1(SW 标题 fallback 至 `来自 {contactName}`)同步。
Cherry-pick stable `2.2.4` 的本地 `avatarUrl` 软清空到 next 预发布线。`scheduleMessage` / `sendInstant` / `updateMessage` 不合法的 `avatarUrl`(`data:` URI / 长度 > 2048 / 非字符串)改为 `console.warn` + 在 payload 上置 `null`(`updateMessage` 路径走 `delete` 以保留服务端原头像),请求继续发送。`Error.code === 'INVALID_AVATAR_URL_LOCAL'` 已移除;当时版本的本地请求体体积预检保留不变,稳定版 2.4.0 已改为可选 `maxPayloadBytes` 且默认不限制。详见 `2.2.4` stable 条目;与 `@rei-standard/amsg-server` 2.4.0-next.1 / `@rei-standard/amsg-instant` 0.8.0-next.1 / `@rei-standard/amsg-sw` 2.1.0-next.1(SW 标题 fallback 至 `来自 {contactName}`)同步。

`next.0` → `next.1` 行为变化只此一项;shared push types re-exports 部分**完全不动**。

Expand Down Expand Up @@ -46,15 +92,15 @@ One import surface — caller apps that consume `ReiClient` and also handle push

### Fix

- **本地预校验 `avatarUrl` + payload 体积**(配合 [`@rei-standard/amsg-instant` 0.6.1](../instant/CHANGELOG.md#061--2026-05-18) / [`@rei-standard/amsg-server` 2.3.1](../server/CHANGELOG.md#231--2026-05-18)):之前 `scheduleMessage` / `sendInstant` / `updateMessage` 是纯 payload-agnostic 透传,业务把 `data:image/...;base64,xxx` 当 `avatarUrl` 传进来,client 会先 AES-GCM 加密、再 POST 出去,绕一圈才在远端拿到 `413` 或 Web Push 4KB 上限报错。现在三个方法在发请求之前做两项本地预检
- **本地预校验 `avatarUrl` + payload 体积**(配合 [`@rei-standard/amsg-instant` 0.6.1](../instant/CHANGELOG.md#061--2026-05-18) / [`@rei-standard/amsg-server` 2.3.1](../server/CHANGELOG.md#231--2026-05-18)):之前 `scheduleMessage` / `sendInstant` / `updateMessage` 是纯 payload-agnostic 透传,业务把 `data:image/...;base64,xxx` 当 `avatarUrl` 传进来,client 会先 AES-GCM 加密、再 POST 出去,绕一圈才在远端拿到 `413` 或 Web Push 4KB 上限报错。当时三个方法在发请求之前做两项本地预检;稳定版 2.4.0 已把请求体体积预检改为可选 `maxPayloadBytes`,默认不限制
- **avatarUrl**:拒 `data:` URI、拒长度 > 2048 字符、必须是字符串。违规 → 抛 `Error` with `.code === 'INVALID_AVATAR_URL_LOCAL'`。
- **payload 体积**:`JSON.stringify(payload)` 的 UTF-8 字节数 > 3072 → 抛 `Error` with `.code === 'PAYLOAD_TOO_LARGE_LOCAL'`,附 `.details = { actualBytes, limitBytes, method }`。3KB 阈值是 Web Push 4KB 硬上限和典型网关 413 阈值往下留的余量;正常 payload(含多轮 messages、apiKey、push subscription)通常 1–2KB
- **payload 体积**:超过当时内置本地阈值会抛 `Error` with `.code === 'PAYLOAD_TOO_LARGE_LOCAL'`,附 `.details = { actualBytes, limitBytes, method }`。此固定阈值在 2.4.0 起不再默认启用
- 两个 code 都带 `LOCAL` 后缀,方便业务和远端返回的 `INVALID_PARAMETERS` / `INVALID_PAYLOAD_FORMAT` 区分(一个不耗远端配额,一个耗)。
- 错误 message 只写「是什么 + 怎么改」(如「头像不支持传入 data: URI,请改为公网可访问的 https:// 图片 URL」),不写「为什么」—— 触发原因写在本 CHANGELOG / README,避免错误对话框塞一整段背景说明。

### Compatibility

- 业务**几乎零修改**:除非之前真的在传 `data:` URI 当 avatarUrl 或传 > 3KB 的 payload(那本来就跑不通),否则升级无感。
- 业务**几乎零修改**:除非之前真的在传 `data:` URI 当 avatarUrl,或命中了当时版本的固定本地体积预检,否则升级无感。
- 加密格式、headers、endpoint、响应 schema 全部不动。
- `scheduleMessage` / `sendInstant` / `updateMessage` 的返回类型不变;新增的两类错误**只在抛出时**才出现。

Expand Down
Loading
Loading