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