dsh-manage-session 2.0.3 → 2.0.4
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/CHANGELOG.ja.md +33 -33
- package/CHANGELOG.ko.md +33 -33
- package/CHANGELOG.md +33 -33
- package/INSTALL.ja.md +164 -164
- package/INSTALL.ko.md +164 -164
- package/INSTALL.md +164 -164
- package/INSTALL.zh.md +164 -164
- package/LICENSE +21 -21
- package/README.en.md +76 -62
- package/README.ja.md +53 -39
- package/README.ko.md +52 -38
- package/README.md +18 -4
- package/cordis.patch.yml +19 -19
- package/dsh.plugin.json +2 -2
- package/locale/de.json +6 -6
- package/locale/en.json +6 -6
- package/locale/es.json +6 -6
- package/locale/fr.json +6 -6
- package/locale/it.json +6 -6
- package/locale/ja.json +6 -6
- package/locale/ko.json +6 -6
- package/locale/ru.json +6 -6
- package/locale/zh.json +6 -6
- package/package.json +9 -9
- package/screenshots.json +3 -3
- package/src/client/bridge-scope.ts +202 -202
- package/src/client/card.tsx +154 -154
- package/src/client/host-api.ts +61 -61
- package/src/client/index.ts +594 -594
- package/src/client/locales.ts +893 -893
- package/src/client/manage-locales.ts +178 -178
- package/src/client/manage.tsx +430 -430
- package/src/client/panel.tsx +131 -131
- package/src/config.ts +43 -43
- package/src/host/health/attribution.ts +118 -118
- package/src/host/health/lossless.ts +128 -128
- package/src/host/health/source-kind.ts +350 -350
- package/src/host/history/archive-source.ts +250 -250
- package/src/host/history/archive.ts +249 -249
- package/src/host/history/favorites.ts +71 -71
- package/src/host/history/purge.ts +370 -370
- package/src/host/registry/archive-registry.ts +317 -317
- package/src/host/settings-bridge.ts +222 -222
|
@@ -1,202 +1,202 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Legacy 设置桥 scope(客户端半身,≤0.1.5 专用)。
|
|
3
|
-
*
|
|
4
|
-
* 为什么存在:旧宿主的 `settingsScope.bind()` 是数据死路(台账 A2——scope
|
|
5
|
-
* status 永远 'unavailable',永不就绪),卡在老线上只剩「不可用」空壳。本
|
|
6
|
-
* 模块把卡片数据面改走自家 webServer 桥(`settings-describe`/`settings-mutate`,
|
|
7
|
-
* 宿主半落在 legacy settings 租约上,context-compression 六格实证的持久化
|
|
8
|
-
* 写路径)。0.1.7+ 不走此路——client/index.ts 的回退链里 configForms 原生
|
|
9
|
-
* 句柄优先,桥只是 legacy 兜底。
|
|
10
|
-
*
|
|
11
|
-
* 形状纪律:快照对象只在状态真正变化时更换引用(useSyncExternalStore 的
|
|
12
|
-
* getSnapshot 缓存语义);describe 在首个 subscribe 时才发起(bundle 级
|
|
13
|
-
* 无头测试只调 getSnapshot、不 subscribe,绝不触发 fetch)。
|
|
14
|
-
*/
|
|
15
|
-
import type { StewardConfig } from '../config.ts'
|
|
16
|
-
|
|
17
|
-
import { callHostAny } from './host-api.ts'
|
|
18
|
-
|
|
19
|
-
/** 与宿主 settings-bridge.ts 的命名空间键一致(存储键稳定承诺)。 */
|
|
20
|
-
const STEWARD_NS = 'manage-session'
|
|
21
|
-
|
|
22
|
-
/** 单视图快照(StewardCardScope / SwitchCardScope 的结构交集)。 */
|
|
23
|
-
export interface BridgeSnapshot<V> {
|
|
24
|
-
status: 'loading' | 'ready' | 'unavailable'
|
|
25
|
-
value: V | undefined
|
|
26
|
-
revision: number | undefined
|
|
27
|
-
writable: boolean
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** 卡片 scope 面(两张卡各自的结构化镜像的交集)。 */
|
|
31
|
-
export interface BridgeScope<V> {
|
|
32
|
-
getSnapshot(): BridgeSnapshot<V>
|
|
33
|
-
subscribe(listener: () => void): () => void
|
|
34
|
-
set(field: string, value: unknown): Promise<void>
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** 共享文档仓:两视图读同一份 describe/mutate 结果。 */
|
|
38
|
-
interface SharedDoc {
|
|
39
|
-
steward: Record<string, unknown>
|
|
40
|
-
status: 'loading' | 'ready' | 'unavailable'
|
|
41
|
-
writable: boolean
|
|
42
|
-
revision: number | undefined
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const shared: SharedDoc = {
|
|
46
|
-
steward: {},
|
|
47
|
-
status: 'loading',
|
|
48
|
-
writable: false,
|
|
49
|
-
revision: undefined,
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const listeners = new Set<() => void>()
|
|
53
|
-
let describeInFlight: Promise<void> | undefined
|
|
54
|
-
|
|
55
|
-
function notify(): void {
|
|
56
|
-
for (const listener of [...listeners]) {
|
|
57
|
-
try {
|
|
58
|
-
listener()
|
|
59
|
-
} catch {
|
|
60
|
-
/* 单个订阅者异常绝不破坏其余通知 */
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function replaceDoc(next: Partial<SharedDoc>): void {
|
|
66
|
-
Object.assign(shared, next)
|
|
67
|
-
notify()
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/** 首个订阅触发一次 describe;并发调用共享同一 in-flight promise。 */
|
|
71
|
-
function ensureDescribe(): Promise<void> {
|
|
72
|
-
if (describeInFlight !== undefined) return describeInFlight
|
|
73
|
-
describeInFlight = callHostAny<{ ok?: boolean; writable?: boolean; revision?: number; namespaces?: Record<string, Record<string, unknown>>; error?: string }>(
|
|
74
|
-
'settings-describe',
|
|
75
|
-
{},
|
|
76
|
-
)
|
|
77
|
-
.then((res) => {
|
|
78
|
-
if (res.ok === true && res.namespaces !== null && typeof res.namespaces === 'object') {
|
|
79
|
-
replaceDoc({
|
|
80
|
-
steward: res.namespaces[STEWARD_NS] ?? {},
|
|
81
|
-
status: 'ready',
|
|
82
|
-
writable: res.writable === true,
|
|
83
|
-
revision: typeof res.revision === 'number' ? res.revision : undefined,
|
|
84
|
-
})
|
|
85
|
-
} else {
|
|
86
|
-
replaceDoc({ status: 'unavailable', writable: false })
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
.catch(() => {
|
|
90
|
-
replaceDoc({ status: 'unavailable', writable: false })
|
|
91
|
-
})
|
|
92
|
-
.finally(() => {
|
|
93
|
-
describeInFlight = undefined
|
|
94
|
-
})
|
|
95
|
-
return describeInFlight
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/** 组装一个视图 scope。`ns` 决定读/写哪个命名空间文档。 */
|
|
99
|
-
function makeBridgeScope<V extends object>(
|
|
100
|
-
ns: string,
|
|
101
|
-
keyOf: (doc: SharedDoc) => Record<string, unknown>,
|
|
102
|
-
): BridgeScope<V> {
|
|
103
|
-
let snapshot: BridgeSnapshot<V> = { status: 'loading', value: undefined, revision: undefined, writable: false }
|
|
104
|
-
|
|
105
|
-
const refresh = (): void => {
|
|
106
|
-
const next: BridgeSnapshot<V> = {
|
|
107
|
-
status: shared.status,
|
|
108
|
-
value: (shared.status === 'ready' ? { ...keyOf(shared) } : undefined) as V | undefined,
|
|
109
|
-
revision: shared.revision,
|
|
110
|
-
writable: shared.writable,
|
|
111
|
-
}
|
|
112
|
-
const changed =
|
|
113
|
-
next.status !== snapshot.status ||
|
|
114
|
-
next.writable !== snapshot.writable ||
|
|
115
|
-
next.revision !== snapshot.revision ||
|
|
116
|
-
JSON.stringify(next.value ?? null) !== JSON.stringify(snapshot.value ?? null)
|
|
117
|
-
if (changed) {
|
|
118
|
-
snapshot = next
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
getSnapshot: () => {
|
|
124
|
-
// 每次读取都先对账共享仓再回缓存(引用稳定性由 refresh 的变更检测保证);
|
|
125
|
-
// 裸读(无 subscribe)只重算缓存,绝不触发 fetch。
|
|
126
|
-
refresh()
|
|
127
|
-
return snapshot
|
|
128
|
-
},
|
|
129
|
-
subscribe: (listener) => {
|
|
130
|
-
listeners.add(listener)
|
|
131
|
-
void ensureDescribe()
|
|
132
|
-
return () => {
|
|
133
|
-
listeners.delete(listener)
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
set: async (field, value) => {
|
|
137
|
-
if (shared.status !== 'ready' || shared.writable === false) {
|
|
138
|
-
throw new Error('设置桥不可写(describe 未就绪或宿主只读)')
|
|
139
|
-
}
|
|
140
|
-
const before = keyOf(shared)[field]
|
|
141
|
-
// 乐观回显:开关即时翻转,权威值由 mutate 响应修正。
|
|
142
|
-
keyOf(shared)[field] = value
|
|
143
|
-
refresh()
|
|
144
|
-
const res = await callHostAny<{ ok?: boolean; revision?: number; namespaces?: Record<string, Record<string, unknown>>; error?: string }>(
|
|
145
|
-
'settings-mutate',
|
|
146
|
-
{ ns, fields: { [field]: value } },
|
|
147
|
-
)
|
|
148
|
-
if (res.ok === true && res.namespaces !== null && typeof res.namespaces === 'object') {
|
|
149
|
-
replaceDoc({
|
|
150
|
-
steward: res.namespaces[STEWARD_NS] ?? shared.steward,
|
|
151
|
-
revision: typeof res.revision === 'number' ? res.revision : shared.revision,
|
|
152
|
-
})
|
|
153
|
-
} else {
|
|
154
|
-
// 失败回滚:开关弹回 = 用户可见的失败信号。
|
|
155
|
-
keyOf(shared)[field] = before
|
|
156
|
-
refresh()
|
|
157
|
-
throw new Error(res.error ?? '设置写入失败')
|
|
158
|
-
}
|
|
159
|
-
},
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
let stewardScope: BridgeScope<StewardConfig> | undefined
|
|
164
|
-
|
|
165
|
-
/** 管家卡/管家入口的桥 scope(命名空间 manage-session)。 */
|
|
166
|
-
export function stewardBridgeScope(): BridgeScope<StewardConfig> {
|
|
167
|
-
stewardScope ??= makeBridgeScope<StewardConfig>(STEWARD_NS, (doc) => doc.steward)
|
|
168
|
-
return stewardScope
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* 原生句柄 + 桥的合成 scope:原生快照**在任意时刻**观测到 unavailable(出生
|
|
174
|
-
* 死或 loading→死,0.1.7 上 steward 条目的 configForms 实测后者)就地读桥,
|
|
175
|
-
* 不做 apply 期时序猜测。写侧同理:原生 unavailable 即走桥(桥在无 legacy
|
|
176
|
-
* settings 服务的宿主上 set 会显式拒绝,卡片保持只读真值)。双订阅:桥的
|
|
177
|
-
* describe 全页一次(模块单例),代价可忽略。
|
|
178
|
-
*/
|
|
179
|
-
export function withBridgeFallback<V extends object>(
|
|
180
|
-
native: BridgeScope<V> | undefined,
|
|
181
|
-
bridge: BridgeScope<V>,
|
|
182
|
-
): BridgeScope<V> {
|
|
183
|
-
if (native === undefined) return bridge
|
|
184
|
-
return {
|
|
185
|
-
getSnapshot: () => {
|
|
186
|
-
const snap = native.getSnapshot()
|
|
187
|
-
return snap.status === 'unavailable' ? bridge.getSnapshot() : snap
|
|
188
|
-
},
|
|
189
|
-
subscribe: (listener) => {
|
|
190
|
-
const offNative = native.subscribe(listener)
|
|
191
|
-
const offBridge = bridge.subscribe(listener)
|
|
192
|
-
return () => {
|
|
193
|
-
offNative()
|
|
194
|
-
offBridge()
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
set: async (field, value) => {
|
|
198
|
-
if (native.getSnapshot().status === 'unavailable') return bridge.set(field, value)
|
|
199
|
-
return native.set(field, value)
|
|
200
|
-
},
|
|
201
|
-
}
|
|
202
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Legacy 设置桥 scope(客户端半身,≤0.1.5 专用)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么存在:旧宿主的 `settingsScope.bind()` 是数据死路(台账 A2——scope
|
|
5
|
+
* status 永远 'unavailable',永不就绪),卡在老线上只剩「不可用」空壳。本
|
|
6
|
+
* 模块把卡片数据面改走自家 webServer 桥(`settings-describe`/`settings-mutate`,
|
|
7
|
+
* 宿主半落在 legacy settings 租约上,context-compression 六格实证的持久化
|
|
8
|
+
* 写路径)。0.1.7+ 不走此路——client/index.ts 的回退链里 configForms 原生
|
|
9
|
+
* 句柄优先,桥只是 legacy 兜底。
|
|
10
|
+
*
|
|
11
|
+
* 形状纪律:快照对象只在状态真正变化时更换引用(useSyncExternalStore 的
|
|
12
|
+
* getSnapshot 缓存语义);describe 在首个 subscribe 时才发起(bundle 级
|
|
13
|
+
* 无头测试只调 getSnapshot、不 subscribe,绝不触发 fetch)。
|
|
14
|
+
*/
|
|
15
|
+
import type { StewardConfig } from '../config.ts'
|
|
16
|
+
|
|
17
|
+
import { callHostAny } from './host-api.ts'
|
|
18
|
+
|
|
19
|
+
/** 与宿主 settings-bridge.ts 的命名空间键一致(存储键稳定承诺)。 */
|
|
20
|
+
const STEWARD_NS = 'manage-session'
|
|
21
|
+
|
|
22
|
+
/** 单视图快照(StewardCardScope / SwitchCardScope 的结构交集)。 */
|
|
23
|
+
export interface BridgeSnapshot<V> {
|
|
24
|
+
status: 'loading' | 'ready' | 'unavailable'
|
|
25
|
+
value: V | undefined
|
|
26
|
+
revision: number | undefined
|
|
27
|
+
writable: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** 卡片 scope 面(两张卡各自的结构化镜像的交集)。 */
|
|
31
|
+
export interface BridgeScope<V> {
|
|
32
|
+
getSnapshot(): BridgeSnapshot<V>
|
|
33
|
+
subscribe(listener: () => void): () => void
|
|
34
|
+
set(field: string, value: unknown): Promise<void>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** 共享文档仓:两视图读同一份 describe/mutate 结果。 */
|
|
38
|
+
interface SharedDoc {
|
|
39
|
+
steward: Record<string, unknown>
|
|
40
|
+
status: 'loading' | 'ready' | 'unavailable'
|
|
41
|
+
writable: boolean
|
|
42
|
+
revision: number | undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const shared: SharedDoc = {
|
|
46
|
+
steward: {},
|
|
47
|
+
status: 'loading',
|
|
48
|
+
writable: false,
|
|
49
|
+
revision: undefined,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const listeners = new Set<() => void>()
|
|
53
|
+
let describeInFlight: Promise<void> | undefined
|
|
54
|
+
|
|
55
|
+
function notify(): void {
|
|
56
|
+
for (const listener of [...listeners]) {
|
|
57
|
+
try {
|
|
58
|
+
listener()
|
|
59
|
+
} catch {
|
|
60
|
+
/* 单个订阅者异常绝不破坏其余通知 */
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function replaceDoc(next: Partial<SharedDoc>): void {
|
|
66
|
+
Object.assign(shared, next)
|
|
67
|
+
notify()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** 首个订阅触发一次 describe;并发调用共享同一 in-flight promise。 */
|
|
71
|
+
function ensureDescribe(): Promise<void> {
|
|
72
|
+
if (describeInFlight !== undefined) return describeInFlight
|
|
73
|
+
describeInFlight = callHostAny<{ ok?: boolean; writable?: boolean; revision?: number; namespaces?: Record<string, Record<string, unknown>>; error?: string }>(
|
|
74
|
+
'settings-describe',
|
|
75
|
+
{},
|
|
76
|
+
)
|
|
77
|
+
.then((res) => {
|
|
78
|
+
if (res.ok === true && res.namespaces !== null && typeof res.namespaces === 'object') {
|
|
79
|
+
replaceDoc({
|
|
80
|
+
steward: res.namespaces[STEWARD_NS] ?? {},
|
|
81
|
+
status: 'ready',
|
|
82
|
+
writable: res.writable === true,
|
|
83
|
+
revision: typeof res.revision === 'number' ? res.revision : undefined,
|
|
84
|
+
})
|
|
85
|
+
} else {
|
|
86
|
+
replaceDoc({ status: 'unavailable', writable: false })
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
.catch(() => {
|
|
90
|
+
replaceDoc({ status: 'unavailable', writable: false })
|
|
91
|
+
})
|
|
92
|
+
.finally(() => {
|
|
93
|
+
describeInFlight = undefined
|
|
94
|
+
})
|
|
95
|
+
return describeInFlight
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** 组装一个视图 scope。`ns` 决定读/写哪个命名空间文档。 */
|
|
99
|
+
function makeBridgeScope<V extends object>(
|
|
100
|
+
ns: string,
|
|
101
|
+
keyOf: (doc: SharedDoc) => Record<string, unknown>,
|
|
102
|
+
): BridgeScope<V> {
|
|
103
|
+
let snapshot: BridgeSnapshot<V> = { status: 'loading', value: undefined, revision: undefined, writable: false }
|
|
104
|
+
|
|
105
|
+
const refresh = (): void => {
|
|
106
|
+
const next: BridgeSnapshot<V> = {
|
|
107
|
+
status: shared.status,
|
|
108
|
+
value: (shared.status === 'ready' ? { ...keyOf(shared) } : undefined) as V | undefined,
|
|
109
|
+
revision: shared.revision,
|
|
110
|
+
writable: shared.writable,
|
|
111
|
+
}
|
|
112
|
+
const changed =
|
|
113
|
+
next.status !== snapshot.status ||
|
|
114
|
+
next.writable !== snapshot.writable ||
|
|
115
|
+
next.revision !== snapshot.revision ||
|
|
116
|
+
JSON.stringify(next.value ?? null) !== JSON.stringify(snapshot.value ?? null)
|
|
117
|
+
if (changed) {
|
|
118
|
+
snapshot = next
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
getSnapshot: () => {
|
|
124
|
+
// 每次读取都先对账共享仓再回缓存(引用稳定性由 refresh 的变更检测保证);
|
|
125
|
+
// 裸读(无 subscribe)只重算缓存,绝不触发 fetch。
|
|
126
|
+
refresh()
|
|
127
|
+
return snapshot
|
|
128
|
+
},
|
|
129
|
+
subscribe: (listener) => {
|
|
130
|
+
listeners.add(listener)
|
|
131
|
+
void ensureDescribe()
|
|
132
|
+
return () => {
|
|
133
|
+
listeners.delete(listener)
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
set: async (field, value) => {
|
|
137
|
+
if (shared.status !== 'ready' || shared.writable === false) {
|
|
138
|
+
throw new Error('设置桥不可写(describe 未就绪或宿主只读)')
|
|
139
|
+
}
|
|
140
|
+
const before = keyOf(shared)[field]
|
|
141
|
+
// 乐观回显:开关即时翻转,权威值由 mutate 响应修正。
|
|
142
|
+
keyOf(shared)[field] = value
|
|
143
|
+
refresh()
|
|
144
|
+
const res = await callHostAny<{ ok?: boolean; revision?: number; namespaces?: Record<string, Record<string, unknown>>; error?: string }>(
|
|
145
|
+
'settings-mutate',
|
|
146
|
+
{ ns, fields: { [field]: value } },
|
|
147
|
+
)
|
|
148
|
+
if (res.ok === true && res.namespaces !== null && typeof res.namespaces === 'object') {
|
|
149
|
+
replaceDoc({
|
|
150
|
+
steward: res.namespaces[STEWARD_NS] ?? shared.steward,
|
|
151
|
+
revision: typeof res.revision === 'number' ? res.revision : shared.revision,
|
|
152
|
+
})
|
|
153
|
+
} else {
|
|
154
|
+
// 失败回滚:开关弹回 = 用户可见的失败信号。
|
|
155
|
+
keyOf(shared)[field] = before
|
|
156
|
+
refresh()
|
|
157
|
+
throw new Error(res.error ?? '设置写入失败')
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let stewardScope: BridgeScope<StewardConfig> | undefined
|
|
164
|
+
|
|
165
|
+
/** 管家卡/管家入口的桥 scope(命名空间 manage-session)。 */
|
|
166
|
+
export function stewardBridgeScope(): BridgeScope<StewardConfig> {
|
|
167
|
+
stewardScope ??= makeBridgeScope<StewardConfig>(STEWARD_NS, (doc) => doc.steward)
|
|
168
|
+
return stewardScope
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 原生句柄 + 桥的合成 scope:原生快照**在任意时刻**观测到 unavailable(出生
|
|
174
|
+
* 死或 loading→死,0.1.7 上 steward 条目的 configForms 实测后者)就地读桥,
|
|
175
|
+
* 不做 apply 期时序猜测。写侧同理:原生 unavailable 即走桥(桥在无 legacy
|
|
176
|
+
* settings 服务的宿主上 set 会显式拒绝,卡片保持只读真值)。双订阅:桥的
|
|
177
|
+
* describe 全页一次(模块单例),代价可忽略。
|
|
178
|
+
*/
|
|
179
|
+
export function withBridgeFallback<V extends object>(
|
|
180
|
+
native: BridgeScope<V> | undefined,
|
|
181
|
+
bridge: BridgeScope<V>,
|
|
182
|
+
): BridgeScope<V> {
|
|
183
|
+
if (native === undefined) return bridge
|
|
184
|
+
return {
|
|
185
|
+
getSnapshot: () => {
|
|
186
|
+
const snap = native.getSnapshot()
|
|
187
|
+
return snap.status === 'unavailable' ? bridge.getSnapshot() : snap
|
|
188
|
+
},
|
|
189
|
+
subscribe: (listener) => {
|
|
190
|
+
const offNative = native.subscribe(listener)
|
|
191
|
+
const offBridge = bridge.subscribe(listener)
|
|
192
|
+
return () => {
|
|
193
|
+
offNative()
|
|
194
|
+
offBridge()
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
set: async (field, value) => {
|
|
198
|
+
if (native.getSnapshot().status === 'unavailable') return bridge.set(field, value)
|
|
199
|
+
return native.set(field, value)
|
|
200
|
+
},
|
|
201
|
+
}
|
|
202
|
+
}
|