@1e0zj/dsh-plugin-mall 0.4.16 → 0.4.17
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/package.json +1 -1
- package/src/client.js +57 -31
- package/src/index.js +11 -3
package/package.json
CHANGED
package/src/client.js
CHANGED
|
@@ -162,8 +162,12 @@ window.__ModuleLoader__.load({
|
|
|
162
162
|
// ——锁必须持续到 job 落终态,不是到 RPC 返回。
|
|
163
163
|
// needsApproval 的 status 是 failed,但那是等用户决定的暂停,
|
|
164
164
|
// 事务没结束,算未了结。
|
|
165
|
-
//
|
|
165
|
+
// cards 预检出了风险卡片,停在那里等用户读完再决定。这期间任务是
|
|
166
166
|
// 终态,但决定还没做,再起一条链就是两条链抢同一个事务。
|
|
167
|
+
// **按 jobId 存字典、不是单槽位**:同时可以停着好几张卡片
|
|
168
|
+
// (批量更新必然如此,手动连点两个包也够)。单槽位时后来的
|
|
169
|
+
// 会顶掉前一张,被顶掉那条从此没有任何入口能做决定,job 却
|
|
170
|
+
// 一直是未了结状态——那一行的更新按钮再也点不亮。
|
|
167
171
|
//
|
|
168
172
|
// sources 里递进来的必须是 ref.current 这类当下的值:判定发生在点击那一
|
|
169
173
|
// 刻,上一次渲染的快照没有意义。
|
|
@@ -174,8 +178,11 @@ window.__ModuleLoader__.load({
|
|
|
174
178
|
for (var spec in installing) {
|
|
175
179
|
if (installing[spec] === true && specPackageName(spec) === name) return true;
|
|
176
180
|
}
|
|
177
|
-
var
|
|
178
|
-
|
|
181
|
+
var cards = sources.cards || {};
|
|
182
|
+
for (var cardId in cards) {
|
|
183
|
+
var card = cards[cardId];
|
|
184
|
+
if (card && specPackageName(card.spec) === name) return true;
|
|
185
|
+
}
|
|
179
186
|
var jobs = sources.jobs || {};
|
|
180
187
|
for (var id in jobs) {
|
|
181
188
|
var job = jobs[id];
|
|
@@ -664,7 +671,7 @@ window.__ModuleLoader__.load({
|
|
|
664
671
|
if (ids.length === 0 && !props.error) return null;
|
|
665
672
|
var hasSettled = ids.some(function (id) {
|
|
666
673
|
var job = props.jobs[id];
|
|
667
|
-
var hasDecision =
|
|
674
|
+
var hasDecision = props.preflights[id] !== undefined
|
|
668
675
|
|| (job.needsApproval && job.needsApproval.length > 0);
|
|
669
676
|
return !isJobActive(job.status) && !hasDecision;
|
|
670
677
|
});
|
|
@@ -677,7 +684,7 @@ window.__ModuleLoader__.load({
|
|
|
677
684
|
ids.map(function (id) {
|
|
678
685
|
var job = props.jobs[id];
|
|
679
686
|
var done = job.status === "completed" || job.status === "failed" || job.status === "killed";
|
|
680
|
-
var preflightDecision = props.
|
|
687
|
+
var preflightDecision = props.preflights[id] || null;
|
|
681
688
|
var awaitingDecision = preflightDecision !== null || (job.needsApproval && job.needsApproval.length > 0);
|
|
682
689
|
return h("div", { key: id, className: "mkt_job" },
|
|
683
690
|
// 不再显示 market-N 这种内部 id——对用户没有意义,反而让人以为
|
|
@@ -697,12 +704,12 @@ window.__ModuleLoader__.load({
|
|
|
697
704
|
? h(ApprovalRequest, {
|
|
698
705
|
spec: job.spec,
|
|
699
706
|
needsApproval: job.needsApproval,
|
|
700
|
-
busy: props.
|
|
707
|
+
busy: props.specBusy(job.spec),
|
|
701
708
|
onApprove: function (names) {
|
|
702
709
|
// 不先 drop:旧条目由重试任务的 track(carryFromId) 原子接管
|
|
703
710
|
// (撤条目 + 日志接续一拍完成)。install RPC 现在只做本地校验、
|
|
704
711
|
// 立刻返回新 job id,但保留这个次序仍然是它最稳的形态——
|
|
705
|
-
// 万一 RPC 失败,旧条目和日志还在。等待期间按钮由
|
|
712
|
+
// 万一 RPC 失败,旧条目和日志还在。等待期间按钮由 specBusy
|
|
706
713
|
// 态显示「继续中…」。
|
|
707
714
|
props.onApprove(job.spec, names, job.approvalToken, id);
|
|
708
715
|
},
|
|
@@ -712,9 +719,9 @@ window.__ModuleLoader__.load({
|
|
|
712
719
|
? h(PreflightCard, {
|
|
713
720
|
spec: preflightDecision.spec,
|
|
714
721
|
report: preflightDecision.report,
|
|
715
|
-
busy: props.
|
|
716
|
-
onConfirm: props.onConfirmPreflight,
|
|
717
|
-
onClose: props.onClosePreflight,
|
|
722
|
+
busy: props.specBusy(preflightDecision.spec),
|
|
723
|
+
onConfirm: function () { props.onConfirmPreflight(id); },
|
|
724
|
+
onClose: function () { props.onClosePreflight(id); },
|
|
718
725
|
})
|
|
719
726
|
: job.detail ? h("div", { className: job.status === "failed" ? "mkt_error" : "mkt_desc" }, job.detail) : null,
|
|
720
727
|
// 预检完成不提示重启——它没有改动任何东西,接下来才是安装。
|
|
@@ -873,17 +880,27 @@ window.__ModuleLoader__.load({
|
|
|
873
880
|
var _hostStartedAt = useState(0);
|
|
874
881
|
var hostStartedAt = _hostStartedAt[0];
|
|
875
882
|
var setHostStartedAt = _hostStartedAt[1];
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
var
|
|
883
|
+
// jobId -> { spec, report, jobId }。字典而不是单个:见 updateChainBusy
|
|
884
|
+
// 的 cards 一节——同时可以有好几张待决策的风险卡片。
|
|
885
|
+
var _preflights = useState({});
|
|
886
|
+
var preflights = _preflights[0];
|
|
887
|
+
var setPreflights = _preflights[1];
|
|
888
|
+
var dropPreflight = useCallback(function (jobId) {
|
|
889
|
+
setPreflights(function (prev) {
|
|
890
|
+
if (!(jobId in prev)) return prev;
|
|
891
|
+
var next = Object.assign({}, prev);
|
|
892
|
+
delete next[jobId];
|
|
893
|
+
return next;
|
|
894
|
+
});
|
|
895
|
+
}, []);
|
|
879
896
|
|
|
880
897
|
// 更新按钮防连点用的三份当下值(见 updateChainBusy)。ref 而不是 state:
|
|
881
898
|
// 判定发生在点击那一刻,而 setState 到重渲染之间还有一拍能点。ref 不触发
|
|
882
899
|
// 渲染,所以每次改动配一次 bumpBusy,让按钮当场变样。
|
|
883
900
|
var updatingRef = useRef({}); // 包名 -> true,点击到预检 job 建立之间
|
|
884
901
|
var installingRef = useRef({}); // spec -> true,安装 RPC 在途
|
|
885
|
-
var
|
|
886
|
-
|
|
902
|
+
var preflightsRef = useRef({});
|
|
903
|
+
preflightsRef.current = preflights; // 渲染期同步,同 resultsRef 的写法
|
|
887
904
|
var _busyTick = useState(0);
|
|
888
905
|
var setBusyTick = _busyTick[1];
|
|
889
906
|
var bumpBusy = useCallback(function () {
|
|
@@ -1064,7 +1081,7 @@ window.__ModuleLoader__.load({
|
|
|
1064
1081
|
return updateChainBusy({
|
|
1065
1082
|
updating: updatingRef.current,
|
|
1066
1083
|
installing: installingRef.current,
|
|
1067
|
-
|
|
1084
|
+
cards: preflightsRef.current,
|
|
1068
1085
|
jobs: jobsRef.current,
|
|
1069
1086
|
}, packageName);
|
|
1070
1087
|
}, [jobsRef]);
|
|
@@ -1131,7 +1148,12 @@ window.__ModuleLoader__.load({
|
|
|
1131
1148
|
// 安全:接过预检的日志、撤掉预检条目,面板上只剩这一个任务。
|
|
1132
1149
|
doRawInstall(spec, {}, jobId);
|
|
1133
1150
|
} else {
|
|
1134
|
-
|
|
1151
|
+
// 按 jobId 落格,不覆盖别的包停着的卡片。
|
|
1152
|
+
setPreflights(function (prev) {
|
|
1153
|
+
var next = Object.assign({}, prev);
|
|
1154
|
+
next[jobId] = { spec: spec, report: report, jobId: jobId };
|
|
1155
|
+
return next;
|
|
1156
|
+
});
|
|
1135
1157
|
}
|
|
1136
1158
|
};
|
|
1137
1159
|
}, [doRawInstall, reportError]);
|
|
@@ -1306,12 +1328,15 @@ window.__ModuleLoader__.load({
|
|
|
1306
1328
|
h(JobsPanel, {
|
|
1307
1329
|
jobs: jobs,
|
|
1308
1330
|
error: error && error.scope === "tasks" ? error.message : null,
|
|
1309
|
-
|
|
1310
|
-
|
|
1331
|
+
preflights: preflights,
|
|
1332
|
+
// 这张卡片/这条批准请求对应的 install RPC 在不在途。原来是「取
|
|
1333
|
+
// installing 里的第一个 spec」的单值,同时两条链在途时只有一条显示
|
|
1334
|
+
// 得对;按 spec 问就没有这个假设了。
|
|
1335
|
+
specBusy: function (spec) { return installing[spec] === true; },
|
|
1311
1336
|
onClear: function () {
|
|
1312
1337
|
Object.keys(jobs).forEach(function (id) {
|
|
1313
1338
|
var job = jobs[id];
|
|
1314
|
-
var hasDecision = job && (
|
|
1339
|
+
var hasDecision = job && (preflights[id] !== undefined
|
|
1315
1340
|
|| (job.needsApproval && job.needsApproval.length > 0));
|
|
1316
1341
|
if (!job || isJobActive(job.status) || hasDecision) return;
|
|
1317
1342
|
var token = job && job.approvalToken ? job.approvalToken : (job && job.spec ? approvalTokensRef.current[job.spec] : undefined);
|
|
@@ -1330,33 +1355,34 @@ window.__ModuleLoader__.load({
|
|
|
1330
1355
|
});
|
|
1331
1356
|
},
|
|
1332
1357
|
onApprove: doApprove,
|
|
1333
|
-
onConfirmPreflight: function () {
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
var report = preflight.report;
|
|
1338
|
-
setPreflight(null);
|
|
1358
|
+
onConfirmPreflight: function (jobId) {
|
|
1359
|
+
var decision = preflights[jobId];
|
|
1360
|
+
if (!decision) return;
|
|
1361
|
+
dropPreflight(jobId);
|
|
1339
1362
|
// consentDigest 来自预检 job 的 extras,装的时候与当前报告比对:
|
|
1340
1363
|
// 用户点「继续」到安装真正开跑之间候选包或 profile 变了,
|
|
1341
1364
|
// 布尔同意不得沿用,要重新看新的警告。
|
|
1342
|
-
doRawInstall(spec, { acceptWarnings: true, acceptedReportDigest: report.consentDigest },
|
|
1365
|
+
doRawInstall(decision.spec, { acceptWarnings: true, acceptedReportDigest: decision.report.consentDigest }, jobId);
|
|
1343
1366
|
},
|
|
1344
|
-
onClosePreflight: function () {
|
|
1345
|
-
|
|
1346
|
-
|
|
1367
|
+
onClosePreflight: function (jobId) {
|
|
1368
|
+
var decision = preflights[jobId];
|
|
1369
|
+
if (decision && decision.spec) delete approvalTokensRef.current[decision.spec];
|
|
1370
|
+
dropPreflight(jobId);
|
|
1347
1371
|
},
|
|
1348
1372
|
onDismiss: function (id) {
|
|
1349
1373
|
var job = jobs[id];
|
|
1350
1374
|
var token = job && job.approvalToken ? job.approvalToken : (job && job.spec ? approvalTokensRef.current[job.spec] : undefined);
|
|
1351
1375
|
if (job && job.spec) delete approvalTokensRef.current[job.spec];
|
|
1352
1376
|
call("jobDismiss", { jobId: id, token: token }).catch(function () {});
|
|
1377
|
+
// job 条目撤了,挂在它上面的卡片没有宿主了。不一并撤的话
|
|
1378
|
+
// updateChainBusy 的 cards 分支会一直锁着那个包名。
|
|
1379
|
+
dropPreflight(id);
|
|
1353
1380
|
dropJob(id);
|
|
1354
1381
|
},
|
|
1355
1382
|
onDrop: dropJob,
|
|
1356
1383
|
onRestart: function () { setRestartConfirm(true); },
|
|
1357
1384
|
restarting: restarting,
|
|
1358
1385
|
hostStartedAt: hostStartedAt,
|
|
1359
|
-
approving: Object.keys(installing).filter(function (s) { return installing[s]; })[0],
|
|
1360
1386
|
}),
|
|
1361
1387
|
h("div", { className: "mkt_list" },
|
|
1362
1388
|
results == null
|
package/src/index.js
CHANGED
|
@@ -3329,7 +3329,7 @@ export async function runSelfTests() {
|
|
|
3329
3329
|
].join("\n"))();
|
|
3330
3330
|
|
|
3331
3331
|
const busy = (patch, name) => front.updateChainBusy(
|
|
3332
|
-
Object.assign({ updating: {}, installing: {},
|
|
3332
|
+
Object.assign({ updating: {}, installing: {}, cards: {}, jobs: {} }, patch), name);
|
|
3333
3333
|
const running = (spec) => ({ spec, status: "running" });
|
|
3334
3334
|
|
|
3335
3335
|
check("更新防连点:裸包名折算", front.specPackageName("foo") === "foo");
|
|
@@ -3350,7 +3350,15 @@ export async function runSelfTests() {
|
|
|
3350
3350
|
check("更新防连点:停在批准卡片上(failed + needsApproval)算未了结",
|
|
3351
3351
|
busy({ jobs: { a: { spec: "foo@1.2.3", status: "failed", needsApproval: [{ name: "x" }] } } }, "foo") === true);
|
|
3352
3352
|
check("更新防连点:停在风险卡片上时锁着(决定还没做,再起一条就是两条链)",
|
|
3353
|
-
busy({
|
|
3353
|
+
busy({ cards: { j1: { spec: "foo@1.2.3" } } }, "foo") === true);
|
|
3354
|
+
// 单槽位时代的真实卡死:第二张卡片顶掉第一张,被顶掉那条再没有入口能
|
|
3355
|
+
// 做决定,job 却一直未了结——那一行的更新按钮永远点不亮。
|
|
3356
|
+
check("更新防连点:多张卡片同时停着,各锁各的包(批量更新的必然形状)",
|
|
3357
|
+
busy({ cards: { j1: { spec: "foo@1.2.3" }, j2: { spec: "bar@2.0.0" } } }, "foo") === true
|
|
3358
|
+
&& busy({ cards: { j1: { spec: "foo@1.2.3" }, j2: { spec: "bar@2.0.0" } } }, "bar") === true);
|
|
3359
|
+
check("更新防连点:撤掉其中一张不影响另一张的锁",
|
|
3360
|
+
busy({ cards: { j2: { spec: "bar@2.0.0" } } }, "foo") === false
|
|
3361
|
+
&& busy({ cards: { j2: { spec: "bar@2.0.0" } } }, "bar") === true);
|
|
3354
3362
|
|
|
3355
3363
|
check("更新防连点:job 落终态后解锁,用户可以重试",
|
|
3356
3364
|
busy({ jobs: { a: { spec: "foo@1.2.3", status: "failed" } } }, "foo") === false);
|
|
@@ -3359,7 +3367,7 @@ export async function runSelfTests() {
|
|
|
3359
3367
|
check("更新防连点:别的包在跑不锁本行",
|
|
3360
3368
|
busy({ jobs: { a: running("bar@1.0.0") } }, "foo") === false);
|
|
3361
3369
|
check("更新防连点:风险卡片停在别的包上不锁本行",
|
|
3362
|
-
busy({
|
|
3370
|
+
busy({ cards: { j1: { spec: "bar@1.0.0" } } }, "foo") === false);
|
|
3363
3371
|
check("更新防连点:什么都没在途 → 不锁", busy({}, "foo") === false);
|
|
3364
3372
|
check("更新防连点:包名为 null 时不误判",
|
|
3365
3373
|
busy({ jobs: { a: running("foo@1.2.3") } }, null) === false);
|