@wenbin_wb/dsh-bridge 2.10.9 → 2.10.11

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.
@@ -20,7 +20,10 @@ export const MOBILE_STYLES_CSS = `
20
20
  --dsh-mobile-safe-bottom: env(safe-area-inset-bottom, 0px);
21
21
  }
22
22
 
23
- @media (max-width: 768px) {
23
+ /* 断点与宿主判据对齐:宿主用 viewportWidth < 768 决定右侧栏自动全屏
24
+ (dsh-client-ui-sidebar-right/lib/client.js),故移动端样式取 <=767px,
25
+ 768px 起彻底交还桌面布局,避免"桥渲染顶栏、宿主却未全屏"的错位。 */
26
+ @media (max-width: 767px) {
24
27
  /* 1. 主框架为 Header 腾出顶部空间 */
25
28
  div[class*="_frame"] {
26
29
  display: flex !important;
@@ -35,6 +38,19 @@ export const MOBILE_STYLES_CSS = `
35
38
  box-sizing: border-box !important;
36
39
  }
37
40
 
41
+ /* 1.1 fixed 全屏面板单独让位:position:fixed 的包含块是 viewport(CSS 2.1 §10.1),
42
+ 不跟随上面 frame 的 padding-top,因此顶部 52px 会落进顶栏覆盖区(#41)。
43
+ 官方右侧栏在 <768px 自动全屏(fixed; inset:0; z-index:40),这里按 data 属性
44
+ 直接位移容器本身,不依赖宿主 CSS-module 哈希类名。
45
+ 真正起作用的是 top;height/max-height 与下面工作台面板那段保持同一写法:
46
+ 宿主当前用 inset:0(无显式高度)时 top 单独即可,但宿主将来若给出显式高度,
47
+ 显式 height 仍能把盒子收在顶栏之下。 */
48
+ [data-sidebar-right-panel="fullscreen"] {
49
+ top: var(--dsh-mobile-header-h, 52px) !important;
50
+ height: calc(100dvh - var(--dsh-mobile-header-h, 52px)) !important;
51
+ max-height: calc(100dvh - var(--dsh-mobile-header-h, 52px)) !important;
52
+ }
53
+
38
54
  /* 2. 顶部原生导航条:100% 还原 DeepSeek App (左侧双横线,右侧(+),中间留白,无多余设置按钮) */
39
55
  .dsh-mobile-app-header {
40
56
  position: fixed !important;
@@ -892,7 +908,7 @@ export const MOBILE_STYLES_CSS = `
892
908
  --dsw-alias-label-primary-foreground: #0f1115;
893
909
  }
894
910
 
895
- @media (min-width: 769px) {
911
+ @media (min-width: 768px) {
896
912
  .dsh-mobile-app-header,
897
913
  .dsh-mobile-backdrop,
898
914
  .dsh-mobile-panel-close-btn {
@@ -0,0 +1,90 @@
1
+ // 资源地址方言兼容(鸿蒙 ArkWeb / HuaweiBrowser 等内核对非特殊 scheme 的 URL 解析差异)
2
+ //
3
+ // 现象:某些内核里 `new URL('dsh-resource://file/session/<id>/<path>').hostname` 返回空串,
4
+ // 而 Chrome/Safari 返回 "file"。DSH 的客户端资源注册表 `@deepseek-ai/dsh-client-resources`
5
+ // 恰好用 URL 的 hostname 当"协议键"(protocolOf),取不到就把该资源的记录判定成"没有 provider":
6
+ // - 记录创建时 protocol 为 undefined → 状态永远停在 "none";
7
+ // - 注册表只在 provider 注册时把已有记录重新挂载一次,之后不会重试 → 刷新也不会恢复;
8
+ // 表现即:右侧栏文件 / 对话里生成的文档预览永远显示「文件资源服务不可用。」,而
9
+ // `resources.providers` 里其实是有 "file" 的(宿主 @deepseek-ai/dsh-api-workspace-files 的
10
+ // provider 注册成功,坏掉的只是地址解析这一步)。鸿蒙(HuaweiBrowser / ArkWeb 7.0)实测复现。
11
+ //
12
+ // 处理:**先做特性探测**,只有在该引擎确有差异时才包一层 `window.URL`,并且只对
13
+ // `dsh-resource:` 协议、且原生 hostname 为空、且原始串能解析出 host 的实例补一个兜底
14
+ // hostname/host(写成实例自有访问器,setter 仍委托原生原型,避免影响赋值语义)。
15
+ // 引擎正常时完全不改动 URL(返回 'not-needed')。
16
+ //
17
+ // 说明:根因在宿主侧(DSH 用 URL.hostname 当协议键),此处是桥侧最小兜底;
18
+ // 宿主上游若改为从原始串解析协议,本模块会自动进入 'not-needed' 分支、不再介入。
19
+
20
+ /**
21
+ * 安装 `dsh-resource:` 地址的 URL 兼容层。
22
+ *
23
+ * @param {object} [win] 便于测试注入的 window(默认取全局 window)
24
+ * @returns {'not-needed'|'installed'|'no-url'|'probe-throw'} 本次的处理结果
25
+ * - `not-needed`:引擎解析正常,未做任何改动
26
+ * - `installed`:引擎有差异,已包装 window.URL
27
+ * - `no-url` / `probe-throw`:环境不适用(无 window.URL / 构造探测失败)
28
+ */
29
+ export function installResourceUrlCompat(win = typeof window === 'undefined' ? undefined : window) {
30
+ if (!win || typeof win.URL !== 'function') return 'no-url';
31
+ const NativeURL = win.URL;
32
+ let probe;
33
+ try {
34
+ probe = new NativeURL('dsh-resource://file/__dsh_probe__/x');
35
+ } catch {
36
+ return 'probe-throw';
37
+ }
38
+ if (probe.hostname === 'file') return 'not-needed';
39
+
40
+ const hostOf = (raw) => {
41
+ const m = /^dsh-resource:\/\/([^/?#]*)/i.exec(String(raw || ''));
42
+ return m && m[1] ? m[1].toLowerCase() : '';
43
+ };
44
+
45
+ function CompatURL(...args) {
46
+ const url = new NativeURL(...args);
47
+ try {
48
+ if (String(url.protocol) === 'dsh-resource:' && !url.hostname) {
49
+ const proto = NativeURL.prototype;
50
+ const hostnameDesc = Object.getOwnPropertyDescriptor(proto, 'hostname');
51
+ const hostDesc = Object.getOwnPropertyDescriptor(proto, 'host');
52
+ // 读取顺序:原生解析结果优先(赋值/改 href 后立即反映新值),为空才回退到"从当前 href 现解析"。
53
+ // 不能缓存成常量,否则 u.hostname='x' 或 u.href=... 之后读回仍是陈旧值(getter/setter 不自洽)。
54
+ const hostOfUrl = (u) => {
55
+ let native;
56
+ try { native = hostnameDesc && hostnameDesc.get ? hostnameDesc.get.call(u) : (u.hostname || ''); } catch { native = ''; }
57
+ if (native) return native;
58
+ let href;
59
+ try { href = String(u.href); } catch { href = ''; }
60
+ return hostOf(href);
61
+ };
62
+ if (hostOfUrl(url)) {
63
+ Object.defineProperty(url, 'hostname', {
64
+ configurable: true,
65
+ enumerable: hostnameDesc ? hostnameDesc.enumerable : true,
66
+ get: () => hostOfUrl(url),
67
+ set: (v) => { if (hostnameDesc && hostnameDesc.set) hostnameDesc.set.call(url, v); },
68
+ });
69
+ if (hostDesc) {
70
+ Object.defineProperty(url, 'host', {
71
+ configurable: true,
72
+ enumerable: hostDesc.enumerable,
73
+ get: () => hostOfUrl(url),
74
+ set: (v) => { if (hostDesc.set) hostDesc.set.call(url, v); },
75
+ });
76
+ }
77
+ }
78
+ }
79
+ } catch { /* 兜底失败就保持原生行为 */ }
80
+ return url;
81
+ }
82
+
83
+ CompatURL.prototype = NativeURL.prototype;
84
+ for (const key of Object.getOwnPropertyNames(NativeURL)) {
85
+ if (key === 'length' || key === 'name' || key === 'prototype') continue;
86
+ try { CompatURL[key] = NativeURL[key]; } catch { /* 只读静态属性忽略 */ }
87
+ }
88
+ win.URL = CompatURL;
89
+ return 'installed';
90
+ }
@@ -90,4 +90,4 @@
90
90
  2. **安全白名单拦截**:
91
91
  - 仅白名单内的用户消息会被放行给 Agent;非白名单用户发送的消息会被直接忽略,绝不消耗 Token 或喂给模型。
92
92
  3. **敏感操作审批**:
93
- - 当 Agent 尝试执行系统命令或敏感文件读写时,Telegram 会自动下发 `[✓ 批准执行]` / `[✕ 拒绝执行]` 交互按键,10 分钟未处理自动超时拒绝。
93
+ - 当 Agent 尝试执行系统命令或敏感文件读写时,Telegram 会自动下发 `[✓ 批准执行]` / `[✕ 拒绝执行]` 交互按键,10 分钟未处理自动超时拒绝。
@@ -40,6 +40,8 @@ export const BRIDGE_ENDPOINTS = {
40
40
  platformStop: 'platformStop',
41
41
  platformStart: 'platformStart',
42
42
  platformUnbind: 'platformUnbind',
43
+ // 本机可用的 DSH agent preset(设置页下拉用;老版本 DSH 返回 available:false)
44
+ listAgentPresets: 'listAgentPresets',
43
45
  // 微信 Bot(v1.x 向后兼容别名,deprecated)
44
46
  wechatGetStatus: 'wechatGetStatus',
45
47
  wechatLogin: 'wechatLogin',
package/lib/bridge-rpc.js CHANGED
@@ -378,6 +378,29 @@ export function installBridgeRpc(ctx, { service, authManager, platformManager, l
378
378
 
379
379
  // ---- 平台管理器(多 IM 平台)----
380
380
 
381
+ // 本机可用的 DSH agent preset(设置页下拉用)。
382
+ // 只读、不含敏感信息:DSH 未提供 agentPresets 服务(旧版本)或名册读取失败时
383
+ // 返回 available:false,前端退化为手填,不影响会话创建。
384
+ if (endpoint === BRIDGE_ENDPOINTS.listAgentPresets) {
385
+ try {
386
+ const presets = ctx.get?.('agentPresets');
387
+ if (!presets?.list) return ok({ available: false, default: null, presets: [] });
388
+ const rows = await presets.list();
389
+ return ok({
390
+ available: true,
391
+ default: typeof presets.defaultId === 'string' ? presets.defaultId : null,
392
+ presets: (Array.isArray(rows) ? rows : [])
393
+ // 坏掉的预设不列出来(DSH 自己也会在挂载前拒绝)
394
+ .filter((p) => p && typeof p.id === 'string' && p.broken === undefined)
395
+ .map((p) => ({ id: p.id, name: typeof p.name === 'string' ? p.name : p.id })),
396
+ });
397
+ } catch (err) {
398
+ // 服务被卸载 / 名册不可读:这是设置页的辅助信息,不该让整个请求失败
399
+ logger?.warn?.('dsh-bridge: listAgentPresets failed: %s', err?.message ?? err);
400
+ return ok({ available: false, default: null, presets: [] });
401
+ }
402
+ }
403
+
381
404
  if (endpoint === BRIDGE_ENDPOINTS.listPlatforms) {
382
405
  if (!platformManager) return ok({});
383
406
  // 每个平台的 login.qrPayload 渲染为 dataURL 后返回
package/lib/compat.js CHANGED
@@ -1,129 +1,172 @@
1
- // 运行时兼容垫片(lib/compat.js)
2
- //
3
- // 背景:DSH 核心依赖链(@deepseek-ai/dsh-timeout ← dsh-llm)在每次 agent 请求上
4
- // 调用 AbortSignal.any([...]),该 API 仅存在于 Node 20.3+/22+。运行在低版本 Node
5
- // (如 Node 18)上时,通过桥接发送消息会直接抛
6
- // "AbortSignal.any is not a function ... (internal)"
7
- // 本模块在缺失时安装规范兼容的垫片;环境本身支持时不做任何事(返回 false)。
8
- //
9
- // 浏览器侧(旧 Safari/WebView)由 lib/index.js 的 HTML_HEAD_INJECTIONS 注入同语义垫片。
10
-
11
- function abortWith(controller, reason) {
12
- try {
13
- controller.abort(reason)
14
- } catch {
15
- try { controller.abort() } catch { /* 不可中止的信号:忽略 */ }
16
- }
17
- }
18
-
19
- /**
20
- * 在目标环境中安装 AbortSignal.any / AbortSignal.timeout 垫片(仅缺失时)。
21
- * @param {object} [target] 可注入的环境(默认 globalThis),便于测试
22
- * @returns {boolean} 是否实际安装了垫片
23
- */
24
- export function installAbortSignalCompat(target = globalThis) {
25
- const Signal = target.AbortSignal
26
- const Controller = target.AbortController
27
- if (!Signal || !Controller) return false
28
-
29
- let installed = false
30
-
31
- // ---- AbortSignal.any ----
32
- if (typeof Signal.any !== 'function') {
33
- Signal.any = (signals) => {
34
- const list = Array.from(signals ?? [])
35
- const controller = new Controller()
36
- // 规范语义:任一源信号已中止 → 立即以该原因中止
37
- for (const s of list) {
38
- if (s && s.aborted) {
39
- abortWith(controller, s.reason)
40
- return controller.signal
41
- }
42
- }
43
- const onAbort = (eventOrSignal) => {
44
- // 真实 EventTarget 的 abort 事件参数是 event(event.target = 信号),
45
- // 部分非标准实现直接传信号本身——两者都兼容取 reason
46
- const src = eventOrSignal && eventOrSignal.target ? eventOrSignal.target : eventOrSignal
47
- cleanup()
48
- abortWith(controller, src ? src.reason : undefined)
49
- }
50
- const cleanup = () => {
51
- for (const s of list) {
52
- try { s.removeEventListener('abort', onAbort) } catch { /* 非标准信号:忽略 */ }
53
- }
54
- }
55
- for (const s of list) {
56
- try { s.addEventListener('abort', onAbort, { once: true }) } catch { /* 非标准信号:忽略 */ }
57
- }
58
- return controller.signal
59
- }
60
- installed = true
61
- }
62
-
63
- // ---- AbortSignal.timeout(Node 17.3+ / 较新浏览器才有,顺手补齐)----
64
- if (typeof Signal.timeout !== 'function') {
65
- Signal.timeout = (delayMs) => {
66
- const controller = new Controller()
67
- const delay = Math.max(0, Number(delayMs) || 0)
68
- const reason = typeof target.DOMException === 'function'
69
- ? new target.DOMException('The operation timed out.', 'TimeoutError')
70
- : new Error('The operation timed out.')
71
- const timer = setTimeout(() => abortWith(controller, reason), delay)
72
- // 规范语义:timeout 信号的超时定时器不阻止进程退出
73
- if (typeof timer.unref === 'function') timer.unref()
74
- return controller.signal
75
- }
76
- installed = true
77
- }
78
-
79
- return installed
80
- }
81
-
82
- /**
83
- * 浏览器侧垫片源码:由代理注入到 HTML <head>(在宿主所有脚本之前执行),
84
- * 为 iOS 16 / 旧 Safari / 旧 WebView(无 AbortSignal.any,Safari 17.4 才加入)
85
- * 上的 DSH 网页客户端补齐。多行可读源码,勿手工压缩成单行(配平易错)。
86
- */
87
- export const BROWSER_ABORT_SIGNAL_POLYFILL = `<script data-dsh-bridge-polyfill="2">
88
- !function () {
89
- try {
90
- var S = self.AbortSignal;
91
- if (typeof S !== 'function') return;
92
- if (typeof S.any !== 'function') {
93
- S.any = function (signals) {
94
- var list = Array.prototype.slice.call(signals || []);
95
- var c = new self.AbortController();
96
- var onAbort = function (ev) {
97
- var src = ev && ev.target ? ev.target : ev;
98
- cleanup();
99
- try { c.abort(src ? src.reason : undefined); } catch (e) { c.abort(); }
100
- };
101
- var cleanup = function () {
102
- for (var i = 0; i < list.length; i++) {
103
- try { list[i].removeEventListener('abort', onAbort); } catch (e) {}
104
- }
105
- };
106
- for (var i = 0; i < list.length; i++) {
107
- if (list[i] && list[i].aborted) {
108
- try { c.abort(list[i].reason); } catch (e) { c.abort(); }
109
- return c.signal;
110
- }
111
- }
112
- for (var i = 0; i < list.length; i++) {
113
- try { list[i].addEventListener('abort', onAbort, { once: true }); } catch (e) {}
114
- }
115
- return c.signal;
116
- };
117
- }
118
- if (typeof S.timeout !== 'function') {
119
- S.timeout = function (ms) {
120
- var c = new self.AbortController();
121
- setTimeout(function () {
122
- try { c.abort(new Error('The operation timed out.')); } catch (e) { c.abort(); }
123
- }, Math.max(0, Number(ms) || 0));
124
- return c.signal;
125
- };
126
- }
127
- } catch (e) { /* 环境异常时不影响页面其余脚本 */ }
128
- }();
129
- </script>`
1
+ // 运行时兼容垫片(lib/compat.js)
2
+ //
3
+ // 背景:DSH 核心依赖链(@deepseek-ai/dsh-timeout ← dsh-llm)在每次 agent 请求上
4
+ // 调用 AbortSignal.any([...]),该 API 仅存在于 Node 20.3+/22+。运行在低版本 Node
5
+ // (如 Node 18)上时,通过桥接发送消息会直接抛
6
+ // "AbortSignal.any is not a function ... (internal)"
7
+ // 本模块在缺失时安装规范兼容的垫片;环境本身支持时不做任何事(返回 false)。
8
+ //
9
+ // 浏览器侧(旧 Safari/WebView)由 lib/index.js 的 HTML_HEAD_INJECTIONS 注入同语义垫片。
10
+
11
+ function abortWith(controller, reason) {
12
+ try {
13
+ controller.abort(reason)
14
+ } catch {
15
+ try { controller.abort() } catch { /* 不可中止的信号:忽略 */ }
16
+ }
17
+ }
18
+
19
+ /**
20
+ * 在目标环境中安装 AbortSignal.any / AbortSignal.timeout 垫片(仅缺失时)。
21
+ * @param {object} [target] 可注入的环境(默认 globalThis),便于测试
22
+ * @returns {boolean} 是否实际安装了垫片
23
+ */
24
+ export function installAbortSignalCompat(target = globalThis) {
25
+ const Signal = target.AbortSignal
26
+ const Controller = target.AbortController
27
+ if (!Signal || !Controller) return false
28
+
29
+ let installed = false
30
+
31
+ // ---- AbortSignal.any ----
32
+ if (typeof Signal.any !== 'function') {
33
+ Signal.any = (signals) => {
34
+ const list = Array.from(signals ?? [])
35
+ const controller = new Controller()
36
+ // 规范语义:任一源信号已中止 → 立即以该原因中止
37
+ for (const s of list) {
38
+ if (s && s.aborted) {
39
+ abortWith(controller, s.reason)
40
+ return controller.signal
41
+ }
42
+ }
43
+ const onAbort = (eventOrSignal) => {
44
+ // 真实 EventTarget 的 abort 事件参数是 event(event.target = 信号),
45
+ // 部分非标准实现直接传信号本身——两者都兼容取 reason
46
+ const src = eventOrSignal && eventOrSignal.target ? eventOrSignal.target : eventOrSignal
47
+ cleanup()
48
+ abortWith(controller, src ? src.reason : undefined)
49
+ }
50
+ const cleanup = () => {
51
+ for (const s of list) {
52
+ try { s.removeEventListener('abort', onAbort) } catch { /* 非标准信号:忽略 */ }
53
+ }
54
+ }
55
+ for (const s of list) {
56
+ try { s.addEventListener('abort', onAbort, { once: true }) } catch { /* 非标准信号:忽略 */ }
57
+ }
58
+ return controller.signal
59
+ }
60
+ installed = true
61
+ }
62
+
63
+ // ---- AbortSignal.timeout(Node 17.3+ / 较新浏览器才有,顺手补齐)----
64
+ if (typeof Signal.timeout !== 'function') {
65
+ Signal.timeout = (delayMs) => {
66
+ const controller = new Controller()
67
+ const delay = Math.max(0, Number(delayMs) || 0)
68
+ const reason = typeof target.DOMException === 'function'
69
+ ? new target.DOMException('The operation timed out.', 'TimeoutError')
70
+ : new Error('The operation timed out.')
71
+ const timer = setTimeout(() => abortWith(controller, reason), delay)
72
+ // 规范语义:timeout 信号的超时定时器不阻止进程退出
73
+ if (typeof timer.unref === 'function') timer.unref()
74
+ return controller.signal
75
+ }
76
+ installed = true
77
+ }
78
+
79
+ return installed
80
+ }
81
+
82
+ /**
83
+ * 浏览器侧垫片源码:由代理注入到 HTML <head>(在宿主所有脚本之前执行),
84
+ * 为 iOS 16 / 旧 Safari / 旧 WebView(无 AbortSignal.any,Safari 17.4 才加入)
85
+ * 上的 DSH 网页客户端补齐。多行可读源码,勿手工压缩成单行(配平易错)。
86
+ */
87
+ export const BROWSER_ABORT_SIGNAL_POLYFILL = `<script data-dsh-bridge-polyfill="2">
88
+ !function () {
89
+ try {
90
+ var S = self.AbortSignal;
91
+ if (typeof S !== 'function') return;
92
+ if (typeof S.any !== 'function') {
93
+ S.any = function (signals) {
94
+ var list = Array.prototype.slice.call(signals || []);
95
+ var c = new self.AbortController();
96
+ var onAbort = function (ev) {
97
+ var src = ev && ev.target ? ev.target : ev;
98
+ cleanup();
99
+ try { c.abort(src ? src.reason : undefined); } catch (e) { c.abort(); }
100
+ };
101
+ var cleanup = function () {
102
+ for (var i = 0; i < list.length; i++) {
103
+ try { list[i].removeEventListener('abort', onAbort); } catch (e) {}
104
+ }
105
+ };
106
+ for (var i = 0; i < list.length; i++) {
107
+ if (list[i] && list[i].aborted) {
108
+ try { c.abort(list[i].reason); } catch (e) { c.abort(); }
109
+ return c.signal;
110
+ }
111
+ }
112
+ for (var i = 0; i < list.length; i++) {
113
+ try { list[i].addEventListener('abort', onAbort, { once: true }); } catch (e) {}
114
+ }
115
+ return c.signal;
116
+ };
117
+ }
118
+ if (typeof S.timeout !== 'function') {
119
+ S.timeout = function (ms) {
120
+ var c = new self.AbortController();
121
+ setTimeout(function () {
122
+ try { c.abort(new Error('The operation timed out.')); } catch (e) { c.abort(); }
123
+ }, Math.max(0, Number(ms) || 0));
124
+ return c.signal;
125
+ };
126
+ }
127
+ } catch (e) { /* 环境异常时不影响页面其余脚本 */ }
128
+ }();
129
+ </script>`
130
+
131
+ /**
132
+ * 浏览器侧垫片源码(二):iOS 16 / 旧 Safari 缺失的 Promise.withResolvers 与 Iterator。
133
+ *
134
+ * 背景(两者都是"整个 app 起不来"级别):
135
+ * 1. `Promise.withResolvers`:Safari 17.4+ / Chrome 119+ 才有。DSH 宿主 HTML 末尾的内联脚本
136
+ * 直接写 `(globalThis.__DSH_BOOT_READY__ ??= Promise.withResolvers()).resolve()`;
137
+ * 另有多个客户端插件在运行期使用(审批、用户提问、cordis 运行时 ctx.timeout/ctx.interval、
138
+ * workspace-files 的文件变更订阅 Follower)。
139
+ * 2. `Iterator`:Safari 18.4+ 才有(迭代器辅助方法)。DSH 自带的 pdf.js 在**模块顶层**就执行
140
+ * `if (typeof Iterator.prototype.join !== "function") Iterator.prototype.join = ...`
141
+ * iOS 16 上没有 Iterator 全局,于是这一行抛 `Can't find variable: Iterator`,
142
+ * 导致 `@deepseek-ai/dsh-client-ui-sidebar-documentpreview` 导入失败 → 整个插件树加载失败,
143
+ * 页面直接显示 "Failed to load plugins"(实测于 iOS 16 真机 + 本机同构模拟)。
144
+ * 这里把真实的内置迭代器共享原型(%IteratorPrototype%)挂到 `Iterator.prototype` 上,
145
+ * 使 pdf.js 的补丁落到真实迭代器原型,而不是一个无用的空对象。
146
+ *
147
+ * 两者都在缺失时才安装;已有该能力的引擎不做任何事。多行可读源码,勿手工压缩成单行(配平易错)。
148
+ */
149
+ export const BROWSER_PROMISE_ITERATOR_POLYFILL = `<script data-dsh-bridge-polyfill="3">
150
+ !function () {
151
+ try {
152
+ if (typeof Promise !== 'undefined' && typeof Promise.withResolvers !== 'function') {
153
+ Promise.withResolvers = function () {
154
+ var resolve, reject;
155
+ var promise = new Promise(function (res, rej) { resolve = res; reject = rej; });
156
+ return { promise: promise, resolve: resolve, reject: reject };
157
+ };
158
+ }
159
+ } catch (e) { /* 环境异常时不影响页面其余脚本 */ }
160
+ try {
161
+ if (typeof self.Iterator === 'undefined') {
162
+ var proto = null;
163
+ try {
164
+ proto = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
165
+ } catch (e2) { proto = null; }
166
+ var IteratorShim = function Iterator() {};
167
+ if (proto && typeof proto === 'object') IteratorShim.prototype = proto;
168
+ self.Iterator = IteratorShim;
169
+ }
170
+ } catch (e) { /* 环境异常时不影响页面其余脚本 */ }
171
+ }();
172
+ </script>`