hiwork-core 0.0.18 → 0.0.19
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 +380 -2
- package/lib/index.js +871 -13
- package/lib/pms-client.js +572 -0
- package/lib/pms-todo-bridge.js +640 -0
- package/lib/types/client/contracts.d.ts +22 -0
- package/lib/types/client/pms-todo.d.ts +357 -0
- package/lib/types/pms-client.d.ts +293 -0
- package/lib/types/pms-todo-bridge.d.ts +426 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -3757,6 +3757,361 @@ var ManagedWorkbenchStore = class {
|
|
|
3757
3757
|
}
|
|
3758
3758
|
};
|
|
3759
3759
|
|
|
3760
|
+
// src/client/pms-todo.ts
|
|
3761
|
+
var PMS_TODO_CHANNEL = "/hiwork-pms-todo";
|
|
3762
|
+
var PmsTodoRpcError = class extends Error {
|
|
3763
|
+
constructor(code, message) {
|
|
3764
|
+
super(message);
|
|
3765
|
+
this.code = code;
|
|
3766
|
+
this.name = "PmsTodoRpcError";
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
3769
|
+
var GENERIC_FAILURE_MESSAGE4 = "PMS \u5F85\u529E\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
3770
|
+
var INVALID_RESPONSE_MESSAGE4 = "PMS \u5F85\u529E\u4E3B\u673A\u8FD4\u56DE\u4E86\u65E0\u6548\u54CD\u5E94\u3002";
|
|
3771
|
+
var INTERNAL_CODE = "INTERNAL";
|
|
3772
|
+
function unwrapPmsTodoResult(value) {
|
|
3773
|
+
if (typeof value !== "object" || value === null || !("ok" in value)) {
|
|
3774
|
+
throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3775
|
+
}
|
|
3776
|
+
const result = value;
|
|
3777
|
+
if (result.ok === true) return result.value;
|
|
3778
|
+
if (result.ok === false) {
|
|
3779
|
+
const code = typeof result.error?.code === "string" ? result.error.code : INTERNAL_CODE;
|
|
3780
|
+
const message = typeof result.error?.message === "string" && result.error.message.trim() !== "" ? result.error.message : GENERIC_FAILURE_MESSAGE4;
|
|
3781
|
+
throw new PmsTodoRpcError(code, message);
|
|
3782
|
+
}
|
|
3783
|
+
throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3784
|
+
}
|
|
3785
|
+
function text5(value) {
|
|
3786
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
3787
|
+
}
|
|
3788
|
+
function isRecord3(value) {
|
|
3789
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3790
|
+
}
|
|
3791
|
+
function countOf(value) {
|
|
3792
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
3793
|
+
}
|
|
3794
|
+
function isFiniteCount(value) {
|
|
3795
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
3796
|
+
}
|
|
3797
|
+
function countOr(value, fallback) {
|
|
3798
|
+
return isFiniteCount(value) ? countOf(value) : fallback;
|
|
3799
|
+
}
|
|
3800
|
+
function toItem(value) {
|
|
3801
|
+
if (!isRecord3(value)) return void 0;
|
|
3802
|
+
const id = value["id"];
|
|
3803
|
+
if (typeof id !== "number" || !Number.isSafeInteger(id)) return void 0;
|
|
3804
|
+
const item = {
|
|
3805
|
+
id,
|
|
3806
|
+
title: text5(value["title"]) ?? "",
|
|
3807
|
+
status: text5(value["status"]) ?? ""
|
|
3808
|
+
};
|
|
3809
|
+
const description = text5(value["description"]);
|
|
3810
|
+
if (description !== void 0) item.description = description;
|
|
3811
|
+
const category = text5(value["category"]);
|
|
3812
|
+
if (category !== void 0) item.category = category;
|
|
3813
|
+
const priority = text5(value["priority"]);
|
|
3814
|
+
if (priority !== void 0) item.priority = priority;
|
|
3815
|
+
const startDate = text5(value["startDate"]);
|
|
3816
|
+
if (startDate !== void 0) item.startDate = startDate;
|
|
3817
|
+
const dueDate = text5(value["dueDate"]);
|
|
3818
|
+
if (dueDate !== void 0) item.dueDate = dueDate;
|
|
3819
|
+
const completedTime = text5(value["completedTime"]);
|
|
3820
|
+
if (completedTime !== void 0) item.completedTime = completedTime;
|
|
3821
|
+
if (typeof value["isOverdue"] === "boolean") item.isOverdue = value["isOverdue"];
|
|
3822
|
+
if (typeof value["isDueSoon"] === "boolean") item.isDueSoon = value["isDueSoon"];
|
|
3823
|
+
return item;
|
|
3824
|
+
}
|
|
3825
|
+
function toStatus(value) {
|
|
3826
|
+
if (!isRecord3(value)) throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3827
|
+
const username = text5(value["username"]);
|
|
3828
|
+
const expiresAt = text5(value["expiresAt"]);
|
|
3829
|
+
return {
|
|
3830
|
+
configured: value["configured"] === true,
|
|
3831
|
+
loggedIn: value["loggedIn"] === true,
|
|
3832
|
+
...username === void 0 ? {} : { username },
|
|
3833
|
+
...expiresAt === void 0 ? {} : { expiresAt }
|
|
3834
|
+
};
|
|
3835
|
+
}
|
|
3836
|
+
function toToday(value) {
|
|
3837
|
+
if (!isRecord3(value)) throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3838
|
+
const rawItems = value["items"];
|
|
3839
|
+
if (!Array.isArray(rawItems)) throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3840
|
+
const items = [];
|
|
3841
|
+
for (const raw of rawItems) {
|
|
3842
|
+
const item = toItem(raw);
|
|
3843
|
+
if (item !== void 0) items.push(item);
|
|
3844
|
+
}
|
|
3845
|
+
return { items, fetchedAt: text5(value["fetchedAt"]) ?? "" };
|
|
3846
|
+
}
|
|
3847
|
+
function toStatistics(value) {
|
|
3848
|
+
if (!isRecord3(value)) throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3849
|
+
return {
|
|
3850
|
+
total: countOf(value["total"]),
|
|
3851
|
+
pending: countOf(value["pending"]),
|
|
3852
|
+
completed: countOf(value["completed"]),
|
|
3853
|
+
overdue: countOf(value["overdue"])
|
|
3854
|
+
};
|
|
3855
|
+
}
|
|
3856
|
+
function toSetStatusResult(value) {
|
|
3857
|
+
if (!isRecord3(value)) throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3858
|
+
const id = value["id"];
|
|
3859
|
+
if (typeof id !== "number" || !Number.isSafeInteger(id)) {
|
|
3860
|
+
throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3861
|
+
}
|
|
3862
|
+
const status = value["status"];
|
|
3863
|
+
if (status !== "PENDING" && status !== "COMPLETED") {
|
|
3864
|
+
throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3865
|
+
}
|
|
3866
|
+
const completedTime = text5(value["completedTime"]);
|
|
3867
|
+
return { id, status, ...completedTime === void 0 ? {} : { completedTime } };
|
|
3868
|
+
}
|
|
3869
|
+
function isInteger(value) {
|
|
3870
|
+
return typeof value === "number" && Number.isSafeInteger(value);
|
|
3871
|
+
}
|
|
3872
|
+
function toTaskItem(value) {
|
|
3873
|
+
if (!isRecord3(value)) return void 0;
|
|
3874
|
+
const id = value["id"];
|
|
3875
|
+
if (!isInteger(id)) return void 0;
|
|
3876
|
+
const numeric = (raw) => typeof raw === "number" && Number.isFinite(raw) ? raw : 0;
|
|
3877
|
+
const item = {
|
|
3878
|
+
id,
|
|
3879
|
+
taskName: text5(value["taskName"]) ?? "",
|
|
3880
|
+
description: text5(value["description"]) ?? "",
|
|
3881
|
+
projectId: numeric(value["projectId"]),
|
|
3882
|
+
projectName: text5(value["projectName"]) ?? "",
|
|
3883
|
+
assigneeId: numeric(value["assigneeId"]),
|
|
3884
|
+
assigneeName: text5(value["assigneeName"]) ?? "",
|
|
3885
|
+
deadline: text5(value["deadline"]) ?? "",
|
|
3886
|
+
status: numeric(value["status"]),
|
|
3887
|
+
statusName: text5(value["statusName"]) ?? "",
|
|
3888
|
+
priority: numeric(value["priority"]),
|
|
3889
|
+
priorityName: text5(value["priorityName"]) ?? "",
|
|
3890
|
+
taskType: text5(value["taskType"]) ?? "",
|
|
3891
|
+
taskTypeName: text5(value["taskTypeName"]) ?? "",
|
|
3892
|
+
auditStatus: numeric(value["auditStatus"]),
|
|
3893
|
+
auditStatusName: text5(value["auditStatusName"]) ?? ""
|
|
3894
|
+
};
|
|
3895
|
+
const projectStageName = text5(value["projectStageName"]);
|
|
3896
|
+
if (projectStageName !== void 0) item.projectStageName = projectStageName;
|
|
3897
|
+
const requirementName = text5(value["requirementName"]);
|
|
3898
|
+
if (requirementName !== void 0) item.requirementName = requirementName;
|
|
3899
|
+
const moduleName = text5(value["moduleName"]);
|
|
3900
|
+
if (moduleName !== void 0) item.moduleName = moduleName;
|
|
3901
|
+
const estimatedHours = value["estimatedHours"];
|
|
3902
|
+
if (typeof estimatedHours === "number" && Number.isFinite(estimatedHours)) item.estimatedHours = estimatedHours;
|
|
3903
|
+
const actualHours = value["actualHours"];
|
|
3904
|
+
if (typeof actualHours === "number" && Number.isFinite(actualHours)) item.actualHours = actualHours;
|
|
3905
|
+
return item;
|
|
3906
|
+
}
|
|
3907
|
+
function toTaskList(value) {
|
|
3908
|
+
if (!isRecord3(value)) throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3909
|
+
const rawItems = value["items"];
|
|
3910
|
+
if (!Array.isArray(rawItems)) throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3911
|
+
const items = [];
|
|
3912
|
+
for (const raw of rawItems) {
|
|
3913
|
+
const item = toTaskItem(raw);
|
|
3914
|
+
if (item !== void 0) items.push(item);
|
|
3915
|
+
}
|
|
3916
|
+
const current = countOf(value["current"]);
|
|
3917
|
+
return {
|
|
3918
|
+
items,
|
|
3919
|
+
total: countOr(value["total"], items.length),
|
|
3920
|
+
current: isFiniteCount(value["current"]) ? current : 1,
|
|
3921
|
+
size: countOr(value["size"], items.length)
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
function toTaskCompleteResult(value) {
|
|
3925
|
+
if (!isRecord3(value)) throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3926
|
+
const id = value["id"];
|
|
3927
|
+
const status = value["status"];
|
|
3928
|
+
if (!isInteger(id) || !isInteger(status)) throw new PmsTodoRpcError(INTERNAL_CODE, INVALID_RESPONSE_MESSAGE4);
|
|
3929
|
+
return { id, status };
|
|
3930
|
+
}
|
|
3931
|
+
var PmsTodoStore = class {
|
|
3932
|
+
constructor(rpc) {
|
|
3933
|
+
this.rpc = rpc;
|
|
3934
|
+
}
|
|
3935
|
+
current;
|
|
3936
|
+
listeners = /* @__PURE__ */ new Set();
|
|
3937
|
+
/**
|
|
3938
|
+
* 在飞请求**连同它所属的世代与端点**一起记账。
|
|
3939
|
+
*
|
|
3940
|
+
* 世代:`clear()`(登出 / 换账号)之后,账上那条是**上一个人**发起的,它的待办属于上一个人
|
|
3941
|
+
* ——复用它等于让新账号看到上一个人的待办。端点:`today` 与 `statistics` 的返回值形状不同,
|
|
3942
|
+
* 只有同一个端点的并发调用才允许合并。
|
|
3943
|
+
*/
|
|
3944
|
+
inFlight;
|
|
3945
|
+
/** 世代号:`clear()` 每调用一次 +1,用来作废它之前发起的在飞请求。 */
|
|
3946
|
+
generation = 0;
|
|
3947
|
+
/** 是否已经为「镜像还空着」的情况探过一次 `status`(避免每次同步读都发一次 RPC)。 */
|
|
3948
|
+
statusProbed = false;
|
|
3949
|
+
/**
|
|
3950
|
+
* 本地镜像(同步)。从没同步过返回 `undefined`。
|
|
3951
|
+
*
|
|
3952
|
+
* ⚠️ 本方法有一次**惰性探测**:镜像还空着且从没探过时,它顺手发起一次 `status` 端点调用
|
|
3953
|
+
* (结果到达后写镜像 + 通知订阅者)。理由——渲染是同步的,而「配没配凭据」只有主机知道;
|
|
3954
|
+
* 没有这一步,`status` 端点就永远没有调用方,界面也只能靠一次失败的 `today()` 去反推。
|
|
3955
|
+
* 因此调用方看到 `undefined` **不是**「未配置」,而是「还不知道」:必须订阅。
|
|
3956
|
+
*/
|
|
3957
|
+
status() {
|
|
3958
|
+
if (this.current === void 0 && !this.statusProbed) {
|
|
3959
|
+
this.statusProbed = true;
|
|
3960
|
+
void this.refreshStatus().catch(() => {
|
|
3961
|
+
});
|
|
3962
|
+
}
|
|
3963
|
+
return this.current;
|
|
3964
|
+
}
|
|
3965
|
+
/**
|
|
3966
|
+
* 显式去主机取一次状态并写镜像(`status()` 的异步入口)。
|
|
3967
|
+
*
|
|
3968
|
+
* 失败**不改镜像**(保留上一次已知状态),也**不在镜像里记错误**:错误属于这一次调用,
|
|
3969
|
+
* 调用方(或下一次 `today()`)自己决定怎么展示。
|
|
3970
|
+
*/
|
|
3971
|
+
async refreshStatus(signal) {
|
|
3972
|
+
const { value, generation } = await this.call("status", "status", {}, toStatus, signal);
|
|
3973
|
+
return this.adopt(value, generation);
|
|
3974
|
+
}
|
|
3975
|
+
/**
|
|
3976
|
+
* 登录(凭据交给 Host;**本层不留存密码、不写任何缓存**)。
|
|
3977
|
+
*
|
|
3978
|
+
* 成功时把返回的状态写进镜像——登录是「身份刚变过」,镜像必须跟着变。
|
|
3979
|
+
*/
|
|
3980
|
+
async login(input) {
|
|
3981
|
+
const payload = { username: input.username, password: input.password };
|
|
3982
|
+
if (input.remember !== void 0) payload["remember"] = input.remember;
|
|
3983
|
+
const { value, generation } = await this.call("login", "login", payload, toStatus);
|
|
3984
|
+
return this.adopt(value, generation);
|
|
3985
|
+
}
|
|
3986
|
+
/** 登出(Host 侧清凭据);返回登出后的状态并写镜像。 */
|
|
3987
|
+
async logout() {
|
|
3988
|
+
const { value, generation } = await this.call("logout", "logout", {}, toStatus);
|
|
3989
|
+
return this.adopt(value, generation);
|
|
3990
|
+
}
|
|
3991
|
+
/**
|
|
3992
|
+
* 今日待办。
|
|
3993
|
+
*
|
|
3994
|
+
* 不做本地缓存(返回**刚拉到的**那一份):待办的时效性就是它的全部价值,把上一次的结果当
|
|
3995
|
+
* 「今日」交出去正是这个功能最容易犯的错;界面要离线态时自己留一份 + 标陈旧(spec §6)。
|
|
3996
|
+
*/
|
|
3997
|
+
async today(signal) {
|
|
3998
|
+
const { value } = await this.call("today", "today", {}, toToday, signal);
|
|
3999
|
+
return value;
|
|
4000
|
+
}
|
|
4001
|
+
/** total / pending / completed / overdue 计数。 */
|
|
4002
|
+
async statistics(signal) {
|
|
4003
|
+
const { value } = await this.call("statistics", "statistics", {}, toStatistics, signal);
|
|
4004
|
+
return value;
|
|
4005
|
+
}
|
|
4006
|
+
/**
|
|
4007
|
+
* 勾完成 / 取消完成。
|
|
4008
|
+
*
|
|
4009
|
+
* 返回值里带服务端回声的 id / status(`PmsTodoFace.setStatus` 折成 `void`;面里不需要它,
|
|
4010
|
+
* 但仓库保留着——回滚那一条待办时用它认人最稳)。
|
|
4011
|
+
*/
|
|
4012
|
+
async setStatus(input) {
|
|
4013
|
+
const { value } = await this.call("setStatus", "setStatus", { id: input.id, status: input.status }, toSetStatusResult);
|
|
4014
|
+
return value;
|
|
4015
|
+
}
|
|
4016
|
+
/**
|
|
4017
|
+
* **我的未完成项目任务**(spec §3 D7):`myTasks` 端点的取数。
|
|
4018
|
+
*
|
|
4019
|
+
* 入参是**空对象**,没有 `userId`——「谁的任务」由 Host 从凭据记录里取(客户端连自己的
|
|
4020
|
+
* PMS userId 都拿不到,这是 §3 D6 形状的一部分)。
|
|
4021
|
+
*
|
|
4022
|
+
* 与 `today` 同款:**不做本地缓存**(返回刚拉到的那一份),但**并发调用合并**(同一时刻问两遍
|
|
4023
|
+
* 没有意义;世代判据同其他端点,换账号之后不复用)。
|
|
4024
|
+
*/
|
|
4025
|
+
async myTasks(signal) {
|
|
4026
|
+
const { value } = await this.call("myTasks", "myTasks", {}, toTaskList, signal);
|
|
4027
|
+
return value;
|
|
4028
|
+
}
|
|
4029
|
+
/**
|
|
4030
|
+
* **完成一条项目任务**(spec §3 D7)。
|
|
4031
|
+
*
|
|
4032
|
+
* 返回值里带服务端回声的 id / status(`PmsTodoFace.completeTask` 折成 `void`;仓库保留着
|
|
4033
|
+
* ——回滚那一条任务时用它认人最稳)。缺权限时抛 `PmsTodoRpcError('FORBIDDEN')`。
|
|
4034
|
+
*
|
|
4035
|
+
* ⚠️ **刻意不走 {@link PmsTodoStore.call}**(即不参与并发合并):`dedupe` 的键是**端点名**,
|
|
4036
|
+
* 而这是带参数的命令——把它并进同一格会让「几乎同时完成 A 和 B」的第二次调用**复用第一次的
|
|
4037
|
+
* 结果**,用户以为 B 完成了,其实请求根本没发出去。Host 侧的 `completeTask` 同样不合并
|
|
4038
|
+
* (那里也注明「点了两次就是两次动作」)。
|
|
4039
|
+
*/
|
|
4040
|
+
async completeTask(input) {
|
|
4041
|
+
const raw = await this.rpc.call(PMS_TODO_CHANNEL, "completeTask", { id: input.id });
|
|
4042
|
+
return toTaskCompleteResult(unwrapPmsTodoResult(raw));
|
|
4043
|
+
}
|
|
4044
|
+
/** 订阅镜像变化(右栏 tab 据此重渲染)。返回退订函数。 */
|
|
4045
|
+
subscribe(listener) {
|
|
4046
|
+
this.listeners.add(listener);
|
|
4047
|
+
return () => {
|
|
4048
|
+
this.listeners.delete(listener);
|
|
4049
|
+
};
|
|
4050
|
+
}
|
|
4051
|
+
/**
|
|
4052
|
+
* 清空镜像(登出 / 换账号:状态是**按人**的,留着会串号),并**作废在飞的请求**。
|
|
4053
|
+
*
|
|
4054
|
+
* 只清镜像是不够的:清空那一刻正有一次拉取在飞时,那次拉取是**上一个人**发起的。所以世代
|
|
4055
|
+
* +1:那之后的调用者一律不复用旧世代的那条,自己重新发一次 RPC。用世代号而**不是**把
|
|
4056
|
+
* `inFlight` 直接抹成 undefined,理由与 `src/client/managed-workbench.ts` 的同名方法逐字相同
|
|
4057
|
+
* (旧 promise 的 `finally` 会回头把新发起的那条记账清掉,两者互相踩)。
|
|
4058
|
+
*
|
|
4059
|
+
* 同时把 `statusProbed` 复位:新账号的镜像必须能再探一次,否则换账号后 `status()` 会一直
|
|
4060
|
+
* 返回 `undefined` 且再也不去问主机。
|
|
4061
|
+
*/
|
|
4062
|
+
clear() {
|
|
4063
|
+
this.generation += 1;
|
|
4064
|
+
this.current = void 0;
|
|
4065
|
+
this.statusProbed = false;
|
|
4066
|
+
this.emit();
|
|
4067
|
+
}
|
|
4068
|
+
/**
|
|
4069
|
+
* 发一次 RPC、解开信封、过第二道形状校验。
|
|
4070
|
+
*
|
|
4071
|
+
* 返回值里带上**发起时的世代**:只有它等于当前世代时,调用方才写镜像(见 {@link adopt})。
|
|
4072
|
+
*/
|
|
4073
|
+
async call(key, endpoint, payload, parse, signal) {
|
|
4074
|
+
return await this.dedupe(key, async (generation) => {
|
|
4075
|
+
const raw = await this.rpc.call(PMS_TODO_CHANNEL, endpoint, payload, signal);
|
|
4076
|
+
return { value: parse(unwrapPmsTodoResult(raw)), generation };
|
|
4077
|
+
});
|
|
4078
|
+
}
|
|
4079
|
+
/**
|
|
4080
|
+
* 并发合并:同一世代 + 同一端点撞在一起(面板挂载与手动刷新常常同拍)时共用一次请求,
|
|
4081
|
+
* 且**共用同一个返回值**——两边对「这一次拉到了什么」的判断必须一致。
|
|
4082
|
+
*/
|
|
4083
|
+
async dedupe(key, task) {
|
|
4084
|
+
const inFlight = this.inFlight;
|
|
4085
|
+
if (inFlight !== void 0 && inFlight.generation === this.generation && inFlight.key === key) {
|
|
4086
|
+
return await inFlight.task;
|
|
4087
|
+
}
|
|
4088
|
+
const generation = this.generation;
|
|
4089
|
+
const promise = task(generation);
|
|
4090
|
+
this.inFlight = { generation, key, task: promise };
|
|
4091
|
+
try {
|
|
4092
|
+
return await promise;
|
|
4093
|
+
} finally {
|
|
4094
|
+
if (this.inFlight?.task === promise) this.inFlight = void 0;
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
/**
|
|
4098
|
+
* 采纳一份成功状态:写镜像 + 通知订阅者。
|
|
4099
|
+
*
|
|
4100
|
+
* 世代不符的那一份**不写镜像**(但仍原样返回给调用方):它是 `clear()` 之前发起的请求,
|
|
4101
|
+
* 属于上一个人。允许它写回等于「清空之后又被上一个人的状态占住」,而换账号时界面正好靠
|
|
4102
|
+
* `status()` 判断该显示引导还是列表。
|
|
4103
|
+
*/
|
|
4104
|
+
adopt(value, generation) {
|
|
4105
|
+
if (generation !== this.generation) return value;
|
|
4106
|
+
this.current = value;
|
|
4107
|
+
this.emit();
|
|
4108
|
+
return value;
|
|
4109
|
+
}
|
|
4110
|
+
emit() {
|
|
4111
|
+
for (const listener of [...this.listeners]) listener();
|
|
4112
|
+
}
|
|
4113
|
+
};
|
|
4114
|
+
|
|
3760
4115
|
// src/client/index.ts
|
|
3761
4116
|
var inject = ["slots", "locale", "remote", "remote.credentials", "settingsScope", "connection"];
|
|
3762
4117
|
var INTERACTION_STYLE_ID = "hiwork-core-interaction-styles";
|
|
@@ -3797,6 +4152,7 @@ function apply(ctx) {
|
|
|
3797
4152
|
const managedMcp = new ManagedMcpStore(connection.rpc);
|
|
3798
4153
|
const managedSkills = new ManagedSkillsStore(connection.rpc);
|
|
3799
4154
|
const managedWorkbench = new ManagedWorkbenchStore(connection.rpc);
|
|
4155
|
+
const pmsTodo = new PmsTodoStore(connection.rpc);
|
|
3800
4156
|
const appVersion = createAppVersion(connection.rpc);
|
|
3801
4157
|
const pluginUpdates = createPluginUpdates(connection.rpc);
|
|
3802
4158
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
@@ -3818,6 +4174,7 @@ function apply(ctx) {
|
|
|
3818
4174
|
managedMcp.clear();
|
|
3819
4175
|
managedSkills.clear();
|
|
3820
4176
|
managedWorkbench.clear();
|
|
4177
|
+
pmsTodo.clear();
|
|
3821
4178
|
}), "hiwork-core: \u7528\u91CF\u7F13\u5B58\u968F\u8D26\u53F7 / Key \u53D8\u5316\u6E05\u7A7A");
|
|
3822
4179
|
ctx.effect(() => observeAboutNavIcon(), "hiwork-core: about nav icon");
|
|
3823
4180
|
ctx.slots.inject("sidebar.brand.mark", () => ctx.slots.register({
|
|
@@ -3836,9 +4193,9 @@ function apply(ctx) {
|
|
|
3836
4193
|
}, UserBadge));
|
|
3837
4194
|
ctx.effect(() => mountLoginGate(scope, storeCredential), "hiwork-core: login gate");
|
|
3838
4195
|
ctx.effect(() => observeHeroDecorations(document, dailyQuotes), "hiwork-core: hero decorations");
|
|
3839
|
-
installFeatureCenter(ctx, managedMcp, managedSkills, managedWorkbench);
|
|
4196
|
+
installFeatureCenter(ctx, managedMcp, managedSkills, managedWorkbench, pmsTodo);
|
|
3840
4197
|
}
|
|
3841
|
-
function installFeatureCenter(ctx, managedMcp, managedSkills, managedWorkbench) {
|
|
4198
|
+
function installFeatureCenter(ctx, managedMcp, managedSkills, managedWorkbench, pmsTodo) {
|
|
3842
4199
|
const featureRegistry = createFeatureRegistry();
|
|
3843
4200
|
let center;
|
|
3844
4201
|
const sessions = ctx.get("sessions");
|
|
@@ -3888,6 +4245,27 @@ function installFeatureCenter(ctx, managedMcp, managedSkills, managedWorkbench)
|
|
|
3888
4245
|
warm: async (signal) => await managedWorkbench.warm(signal),
|
|
3889
4246
|
refresh: async (signal) => await managedWorkbench.refresh(signal),
|
|
3890
4247
|
subscribe: (listener) => managedWorkbench.subscribe(listener)
|
|
4248
|
+
},
|
|
4249
|
+
// PMS 个人待办面:工作台域(hiwork-workbench)按这个成员判断「有没有这个能力」,老 core 上
|
|
4250
|
+
// 它是 undefined,右栏 tab 降级成「请升级 HiWork 客户端」(spec §6 的降级矩阵第一行)。
|
|
4251
|
+
// 七项与 spec §4.3 逐字对应;核心**只发端点调用**——凭据与 token 全在 Host(§3 D6)。
|
|
4252
|
+
// `setStatus` 在面里折成 void(§4.3 冻结的形状),仓库返回值留给诊断与测试。
|
|
4253
|
+
// `myTasks` / `completeTask` 是 §3 D7 追加的两项,**在面里也是可选成员**(旧 core 只有前七项,
|
|
4254
|
+
// 消费方按 `face.pmsTodo.myTasks !== undefined` 判断,见 src/client/pms-todo.ts 的版本闸口)。
|
|
4255
|
+
pmsTodo: {
|
|
4256
|
+
status: () => pmsTodo.status(),
|
|
4257
|
+
login: async (input) => await pmsTodo.login(input),
|
|
4258
|
+
logout: async () => await pmsTodo.logout(),
|
|
4259
|
+
today: async (signal) => await pmsTodo.today(signal),
|
|
4260
|
+
statistics: async (signal) => await pmsTodo.statistics(signal),
|
|
4261
|
+
setStatus: async (input) => {
|
|
4262
|
+
await pmsTodo.setStatus(input);
|
|
4263
|
+
},
|
|
4264
|
+
myTasks: async (signal) => await pmsTodo.myTasks(signal),
|
|
4265
|
+
completeTask: async (input) => {
|
|
4266
|
+
await pmsTodo.completeTask(input);
|
|
4267
|
+
},
|
|
4268
|
+
subscribe: (listener) => pmsTodo.subscribe(listener)
|
|
3891
4269
|
}
|
|
3892
4270
|
};
|
|
3893
4271
|
ctx.provide(HIWORK_FEATURE_CENTER_SERVICE, face);
|