@dsh-plus/lifeboat 0.2.11 → 0.2.12

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/lib/client.js CHANGED
@@ -50,28 +50,24 @@ window.__ModuleLoader__.load({
50
50
  }));
51
51
  //#endregion
52
52
  //#region ../shared/src/client/fetch.ts
53
- /**
54
- * 同源自定义端点的最小 fetch 封装(各插件 client/api.ts 的公共收编版)。
55
- * 仅同源(credentials: same-origin),错误统一抛 Error(body.error)。
56
- * @module @dsh-plus/shared/client/fetch
57
- */
58
- async function parse(res) {
53
+ async function parse(res, guard) {
59
54
  const body = await res.json();
60
55
  if (!res.ok) throw new Error(body.error ?? `HTTP ${res.status}`);
56
+ if (guard !== void 0 && !guard(body)) throw new Error(`响应结构不符合预期(${res.url})`);
61
57
  return body;
62
58
  }
63
59
  /** GET 同源 JSON 端点。 */
64
- async function getJson(route) {
65
- return parse(await fetch(route, { credentials: "same-origin" }));
60
+ async function getJson(route, guard) {
61
+ return parse(await fetch(route, { credentials: "same-origin" }), guard);
66
62
  }
67
63
  /** POST 同源 JSON 端点(body 可省略)。 */
68
- async function postJson(route, body) {
64
+ async function postJson(route, body, guard) {
69
65
  return parse(await fetch(route, {
70
66
  method: "POST",
71
67
  credentials: "same-origin",
72
68
  headers: { "content-type": "application/json" },
73
- body: body === void 0 ? void 0 : JSON.stringify(body)
74
- }));
69
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
70
+ }), guard);
75
71
  }
76
72
  var init_fetch = __esmMin((() => {}));
77
73
  //#endregion
@@ -157,6 +153,12 @@ window.__ModuleLoader__.load({
157
153
  //#endregion
158
154
  //#region src/client/health-tab.tsx
159
155
  var health_tab_exports = /* @__PURE__ */ __exportAll({ mountHealthTab: () => mountHealthTab });
156
+ /** 端点应答形状守卫:三字段缺一即判非法(渲染期直接 map journal/quarantined)。 */
157
+ function isStatusResponse(value) {
158
+ if (typeof value !== "object" || value === null) return false;
159
+ const status = value;
160
+ return Array.isArray(status.journal) && Array.isArray(status.quarantined);
161
+ }
160
162
  function HealthTab(props) {
161
163
  const { t } = props;
162
164
  const [status, setStatus] = (0, react.useState)(null);
@@ -165,7 +167,7 @@ window.__ModuleLoader__.load({
165
167
  const [notice, setNotice] = (0, react.useState)(null);
166
168
  const [confirming, setConfirming] = (0, react.useState)(null);
167
169
  const load = (0, react.useCallback)(() => {
168
- getJson(ROUTE_STATUS).then((data) => {
170
+ getJson(ROUTE_STATUS, isStatusResponse).then((data) => {
169
171
  setStatus(data);
170
172
  setFailed(false);
171
173
  }).catch(() => setFailed(true));
package/lib/index.d.ts CHANGED
@@ -17,7 +17,7 @@ type LifeboatConfig = Schemastery.TypeT<typeof Config>;
17
17
  //#endregion
18
18
  //#region src/index.d.ts
19
19
  declare const name = "dsh-plus-lifeboat";
20
- declare const inject: readonly ["settings", "llm"];
20
+ declare const inject: readonly ['settings', 'llm'];
21
21
  declare function apply(ctx: Context, config: LifeboatConfig): void;
22
22
  //#endregion
23
23
  export { Config, apply, inject, name };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dsh-plus/lifeboat",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "dsh-plus service plugin: 故障救生艇——兄弟插件崩溃时自动隔离(写用户 patch 层禁用),llm-pi 缺席时把其配置临时翻译为官方 llm-pi-ai 路由,恢复后自动还原",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "yaml": "^2.9.0",
34
- "@dsh-plus/shared": "0.1.15"
34
+ "@dsh-plus/shared": "0.1.16"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "@deepseek-ai/cordis": "^4.0.2",
@@ -34,6 +34,13 @@ interface StatusResponse {
34
34
  quarantined: string[]
35
35
  }
36
36
 
37
+ /** 端点应答形状守卫:三字段缺一即判非法(渲染期直接 map journal/quarantined)。 */
38
+ function isStatusResponse(value: unknown): value is StatusResponse {
39
+ if (typeof value !== 'object' || value === null) return false
40
+ const status = value as { journal?: unknown; quarantined?: unknown }
41
+ return Array.isArray(status.journal) && Array.isArray(status.quarantined)
42
+ }
43
+
37
44
  interface InventoryRow {
38
45
  id: string
39
46
  enabled: boolean
@@ -130,7 +137,7 @@ function HealthTab(props: { t(key: string): string }): ReactElement {
130
137
  const [confirming, setConfirming] = useState<string | null>(null)
131
138
 
132
139
  const load = useCallback((): void => {
133
- getJson<StatusResponse>(ROUTE_STATUS)
140
+ getJson<StatusResponse>(ROUTE_STATUS, isStatusResponse)
134
141
  .then((data) => {
135
142
  setStatus(data)
136
143
  setFailed(false)