dsh-calorie 0.1.2 → 0.1.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/dist/bridge.d.ts +9 -0
- package/dist/bridge.d.ts.map +1 -1
- package/dist/bridge.js +17 -3
- package/dist/bridge.js.map +1 -1
- package/dist/client.js +154 -83
- package/dist/client.js.map +1 -1
- package/dist/contract.d.ts +2 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +7 -0
- package/dist/contract.js.map +1 -1
- package/package.json +1 -1
package/dist/bridge.d.ts
CHANGED
|
@@ -17,6 +17,15 @@ export declare function handleHostCall(key: string, params?: Record<string, unkn
|
|
|
17
17
|
export declare function requestViaHost(host: {
|
|
18
18
|
call(method: string, args: unknown): Promise<unknown>;
|
|
19
19
|
}, key: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
20
|
+
/** spawn 超时毫秒:子进程超期未退即杀掉,转 fetch-failed,绝不无限挂起。
|
|
21
|
+
* #48 真机根因:Desktop 宿主的 process.execPath 是 Electron 二进制,直 spawn 会起 GUI 子进程永不退出。 */
|
|
22
|
+
export declare const SPAWN_TIMEOUT_MS: 20000;
|
|
23
|
+
/** node 可执行体解析(纯函数,execPath 可注入单测)。
|
|
24
|
+
* execPath 是 node 即直用;否则(Electron 宿主)沿用该二进制但加官方 ELECTRON_RUN_AS_NODE 语义当 node 用。 */
|
|
25
|
+
export declare function resolveNodeBin(execPath?: string): {
|
|
26
|
+
readonly bin: string;
|
|
27
|
+
readonly extraEnv: Record<string, string>;
|
|
28
|
+
};
|
|
20
29
|
/** 同步取数:spawn 技能 cmd_read,返回 envelope data(缺失阻断)。 */
|
|
21
30
|
export declare function readViaCli(key: string, params?: Record<string, unknown>): unknown;
|
|
22
31
|
//# sourceMappingURL=bridge.d.ts.map
|
package/dist/bridge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,aAAa,EAAG,eAAwB,CAAC;AACtD,eAAO,MAAM,SAAS,EAAG,6CAAsD,CAAC;AAChF,eAAO,MAAM,gBAAgB,EAAG,oBAA6B,CAAC;AAC9D,eAAO,MAAM,eAAe,EAAG,eAAwB,CAAC;AAExD,eAAO,MAAM,oBAAoB,mJAC4B,CAAC;AAE9D,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,GAAG,UAAU,GAAG,cAAc,CAAC;gBAChE,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM;CAK5D;AAMD,4CAA4C;AAC5C,eAAO,MAAM,aAAa,EAAG,sBAA+B,CAAC;AAe7D,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAOpD;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAEzF;AAED,oCAAoC;AACpC,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnK;AAED,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,aAAa,EAAG,eAAwB,CAAC;AACtD,eAAO,MAAM,SAAS,EAAG,6CAAsD,CAAC;AAChF,eAAO,MAAM,gBAAgB,EAAG,oBAA6B,CAAC;AAC9D,eAAO,MAAM,eAAe,EAAG,eAAwB,CAAC;AAExD,eAAO,MAAM,oBAAoB,mJAC4B,CAAC;AAE9D,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,aAAa,GAAG,cAAc,GAAG,UAAU,GAAG,cAAc,CAAC;gBAChE,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM;CAK5D;AAMD,4CAA4C;AAC5C,eAAO,MAAM,aAAa,EAAG,sBAA+B,CAAC;AAe7D,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,wBAAgB,gBAAgB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAOpD;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAEzF;AAED,oCAAoC;AACpC,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEnK;AAED;kFACkF;AAClF,eAAO,MAAM,gBAAgB,EAAG,KAAe,CAAC;AAEhD;oFACoF;AACpF,wBAAgB,cAAc,CAAC,QAAQ,GAAE,MAAyB,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAGvI;AAED,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAwBrF"}
|
package/dist/bridge.js
CHANGED
|
@@ -62,14 +62,28 @@ export function handleHostCall(key, params = {}) {
|
|
|
62
62
|
export function requestViaHost(host, key, params = {}) {
|
|
63
63
|
return host.call(HOST_CALL_METHOD, { key, params });
|
|
64
64
|
}
|
|
65
|
+
/** spawn 超时毫秒:子进程超期未退即杀掉,转 fetch-failed,绝不无限挂起。
|
|
66
|
+
* #48 真机根因:Desktop 宿主的 process.execPath 是 Electron 二进制,直 spawn 会起 GUI 子进程永不退出。 */
|
|
67
|
+
export const SPAWN_TIMEOUT_MS = 20_000;
|
|
68
|
+
/** node 可执行体解析(纯函数,execPath 可注入单测)。
|
|
69
|
+
* execPath 是 node 即直用;否则(Electron 宿主)沿用该二进制但加官方 ELECTRON_RUN_AS_NODE 语义当 node 用。 */
|
|
70
|
+
export function resolveNodeBin(execPath = process.execPath) {
|
|
71
|
+
if (/(^|[\\/])node(\.exe)?$/i.test(execPath))
|
|
72
|
+
return { bin: execPath, extraEnv: {} };
|
|
73
|
+
return { bin: execPath, extraEnv: { ELECTRON_RUN_AS_NODE: '1' } };
|
|
74
|
+
}
|
|
65
75
|
/** 同步取数:spawn 技能 cmd_read,返回 envelope data(缺失阻断)。 */
|
|
66
76
|
export function readViaCli(key, params = {}) {
|
|
67
77
|
const bin = cliPath();
|
|
68
78
|
assertCliPresent(bin);
|
|
69
|
-
const node =
|
|
70
|
-
const r = spawnSync(node, [bin, key, '--params', JSON.stringify(params)], { encoding: 'utf8' });
|
|
71
|
-
if (r.error)
|
|
79
|
+
const { bin: node, extraEnv } = resolveNodeBin();
|
|
80
|
+
const r = spawnSync(node, [bin, key, '--params', JSON.stringify(params)], { encoding: 'utf8', timeout: SPAWN_TIMEOUT_MS, env: { ...process.env, ...extraEnv } });
|
|
81
|
+
if (r.error) {
|
|
82
|
+
if (r.error?.code === 'ETIMEDOUT') {
|
|
83
|
+
throw new SkillBridgeError('fetch-failed', '出口超 ' + String(SPAWN_TIMEOUT_MS / 1000) + 's 未退,已杀掉(宿主非 node 时见 resolveNodeBin)');
|
|
84
|
+
}
|
|
72
85
|
throw new SkillBridgeError('fetch-failed', '出口 spawn 失败:' + r.error.message);
|
|
86
|
+
}
|
|
73
87
|
if (r.status !== 0) {
|
|
74
88
|
const tail = String(r.stderr ?? '').trim().split('\n').pop() ?? '';
|
|
75
89
|
throw new SkillBridgeError('fetch-failed', '出口非 0(' + String(r.status) + '):' + tail);
|
package/dist/bridge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,aAAa,GAAG,eAAwB,CAAC;AACtD,MAAM,CAAC,MAAM,SAAS,GAAG,6CAAsD,CAAC;AAChF,MAAM,CAAC,MAAM,gBAAgB,GAAG,oBAA6B,CAAC;AAC9D,MAAM,CAAC,MAAM,eAAe,GAAG,eAAwB,CAAC;AAExD,MAAM,CAAC,MAAM,oBAAoB,GAC/B,2DAA2D,CAAC;AAE9D,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAA+D;IAC5E,YAAY,IAA8B,EAAE,OAAe;QACzD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,SAAS,QAAQ;IACf,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACzE,CAAC;AAED,4CAA4C;AAC5C,MAAM,CAAC,MAAM,aAAa,GAAG,sBAA+B,CAAC;AAE7D;;;6BAG6B;AAC7B,SAAS,eAAe;IACtB,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,aAAa,GAAG,eAAe,CAAC,CAAC;QACxF,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,aAAa,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,OAAO,eAAe,EAAE,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAa;IAC5C,MAAM,CAAC,GAAG,IAAI,IAAI,OAAO,EAAE,CAAC;IAC5B,IAAI,CAAC;QACH,UAAU,CAAC,CAAC,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,gBAAgB,CAAC,aAAa,EAAE,iBAAiB,GAAG,CAAC,CAAC,CAAC;IACnE,CAAC;AACH,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,SAAkC,EAAE;IAC9E,OAAO,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACjC,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,cAAc,CAAC,IAA+D,EAAE,GAAW,EAAE,SAAkC,EAAE;IAC/I,OAAO,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,SAAkC,EAAE;IAC1E,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtB,MAAM,
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -39,6 +39,12 @@ window.__ModuleLoader__.load({
|
|
|
39
39
|
const RPC_ENDPOINT_READ = "read";
|
|
40
40
|
/** 面板默认读键(与 HITL 一致性对比同键)。 */
|
|
41
41
|
const DEFAULT_READ_KEY = "calorie.view.home";
|
|
42
|
+
/** 信封守卫:供 client 拆包前校验(port 拥有信封定义,守卫住 port)。 */
|
|
43
|
+
function isRpcResult(raw) {
|
|
44
|
+
if (typeof raw !== "object" || raw === null) return false;
|
|
45
|
+
const ok = raw.ok;
|
|
46
|
+
return ok === true || ok === false;
|
|
47
|
+
}
|
|
42
48
|
//#endregion
|
|
43
49
|
//#region src/slot.ts
|
|
44
50
|
const SLOT_TITLE = "卡路里";
|
|
@@ -55,12 +61,19 @@ window.__ModuleLoader__.load({
|
|
|
55
61
|
/** dsh-calorie client 适配器(六边形:port=contract+dsh-ctx 镜像,adapter=本文件)。
|
|
56
62
|
*
|
|
57
63
|
* 产物经 tsdown 打成 loader 工厂包(browser/CJS,见 tsdown.config.ts)。
|
|
58
|
-
* 本文件传递闭包:禁 node、禁 DOM 直写(document/window/process
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
64
|
+
* 本文件传递闭包:禁 node、禁 DOM 直写(document/window/process)。
|
|
65
|
+
* 注册两处(grill 定案,新术语见 CONTEXT.md):
|
|
66
|
+
* (a) 技能设置页 → 爱生活页签槽(总管声明的 children,各技能自研自家配置页);
|
|
67
|
+
* (b) 技能功能页 → sidebar槽(better-sidebar 服务,非 DSH slot;软依赖,
|
|
68
|
+
* 缺席即跳过,不断链。DSH右侧槽 + sidebar.footer.action 彻底出局,已删)。
|
|
69
|
+
* 有界重试例外依据(唯一允许的定时器):注册重试非数据轮询,照抄
|
|
70
|
+
* dsh-mattpocock-skills-deck/src/client/panelAssembly.js:100-108
|
|
71
|
+
* (betterSidebar 服务可能晚于本模块到达,最多 10 次、间隔 1000ms;卸载清理)。
|
|
72
|
+
* 只用 dsh-ctx 镜像内成员(slots.inject/register、connection.rpc.call 经闭包现取、
|
|
73
|
+
* betterSidebar 经 ctx.get 运行时取);取数只经 connection.rpc.call 进 host 通道;
|
|
74
|
+
* 缺席/错误纯条件渲染,不返空冒充。组件 React.createElement 手写,不引入 JSX。
|
|
62
75
|
*/
|
|
63
|
-
const inject = ["slots"
|
|
76
|
+
const inject = ["slots"];
|
|
64
77
|
/** 本地今日 YYYY-MM-DD(与面板默认读键的 date 参数同形)。 */
|
|
65
78
|
function todayString() {
|
|
66
79
|
const d = /* @__PURE__ */ new Date();
|
|
@@ -83,111 +96,169 @@ window.__ModuleLoader__.load({
|
|
|
83
96
|
}
|
|
84
97
|
return String(value);
|
|
85
98
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
99
|
+
/** 单次读数(纯异步函数,无 hook;挂载期调用一次,无轮询)。 */
|
|
100
|
+
async function fetchRead(call, key, params) {
|
|
101
|
+
if (typeof call !== "function") return {
|
|
102
|
+
ok: false,
|
|
103
|
+
absent: true,
|
|
104
|
+
message: "宿主连接缺席:connection.rpc.call 不可用"
|
|
105
|
+
};
|
|
106
|
+
let result;
|
|
107
|
+
try {
|
|
108
|
+
const raw = await call(RPC_CHANNEL, RPC_ENDPOINT_READ, {
|
|
109
|
+
key,
|
|
110
|
+
params
|
|
111
|
+
}, AbortSignal.timeout(2e4));
|
|
112
|
+
if (!isRpcResult(raw)) return {
|
|
113
|
+
ok: false,
|
|
114
|
+
absent: false,
|
|
115
|
+
message: "回执信封异常(非 ok 信封)"
|
|
116
|
+
};
|
|
117
|
+
result = raw;
|
|
118
|
+
} catch (e) {
|
|
119
|
+
if (e instanceof Error && e.name === "AbortError") return {
|
|
120
|
+
ok: false,
|
|
121
|
+
absent: false,
|
|
122
|
+
message: "取数超时(20s):宿主未回,先查宿主日志与 DB 环境"
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
absent: false,
|
|
127
|
+
message: `取数失败:${e instanceof Error ? e.message : String(e)}`
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (!result.ok) {
|
|
131
|
+
const code = result.error?.code ?? "unknown";
|
|
132
|
+
const message = result.error?.message ?? "";
|
|
133
|
+
if (code === "missing-cli") return {
|
|
134
|
+
ok: false,
|
|
135
|
+
absent: true,
|
|
136
|
+
message: `技能出口缺席(missing-cli):${message}`
|
|
137
|
+
};
|
|
138
|
+
return {
|
|
139
|
+
ok: false,
|
|
140
|
+
absent: false,
|
|
141
|
+
message: `取数失败(${code}):${message}`
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const text = extractTotal(result.value);
|
|
145
|
+
if (text === null) return {
|
|
146
|
+
ok: false,
|
|
147
|
+
absent: false,
|
|
148
|
+
message: "取数为空(不冒充正常)"
|
|
149
|
+
};
|
|
150
|
+
return {
|
|
151
|
+
ok: true,
|
|
152
|
+
text
|
|
153
|
+
};
|
|
90
154
|
}
|
|
91
|
-
/**
|
|
92
|
-
function
|
|
155
|
+
/** 技能功能页:sidebar槽里展开的干活区(查数调数,不管配置)。挂载期一次 RPC。 */
|
|
156
|
+
function CalorieWork(props) {
|
|
93
157
|
const [state, setState] = react.useState({ kind: "loading" });
|
|
94
158
|
react.useEffect(() => {
|
|
95
159
|
let alive = true;
|
|
96
160
|
const date = todayString();
|
|
97
|
-
const call = props.call;
|
|
98
161
|
(async () => {
|
|
99
|
-
|
|
100
|
-
if (alive) setState({
|
|
101
|
-
kind: "absent",
|
|
102
|
-
message: "宿主连接缺席:connection.rpc.call 不可用"
|
|
103
|
-
});
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
let result;
|
|
107
|
-
try {
|
|
108
|
-
const raw = await call(RPC_CHANNEL, RPC_ENDPOINT_READ, {
|
|
109
|
-
key: DEFAULT_READ_KEY,
|
|
110
|
-
params: { date }
|
|
111
|
-
});
|
|
112
|
-
if (!isEnvelope(raw)) {
|
|
113
|
-
if (alive) setState({
|
|
114
|
-
kind: "error",
|
|
115
|
-
message: "回执信封异常(非 ok 信封)"
|
|
116
|
-
});
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
result = raw;
|
|
120
|
-
} catch (e) {
|
|
121
|
-
if (alive) setState({
|
|
122
|
-
kind: "error",
|
|
123
|
-
message: `取数失败:${e instanceof Error ? e.message : String(e)}`
|
|
124
|
-
});
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
162
|
+
const r = await fetchRead(props.getCall(), DEFAULT_READ_KEY, { date });
|
|
127
163
|
if (!alive) return;
|
|
128
|
-
if (
|
|
129
|
-
const code = result.error?.code ?? "unknown";
|
|
130
|
-
const message = result.error?.message ?? "";
|
|
131
|
-
if (code === "missing-cli") setState({
|
|
132
|
-
kind: "absent",
|
|
133
|
-
message: `技能出口缺席(missing-cli):${message}`
|
|
134
|
-
});
|
|
135
|
-
else setState({
|
|
136
|
-
kind: "error",
|
|
137
|
-
message: `取数失败(${code}):${message}`
|
|
138
|
-
});
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
const total = extractTotal(result.value);
|
|
142
|
-
if (total === null) {
|
|
143
|
-
setState({
|
|
144
|
-
kind: "error",
|
|
145
|
-
message: "取数为空(不冒充正常)"
|
|
146
|
-
});
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
setState({
|
|
164
|
+
if (r.ok) setState({
|
|
150
165
|
kind: "data",
|
|
151
166
|
date,
|
|
152
|
-
total
|
|
167
|
+
total: r.text
|
|
168
|
+
});
|
|
169
|
+
else if (r.absent) setState({
|
|
170
|
+
kind: "absent",
|
|
171
|
+
message: r.message
|
|
172
|
+
});
|
|
173
|
+
else setState({
|
|
174
|
+
kind: "error",
|
|
175
|
+
message: r.message
|
|
153
176
|
});
|
|
154
177
|
})();
|
|
155
178
|
return () => {
|
|
156
179
|
alive = false;
|
|
157
180
|
};
|
|
158
|
-
}, [props.
|
|
181
|
+
}, [props.getCall]);
|
|
159
182
|
if (state.kind === "loading") return react.createElement("div", null, "卡路里加载中…");
|
|
160
183
|
if (state.kind === "data") return react.createElement("div", null, react.createElement("div", null, `卡路里 · ${state.date}`), react.createElement("div", null, `total ${state.total}`));
|
|
161
184
|
return react.createElement("div", null, state.message);
|
|
162
185
|
}
|
|
163
|
-
/** 边栏入口:标题 + 面板同体,紧凑只读。 */
|
|
164
|
-
function CalorieAction(props) {
|
|
165
|
-
return react.createElement("div", null, react.createElement("div", null, SLOT_TITLE), react.createElement(CaloriePanel, { call: props.call }));
|
|
166
|
-
}
|
|
167
186
|
function controlLabel(control) {
|
|
168
187
|
if (control === "switch") return "开关(缺省启用,只读)";
|
|
169
188
|
if (control === "text") return "文本(只读)";
|
|
170
189
|
if (control === "number") return "数字(只读)";
|
|
171
190
|
return `${control}(只读)`;
|
|
172
191
|
}
|
|
173
|
-
/**
|
|
174
|
-
function
|
|
175
|
-
|
|
192
|
+
/** 技能设置页:爱生活页签条下的一页(只读设置行 + 状态读数,禁做假开关)。 */
|
|
193
|
+
function CalorieConfig(props) {
|
|
194
|
+
const [state, setState] = react.useState({ kind: "loading" });
|
|
195
|
+
react.useEffect(() => {
|
|
196
|
+
let alive = true;
|
|
197
|
+
const date = todayString();
|
|
198
|
+
(async () => {
|
|
199
|
+
const r = await fetchRead(props.getCall(), DEFAULT_READ_KEY, { date });
|
|
200
|
+
if (!alive) return;
|
|
201
|
+
if (r.ok) setState({
|
|
202
|
+
kind: "data",
|
|
203
|
+
date,
|
|
204
|
+
total: r.text
|
|
205
|
+
});
|
|
206
|
+
else if (r.absent) setState({
|
|
207
|
+
kind: "absent",
|
|
208
|
+
message: r.message
|
|
209
|
+
});
|
|
210
|
+
else setState({
|
|
211
|
+
kind: "error",
|
|
212
|
+
message: r.message
|
|
213
|
+
});
|
|
214
|
+
})();
|
|
215
|
+
return () => {
|
|
216
|
+
alive = false;
|
|
217
|
+
};
|
|
218
|
+
}, [props.getCall]);
|
|
219
|
+
const status = state.kind === "loading" ? "状态读取中…" : state.kind === "data" ? `今日 ${state.date} · total ${state.total}` : state.message;
|
|
220
|
+
return react.createElement("div", null, react.createElement("div", null, status), react.createElement("div", null, SETTING_ROWS.map((row) => react.createElement("div", { key: row.key }, `${row.title} · ${controlLabel(row.control)}`))));
|
|
176
221
|
}
|
|
177
222
|
function apply(ctx) {
|
|
178
|
-
const
|
|
179
|
-
ctx.slots.inject("
|
|
180
|
-
name: "
|
|
223
|
+
const getCall = () => ctx.connection?.rpc?.call ?? null;
|
|
224
|
+
ctx.slots.inject("ilife.config-tab", () => ctx.slots.register({
|
|
225
|
+
name: "ilife.config-tab",
|
|
181
226
|
id: "dsh-calorie",
|
|
182
|
-
order:
|
|
183
|
-
label: () => SLOT_TITLE
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
227
|
+
order: 75,
|
|
228
|
+
label: () => SLOT_TITLE
|
|
229
|
+
}, () => react.createElement(CalorieConfig, { getCall })));
|
|
230
|
+
const ensureWorkTab = () => {
|
|
231
|
+
try {
|
|
232
|
+
const get = ctx.get;
|
|
233
|
+
const bs = typeof get === "function" ? get("betterSidebar") : void 0;
|
|
234
|
+
if (!bs || typeof bs.registerTab !== "function") return false;
|
|
235
|
+
const dispose = bs.registerTab({
|
|
236
|
+
id: "ilife-calorie:work",
|
|
237
|
+
title: "卡路里",
|
|
238
|
+
order: 61,
|
|
239
|
+
single: true,
|
|
240
|
+
component: () => react.createElement(CalorieWork, { getCall })
|
|
241
|
+
});
|
|
242
|
+
ctx.effect(() => () => {
|
|
243
|
+
try {
|
|
244
|
+
dispose();
|
|
245
|
+
} catch {}
|
|
246
|
+
}, "dsh-calorie: sidebar work tab");
|
|
247
|
+
return true;
|
|
248
|
+
} catch {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
if (!ensureWorkTab()) {
|
|
253
|
+
let tries = 0;
|
|
254
|
+
const timer = setInterval(() => {
|
|
255
|
+
tries++;
|
|
256
|
+
if (ensureWorkTab() || tries >= 10) clearInterval(timer);
|
|
257
|
+
}, 1e3);
|
|
258
|
+
ctx.effect(() => () => {
|
|
259
|
+
clearInterval(timer);
|
|
260
|
+
}, "dsh-calorie: sidebar work tab retry");
|
|
261
|
+
}
|
|
191
262
|
}
|
|
192
263
|
//#endregion
|
|
193
264
|
exports.apply = apply;
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["React"],"sources":["../src/contract.ts","../src/slot.ts","../src/settings.ts","../src/client.ts"],"sourcesContent":["/** dsh-calorie 端口(port):DSH 世界与卡路里 hexagon 之间的契约。\n *\n * browser 安全:零 node 导入。host 适配器(注册 RPC 通道)与 client\n * 适配器(调用 RPC)共用同一份定义;单测与 loader 回路以此为准。\n * 通道命名遵守宿主约束:单段且不等于 /api(见 cookbook「RPC 通道」)。\n */\nexport const RPC_CHANNEL = '/ilife-calorie' as const;\nexport const RPC_ENDPOINT_READ = 'read' as const;\n\n/** 读请求载荷:与技能 CLI 的 key/params 同形(见 skill-calorie SKILL.md)。 */\nexport interface ReadPayload {\n readonly key: string;\n readonly params: Record<string, unknown>;\n}\n\nexport interface RpcError {\n readonly code: string;\n readonly message: string;\n readonly details: Record<string, unknown>;\n}\n\nexport type RpcResult =\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly error: RpcError };\n\nexport function ok(value: unknown): RpcResult {\n return { ok: true, value };\n}\n\nexport function fail(code: string, message: string): RpcResult {\n return { ok: false, error: { code, message, details: {} } };\n}\n\n/** 载荷校验:形状不对返回 null,由调用方按 bad-request 回绝(不抛)。 */\nexport function parseReadPayload(raw: unknown): ReadPayload | null {\n if (typeof raw !== 'object' || raw === null) return null;\n const { key, params } = raw as { key?: unknown; params?: unknown };\n if (typeof key !== 'string' || key.length === 0) return null;\n if (typeof params !== 'object' || params === null || Array.isArray(params)) return null;\n return { key, params: params as Record<string, unknown> };\n}\n\n/** 面板默认读键(与 HITL 一致性对比同键)。 */\nexport const DEFAULT_READ_KEY = 'calorie.view.home' as const;\n","/** dsh-calorie 槽位描述(P10 脚手架,P3 #4 定案)。\n *\n * 写死槽位原生组件:id 命名空间 ilife:calorie,order 75。\n * 注册与打开只走 registerTab/openTab 的路径导航;子页后缀(如 ilife:calorie:*)\n * 只在主面板内导航,不占栏。缺席纯条件渲染,不轮询(本文件无定时器;\n * 有界重试归一由 base-render 拥有,单品不自建)。\n */\n\nexport const SKILL = 'calorie' as const;\nexport const SLOT_ID = 'ilife:calorie' as const;\nexport const SLOT_ORDER = 75 as const;\nexport const SLOT_TITLE = '卡路里' as const;\nexport const PLUGIN = 'dsh-calorie' as const;\nexport const MANAGER_PLUGIN = 'dsh-life-pack' as const;\n\nexport interface SlotDescriptor {\n readonly skill: string;\n readonly slotId: string;\n readonly order: number;\n readonly title: string;\n readonly kind: 'page';\n}\n\nexport function slotDescriptor(): SlotDescriptor {\n return { skill: SKILL, slotId: SLOT_ID, order: SLOT_ORDER, title: SLOT_TITLE, kind: 'page' };\n}\n\n/** 写死原生组件描述(纯数据,无按需加载,无外嵌页)。 */\nexport const TAB_COMPONENT = { kind: 'native', name: 'CaloriePanel' } as const;\n\nexport interface TabsPort {\n hasTabs(): boolean;\n registerTab(entry: { readonly id: string; readonly title: string; readonly order: number; readonly single: boolean; readonly component: unknown }): () => void;\n openTab(seed: { readonly type: string; readonly path: string }, scope?: { readonly sessionId?: string }): void;\n}\n\n/** 自注册:幂等(调用方持 disposer),失败返回 false 由调用方决定重试。 */\nexport function registerSingle(port: TabsPort): (() => void) | null {\n if (!port.hasTabs()) return null;\n return port.registerTab({ id: SLOT_ID, title: SLOT_TITLE, order: SLOT_ORDER, single: true, component: TAB_COMPONENT });\n}\n\n/** 内容型打开:path seed,折叠面板自动展开。 */\nexport function openSingle(port: TabsPort, sessionId?: string): void {\n if (!port.hasTabs()) return;\n port.openTab({ type: SLOT_ID, path: SLOT_ID }, sessionId ? { sessionId } : undefined);\n}\n","/** dsh-calorie 设置页(P10 脚手架:设置页住单品包)。\n *\n * 一次 registerTab,两处呈现:边栏 + 号菜单多一项,DSH 设置页侧边卡片\n * 分区自动多一张小卡片(注册表驱动、无硬编码,开关缺省启用)。\n * 本文件只声明本单品自己的设置行;总管不声明设置(见 boundaries 断言)。\n */\n\nexport const SETTINGS_OWNER = 'dsh-calorie' as const;\nexport const SETTINGS_SLOT = 'ilife:calorie' as const;\n\nexport interface SettingRow {\n readonly key: string;\n readonly title: string;\n readonly control: 'switch' | 'text' | 'number';\n}\n\n/** 脚手架期唯一设置行:总开关(缺省启用,与官方关闭语义对齐)。 */\nexport const SETTING_ROWS: readonly SettingRow[] = [\n { key: 'enabled', title: '卡路里面板启用', control: 'switch' },\n];\n","/** dsh-calorie client 适配器(六边形:port=contract+dsh-ctx 镜像,adapter=本文件)。\n *\n * 产物经 tsdown 打成 loader 工厂包(browser/CJS,见 tsdown.config.ts)。\n * 本文件传递闭包:禁 node、禁 DOM 直写(document/window/process)、禁定时器/轮询。\n * 只用 dsh-ctx 镜像内成员(slots.inject/register、connection.rpc.call);\n * 取数只经 connection.rpc.call 进 host 通道;缺席/错误纯条件渲染,不返空冒充。\n * 组件 React.createElement 手写,不引入 JSX 构建复杂度。\n */\nimport * as React from 'react';\nimport { RPC_CHANNEL, RPC_ENDPOINT_READ, DEFAULT_READ_KEY } from './contract.js';\nimport { SLOT_TITLE } from './slot.js';\nimport { SETTING_ROWS } from './settings.js';\nimport type { ClientCtx, RpcCallResult } from './dsh-ctx.js';\n\nexport const inject = ['slots', 'connection'];\n\ntype PanelState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'data'; readonly date: string; readonly total: string }\n | { readonly kind: 'absent'; readonly message: string }\n | { readonly kind: 'error'; readonly message: string };\n\n/** 本地今日 YYYY-MM-DD(与面板默认读键的 date 参数同形)。 */\nfunction todayString(): string {\n const d = new Date();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${d.getFullYear()}-${m}-${day}`;\n}\n\n/** 数据态取值:有 total 显示 total,有值无 total 显示原值 JSON,空值返回 null(由调用方进错误态)。 */\nfunction extractTotal(value: unknown): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === 'string') return value.length > 0 ? value : null;\n if (typeof value === 'object') {\n const total = (value as { total?: unknown }).total;\n if (total !== null && total !== undefined) return String(total);\n try {\n return JSON.stringify(value);\n } catch {\n return null;\n }\n }\n return String(value);\n}\n\nfunction isEnvelope(raw: unknown): raw is RpcCallResult {\n if (typeof raw !== 'object' || raw === null) return false;\n const ok = (raw as { ok?: unknown }).ok;\n return ok === true || ok === false;\n}\n\n/** 卡路里面板:挂载期一次 RPC,无轮询;loading / 数据 / 缺席或错误三态。 */\nfunction CaloriePanel(props: { call: unknown }): React.ReactElement {\n const [state, setState] = React.useState<PanelState>({ kind: 'loading' });\n React.useEffect(() => {\n let alive = true;\n const date = todayString();\n const call = props.call;\n (async () => {\n // extractRpc 同式守卫:非函数即缺席态。\n if (typeof call !== 'function') {\n if (alive) setState({ kind: 'absent', message: '宿主连接缺席:connection.rpc.call 不可用' });\n return;\n }\n let result: RpcCallResult;\n try {\n const raw: unknown = await call(RPC_CHANNEL, RPC_ENDPOINT_READ, {\n key: DEFAULT_READ_KEY,\n params: { date },\n });\n if (!isEnvelope(raw)) {\n if (alive) setState({ kind: 'error', message: '回执信封异常(非 ok 信封)' });\n return;\n }\n result = raw;\n } catch (e) {\n if (alive) setState({ kind: 'error', message: `取数失败:${e instanceof Error ? e.message : String(e)}` });\n return;\n }\n if (!alive) return;\n if (!result.ok) {\n const code = result.error?.code ?? 'unknown';\n const message = result.error?.message ?? '';\n if (code === 'missing-cli') {\n setState({ kind: 'absent', message: `技能出口缺席(missing-cli):${message}` });\n } else {\n setState({ kind: 'error', message: `取数失败(${code}):${message}` });\n }\n return;\n }\n const total = extractTotal(result.value);\n if (total === null) {\n setState({ kind: 'error', message: '取数为空(不冒充正常)' });\n return;\n }\n setState({ kind: 'data', date, total });\n })();\n return () => {\n alive = false;\n };\n }, [props.call]);\n if (state.kind === 'loading') {\n return React.createElement('div', null, '卡路里加载中…');\n }\n if (state.kind === 'data') {\n return React.createElement(\n 'div',\n null,\n React.createElement('div', null, `卡路里 · ${state.date}`),\n React.createElement('div', null, `total ${state.total}`),\n );\n }\n return React.createElement('div', null, state.message);\n}\n\n/** 边栏入口:标题 + 面板同体,紧凑只读。 */\nfunction CalorieAction(props: { call: unknown }): React.ReactElement {\n return React.createElement(\n 'div',\n null,\n React.createElement('div', null, SLOT_TITLE),\n React.createElement(CaloriePanel, { call: props.call }),\n );\n}\n\nfunction controlLabel(control: string): string {\n if (control === 'switch') return '开关(缺省启用,只读)';\n if (control === 'text') return '文本(只读)';\n if (control === 'number') return '数字(只读)';\n return `${control}(只读)`;\n}\n\n/** 设置卡:面板 + 设置行只读呈现(禁做假开关,无交互控件)。 */\nfunction CalorieSettings(props: { call: unknown }): React.ReactElement {\n return React.createElement(\n 'div',\n null,\n React.createElement(CaloriePanel, { call: props.call }),\n React.createElement(\n 'div',\n null,\n SETTING_ROWS.map((row) =>\n React.createElement('div', { key: row.key }, `${row.title} · ${controlLabel(row.control)}`),\n ),\n ),\n );\n}\n\nexport function apply(ctx: ClientCtx): void {\n // 调用口在注册期快照进组件 props(extractRpc 同式,非函数由组件判缺席)。\n const call: unknown = ctx.connection?.rpc?.call ?? null;\n ctx.slots.inject('settings.section', () =>\n ctx.slots.register(\n {\n name: 'settings.section',\n id: 'dsh-calorie',\n order: 23,\n label: () => SLOT_TITLE,\n inject: () => ({}),\n },\n () => React.createElement(CalorieSettings, { call }),\n ),\n );\n ctx.slots.inject('sidebar.footer.action', () =>\n ctx.slots.register(\n {\n name: 'sidebar.footer.action',\n id: 'dsh-calorie',\n order: 75,\n },\n () => React.createElement(CalorieAction, { call }),\n ),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["React"],"sources":["../src/contract.ts","../src/slot.ts","../src/settings.ts","../src/client.ts"],"sourcesContent":["/** dsh-calorie 端口(port):DSH 世界与卡路里 hexagon 之间的契约。\n *\n * browser 安全:零 node 导入。host 适配器(注册 RPC 通道)与 client\n * 适配器(调用 RPC)共用同一份定义;单测与 loader 回路以此为准。\n * 通道命名遵守宿主约束:单段且不等于 /api(见 cookbook「RPC 通道」)。\n */\nexport const RPC_CHANNEL = '/ilife-calorie' as const;\nexport const RPC_ENDPOINT_READ = 'read' as const;\n\n/** 读请求载荷:与技能 CLI 的 key/params 同形(见 skill-calorie SKILL.md)。 */\nexport interface ReadPayload {\n readonly key: string;\n readonly params: Record<string, unknown>;\n}\n\nexport interface RpcError {\n readonly code: string;\n readonly message: string;\n readonly details: Record<string, unknown>;\n}\n\nexport type RpcResult =\n | { readonly ok: true; readonly value: unknown }\n | { readonly ok: false; readonly error: RpcError };\n\nexport function ok(value: unknown): RpcResult {\n return { ok: true, value };\n}\n\nexport function fail(code: string, message: string): RpcResult {\n return { ok: false, error: { code, message, details: {} } };\n}\n\n/** 载荷校验:形状不对返回 null,由调用方按 bad-request 回绝(不抛)。 */\nexport function parseReadPayload(raw: unknown): ReadPayload | null {\n if (typeof raw !== 'object' || raw === null) return null;\n const { key, params } = raw as { key?: unknown; params?: unknown };\n if (typeof key !== 'string' || key.length === 0) return null;\n if (typeof params !== 'object' || params === null || Array.isArray(params)) return null;\n return { key, params: params as Record<string, unknown> };\n}\n\n/** 面板默认读键(与 HITL 一致性对比同键)。 */\nexport const DEFAULT_READ_KEY = 'calorie.view.home' as const;\n\n/** 信封守卫:供 client 拆包前校验(port 拥有信封定义,守卫住 port)。 */\nexport function isRpcResult(raw: unknown): raw is RpcResult {\n if (typeof raw !== 'object' || raw === null) return false;\n const ok = (raw as { ok?: unknown }).ok;\n return ok === true || ok === false;\n}\n","/** dsh-calorie 槽位描述(P10 脚手架,P3 #4 定案)。\n *\n * 写死槽位原生组件:id 命名空间 ilife:calorie,order 75。\n * 注册与打开只走 registerTab/openTab 的路径导航;子页后缀(如 ilife:calorie:*)\n * 只在主面板内导航,不占栏。缺席纯条件渲染,不轮询(本文件无定时器;\n * 有界重试归一由 base-render 拥有,单品不自建)。\n */\n\nexport const SKILL = 'calorie' as const;\nexport const SLOT_ID = 'ilife:calorie' as const;\nexport const SLOT_ORDER = 75 as const;\nexport const SLOT_TITLE = '卡路里' as const;\nexport const PLUGIN = 'dsh-calorie' as const;\nexport const MANAGER_PLUGIN = 'dsh-life-pack' as const;\n\nexport interface SlotDescriptor {\n readonly skill: string;\n readonly slotId: string;\n readonly order: number;\n readonly title: string;\n readonly kind: 'page';\n}\n\nexport function slotDescriptor(): SlotDescriptor {\n return { skill: SKILL, slotId: SLOT_ID, order: SLOT_ORDER, title: SLOT_TITLE, kind: 'page' };\n}\n\n/** 写死原生组件描述(纯数据,无按需加载,无外嵌页)。 */\nexport const TAB_COMPONENT = { kind: 'native', name: 'CaloriePanel' } as const;\n\nexport interface TabsPort {\n hasTabs(): boolean;\n registerTab(entry: { readonly id: string; readonly title: string; readonly order: number; readonly single: boolean; readonly component: unknown }): () => void;\n openTab(seed: { readonly type: string; readonly path: string }, scope?: { readonly sessionId?: string }): void;\n}\n\n/** 自注册:幂等(调用方持 disposer),失败返回 false 由调用方决定重试。 */\nexport function registerSingle(port: TabsPort): (() => void) | null {\n if (!port.hasTabs()) return null;\n return port.registerTab({ id: SLOT_ID, title: SLOT_TITLE, order: SLOT_ORDER, single: true, component: TAB_COMPONENT });\n}\n\n/** 内容型打开:path seed,折叠面板自动展开。 */\nexport function openSingle(port: TabsPort, sessionId?: string): void {\n if (!port.hasTabs()) return;\n port.openTab({ type: SLOT_ID, path: SLOT_ID }, sessionId ? { sessionId } : undefined);\n}\n","/** dsh-calorie 设置页(P10 脚手架:设置页住单品包)。\n *\n * 一次 registerTab,两处呈现:边栏 + 号菜单多一项,DSH 设置页侧边卡片\n * 分区自动多一张小卡片(注册表驱动、无硬编码,开关缺省启用)。\n * 本文件只声明本单品自己的设置行;总管不声明设置(见 boundaries 断言)。\n */\n\nexport const SETTINGS_OWNER = 'dsh-calorie' as const;\nexport const SETTINGS_SLOT = 'ilife:calorie' as const;\n\nexport interface SettingRow {\n readonly key: string;\n readonly title: string;\n readonly control: 'switch' | 'text' | 'number';\n}\n\n/** 脚手架期唯一设置行:总开关(缺省启用,与官方关闭语义对齐)。 */\nexport const SETTING_ROWS: readonly SettingRow[] = [\n { key: 'enabled', title: '卡路里面板启用', control: 'switch' },\n];\n","/** dsh-calorie client 适配器(六边形:port=contract+dsh-ctx 镜像,adapter=本文件)。\n *\n * 产物经 tsdown 打成 loader 工厂包(browser/CJS,见 tsdown.config.ts)。\n * 本文件传递闭包:禁 node、禁 DOM 直写(document/window/process)。\n * 注册两处(grill 定案,新术语见 CONTEXT.md):\n * (a) 技能设置页 → 爱生活页签槽(总管声明的 children,各技能自研自家配置页);\n * (b) 技能功能页 → sidebar槽(better-sidebar 服务,非 DSH slot;软依赖,\n * 缺席即跳过,不断链。DSH右侧槽 + sidebar.footer.action 彻底出局,已删)。\n * 有界重试例外依据(唯一允许的定时器):注册重试非数据轮询,照抄\n * dsh-mattpocock-skills-deck/src/client/panelAssembly.js:100-108\n * (betterSidebar 服务可能晚于本模块到达,最多 10 次、间隔 1000ms;卸载清理)。\n * 只用 dsh-ctx 镜像内成员(slots.inject/register、connection.rpc.call 经闭包现取、\n * betterSidebar 经 ctx.get 运行时取);取数只经 connection.rpc.call 进 host 通道;\n * 缺席/错误纯条件渲染,不返空冒充。组件 React.createElement 手写,不引入 JSX。\n */\nimport * as React from 'react';\nimport { RPC_CHANNEL, RPC_ENDPOINT_READ, DEFAULT_READ_KEY, isRpcResult } from './contract.js';\nimport { SLOT_TITLE } from './slot.js';\nimport { SETTING_ROWS } from './settings.js';\nimport type { ClientCtx, RpcCallResult } from './dsh-ctx.js';\n\nexport const inject = ['slots'];\n\n/** 调用口取用器:每次取数时现取(connection 后到也不永久缺席)。 */\nexport type GetCall = () => unknown;\n\ntype PanelState =\n | { readonly kind: 'loading' }\n | { readonly kind: 'data'; readonly date: string; readonly total: string }\n | { readonly kind: 'absent'; readonly message: string }\n | { readonly kind: 'error'; readonly message: string };\n\n/** 本地今日 YYYY-MM-DD(与面板默认读键的 date 参数同形)。 */\nfunction todayString(): string {\n const d = new Date();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${d.getFullYear()}-${m}-${day}`;\n}\n\n/** 数据态取值:有 total 显示 total,有值无 total 显示原值 JSON,空值返回 null(由调用方进错误态)。 */\nfunction extractTotal(value: unknown): string | null {\n if (value === null || value === undefined) return null;\n if (typeof value === 'string') return value.length > 0 ? value : null;\n if (typeof value === 'object') {\n const total = (value as { total?: unknown }).total;\n if (total !== null && total !== undefined) return String(total);\n try {\n return JSON.stringify(value);\n } catch {\n return null;\n }\n }\n return String(value);\n}\n\ntype ReadOutcome =\n | { readonly ok: true; readonly text: string }\n | { readonly ok: false; readonly absent: boolean; readonly message: string };\n\n/** 单次读数(纯异步函数,无 hook;挂载期调用一次,无轮询)。 */\nasync function fetchRead(call: unknown, key: string, params: Record<string, unknown>): Promise<ReadOutcome> {\n // extractRpc 同式守卫:非函数即缺席态。\n if (typeof call !== 'function') return { ok: false, absent: true, message: '宿主连接缺席:connection.rpc.call 不可用' };\n let result: RpcCallResult;\n try {\n // 防御纵深:宿主侧再 hang,UI 最多转 20s 圈(AbortSignal.timeout 无字面定时器,不触发轮询门)。\n const raw: unknown = await call(RPC_CHANNEL, RPC_ENDPOINT_READ, { key, params }, AbortSignal.timeout(20_000));\n if (!isRpcResult(raw)) return { ok: false, absent: false, message: '回执信封异常(非 ok 信封)' };\n result = raw;\n } catch (e) {\n if (e instanceof Error && e.name === 'AbortError') return { ok: false, absent: false, message: '取数超时(20s):宿主未回,先查宿主日志与 DB 环境' };\n return { ok: false, absent: false, message: `取数失败:${e instanceof Error ? e.message : String(e)}` };\n }\n if (!result.ok) {\n const code = result.error?.code ?? 'unknown';\n const message = result.error?.message ?? '';\n if (code === 'missing-cli') return { ok: false, absent: true, message: `技能出口缺席(missing-cli):${message}` };\n return { ok: false, absent: false, message: `取数失败(${code}):${message}` };\n }\n const text = extractTotal(result.value);\n if (text === null) return { ok: false, absent: false, message: '取数为空(不冒充正常)' };\n return { ok: true, text };\n}\n\n/** 技能功能页:sidebar槽里展开的干活区(查数调数,不管配置)。挂载期一次 RPC。 */\nfunction CalorieWork(props: { getCall: GetCall }): React.ReactElement {\n const [state, setState] = React.useState<PanelState>({ kind: 'loading' });\n React.useEffect(() => {\n let alive = true;\n const date = todayString();\n void (async () => {\n const r = await fetchRead(props.getCall(), DEFAULT_READ_KEY, { date });\n if (!alive) return;\n if (r.ok) setState({ kind: 'data', date, total: r.text });\n else if (r.absent) setState({ kind: 'absent', message: r.message });\n else setState({ kind: 'error', message: r.message });\n })();\n return () => {\n alive = false;\n };\n }, [props.getCall]);\n if (state.kind === 'loading') {\n return React.createElement('div', null, '卡路里加载中…');\n }\n if (state.kind === 'data') {\n return React.createElement(\n 'div',\n null,\n React.createElement('div', null, `卡路里 · ${state.date}`),\n React.createElement('div', null, `total ${state.total}`),\n );\n }\n return React.createElement('div', null, state.message);\n}\n\nfunction controlLabel(control: string): string {\n if (control === 'switch') return '开关(缺省启用,只读)';\n if (control === 'text') return '文本(只读)';\n if (control === 'number') return '数字(只读)';\n return `${control}(只读)`;\n}\n\n/** 技能设置页:爱生活页签条下的一页(只读设置行 + 状态读数,禁做假开关)。 */\nfunction CalorieConfig(props: { getCall: GetCall }): React.ReactElement {\n const [state, setState] = React.useState<PanelState>({ kind: 'loading' });\n React.useEffect(() => {\n let alive = true;\n const date = todayString();\n void (async () => {\n const r = await fetchRead(props.getCall(), DEFAULT_READ_KEY, { date });\n if (!alive) return;\n if (r.ok) setState({ kind: 'data', date, total: r.text });\n else if (r.absent) setState({ kind: 'absent', message: r.message });\n else setState({ kind: 'error', message: r.message });\n })();\n return () => {\n alive = false;\n };\n }, [props.getCall]);\n const status =\n state.kind === 'loading'\n ? '状态读取中…'\n : state.kind === 'data'\n ? `今日 ${state.date} · total ${state.total}`\n : state.message;\n return React.createElement(\n 'div',\n null,\n React.createElement('div', null, status),\n React.createElement(\n 'div',\n null,\n SETTING_ROWS.map((row) =>\n React.createElement('div', { key: row.key }, `${row.title} · ${controlLabel(row.control)}`),\n ),\n ),\n );\n}\n\ninterface BetterSidebarService {\n registerTab(descriptor: {\n readonly id: string;\n readonly title: string | (() => string);\n readonly order?: number;\n readonly single?: boolean;\n readonly component: (props: unknown) => React.ReactElement | null;\n }): () => void;\n}\n\nexport function apply(ctx: ClientCtx): void {\n // 调用口取用器透传给组件(每次取数现取;非函数由组件判缺席)。\n const getCall: GetCall = () => ctx.connection?.rpc?.call ?? null;\n\n // (a) 技能设置页 → 爱生活页签槽(总管声明;总管缺席时 inject 等待,不断链)。\n ctx.slots.inject('ilife.config-tab', () =>\n ctx.slots.register(\n {\n name: 'ilife.config-tab',\n id: 'dsh-calorie',\n order: 75,\n label: () => SLOT_TITLE,\n },\n () => React.createElement(CalorieConfig, { getCall }),\n ),\n );\n\n // (b) 技能功能页 → sidebar槽(软依赖:betterSidebar 缺席即跳过)。\n const ensureWorkTab = (): boolean => {\n try {\n const get = ctx.get;\n const bs = (typeof get === 'function' ? get('betterSidebar') : undefined) as BetterSidebarService | undefined;\n if (!bs || typeof bs.registerTab !== 'function') return false;\n const dispose = bs.registerTab({\n id: 'ilife-calorie:work',\n title: '卡路里',\n order: 61,\n single: true,\n component: () => React.createElement(CalorieWork, { getCall }),\n });\n ctx.effect(() => () => {\n try {\n dispose();\n } catch {\n /* 卸载清理失败忽略 */\n }\n }, 'dsh-calorie: sidebar work tab');\n return true;\n } catch {\n return false;\n }\n };\n if (!ensureWorkTab()) {\n let tries = 0;\n const timer = setInterval(() => {\n tries++;\n if (ensureWorkTab() || tries >= 10) clearInterval(timer);\n }, 1000);\n ctx.effect(() => () => {\n clearInterval(timer);\n }, 'dsh-calorie: sidebar work tab retry');\n }\n}\n"],"mappings":"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"}
|
package/dist/contract.d.ts
CHANGED
|
@@ -29,4 +29,6 @@ export declare function fail(code: string, message: string): RpcResult;
|
|
|
29
29
|
export declare function parseReadPayload(raw: unknown): ReadPayload | null;
|
|
30
30
|
/** 面板默认读键(与 HITL 一致性对比同键)。 */
|
|
31
31
|
export declare const DEFAULT_READ_KEY: "calorie.view.home";
|
|
32
|
+
/** 信封守卫:供 client 拆包前校验(port 拥有信封定义,守卫住 port)。 */
|
|
33
|
+
export declare function isRpcResult(raw: unknown): raw is RpcResult;
|
|
32
34
|
//# sourceMappingURL=contract.d.ts.map
|
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAG,gBAAyB,CAAC;AACrD,eAAO,MAAM,iBAAiB,EAAG,MAAe,CAAC;AAEjD,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC9C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AAErD,wBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAE5C;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAE7D;AAED,iDAAiD;AACjD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAMjE;AAED,8BAA8B;AAC9B,eAAO,MAAM,gBAAgB,EAAG,mBAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAG,gBAAyB,CAAC;AACrD,eAAO,MAAM,iBAAiB,EAAG,MAAe,CAAC;AAEjD,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC9C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AAErD,wBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CAE5C;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAE7D;AAED,iDAAiD;AACjD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAMjE;AAED,8BAA8B;AAC9B,eAAO,MAAM,gBAAgB,EAAG,mBAA4B,CAAC;AAE7D,iDAAiD;AACjD,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,SAAS,CAI1D"}
|
package/dist/contract.js
CHANGED
|
@@ -25,4 +25,11 @@ export function parseReadPayload(raw) {
|
|
|
25
25
|
}
|
|
26
26
|
/** 面板默认读键(与 HITL 一致性对比同键)。 */
|
|
27
27
|
export const DEFAULT_READ_KEY = 'calorie.view.home';
|
|
28
|
+
/** 信封守卫:供 client 拆包前校验(port 拥有信封定义,守卫住 port)。 */
|
|
29
|
+
export function isRpcResult(raw) {
|
|
30
|
+
if (typeof raw !== 'object' || raw === null)
|
|
31
|
+
return false;
|
|
32
|
+
const ok = raw.ok;
|
|
33
|
+
return ok === true || ok === false;
|
|
34
|
+
}
|
|
28
35
|
//# sourceMappingURL=contract.js.map
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,gBAAyB,CAAC;AACrD,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAe,CAAC;AAkBjD,MAAM,UAAU,EAAE,CAAC,KAAc;IAC/B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY,EAAE,OAAe;IAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAAC;AAC9D,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAA0C,CAAC;IACnE,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxF,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAiC,EAAE,CAAC;AAC5D,CAAC;AAED,8BAA8B;AAC9B,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAA4B,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,gBAAyB,CAAC;AACrD,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAe,CAAC;AAkBjD,MAAM,UAAU,EAAE,CAAC,KAAc;IAC/B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,IAAY,EAAE,OAAe;IAChD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,CAAC;AAC9D,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAA0C,CAAC;IACnE,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7D,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IACxF,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAiC,EAAE,CAAC;AAC5D,CAAC;AAED,8BAA8B;AAC9B,MAAM,CAAC,MAAM,gBAAgB,GAAG,mBAA4B,CAAC;AAE7D,iDAAiD;AACjD,MAAM,UAAU,WAAW,CAAC,GAAY;IACtC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC1D,MAAM,EAAE,GAAI,GAAwB,CAAC,EAAE,CAAC;IACxC,OAAO,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,KAAK,CAAC;AACrC,CAAC"}
|