补齐移动壳分享失败观测
移动系统分享失败记录原生异常日志 移动分享单测覆盖稳定错误和日志边界 配置门禁和文档同步分享观测约束
This commit is contained in:
@@ -1708,10 +1708,13 @@ for (const snippet of [
|
||||
'let currentShareTarget: ShareOpenPayload | null = null;',
|
||||
'currentShareTarget = normalizedTarget.payload;',
|
||||
'setMobileHostBridgeShareTarget',
|
||||
'logMobileShareFailure',
|
||||
'mobile share failed for',
|
||||
'request: HostBridgeRequest',
|
||||
"throw invalidRequest('target is required')",
|
||||
'const normalizedTarget = normalizeHostBridgeShareOpenPayload(target);',
|
||||
"'share target is invalid'",
|
||||
"logMobileShareFailure('open.share', error)",
|
||||
"message: 'share unavailable'",
|
||||
'ok(request, true)',
|
||||
'resetMobileHostBridgeShareTargetForTest',
|
||||
@@ -1738,6 +1741,8 @@ for (const snippet of [
|
||||
'stores only the normalized cached share payload',
|
||||
'keeps the previous cached target when a new target is missing or invalid',
|
||||
'maps native share sheet failures to stable host errors',
|
||||
"vi.spyOn(console, 'warn')",
|
||||
'mobile share failed for open.share',
|
||||
'does not fall back to cached target when explicit payload is unsafe',
|
||||
'rejects empty share requests before opening native share sheet',
|
||||
"message: 'share unavailable'",
|
||||
|
||||
@@ -32,6 +32,7 @@ function request(
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
vi.mocked(Share.share).mockReset();
|
||||
resetMobileHostBridgeShareTargetForTest();
|
||||
});
|
||||
@@ -187,7 +188,9 @@ describe('mobile share helpers', () => {
|
||||
});
|
||||
|
||||
test('maps native share sheet failures to stable host errors', async () => {
|
||||
vi.mocked(Share.share).mockRejectedValueOnce(new Error('native share failed'));
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const nativeError = new Error('native share failed');
|
||||
vi.mocked(Share.share).mockRejectedValueOnce(nativeError);
|
||||
|
||||
await expect(
|
||||
openShare(
|
||||
@@ -200,6 +203,10 @@ describe('mobile share helpers', () => {
|
||||
code: 'host_error',
|
||||
message: 'share unavailable',
|
||||
});
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'mobile share failed for open.share',
|
||||
nativeError,
|
||||
);
|
||||
});
|
||||
|
||||
test.each([
|
||||
|
||||
@@ -10,6 +10,10 @@ import { invalidRequest, ok } from './protocol';
|
||||
|
||||
let currentShareTarget: ShareOpenPayload | null = null;
|
||||
|
||||
function logMobileShareFailure(label: string, error: unknown) {
|
||||
console.warn(`mobile share failed for ${label}`, error);
|
||||
}
|
||||
|
||||
export function setMobileHostBridgeShareTarget(request: HostBridgeRequest) {
|
||||
const payload = request.payload;
|
||||
const target =
|
||||
@@ -65,7 +69,8 @@ export async function openShare(request: HostBridgeRequest) {
|
||||
message: message || url || sharePayload?.title || '',
|
||||
url,
|
||||
});
|
||||
} catch {
|
||||
} catch (error) {
|
||||
logMobileShareFailure('open.share', error);
|
||||
throw {
|
||||
code: 'host_error',
|
||||
message: 'share unavailable',
|
||||
|
||||
@@ -3148,6 +3148,12 @@
|
||||
- 决策:`apps/desktop-shell/src-tauri/src/host_bridge/network.rs` 必须在网络状态解析失败时记录 `desktop network failed for status.resolve` 日志。HostBridge 对 H5 仍只返回稳定 `network status unavailable`,不透传 resolver、线程或系统错误细节。
|
||||
- 验证方式:`cargo test --manifest-path apps/desktop-shell/src-tauri/Cargo.toml host_bridge::network`、`npm run desktop-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-06-20 移动壳系统分享异常必须可观测
|
||||
|
||||
- 背景:移动壳 `share.open` 调用 React Native 系统分享面板,失败时 H5 只需要知道分享不可用并保留复制链接等回退;但如果原生分享面板 reject 没有日志,开发侧无法区分分享面板不可用、系统取消异常或平台分享模块异常。
|
||||
- 决策:`apps/mobile-shell/src/host-bridge/share.ts` 必须在 `Share.share(...)` reject 时记录 `mobile share failed for open.share` 日志。HostBridge 对 H5 仍只返回稳定 `share unavailable`,不透传原生分享面板异常明细。
|
||||
- 验证方式:`npm run mobile-shell:test -- --run src/host-bridge/share.test.ts`、`npm run mobile-shell:typecheck`、`npm run check:native-shells`、`npm run check:encoding`、`git diff --check`。
|
||||
|
||||
## 2026-06-20 移动壳门禁脚本必须自登记自扫描
|
||||
|
||||
- 背景:Expo 移动壳单端检查已把 `apps/mobile-shell/scripts/` 纳入生产源码扫描入口,但 `check-config.mjs` 自身仍被排除在脚本清单和替身词扫描之外;这会让移动壳与桌面壳门禁结构不一致,也可能让后续门禁反查内容绕过生产替身词规则。
|
||||
|
||||
@@ -320,6 +320,8 @@ GameBridge 禁止:
|
||||
|
||||
2026-06-20 追加:移动壳文件能力的原生系统异常必须可观测。Expo Sharing 可用性 / 分享面板、DocumentPicker、文本 / base64 文件读取、相册 / 相机权限请求和相册 / 相机打开失败时,`apps/mobile-shell/src/host-bridge/files.ts` 必须记录 `mobile HostBridge file failed for ...` 日志;HostBridge 回包仍只返回稳定的 `host_error` / `unsupported_capability` / `cancelled` / `invalid_request` 语义,不把原生异常明细透传给 H5。移动壳配置检查反查这些日志 label 和对应单测,避免用户可见文件导入导出动作失败后只剩静默兜底。
|
||||
|
||||
2026-06-20 追加:移动壳系统分享失败必须可观测。React Native `Share.share(...)` reject 时,`apps/mobile-shell/src/host-bridge/share.ts` 必须记录 `mobile share failed for open.share` 日志;HostBridge 回包仍只暴露稳定 `share unavailable` 语义,不透传原生分享面板异常明细。
|
||||
|
||||
2026-06-19 追加:视觉小说结果页素材选择弹窗在移动壳声明 `file.importImage` / `file.importAudio` 时优先打开宿主系统图片或音频选择器,把 HostBridge 返回的 base64 副本转换成浏览器 `File` 后继续调用 `uploadVisualNovelAsset`,不绕过平台素材上传、历史素材、AI 图片生成或草稿写回链路;用户取消原生选择时停留在壳流程内,不连带弹出浏览器文件输入。普通浏览器、小程序和未声明能力的裁剪壳继续使用原隐藏文件输入。
|
||||
|
||||
2026-06-19 追加:帮助与反馈页在移动壳声明 `file.captureImage` 时展示“拍摄凭证”入口,调用 Expo 相机拍摄后把 HostBridge 返回的图片副本转换成浏览器 `File`,继续复用反馈页最多 4 张、单张 1 MiB、总 4 MiB、图片 MIME、data URL 预览和提交 payload 校验。拍摄取消时停留在壳流程内,不触发浏览器文件输入;Tauri 桌面壳不声明拍摄能力,普通浏览器、小程序和未声明能力的裁剪壳不显示该入口。
|
||||
|
||||
Reference in New Issue
Block a user