dshmarket 1.70.3 → 1.71.1
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/client/client.js +131 -76
- package/lib/install.js +11 -9
- package/lib/release-candidates.js +101 -0
- package/lib/routes.js +132 -24
- package/lib/types/install.d.ts +1 -0
- package/lib/types/release-candidates.d.ts +26 -0
- package/lib/types/updates.d.ts +18 -3
- package/lib/updates.js +189 -21
- package/package.json +1 -1
- package/src/client/MarketSection.tsx +78 -33
- package/src/client/locales.ts +4 -0
- package/src/client/market-data.ts +9 -5
- package/src/client/operations.ts +16 -5
- package/src/install.ts +12 -10
- package/src/release-candidates.ts +115 -0
- package/src/routes.ts +135 -21
- package/src/updates.ts +169 -26
package/client/client.js
CHANGED
|
@@ -398,6 +398,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
398
398
|
updateFail: "更新失败",
|
|
399
399
|
upToDate: "已是最新",
|
|
400
400
|
updateHeldTag: "新版等待中",
|
|
401
|
+
updateHeldTarget: "最新版 {0} 刚发布,还要约 {1} 小时。先更新到 {2}。",
|
|
402
|
+
updateHeldTargetUnknown: "最新版 {0} 的发布时间暂不确定,还需等待。先更新到 {2}。",
|
|
401
403
|
updateHeldHint: "这个 profile 要求新版本发布满一段时间才允许安装,大约还要 {1} 小时。",
|
|
402
404
|
updateHeldEstimatedHint: "新版本已经出现在 /latest,但镜像的完整信息还没同步,所以先不提供更新;大约 {1} 分钟后会重新检查。",
|
|
403
405
|
newerPrereleaseAvailable: "预发布版 {0} 已发布",
|
|
@@ -1159,6 +1161,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
1159
1161
|
updateFail: "Update failed",
|
|
1160
1162
|
upToDate: "Up to date",
|
|
1161
1163
|
updateHeldTag: "new version waiting",
|
|
1164
|
+
updateHeldTarget: "Latest {0} was just released; about {1} hours to wait. Update to {2} for now.",
|
|
1165
|
+
updateHeldTargetUnknown: "The release time of latest {0} is unknown, so it still needs to wait. Update to {2} for now.",
|
|
1162
1166
|
updateHeldHint: "This profile waits a while before it installs a fresh release; about {1} hour(s) left.",
|
|
1163
1167
|
updateHeldEstimatedHint: "The new version is listed, but the registry mirror has not caught up with its metadata, so the update is held; the market checks again in about {1} minute(s).",
|
|
1164
1168
|
newerPrereleaseAvailable: "prerelease {0} published",
|
|
@@ -1948,6 +1952,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
1948
1952
|
if (row.restore === true && world.updates[row.name]?.kind === "linked" && findCatalogEntryForLocal(world.plugins, row.name) !== null) return null;
|
|
1949
1953
|
return "no-update";
|
|
1950
1954
|
}
|
|
1955
|
+
if (row.kind === "update" && row.eligibleVersion !== void 0 && row.eligibleVersion !== world.updates[row.name]?.eligibleVersion) return "target-changed";
|
|
1951
1956
|
return null;
|
|
1952
1957
|
}
|
|
1953
1958
|
function installedForCatalog(installed, bundles) {
|
|
@@ -4156,9 +4161,9 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
4156
4161
|
* scripts awaiting approval, a held release. `detail` is kept for the same
|
|
4157
4162
|
* reason: it is the row's own report.
|
|
4158
4163
|
*
|
|
4159
|
-
*
|
|
4160
|
-
*
|
|
4161
|
-
*
|
|
4164
|
+
* Failed updates awaiting build approval also keep their original update
|
|
4165
|
+
* options so approving after a reload cannot select a different target.
|
|
4166
|
+
* `blockedBy` and `conflicts` are deliberately dropped. `needsRefresh` is written so the
|
|
4162
4167
|
* stored shape stays the record's own, but the restore drops it: only the page
|
|
4163
4168
|
* that wrote it can still be waiting for that reload.
|
|
4164
4169
|
*/
|
|
@@ -4172,6 +4177,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
4172
4177
|
...record.reason === void 0 ? {} : { reason: record.reason },
|
|
4173
4178
|
...record.detail === void 0 ? {} : { detail: record.detail },
|
|
4174
4179
|
...record.blockedBuilds === void 0 ? {} : { blockedBuilds: [...record.blockedBuilds] },
|
|
4180
|
+
...record.kind === "update" && record.state === "failed" && (record.blockedBuilds?.length ?? 0) > 0 && record.updateOptions !== void 0 ? { updateOptions: { ...record.updateOptions } } : {},
|
|
4175
4181
|
...record.needsRefresh === true ? { needsRefresh: true } : {},
|
|
4176
4182
|
...record.heldRelease === void 0 ? {} : { heldRelease: { ...record.heldRelease } }
|
|
4177
4183
|
}));
|
|
@@ -4222,6 +4228,13 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
4222
4228
|
const detail = asText(row.detail);
|
|
4223
4229
|
const blockedBuilds = asStringList(row.blockedBuilds);
|
|
4224
4230
|
const heldRelease = asHeldRelease(row.heldRelease);
|
|
4231
|
+
const options = row.updateOptions !== null && typeof row.updateOptions === "object" && !Array.isArray(row.updateOptions) ? row.updateOptions : void 0;
|
|
4232
|
+
const updateOptions = kind === "update" && state === "failed" && blockedBuilds !== void 0 && options !== void 0 ? {
|
|
4233
|
+
...typeof options.force === "boolean" ? { force: options.force } : {},
|
|
4234
|
+
...typeof options.restore === "boolean" ? { restore: options.restore } : {},
|
|
4235
|
+
...asText(options.compatVersion) === void 0 ? {} : { compatVersion: asText(options.compatVersion) },
|
|
4236
|
+
...asText(options.eligibleVersion) === void 0 ? {} : { eligibleVersion: asText(options.eligibleVersion) }
|
|
4237
|
+
} : void 0;
|
|
4225
4238
|
return {
|
|
4226
4239
|
id,
|
|
4227
4240
|
kind,
|
|
@@ -4231,6 +4244,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
4231
4244
|
...reason === void 0 ? {} : { reason },
|
|
4232
4245
|
...detail === void 0 ? {} : { detail },
|
|
4233
4246
|
...blockedBuilds === void 0 ? {} : { blockedBuilds },
|
|
4247
|
+
...updateOptions === void 0 ? {} : { updateOptions },
|
|
4234
4248
|
...heldRelease === void 0 ? {} : { heldRelease }
|
|
4235
4249
|
};
|
|
4236
4250
|
}
|
|
@@ -8987,18 +9001,20 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
8987
9001
|
kind,
|
|
8988
9002
|
name,
|
|
8989
9003
|
restore: options.restore === true,
|
|
9004
|
+
...typeof options.eligibleVersion === "string" ? { eligibleVersion: options.eligibleVersion } : {},
|
|
8990
9005
|
...url === void 0 ? {} : { url }
|
|
8991
9006
|
}, {
|
|
8992
9007
|
installed,
|
|
8993
9008
|
updates,
|
|
8994
9009
|
plugins: data.plugins
|
|
8995
9010
|
});
|
|
8996
|
-
if (verdict !== null) return stale(verdict === "gone" ? t("agentQueueStaleGone") : t("agentQueueStaleNoUpdate"));
|
|
9011
|
+
if (verdict !== null) return stale(verdict === "target-changed" ? localizeBilingual("更新目标已变化,已跳过。请重新选择更新。 / The update target changed, so this task was skipped. Choose the update again.", lang) : verdict === "gone" ? t("agentQueueStaleGone") : t("agentQueueStaleNoUpdate"));
|
|
8997
9012
|
if (kind === "install" && url === void 0) return [];
|
|
8998
9013
|
const updateOptions = kind === "update" ? {
|
|
8999
9014
|
force: options.force === true,
|
|
9000
9015
|
restore: options.restore === true,
|
|
9001
|
-
...typeof options.compatVersion === "string" ? { compatVersion: options.compatVersion } : {}
|
|
9016
|
+
...typeof options.compatVersion === "string" ? { compatVersion: options.compatVersion } : {},
|
|
9017
|
+
...typeof options.eligibleVersion === "string" ? { eligibleVersion: options.eligibleVersion } : {}
|
|
9002
9018
|
} : void 0;
|
|
9003
9019
|
return [{
|
|
9004
9020
|
ok: true,
|
|
@@ -9045,6 +9061,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
9045
9061
|
}, [
|
|
9046
9062
|
data,
|
|
9047
9063
|
t,
|
|
9064
|
+
lang,
|
|
9048
9065
|
queueSnapshotReady,
|
|
9049
9066
|
installed,
|
|
9050
9067
|
updates
|
|
@@ -10091,7 +10108,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10091
10108
|
data,
|
|
10092
10109
|
repoHints
|
|
10093
10110
|
]);
|
|
10094
|
-
const recentlyAdded = (0, react.useMemo)(() => data === null || tab !== "discover" || q.trim() !== "" || cat !== "all" || timeRange !== "all" ? [] : visiblePlugins(data.plugins, {
|
|
10111
|
+
const recentlyAdded = (0, react.useMemo)(() => data === null || tab !== "discover" || q.trim() !== "" || cat !== "all" || timeRange !== "all" || sortField !== "downloads" || sortDir !== "desc" ? [] : visiblePlugins(data.plugins, {
|
|
10095
10112
|
category: "all",
|
|
10096
10113
|
query: "",
|
|
10097
10114
|
lang,
|
|
@@ -10105,6 +10122,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10105
10122
|
q,
|
|
10106
10123
|
cat,
|
|
10107
10124
|
timeRange,
|
|
10125
|
+
sortField,
|
|
10126
|
+
sortDir,
|
|
10108
10127
|
lang,
|
|
10109
10128
|
hostCompatibility,
|
|
10110
10129
|
compatibleWithHost,
|
|
@@ -10683,7 +10702,14 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10683
10702
|
body: "{}"
|
|
10684
10703
|
}).catch(() => {});
|
|
10685
10704
|
}, []);
|
|
10686
|
-
const doUpdate = (0, react.useCallback)((name, force = false, restore = false, compatVersion, confirmAgentsRunning = false) => {
|
|
10705
|
+
const doUpdate = (0, react.useCallback)((name, force = false, restore = false, compatVersion, confirmAgentsRunning = false, eligibleVersion) => {
|
|
10706
|
+
const expectedVersion = compatVersion === void 0 && !restore ? eligibleVersion ?? updates[name]?.eligibleVersion : void 0;
|
|
10707
|
+
const updateOptions = {
|
|
10708
|
+
force,
|
|
10709
|
+
restore,
|
|
10710
|
+
compatVersion,
|
|
10711
|
+
eligibleVersion: expectedVersion
|
|
10712
|
+
};
|
|
10687
10713
|
setInstallError(null);
|
|
10688
10714
|
setActivationWarnings([]);
|
|
10689
10715
|
setStaleName((prev) => prev === name ? null : prev);
|
|
@@ -10698,11 +10724,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10698
10724
|
kind: "update",
|
|
10699
10725
|
name,
|
|
10700
10726
|
state: "running",
|
|
10701
|
-
updateOptions
|
|
10702
|
-
force,
|
|
10703
|
-
restore,
|
|
10704
|
-
compatVersion
|
|
10705
|
-
}
|
|
10727
|
+
updateOptions
|
|
10706
10728
|
}));
|
|
10707
10729
|
return fetch(api("/dsh-market/update"), {
|
|
10708
10730
|
method: "POST",
|
|
@@ -10712,6 +10734,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10712
10734
|
...force ? { force: true } : {},
|
|
10713
10735
|
...restore ? { restore: true } : {},
|
|
10714
10736
|
...compatVersion !== void 0 ? { compatVersion } : {},
|
|
10737
|
+
...expectedVersion !== void 0 ? { eligibleVersion: expectedVersion } : {},
|
|
10715
10738
|
...confirmAgentsRunning ? { confirmAgentsRunning: true } : {}
|
|
10716
10739
|
})
|
|
10717
10740
|
}).then((res) => res.json().then((body) => ({
|
|
@@ -10726,6 +10749,15 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10726
10749
|
if (body.partial === true) setInstallError(t("partialNote"));
|
|
10727
10750
|
return;
|
|
10728
10751
|
}
|
|
10752
|
+
if (status === 409 && body.staleTarget === true) {
|
|
10753
|
+
setRecords((list) => patch(list, updateRecordId, {
|
|
10754
|
+
state: "failed",
|
|
10755
|
+
reason: String(body.error ?? "Update target changed")
|
|
10756
|
+
}));
|
|
10757
|
+
setInstallError(localizeBilingual(String(body.error ?? "Update target changed"), lang));
|
|
10758
|
+
refreshInstalled(true);
|
|
10759
|
+
return;
|
|
10760
|
+
}
|
|
10729
10761
|
if (status === 200 && body.ok && body.skipped === "current") {
|
|
10730
10762
|
setRecords((list) => drop(list, updateRecordId));
|
|
10731
10763
|
refreshInstalled(true);
|
|
@@ -10776,7 +10808,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10776
10808
|
if (Array.isArray(body.ignoredBuilds) && body.ignoredBuilds.length > 0) setBuildsSkipped({
|
|
10777
10809
|
updateName: name,
|
|
10778
10810
|
names: body.ignoredBuilds.map(String),
|
|
10779
|
-
|
|
10811
|
+
updateOptions
|
|
10780
10812
|
});
|
|
10781
10813
|
const orphans = Array.isArray(body.orphanBundles) ? body.orphanBundles.map(String) : [];
|
|
10782
10814
|
const failure = failureText(body);
|
|
@@ -10788,7 +10820,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10788
10820
|
].filter(Boolean).join("\n").trim().slice(0, 1200);
|
|
10789
10821
|
setRecords((list) => patch(list, updateRecordId, {
|
|
10790
10822
|
state: "failed",
|
|
10791
|
-
reason: clipped
|
|
10823
|
+
reason: clipped,
|
|
10824
|
+
...Array.isArray(body.ignoredBuilds) && body.ignoredBuilds.length > 0 ? { blockedBuilds: body.ignoredBuilds.map(String) } : {}
|
|
10792
10825
|
}));
|
|
10793
10826
|
setInstallError((restore ? t("restoreFail") : t("updateFail")) + ": " + name + " — " + localizeBilingual(clipped, lang));
|
|
10794
10827
|
}
|
|
@@ -10797,7 +10830,8 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
10797
10830
|
refreshInstalled,
|
|
10798
10831
|
installed,
|
|
10799
10832
|
t,
|
|
10800
|
-
lang
|
|
10833
|
+
lang,
|
|
10834
|
+
updates
|
|
10801
10835
|
]);
|
|
10802
10836
|
const doSourceMigration = (0, react.useCallback)((name, confirmAgentsRunning = false, renameTo) => {
|
|
10803
10837
|
setInstallError(null);
|
|
@@ -11397,7 +11431,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11397
11431
|
if (task.kind === "install") {
|
|
11398
11432
|
const plugin = dataRef.current?.plugins.find((candidate) => candidate.url === task.url);
|
|
11399
11433
|
if (plugin !== void 0) doInstallRef.current?.(plugin);
|
|
11400
|
-
} else if (task.kind === "update") doUpdateRef.current?.(task.name, task.updateOptions?.force, task.updateOptions?.restore, task.updateOptions?.compatVersion);
|
|
11434
|
+
} else if (task.kind === "update") doUpdateRef.current?.(task.name, task.updateOptions?.force, task.updateOptions?.restore, task.updateOptions?.compatVersion, false, task.updateOptions?.eligibleVersion);
|
|
11401
11435
|
else doUninstallRef.current?.(task.name);
|
|
11402
11436
|
} finally {
|
|
11403
11437
|
drainingRef.current = false;
|
|
@@ -11437,7 +11471,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11437
11471
|
doInstall(plugin);
|
|
11438
11472
|
} else if (record.kind === "update") {
|
|
11439
11473
|
setRecords((prev) => drop(prev, record.id));
|
|
11440
|
-
doUpdate(record.name, record.updateOptions?.force, record.updateOptions?.restore, record.updateOptions?.compatVersion, confirmAgentsRunning);
|
|
11474
|
+
doUpdate(record.name, record.updateOptions?.force, record.updateOptions?.restore, record.updateOptions?.compatVersion, confirmAgentsRunning, record.updateOptions?.eligibleVersion);
|
|
11441
11475
|
} else {
|
|
11442
11476
|
setRecords((prev) => drop(prev, record.id));
|
|
11443
11477
|
doUninstall(record.name, confirmAgentsRunning);
|
|
@@ -11704,22 +11738,26 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
11704
11738
|
});
|
|
11705
11739
|
}, [bootId]);
|
|
11706
11740
|
const doUpdateAll = (0, react.useCallback)(() => {
|
|
11707
|
-
const names = reminderBatchUpdatableNames.
|
|
11741
|
+
const names = reminderBatchUpdatableNames.map((name) => ({
|
|
11742
|
+
name,
|
|
11743
|
+
eligibleVersion: updates[name]?.eligibleVersion
|
|
11744
|
+
}));
|
|
11708
11745
|
setUpdatingAll(true);
|
|
11709
11746
|
const next = () => {
|
|
11710
|
-
const
|
|
11711
|
-
if (
|
|
11747
|
+
const item = names.shift();
|
|
11748
|
+
if (item === void 0) {
|
|
11712
11749
|
setUpdatingAll(false);
|
|
11713
11750
|
refreshInstalled(true);
|
|
11714
11751
|
return;
|
|
11715
11752
|
}
|
|
11716
|
-
doUpdate(name).then(next, next);
|
|
11753
|
+
doUpdate(item.name, false, false, void 0, false, item.eligibleVersion).then(next, next);
|
|
11717
11754
|
};
|
|
11718
11755
|
next();
|
|
11719
11756
|
}, [
|
|
11720
11757
|
reminderBatchUpdatableNames,
|
|
11721
11758
|
doUpdate,
|
|
11722
|
-
refreshInstalled
|
|
11759
|
+
refreshInstalled,
|
|
11760
|
+
updates
|
|
11723
11761
|
]);
|
|
11724
11762
|
const finishRestore = (0, react.useCallback)((body) => {
|
|
11725
11763
|
const errors = Array.isArray(body.errors) ? body.errors : [];
|
|
@@ -13123,7 +13161,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
13123
13161
|
const plugin = record.url === void 0 ? void 0 : data?.plugins.find((p) => p.url === record.url);
|
|
13124
13162
|
approveAndRetry(names, () => {
|
|
13125
13163
|
if (plugin !== void 0) doInstall(plugin);
|
|
13126
|
-
else doUpdate(record.name, false,
|
|
13164
|
+
else doUpdate(record.name, record.updateOptions?.force, record.updateOptions?.restore, record.updateOptions?.compatVersion, false, record.updateOptions?.eligibleVersion);
|
|
13127
13165
|
});
|
|
13128
13166
|
}
|
|
13129
13167
|
})
|
|
@@ -13374,11 +13412,11 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
13374
13412
|
size: "sm",
|
|
13375
13413
|
disabled: busyUrl !== null,
|
|
13376
13414
|
onClick: () => {
|
|
13377
|
-
const { plugin, updateName, names,
|
|
13415
|
+
const { plugin, updateName, names, updateOptions } = buildsSkipped;
|
|
13378
13416
|
setBuildsSkipped(null);
|
|
13379
13417
|
approveAndRetry(names, () => {
|
|
13380
13418
|
if (plugin !== void 0) doInstall(plugin);
|
|
13381
|
-
else if (updateName !== void 0) doUpdate(updateName,
|
|
13419
|
+
else if (updateName !== void 0) doUpdate(updateName, updateOptions?.force, updateOptions?.restore, updateOptions?.compatVersion, false, updateOptions?.eligibleVersion);
|
|
13382
13420
|
});
|
|
13383
13421
|
},
|
|
13384
13422
|
children: t("approveBuilds")
|
|
@@ -14594,56 +14632,70 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
14594
14632
|
"aria-hidden": "true",
|
|
14595
14633
|
children: "·"
|
|
14596
14634
|
});
|
|
14597
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
14598
|
-
|
|
14599
|
-
|
|
14600
|
-
|
|
14601
|
-
|
|
14602
|
-
|
|
14603
|
-
|
|
14604
|
-
|
|
14605
|
-
|
|
14606
|
-
|
|
14607
|
-
|
|
14608
|
-
|
|
14609
|
-
|
|
14610
|
-
|
|
14611
|
-
|
|
14612
|
-
|
|
14613
|
-
|
|
14614
|
-
|
|
14615
|
-
|
|
14616
|
-
|
|
14617
|
-
|
|
14618
|
-
|
|
14619
|
-
|
|
14620
|
-
|
|
14621
|
-
|
|
14622
|
-
|
|
14623
|
-
|
|
14624
|
-
|
|
14625
|
-
|
|
14626
|
-
|
|
14627
|
-
|
|
14628
|
-
|
|
14629
|
-
|
|
14630
|
-
|
|
14631
|
-
|
|
14632
|
-
className: Market_module_css_default.
|
|
14635
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
14636
|
+
updatable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14637
|
+
className: Market_module_css_default.irowStatus,
|
|
14638
|
+
children: [!generation && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
14639
|
+
className: Market_module_css_default.irowStatusPart,
|
|
14640
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14641
|
+
className: Market_module_css_default.irowStatusFact,
|
|
14642
|
+
children: status?.eligibleVersion != null ? t("hostUpdateReady").replace("{0}", displayLatest(status.eligibleVersion)) : status?.latest != null ? t("hostUpdateReady").replace("{0}", displayLatest(status.latest)) : t("updateAvailableShort")
|
|
14643
|
+
}), sep]
|
|
14644
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
14645
|
+
type: "button",
|
|
14646
|
+
className: Market_module_css_default.notesLink,
|
|
14647
|
+
onClick: () => openNotes(name, status?.current ?? null, status?.latest ?? null, repoUrl),
|
|
14648
|
+
children: `${t("notesLink")} ›`
|
|
14649
|
+
})]
|
|
14650
|
+
}),
|
|
14651
|
+
updatable && status?.eligibleVersion !== void 0 && status.restoreRequired !== true && status.heldUpdate !== void 0 && status.heldUpdate.latest !== status.eligibleVersion && (() => {
|
|
14652
|
+
const left = Date.parse(status.heldUpdate.until) - Date.now();
|
|
14653
|
+
const known = status.heldUpdate.estimated !== true && Number.isFinite(left) && left > 0;
|
|
14654
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
14655
|
+
className: Market_module_css_default.irowStatus,
|
|
14656
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14657
|
+
className: Market_module_css_default.metaInline,
|
|
14658
|
+
children: t(known ? "updateHeldTarget" : "updateHeldTargetUnknown").replace("{0}", displayLatest(status.heldUpdate.latest)).replace("{1}", String(Math.max(1, Math.ceil(left / 36e5)))).replace("{2}", displayLatest(status.eligibleVersion))
|
|
14659
|
+
})
|
|
14660
|
+
});
|
|
14661
|
+
})(),
|
|
14662
|
+
(exempt || ignored) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14663
|
+
className: Market_module_css_default.irowStatus,
|
|
14664
|
+
children: [
|
|
14665
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14666
|
+
className: Market_module_css_default.irowStatusIcon,
|
|
14667
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconBellOff, { size: 16 })
|
|
14668
|
+
}),
|
|
14669
|
+
exempt ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14670
|
+
className: Market_module_css_default.metaInline,
|
|
14633
14671
|
title: t("updateExemptHint"),
|
|
14634
|
-
|
|
14635
|
-
|
|
14636
|
-
|
|
14637
|
-
|
|
14638
|
-
|
|
14639
|
-
|
|
14640
|
-
|
|
14641
|
-
|
|
14642
|
-
|
|
14672
|
+
role: "status",
|
|
14673
|
+
children: t("updateExemptMark")
|
|
14674
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14675
|
+
className: Market_module_css_default.metaInline,
|
|
14676
|
+
role: "status",
|
|
14677
|
+
children: t("updateNoticeIgnored")
|
|
14678
|
+
}),
|
|
14679
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14680
|
+
className: Market_module_css_default.irowStatusAction,
|
|
14681
|
+
children: exempt ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
14682
|
+
type: "button",
|
|
14683
|
+
className: Market_module_css_default.noteToggle,
|
|
14684
|
+
title: t("updateExemptHint"),
|
|
14685
|
+
"aria-label": `${t("updateExemptRestore")} ${name}`,
|
|
14686
|
+
onClick: () => toggleUpdateExempt(name),
|
|
14687
|
+
children: t("updateExemptRestore")
|
|
14688
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
14689
|
+
type: "button",
|
|
14690
|
+
className: Market_module_css_default.noteToggle,
|
|
14691
|
+
"aria-label": `${t("updateExemptRestore")} ${name}`,
|
|
14692
|
+
onClick: () => unignoreUpdateNotice(name),
|
|
14693
|
+
children: t("updateExemptRestore")
|
|
14694
|
+
})
|
|
14643
14695
|
})
|
|
14644
|
-
|
|
14645
|
-
|
|
14646
|
-
|
|
14696
|
+
]
|
|
14697
|
+
})
|
|
14698
|
+
] });
|
|
14647
14699
|
})(),
|
|
14648
14700
|
!off && act !== void 0 && meta !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
14649
14701
|
className: Market_module_css_default.act,
|
|
@@ -14783,7 +14835,7 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
14783
14835
|
className: Market_module_css_default.metaTag,
|
|
14784
14836
|
title: t("hostUpdateHint"),
|
|
14785
14837
|
children: t("hostUpdateReady").replace("{0}", status.latest)
|
|
14786
|
-
}) : status && status.updateAvailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
14838
|
+
}) : status && status.updateAvailable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
14787
14839
|
variant: "outline",
|
|
14788
14840
|
size: "sm",
|
|
14789
14841
|
className: Market_module_css_default.warnBtn,
|
|
@@ -14792,8 +14844,11 @@ window.__ModuleLoader__.load({ id: "dshmarket", factory: (require) => {
|
|
|
14792
14844
|
if (status.restoreRequired === true) askRestore(name);
|
|
14793
14845
|
else doUpdate(name);
|
|
14794
14846
|
},
|
|
14795
|
-
children: status.restoreRequired === true ? t("restoreOnline") : t("update")
|
|
14796
|
-
})
|
|
14847
|
+
children: status.restoreRequired === true ? t("restoreOnline") : status.eligibleVersion !== void 0 ? `${t("update")} ${displayLatest(status.eligibleVersion)}` : t("update")
|
|
14848
|
+
}), status.heldUpdate !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
14849
|
+
className: Market_module_css_default.metaTag,
|
|
14850
|
+
children: t("updateHeldTag").replace("{0}", displayLatest(status.heldUpdate.latest))
|
|
14851
|
+
})] }) : status?.heldUpdate !== void 0 ? (() => {
|
|
14797
14852
|
const left = Date.parse(status.heldUpdate.until) - Date.now();
|
|
14798
14853
|
const estimated = status.heldUpdate.estimated === true;
|
|
14799
14854
|
const amount = String(Math.max(1, Math.ceil(left / (estimated ? 6e4 : 36e5))));
|
package/lib/install.js
CHANGED
|
@@ -119,6 +119,9 @@ export function isUnpublishedHostPeer(pkg, profile, explicitDir) {
|
|
|
119
119
|
*/
|
|
120
120
|
export async function withHoistRecovery(run, profile, pluginArgs, profileDirectory, options = {}) {
|
|
121
121
|
const marketFlags = options.marketFlags !== false;
|
|
122
|
+
// An already verified mature update must not rewrite the user's workspace
|
|
123
|
+
// policy as a side effect of market-level recovery (#991).
|
|
124
|
+
const repairProfilePolicy = options.preserveProfilePolicy !== true;
|
|
122
125
|
/** The option a recovery step needed and this host does not accept (#732). */
|
|
123
126
|
let unavailableOption = null;
|
|
124
127
|
// Before the FIRST run, not after a failure: the shadowed duplicate rule
|
|
@@ -127,7 +130,7 @@ export async function withHoistRecovery(run, profile, pluginArgs, profileDirecto
|
|
|
127
130
|
// small file read. Every verb that resolves the graph is covered, not just
|
|
128
131
|
// add and remove: an `install` or an in-place `update` reads the same key.
|
|
129
132
|
const verb = pluginArgs.find(argument => !argument.startsWith('-'));
|
|
130
|
-
if (verb === 'add' || verb === 'remove' || verb === 'install' || verb === 'update') {
|
|
133
|
+
if (repairProfilePolicy && (verb === 'add' || verb === 'remove' || verb === 'install' || verb === 'update')) {
|
|
131
134
|
const merged = mergeDuplicateReleaseAgeExcludes(profile, profileDirectory);
|
|
132
135
|
if (merged.length > 0) {
|
|
133
136
|
logEvent('warn', 'install', `minimumReleaseAgeExclude held several rules for ${merged.join(', ')}, and pnpm honours only the FIRST per name (#732) — merged each package's rules into one, before running`);
|
|
@@ -145,7 +148,7 @@ export async function withHoistRecovery(run, profile, pluginArgs, profileDirecto
|
|
|
145
148
|
// appended stayed dead (@Mlte0907, 1.66.7). The merge is a no-op when
|
|
146
149
|
// nothing is duplicated, so asking it costs one file read.
|
|
147
150
|
let repairedDuringRun = false;
|
|
148
|
-
if (!ok(result) && !result.cancelled && (pluginArgs[0] === 'add' || pluginArgs[0] === 'remove')) {
|
|
151
|
+
if (repairProfilePolicy && !ok(result) && !result.cancelled && (pluginArgs[0] === 'add' || pluginArgs[0] === 'remove')) {
|
|
149
152
|
const mergedNow = mergeDuplicateReleaseAgeExcludes(profile, profileDirectory);
|
|
150
153
|
if (mergedNow.length > 0) {
|
|
151
154
|
repairedDuringRun = true;
|
|
@@ -155,7 +158,7 @@ export async function withHoistRecovery(run, profile, pluginArgs, profileDirecto
|
|
|
155
158
|
}
|
|
156
159
|
if (!ok(result) && !result.cancelled && !repairedDuringRun) {
|
|
157
160
|
const failure = classifyPnpmFailure(`${result.stderr}\n${result.stdout}`, result.exitCode);
|
|
158
|
-
if (failure?.code === 'unparseable-build-key') {
|
|
161
|
+
if (failure?.code === 'unparseable-build-key' && repairProfilePolicy) {
|
|
159
162
|
// The profile's own allowBuilds block is what fails, so no retry of the
|
|
160
163
|
// same command can pass until it is repaired (#698). Drop only the
|
|
161
164
|
// source-form keys this pnpm cannot parse — bare names stay, and on
|
|
@@ -187,15 +190,14 @@ export async function withHoistRecovery(run, profile, pluginArgs, profileDirecto
|
|
|
187
190
|
// (#732) or extending a version union (#733). Either way the same
|
|
188
191
|
// rewrite fixes it, needs no option at all, and so also works on the
|
|
189
192
|
// desktop bridge that refuses options. Retry the SAME argv afterwards.
|
|
190
|
-
const mergedNow = mergeDuplicateReleaseAgeExcludes(profile, profileDirectory);
|
|
193
|
+
const mergedNow = repairProfilePolicy ? mergeDuplicateReleaseAgeExcludes(profile, profileDirectory) : [];
|
|
191
194
|
if (mergedNow.length > 0) {
|
|
192
195
|
logEvent('warn', 'install', `pnpm appended a second minimumReleaseAgeExclude rule for ${mergedNow.join(', ')} and honours only the first, shadowing its own entry (#732) — merged them into one and retrying once`);
|
|
193
196
|
result = await run(profile, pluginArgs);
|
|
194
197
|
}
|
|
195
|
-
else if (options.releaseAgeBypass === false) {
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
// job, so the command stands as it is.
|
|
198
|
+
else if (options.releaseAgeBypass === false || options.preserveProfilePolicy === true) {
|
|
199
|
+
// Fresh installs (#594) and mature updates/recovery (#991) never
|
|
200
|
+
// bypass age enforcement. Preserve the native failure and policy.
|
|
199
201
|
}
|
|
200
202
|
else if (!marketFlags) {
|
|
201
203
|
unavailableOption = RELEASE_AGE_OVERRIDE;
|
|
@@ -399,7 +401,7 @@ export async function withHoistRecovery(run, profile, pluginArgs, profileDirecto
|
|
|
399
401
|
// after a recovery retry or an official Desktop manager install. Repair
|
|
400
402
|
// the final file now, before another plugin operation reads it; a successful
|
|
401
403
|
// command must never be retried just because its exclusions needed merging.
|
|
402
|
-
if (ok(result) && (verb === 'add' || verb === 'remove' || verb === 'install' || verb === 'update')) {
|
|
404
|
+
if (repairProfilePolicy && ok(result) && (verb === 'add' || verb === 'remove' || verb === 'install' || verb === 'update')) {
|
|
403
405
|
try {
|
|
404
406
|
const merged = mergeDuplicateReleaseAgeExcludes(profile, profileDirectory);
|
|
405
407
|
if (merged.length > 0) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
function semver(value) {
|
|
2
|
+
if (typeof value !== 'string')
|
|
3
|
+
return null;
|
|
4
|
+
const matched = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.exec(value);
|
|
5
|
+
if (!matched)
|
|
6
|
+
return null;
|
|
7
|
+
const prerelease = matched[4]?.split('.') ?? [];
|
|
8
|
+
if (prerelease.some(part => /^\d+$/.test(part) && part.length > 1 && part.startsWith('0')))
|
|
9
|
+
return null;
|
|
10
|
+
return { core: [BigInt(matched[1]), BigInt(matched[2]), BigInt(matched[3])], prerelease };
|
|
11
|
+
}
|
|
12
|
+
/** Accept stable SemVer, including build metadata, at both API boundaries. */
|
|
13
|
+
export function isStableReleaseVersion(value) {
|
|
14
|
+
const parsed = semver(value);
|
|
15
|
+
return parsed !== null && parsed.prerelease.length === 0;
|
|
16
|
+
}
|
|
17
|
+
function compare(a, b) {
|
|
18
|
+
for (let i = 0; i < 3; i++) {
|
|
19
|
+
if (a.core[i] !== b.core[i])
|
|
20
|
+
return a.core[i] > b.core[i] ? 1 : -1;
|
|
21
|
+
}
|
|
22
|
+
if (a.prerelease.length === 0 || b.prerelease.length === 0)
|
|
23
|
+
return Math.sign(b.prerelease.length - a.prerelease.length);
|
|
24
|
+
for (let i = 0; i < Math.max(a.prerelease.length, b.prerelease.length); i++) {
|
|
25
|
+
const x = a.prerelease[i], y = b.prerelease[i];
|
|
26
|
+
if (x === undefined)
|
|
27
|
+
return -1;
|
|
28
|
+
if (y === undefined)
|
|
29
|
+
return 1;
|
|
30
|
+
if (x === y)
|
|
31
|
+
continue;
|
|
32
|
+
const xn = /^\d+$/.test(x), yn = /^\d+$/.test(y);
|
|
33
|
+
if (xn && yn)
|
|
34
|
+
return BigInt(x) > BigInt(y) ? 1 : -1;
|
|
35
|
+
if (xn !== yn)
|
|
36
|
+
return xn ? -1 : 1;
|
|
37
|
+
return x > y ? 1 : -1;
|
|
38
|
+
}
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
export function strictPublishTime(value) {
|
|
42
|
+
if (typeof value !== 'string' || !/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?Z$/.test(value))
|
|
43
|
+
return null;
|
|
44
|
+
const timestamp = Date.parse(value);
|
|
45
|
+
if (!Number.isFinite(timestamp))
|
|
46
|
+
return null;
|
|
47
|
+
return new Date(timestamp).toISOString().slice(0, 19) === value.slice(0, 19) ? timestamp : null;
|
|
48
|
+
}
|
|
49
|
+
export function selectMatureUpdate(packument, installed, now, policy) {
|
|
50
|
+
const latest = typeof packument?.['dist-tags']?.latest === 'string' ? packument['dist-tags'].latest : null;
|
|
51
|
+
const empty = (reason) => ({ candidate: null, latest, held: null, reason, nextMaturityAt: null, latestMaturityAt: null });
|
|
52
|
+
const top = semver(latest);
|
|
53
|
+
if (!top || top.prerelease.length || !packument.versions || !packument.time ||
|
|
54
|
+
typeof packument.versions !== 'object' || typeof packument.time !== 'object' ||
|
|
55
|
+
!Object.hasOwn(packument.versions, latest) ||
|
|
56
|
+
(policy.latestFromEndpoint != null && policy.latestFromEndpoint !== latest))
|
|
57
|
+
return empty('inconsistent');
|
|
58
|
+
const ceiling = policy.maxVersion === undefined ? top : semver(policy.maxVersion);
|
|
59
|
+
if (!ceiling || ceiling.prerelease.length ||
|
|
60
|
+
(policy.maxVersion !== undefined && !Object.hasOwn(packument.versions, policy.maxVersion)))
|
|
61
|
+
return empty('inconsistent');
|
|
62
|
+
const current = semver(installed);
|
|
63
|
+
if (!current || current.prerelease.length)
|
|
64
|
+
return empty('invalid-installed');
|
|
65
|
+
const ageMs = policy.minimumReleaseAgeMs;
|
|
66
|
+
if (!Number.isSafeInteger(ageMs) || ageMs < 0 || !Number.isFinite(now))
|
|
67
|
+
return empty('policy-unknown');
|
|
68
|
+
let candidate = null;
|
|
69
|
+
let candidateSemver = null;
|
|
70
|
+
let nextMaturityAt = null;
|
|
71
|
+
for (const version of Object.keys(packument.versions)) {
|
|
72
|
+
const parsed = semver(version);
|
|
73
|
+
if (!parsed || parsed.prerelease.length || compare(parsed, current) <= 0 || compare(parsed, top) > 0 || compare(parsed, ceiling) > 0)
|
|
74
|
+
continue;
|
|
75
|
+
const published = strictPublishTime(packument.time[version]);
|
|
76
|
+
const exempt = policy.exemptPackage === true || policy.exemptVersions?.has(version) === true;
|
|
77
|
+
// A declared exemption is not evidence for a fabricated or absent timestamp.
|
|
78
|
+
if (published === null || published > now || (!exempt && published > now - ageMs)) {
|
|
79
|
+
if (published !== null && published <= now && Number.isFinite(published + ageMs)) {
|
|
80
|
+
const at = published + ageMs;
|
|
81
|
+
if (nextMaturityAt === null || at < nextMaturityAt)
|
|
82
|
+
nextMaturityAt = at;
|
|
83
|
+
}
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (candidateSemver === null || compare(parsed, candidateSemver) > 0) {
|
|
87
|
+
candidate = version;
|
|
88
|
+
candidateSemver = parsed;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const latestPublished = strictPublishTime(packument.time[latest]);
|
|
92
|
+
const latestExempt = policy.exemptPackage === true || policy.exemptVersions?.has(latest) === true;
|
|
93
|
+
const latestMature = latestPublished !== null && latestPublished <= now &&
|
|
94
|
+
(latestExempt || latestPublished <= now - ageMs);
|
|
95
|
+
return {
|
|
96
|
+
candidate, latest, held: latestMature ? null : latest,
|
|
97
|
+
reason: candidate ? 'eligible' : latestMature ? 'current' : latestPublished === null ? 'unknown-age' : 'held',
|
|
98
|
+
nextMaturityAt,
|
|
99
|
+
latestMaturityAt: latestPublished === null || latestExempt ? null : latestPublished + ageMs,
|
|
100
|
+
};
|
|
101
|
+
}
|