dsh-plugin-upgrader 1.4.3 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -120,6 +120,41 @@ window.__ModuleLoader__.load({
120
120
  buildBlocked: "\u8FD9\u4E2A\u5305\u6709\u672A\u6279\u51C6\u7684\u6784\u5EFA\u811A\u672C",
121
121
  approve: "\u6279\u51C6\u5E76\u91CD\u542F",
122
122
  registry: "\u6CE8\u518C\u8868",
123
+ registryChange: "\u66F4\u6362\u5B89\u88C5\u6E90",
124
+ registryFollowPnpm: "\u8DDF\u968F pnpm \u914D\u7F6E",
125
+ registryFollowPnpmHint: "\u7528 pnpm \u81EA\u5DF1\u914D\u7F6E\u7684\u6E90\uFF0C\u901A\u5E38\u662F .npmrc \u91CC\u90A3\u4E00\u4E2A",
126
+ registryLoading: "\u6B63\u5728\u8BFB\u53D6\u8BBE\u7F6E\u2026",
127
+ registryLocked: "\u8FD9\u4E2A\u7248\u672C\u6CA1\u6709\u8BBE\u7F6E\u5165\u53E3\uFF0C\u65E0\u6CD5\u5728\u8FD9\u91CC\u4FEE\u6539\u5B89\u88C5\u6E90",
128
+ registryInvalid: "\u8BF7\u586B\u4E00\u4E2A http(s) \u5730\u5740\uFF0C\u4F8B\u5982 https://registry.npmmirror.com/",
129
+ registryPlaceholder: "https://registry.example.com/",
130
+ registrySaveFailed: "\u6CA1\u80FD\u4FDD\u5B58\uFF0C\u8BF7\u518D\u8BD5\u4E00\u6B21",
131
+ registryUse: "\u4F7F\u7528\u8FD9\u4E2A\u6E90",
132
+ sourcesTitle: "\u5B89\u88C5\u6E90",
133
+ sourcesHint: "\u81EA\u5E26\u6E90\u4E0D\u80FD\u6539\u4E5F\u4E0D\u80FD\u5220\uFF1B\u81EA\u5DF1\u52A0\u7684\u6E90\u53EF\u4EE5\u968F\u65F6\u6539\u56DE\u6765\u3002",
134
+ sourcesClose: "\u5173\u95ED",
135
+ sourcesBuiltin: "\u81EA\u5E26\uFF08\u5E38\u7528\uFF09",
136
+ sourcesHost: "\u5BBF\u4E3B\u914D\u7F6E\u7684",
137
+ sourcesHostHint: "pluginManager \u91CC\u914D\u7F6E\u7684\u6E90\uFF0C\u60F3\u7559\u4E0B\u6765\u5F53\u5E38\u7528\u6E90\u5C31\u70B9\u300C\u6536\u8FDB\u5217\u8868\u300D\u3002",
138
+ sourcesCustom: "\u6211\u81EA\u5DF1\u52A0\u7684",
139
+ sourcesNone: "\u8FD8\u6CA1\u6709\u81EA\u5DF1\u52A0\u8FC7\u6E90\u3002",
140
+ sourcesAdd: "\u6DFB\u52A0",
141
+ sourcesCurrent: "\u5F53\u524D\u4F7F\u7528",
142
+ sourceBuiltin: "\u81EA\u5E26",
143
+ sourceHost: "\u5BBF\u4E3B",
144
+ sourceKeep: "\u6536\u8FDB\u5217\u8868",
145
+ sourceInUse: "\u5728\u7528",
146
+ sourceTest: "\u6D4B\u8BD5",
147
+ sourceTestCurrent: "\u6D4B\u8BD5\u5F53\u524D\u6E90",
148
+ sourceEdit: "\u4FEE\u6539",
149
+ sourceDelete: "\u5220\u9664",
150
+ sourceSave: "\u4FDD\u5B58",
151
+ sourceCancel: "\u53D6\u6D88",
152
+ sourceApply: "\u7528\u5B83",
153
+ sourceApplied: "\u5DF2\u7528",
154
+ sourceTesting: "\u6D4B\u8BD5\u4E2D\u2026",
155
+ sourceReachable: "\u53EF\u4EE5\u8FDE\u4E0A",
156
+ sourceUnreachable: "\u8FDE\u4E0D\u4E0A",
157
+ sourceExists: "\u8FD9\u4E2A\u6E90\u5DF2\u7ECF\u5728\u5217\u8868\u91CC\u4E86",
123
158
  generated: "\u6570\u636E\u66F4\u65B0\u4E8E",
124
159
  sourcePath: "\u672C\u5730\u8DEF\u5F84\u5B89\u88C5\uFF0C\u65E0\u6CD5\u68C0\u67E5\u7248\u672C",
125
160
  sourceGit: "Git \u5B89\u88C5\uFF0C\u65E0\u6CD5\u68C0\u67E5\u7248\u672C",
@@ -204,6 +239,41 @@ window.__ModuleLoader__.load({
204
239
  buildBlocked: "This package has unapproved build scripts",
205
240
  approve: "Approve and retry",
206
241
  registry: "Registry",
242
+ registryChange: "Change install source",
243
+ registryFollowPnpm: "Follow pnpm's config",
244
+ registryFollowPnpmHint: "Use the registry pnpm itself is configured with, usually the one in .npmrc",
245
+ registryLoading: "Reading the setting\u2026",
246
+ registryLocked: "This build has no settings entry, so the source cannot be changed here",
247
+ registryInvalid: "Enter an http(s) address, for example https://registry.npmmirror.com/",
248
+ registryPlaceholder: "https://registry.example.com/",
249
+ registrySaveFailed: "Could not save it \u2014 try again",
250
+ registryUse: "Use this source",
251
+ sourcesTitle: "Install sources",
252
+ sourcesHint: "The built-in sources cannot be changed or removed. The ones you add can.",
253
+ sourcesClose: "Close",
254
+ sourcesBuiltin: "Built in",
255
+ sourcesHost: "Configured on the host",
256
+ sourcesHostHint: 'Sources from pluginManager. Keep one for later with "Add to my list".',
257
+ sourcesCustom: "Added by you",
258
+ sourcesNone: "You have not added a source yet.",
259
+ sourcesAdd: "Add",
260
+ sourcesCurrent: "In use",
261
+ sourceBuiltin: "Built in",
262
+ sourceHost: "Host",
263
+ sourceKeep: "Add to my list",
264
+ sourceInUse: "In use",
265
+ sourceTest: "Test",
266
+ sourceTestCurrent: "Test the current source",
267
+ sourceEdit: "Edit",
268
+ sourceDelete: "Delete",
269
+ sourceSave: "Save",
270
+ sourceCancel: "Cancel",
271
+ sourceApply: "Use it",
272
+ sourceApplied: "In use",
273
+ sourceTesting: "Testing\u2026",
274
+ sourceReachable: "Reachable",
275
+ sourceUnreachable: "Unreachable",
276
+ sourceExists: "That source is already in the list",
207
277
  generated: "Updated",
208
278
  sourcePath: "Installed from a local path, so no versions are published",
209
279
  sourceGit: "Installed from git, so no versions are published",
@@ -351,6 +421,15 @@ window.__ModuleLoader__.load({
351
421
  } catch {
352
422
  }
353
423
  }
424
+ async function probeRegistry(registry) {
425
+ const response = await fetch(`${API}/registry/test`, {
426
+ method: "POST",
427
+ credentials: "same-origin",
428
+ headers: { "content-type": "application/json" },
429
+ body: JSON.stringify({ registry })
430
+ });
431
+ return readJson(response);
432
+ }
354
433
 
355
434
  // src/client/boot.ts
356
435
  function bootBatches(wire) {
@@ -427,9 +506,9 @@ window.__ModuleLoader__.load({
427
506
  }
428
507
  function activeLocaleId(locale) {
429
508
  try {
430
- const read = locale?.getLocale;
431
- if (typeof read !== "function") return "en";
432
- return String(read.call(locale)?.active ?? "en");
509
+ const read2 = locale?.getLocale;
510
+ if (typeof read2 !== "function") return "en";
511
+ return String(read2.call(locale)?.active ?? "en");
433
512
  } catch {
434
513
  return "en";
435
514
  }
@@ -456,6 +535,118 @@ window.__ModuleLoader__.load({
456
535
  );
457
536
  }
458
537
 
538
+ // src/client/registry.ts
539
+ var REGISTRY_FIELD = "registry";
540
+ var SOURCES_FIELD = "customRegistries";
541
+ var WELL_KNOWN_REGISTRIES = [
542
+ "https://registry.npmjs.org/",
543
+ "https://registry.npmmirror.com/"
544
+ ];
545
+ function isBuiltinRegistry(url) {
546
+ const key = comparisonKey(url);
547
+ return WELL_KNOWN_REGISTRIES.some((known) => comparisonKey(known) === key);
548
+ }
549
+ var LOCKED = (reason) => ({
550
+ saved: "",
551
+ custom: [],
552
+ writable: false,
553
+ unavailable: reason,
554
+ save: () => Promise.resolve(false),
555
+ saveSources: () => Promise.resolve(false)
556
+ });
557
+ function comparisonKey(url) {
558
+ return url.trim().replace(/\/+$/, "").toLowerCase();
559
+ }
560
+ function normalizeSource(url) {
561
+ const trimmed = url.trim();
562
+ return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
563
+ }
564
+ function isRegistryAddress(url) {
565
+ return /^https?:\/\/\S+$/i.test(url.trim());
566
+ }
567
+ function read(snapshot) {
568
+ const saved = snapshot?.value?.[REGISTRY_FIELD];
569
+ const custom = snapshot?.value?.[SOURCES_FIELD];
570
+ return {
571
+ saved: typeof saved === "string" ? saved : "",
572
+ custom: Array.isArray(custom) ? custom.filter((entry) => typeof entry === "string" && entry.trim() !== "") : [],
573
+ writable: snapshot?.writable === true && snapshot.status === "ready"
574
+ };
575
+ }
576
+ function useRegistrySetting(configForms, namespace) {
577
+ const React = getReact();
578
+ const [snapshot, setSnapshot] = React.useState(void 0);
579
+ React.useEffect(() => {
580
+ if (configForms === void 0) return void 0;
581
+ let live = true;
582
+ const form = configForms.get(namespace);
583
+ const sync = () => {
584
+ if (live) setSnapshot(form.getSnapshot());
585
+ };
586
+ sync();
587
+ const off = form.subscribe(sync);
588
+ return () => {
589
+ live = false;
590
+ if (typeof off === "function") off();
591
+ };
592
+ }, [configForms, namespace]);
593
+ if (configForms === void 0) {
594
+ return LOCKED("no-config-forms");
595
+ }
596
+ const { saved, custom, writable } = read(snapshot);
597
+ return {
598
+ saved,
599
+ custom,
600
+ writable,
601
+ unavailable: snapshot === void 0 ? "loading" : writable ? void 0 : "not-writable",
602
+ save: async (value) => {
603
+ if (configForms === void 0) return false;
604
+ return configForms.get(namespace).set(REGISTRY_FIELD, value);
605
+ },
606
+ saveSources: async (value) => {
607
+ if (configForms === void 0) return false;
608
+ return configForms.get(namespace).set(SOURCES_FIELD, [...value]);
609
+ }
610
+ };
611
+ }
612
+ function offeredRegistries(known) {
613
+ const out = [];
614
+ const seen = /* @__PURE__ */ new Set();
615
+ for (const candidate of [...known]) {
616
+ if (typeof candidate !== "string") continue;
617
+ const url = candidate.trim();
618
+ if (url === "") continue;
619
+ const key = comparisonKey(url);
620
+ if (seen.has(key)) continue;
621
+ seen.add(key);
622
+ out.push(normalizeSource(url));
623
+ }
624
+ return out;
625
+ }
626
+ function addSource(custom, url) {
627
+ const trimmed = url.trim();
628
+ if (!isRegistryAddress(trimmed)) return void 0;
629
+ const key = comparisonKey(trimmed);
630
+ if (isBuiltinRegistry(trimmed)) return void 0;
631
+ if (custom.some((entry) => comparisonKey(entry) === key)) return void 0;
632
+ return [...custom, normalizeSource(trimmed)];
633
+ }
634
+ function editSource(custom, index, url) {
635
+ if (!Number.isInteger(index) || index < 0 || index >= custom.length) return void 0;
636
+ const trimmed = url.trim();
637
+ if (!isRegistryAddress(trimmed)) return void 0;
638
+ const key = comparisonKey(trimmed);
639
+ if (isBuiltinRegistry(trimmed)) return void 0;
640
+ if (custom.some((entry, at) => at !== index && comparisonKey(entry) === key)) return void 0;
641
+ const next = [...custom];
642
+ next[index] = normalizeSource(trimmed);
643
+ return next;
644
+ }
645
+ function removeSource(custom, index) {
646
+ if (!Number.isInteger(index) || index < 0 || index >= custom.length) return void 0;
647
+ return custom.filter((_entry, at) => at !== index);
648
+ }
649
+
459
650
  // src/client/styles.ts
460
651
  var STYLE_ID = "dsh-plugin-upgrader-style";
461
652
  var CSS = `
@@ -507,6 +698,45 @@ window.__ModuleLoader__.load({
507
698
  .dsh-pu-note { font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
508
699
  .dsh-pu-note.is-err { color: var(--dsw-alias-state-error-primary); }
509
700
  .dsh-pu-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; color: var(--dsw-alias-label-secondary); }
701
+ .dsh-pu-reg { position: relative; display: inline-flex; align-items: center; gap: 6px; }
702
+ .dsh-pu-reg-btn { max-width: 360px; }
703
+ .dsh-pu-reg-btn.is-open { border-color: var(--dsw-alias-brand-primary); }
704
+ .dsh-pu-reg-label { color: var(--dsw-alias-label-secondary); flex: none; }
705
+ .dsh-pu-reg-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 250px; }
706
+ .dsh-pu-reg-caret { color: var(--dsw-alias-label-tertiary); font-size: 9px; flex: none; }
707
+ .dsh-pu-reg-panel { position: absolute; z-index: 20; top: calc(100% + 6px); left: 0; min-width: 320px; max-width: 420px; display: flex; flex-direction: column; gap: 2px; padding: 6px; border-radius: 10px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-1); box-shadow: 0 12px 28px rgba(0, 0, 0, .18); }
708
+ .dsh-pu-reg-item { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 6px 8px; border-radius: 6px; border: 0; background: none; color: var(--dsw-alias-label-primary); cursor: pointer; text-align: left; font-family: inherit; font-size: 11px; }
709
+ .dsh-pu-reg-item:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
710
+ .dsh-pu-reg-item:disabled { opacity: .55; cursor: default; }
711
+ .dsh-pu-reg-item.is-active { background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent); box-shadow: inset 2px 0 0 var(--dsw-alias-brand-primary); }
712
+ .dsh-pu-reg-item small { color: var(--dsw-alias-label-tertiary); font-size: 10px; }
713
+ .dsh-pu-reg-custom { display: flex; gap: 6px; align-items: center; padding: 6px 4px 2px; border-top: 1px dashed var(--dsw-alias-border-l1); margin-top: 4px; }
714
+ .dsh-pu-reg-custom input { flex: 1; min-width: 0; height: 26px; border-radius: 6px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); padding: 0 8px; font-size: 11px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; outline: none; }
715
+ .dsh-pu-reg-custom input:focus { border-color: var(--dsw-alias-brand-primary); }
716
+ .dsh-pu-reg-problem { font-size: 10.5px; color: var(--dsw-alias-state-error-primary); padding: 4px 4px 2px; }
717
+ .dsh-pu-modal { position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center; padding: 24px; }
718
+ .dsh-pu-modal-backdrop { position: absolute; inset: 0; background: rgba(0, 0, 0, .38); }
719
+ .dsh-pu-modal-body { position: relative; display: flex; flex-direction: column; gap: 8px; width: min(560px, 100%); max-height: min(640px, 100%); overflow: auto; padding: 16px; border-radius: 14px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-1); box-shadow: 0 24px 64px rgba(0, 0, 0, .28); }
720
+ .dsh-pu-modal-head { display: flex; align-items: center; gap: 10px; }
721
+ .dsh-pu-modal-title { font-size: 14px; font-weight: 600; flex: 1; min-width: 0; }
722
+ .dsh-pu-modal-sub { margin: 0; font-size: 11px; color: var(--dsw-alias-label-tertiary); line-height: 1.5; }
723
+ .dsh-pu-src-group { font-size: 11px; font-weight: 600; color: var(--dsw-alias-label-secondary); padding-top: 6px; }
724
+ .dsh-pu-src-list { display: flex; flex-direction: column; gap: 6px; }
725
+ .dsh-pu-src-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 9px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-2); }
726
+ .dsh-pu-src-row.is-active { border-color: var(--dsw-alias-brand-primary); background: color-mix(in srgb, var(--dsw-alias-brand-primary) 9%, transparent); }
727
+ .dsh-pu-src-row.is-editing { flex-direction: column; align-items: stretch; }
728
+ .dsh-pu-src-id { display: flex; flex-direction: column; gap: 4px; min-width: 0; flex: 1; }
729
+ .dsh-pu-src-url { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-primary); }
730
+ .dsh-pu-src-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
731
+ .dsh-pu-src-actions { display: flex; align-items: center; gap: 4px; flex: none; flex-wrap: wrap; }
732
+ .dsh-pu-src-verdict { flex-basis: 100%; display: flex; align-items: center; gap: 6px; font-size: 10.5px; color: var(--dsw-alias-label-tertiary); }
733
+ .dsh-pu-src-verdict.is-ok { color: var(--dsw-alias-state-success-primary); }
734
+ .dsh-pu-src-verdict.is-err { color: var(--dsw-alias-state-error-primary); }
735
+ .dsh-pu-src-input { flex: 1; min-width: 0; height: 28px; border-radius: 7px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); padding: 0 9px; font-size: 11px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; outline: none; }
736
+ .dsh-pu-src-input:focus { border-color: var(--dsw-alias-brand-primary); }
737
+ .dsh-pu-src-add { display: flex; align-items: center; gap: 6px; padding-top: 4px; }
738
+ .dsh-pu-src-foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-top: 8px; border-top: 1px dashed var(--dsw-alias-border-l1); }
739
+ .dsh-pu-src-foot .dsh-pu-hint { flex: 1; min-width: 160px; }
510
740
  .dsh-pu-tag { font-size: 10px; line-height: 1; padding: 3px 6px; border-radius: 999px; border: 1px solid var(--dsw-alias-border-l1); color: var(--dsw-alias-label-secondary); font-weight: 500; white-space: nowrap; }
511
741
  .dsh-pu-tag.is-new { border-color: var(--dsw-alias-brand-primary); color: var(--dsw-alias-brand-primary); background: color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, transparent); }
512
742
  .dsh-pu-tag.is-err { border-color: var(--dsw-alias-state-error-primary); color: var(--dsw-alias-state-error-primary); }
@@ -872,16 +1102,476 @@ window.__ModuleLoader__.load({
872
1102
  );
873
1103
  }
874
1104
 
1105
+ // src/client/components/SourceDialog.ts
1106
+ function RegistrySourceButton(props) {
1107
+ const h = getH();
1108
+ const React = getReact();
1109
+ const { t, active, known, setting, onChanged, disabled } = props;
1110
+ const [open, setOpen] = React.useState(false);
1111
+ const [draft, setDraft] = React.useState("");
1112
+ const [editing, setEditing] = React.useState(void 0);
1113
+ const [editDraft, setEditDraft] = React.useState("");
1114
+ const [busy, setBusy] = React.useState(false);
1115
+ const [problem, setProblem] = React.useState(void 0);
1116
+ const [probes, setProbes] = React.useState({});
1117
+ React.useEffect(() => {
1118
+ if (!open) return void 0;
1119
+ const onKey = (event) => {
1120
+ if (event.key !== "Escape") return;
1121
+ event.preventDefault();
1122
+ setOpen(false);
1123
+ };
1124
+ document.addEventListener("keydown", onKey);
1125
+ return () => document.removeEventListener("keydown", onKey);
1126
+ }, [open]);
1127
+ const saved = setting.saved.trim();
1128
+ const current = saved === "" ? t("registryFollowPnpm") : saved;
1129
+ const custom = setting.custom;
1130
+ const hostSources = offeredRegistries(known).filter(
1131
+ (url) => !isBuiltinRegistry(url) && !custom.some((entry) => comparisonKey(entry) === comparisonKey(url)) && comparisonKey(url) !== comparisonKey(saved)
1132
+ );
1133
+ if (setting.unavailable === "no-config-forms") {
1134
+ return h("span", { className: "dsh-pu-mono" }, `${t("registry")}: ${active}`);
1135
+ }
1136
+ const apply2 = async (value) => {
1137
+ if (busy) return false;
1138
+ setProblem(void 0);
1139
+ const trimmed = value.trim();
1140
+ if (trimmed !== "" && !isRegistryAddress(trimmed)) {
1141
+ setProblem(t("registryInvalid"));
1142
+ return false;
1143
+ }
1144
+ setBusy(true);
1145
+ const ok = await setting.save(trimmed);
1146
+ setBusy(false);
1147
+ if (!ok) {
1148
+ setProblem(t("registrySaveFailed"));
1149
+ return false;
1150
+ }
1151
+ onChanged();
1152
+ return true;
1153
+ };
1154
+ const writeSources = async (next) => {
1155
+ setProblem(void 0);
1156
+ setBusy(true);
1157
+ const ok = await setting.saveSources(next);
1158
+ setBusy(false);
1159
+ if (!ok) {
1160
+ setProblem(t("registrySaveFailed"));
1161
+ return false;
1162
+ }
1163
+ return true;
1164
+ };
1165
+ const add = async (value) => {
1166
+ const candidate = value ?? draft;
1167
+ const next = addSource(custom, candidate);
1168
+ if (next === void 0) {
1169
+ setProblem(isRegistryAddress(candidate) ? t("sourceExists") : t("registryInvalid"));
1170
+ return;
1171
+ }
1172
+ if (!await writeSources(next)) return;
1173
+ if (value === void 0) setDraft("");
1174
+ };
1175
+ const commitEdit = async (index) => {
1176
+ const next = editSource(custom, index, editDraft);
1177
+ if (next === void 0) {
1178
+ setProblem(isRegistryAddress(editDraft) ? t("sourceExists") : t("registryInvalid"));
1179
+ return;
1180
+ }
1181
+ const wasActive = comparisonKey(custom[index] ?? "") === comparisonKey(saved);
1182
+ if (!await writeSources(next)) return;
1183
+ setEditing(void 0);
1184
+ setEditDraft("");
1185
+ if (wasActive) await apply2(next[index] ?? "");
1186
+ };
1187
+ const drop = async (index) => {
1188
+ const next = removeSource(custom, index);
1189
+ if (next === void 0) return;
1190
+ const wasActive = comparisonKey(custom[index] ?? "") === comparisonKey(saved);
1191
+ if (!await writeSources(next)) return;
1192
+ if (editing === index) setEditing(void 0);
1193
+ if (wasActive) await apply2("");
1194
+ };
1195
+ const test = async (url) => {
1196
+ const key = comparisonKey(url);
1197
+ setProbes((previous) => ({ ...previous, [key]: { status: "testing" } }));
1198
+ let result;
1199
+ try {
1200
+ result = await probeRegistry(url);
1201
+ } catch (error) {
1202
+ const detail = error instanceof Error ? error.message : String(error);
1203
+ setProbes((previous) => ({
1204
+ ...previous,
1205
+ [key]: { status: "fail", detail }
1206
+ }));
1207
+ return;
1208
+ }
1209
+ setProbes((previous) => ({
1210
+ ...previous,
1211
+ [key]: {
1212
+ status: result.ok ? "ok" : "fail",
1213
+ detail: result.ok && result.ms !== void 0 ? `${result.detail} \xB7 ${result.ms} ms` : result.detail
1214
+ }
1215
+ }));
1216
+ };
1217
+ const verdict = (url) => {
1218
+ const state = probes[comparisonKey(url)];
1219
+ if (state === void 0) return null;
1220
+ if (state.status === "testing") {
1221
+ return h(
1222
+ "span",
1223
+ { className: "dsh-pu-src-verdict" },
1224
+ h("i", { className: "dsh-pu-spin" }),
1225
+ t("sourceTesting")
1226
+ );
1227
+ }
1228
+ const ok = state.status === "ok";
1229
+ return h(
1230
+ "span",
1231
+ { className: `dsh-pu-src-verdict ${ok ? "is-ok" : "is-err"}` },
1232
+ `${ok ? t("sourceReachable") : t("sourceUnreachable")}${state.detail === void 0 ? "" : ` \xB7 ${state.detail}`}`
1233
+ );
1234
+ };
1235
+ const row = (url, inUse, editable, index) => h(
1236
+ "div",
1237
+ { className: `dsh-pu-src-row${inUse ? " is-active" : ""}`, key: url },
1238
+ h(
1239
+ "div",
1240
+ { className: "dsh-pu-src-id" },
1241
+ h("span", { className: "dsh-pu-mono dsh-pu-src-url" }, url),
1242
+ h(
1243
+ "div",
1244
+ { className: "dsh-pu-src-meta" },
1245
+ editable ? null : h("span", { className: "dsh-pu-tag" }, t("sourceBuiltin")),
1246
+ inUse ? h("span", { className: "dsh-pu-tag is-new" }, t("sourceInUse")) : null
1247
+ )
1248
+ ),
1249
+ h(
1250
+ "div",
1251
+ { className: "dsh-pu-src-actions" },
1252
+ h(
1253
+ "button",
1254
+ {
1255
+ type: "button",
1256
+ className: "dsh-pu-btn is-tiny",
1257
+ disabled: busy || probes[comparisonKey(url)]?.status === "testing",
1258
+ onClick: () => {
1259
+ void test(url);
1260
+ }
1261
+ },
1262
+ t("sourceTest")
1263
+ ),
1264
+ editable ? h(
1265
+ "button",
1266
+ {
1267
+ type: "button",
1268
+ className: "dsh-pu-btn is-tiny",
1269
+ disabled: busy,
1270
+ onClick: () => {
1271
+ setProblem(void 0);
1272
+ setEditing(index);
1273
+ setEditDraft(url);
1274
+ }
1275
+ },
1276
+ t("sourceEdit")
1277
+ ) : null,
1278
+ editable ? h(
1279
+ "button",
1280
+ {
1281
+ type: "button",
1282
+ className: "dsh-pu-btn is-tiny is-ghost",
1283
+ disabled: busy,
1284
+ onClick: () => {
1285
+ void drop(index);
1286
+ }
1287
+ },
1288
+ t("sourceDelete")
1289
+ ) : null,
1290
+ h(
1291
+ "button",
1292
+ {
1293
+ type: "button",
1294
+ className: `dsh-pu-btn is-tiny${inUse ? "" : " is-primary"}`,
1295
+ disabled: busy || inUse,
1296
+ onClick: () => {
1297
+ void apply2(url);
1298
+ }
1299
+ },
1300
+ inUse ? t("sourceApplied") : t("sourceApply")
1301
+ )
1302
+ ),
1303
+ verdict(url)
1304
+ );
1305
+ return h(
1306
+ "div",
1307
+ { className: "dsh-pu-reg" },
1308
+ h(
1309
+ "button",
1310
+ {
1311
+ type: "button",
1312
+ className: `dsh-pu-btn is-tiny dsh-pu-reg-btn${open ? " is-open" : ""}`,
1313
+ disabled: disabled || setting.writable !== true,
1314
+ title: setting.writable === true ? t("registryChange") : t("registryLocked"),
1315
+ onClick: () => setOpen((value) => !value)
1316
+ },
1317
+ h("span", { className: "dsh-pu-reg-label" }, `${t("registry")}:`),
1318
+ h("span", { className: "dsh-pu-mono dsh-pu-reg-value" }, current),
1319
+ h("span", { className: "dsh-pu-reg-caret" }, "\u22EF")
1320
+ ),
1321
+ setting.writable === true ? null : h(
1322
+ "span",
1323
+ { className: "dsh-pu-hint" },
1324
+ setting.unavailable === "loading" ? t("registryLoading") : t("registryLocked")
1325
+ ),
1326
+ open ? h(
1327
+ "div",
1328
+ { className: "dsh-pu-modal" },
1329
+ h("div", {
1330
+ className: "dsh-pu-modal-backdrop",
1331
+ onClick: () => setOpen(false),
1332
+ "aria-hidden": "true"
1333
+ }),
1334
+ h(
1335
+ "div",
1336
+ {
1337
+ className: "dsh-pu-modal-body",
1338
+ role: "dialog",
1339
+ "aria-modal": "true",
1340
+ "aria-label": t("sourcesTitle")
1341
+ },
1342
+ h(
1343
+ "div",
1344
+ { className: "dsh-pu-modal-head" },
1345
+ h("span", { className: "dsh-pu-modal-title" }, t("sourcesTitle")),
1346
+ h(
1347
+ "button",
1348
+ {
1349
+ type: "button",
1350
+ className: "dsh-pu-btn is-ghost is-tiny",
1351
+ onClick: () => setOpen(false),
1352
+ "aria-label": t("sourcesClose")
1353
+ },
1354
+ "\u2715"
1355
+ )
1356
+ ),
1357
+ h("p", { className: "dsh-pu-modal-sub" }, t("sourcesHint")),
1358
+ h("div", { className: "dsh-pu-src-group" }, t("sourcesBuiltin")),
1359
+ h(
1360
+ "div",
1361
+ { className: "dsh-pu-src-list" },
1362
+ WELL_KNOWN_REGISTRIES.map(
1363
+ (url) => row(url, comparisonKey(saved) === comparisonKey(url), false, -1)
1364
+ )
1365
+ ),
1366
+ hostSources.length === 0 ? null : h(
1367
+ "div",
1368
+ null,
1369
+ h("div", { className: "dsh-pu-src-group" }, t("sourcesHost")),
1370
+ h("p", { className: "dsh-pu-hint" }, t("sourcesHostHint")),
1371
+ h(
1372
+ "div",
1373
+ { className: "dsh-pu-src-list" },
1374
+ hostSources.map(
1375
+ (url) => h(
1376
+ "div",
1377
+ { className: "dsh-pu-src-row", key: url },
1378
+ h(
1379
+ "div",
1380
+ { className: "dsh-pu-src-id" },
1381
+ h("span", { className: "dsh-pu-mono dsh-pu-src-url" }, url),
1382
+ h(
1383
+ "div",
1384
+ { className: "dsh-pu-src-meta" },
1385
+ h("span", { className: "dsh-pu-tag" }, t("sourceHost"))
1386
+ )
1387
+ ),
1388
+ h(
1389
+ "div",
1390
+ { className: "dsh-pu-src-actions" },
1391
+ h(
1392
+ "button",
1393
+ {
1394
+ type: "button",
1395
+ className: "dsh-pu-btn is-tiny",
1396
+ disabled: busy || probes[comparisonKey(url)]?.status === "testing",
1397
+ onClick: () => {
1398
+ void test(url);
1399
+ }
1400
+ },
1401
+ t("sourceTest")
1402
+ ),
1403
+ h(
1404
+ "button",
1405
+ {
1406
+ type: "button",
1407
+ className: "dsh-pu-btn is-tiny",
1408
+ disabled: busy,
1409
+ onClick: () => {
1410
+ void add(url);
1411
+ }
1412
+ },
1413
+ t("sourceKeep")
1414
+ ),
1415
+ h(
1416
+ "button",
1417
+ {
1418
+ type: "button",
1419
+ className: "dsh-pu-btn is-tiny is-primary",
1420
+ disabled: busy,
1421
+ onClick: () => {
1422
+ void apply2(url);
1423
+ }
1424
+ },
1425
+ t("sourceApply")
1426
+ )
1427
+ ),
1428
+ verdict(url)
1429
+ )
1430
+ )
1431
+ )
1432
+ ),
1433
+ h("div", { className: "dsh-pu-src-group" }, t("sourcesCustom")),
1434
+ custom.length === 0 ? h("p", { className: "dsh-pu-hint" }, t("sourcesNone")) : h(
1435
+ "div",
1436
+ { className: "dsh-pu-src-list" },
1437
+ custom.map(
1438
+ (url, index) => editing === index ? h(
1439
+ "div",
1440
+ { className: "dsh-pu-src-row is-editing", key: `${url}#edit` },
1441
+ h("input", {
1442
+ type: "url",
1443
+ inputMode: "url",
1444
+ className: "dsh-pu-src-input",
1445
+ value: editDraft,
1446
+ "aria-label": t("sourceEdit"),
1447
+ onChange: (event) => setEditDraft(event.target.value),
1448
+ onKeyDown: (event) => {
1449
+ if (event.key === "Enter") {
1450
+ event.preventDefault();
1451
+ void commitEdit(index);
1452
+ }
1453
+ if (event.key === "Escape") {
1454
+ event.preventDefault();
1455
+ setEditing(void 0);
1456
+ }
1457
+ }
1458
+ }),
1459
+ h(
1460
+ "div",
1461
+ { className: "dsh-pu-src-actions" },
1462
+ h(
1463
+ "button",
1464
+ {
1465
+ type: "button",
1466
+ className: "dsh-pu-btn is-tiny is-primary",
1467
+ disabled: busy || editDraft.trim() === "",
1468
+ onClick: () => {
1469
+ void commitEdit(index);
1470
+ }
1471
+ },
1472
+ t("sourceSave")
1473
+ ),
1474
+ h(
1475
+ "button",
1476
+ {
1477
+ type: "button",
1478
+ className: "dsh-pu-btn is-tiny is-ghost",
1479
+ disabled: busy,
1480
+ onClick: () => setEditing(void 0)
1481
+ },
1482
+ t("sourceCancel")
1483
+ )
1484
+ )
1485
+ ) : row(url, comparisonKey(saved) === comparisonKey(url), true, index)
1486
+ )
1487
+ ),
1488
+ h(
1489
+ "div",
1490
+ { className: "dsh-pu-src-add" },
1491
+ h("input", {
1492
+ type: "url",
1493
+ inputMode: "url",
1494
+ className: "dsh-pu-src-input",
1495
+ placeholder: t("registryPlaceholder"),
1496
+ value: draft,
1497
+ "aria-label": t("sourcesAdd"),
1498
+ onChange: (event) => setDraft(event.target.value),
1499
+ onKeyDown: (event) => {
1500
+ if (event.key !== "Enter") return;
1501
+ event.preventDefault();
1502
+ void add();
1503
+ }
1504
+ }),
1505
+ h(
1506
+ "button",
1507
+ {
1508
+ type: "button",
1509
+ className: "dsh-pu-btn is-tiny",
1510
+ disabled: busy || draft.trim() === "",
1511
+ onClick: () => {
1512
+ void add();
1513
+ }
1514
+ },
1515
+ t("sourcesAdd")
1516
+ )
1517
+ ),
1518
+ problem === void 0 ? null : h("div", { className: "dsh-pu-reg-problem" }, problem),
1519
+ h(
1520
+ "div",
1521
+ { className: "dsh-pu-src-foot" },
1522
+ h(
1523
+ "span",
1524
+ { className: "dsh-pu-hint" },
1525
+ h("span", null, `${t("sourcesCurrent")}: `),
1526
+ h("span", { className: "dsh-pu-mono" }, active === "" ? t("registryFollowPnpm") : active)
1527
+ ),
1528
+ h(
1529
+ "button",
1530
+ {
1531
+ type: "button",
1532
+ className: "dsh-pu-btn is-tiny",
1533
+ disabled: active === "" || probes[comparisonKey(active)]?.status === "testing",
1534
+ onClick: () => {
1535
+ void test(active);
1536
+ }
1537
+ },
1538
+ t("sourceTestCurrent")
1539
+ )
1540
+ ),
1541
+ active === "" ? null : verdict(active),
1542
+ h(
1543
+ "div",
1544
+ { className: "dsh-pu-src-foot" },
1545
+ h("span", { className: "dsh-pu-hint" }, t("registryFollowPnpmHint")),
1546
+ h(
1547
+ "button",
1548
+ {
1549
+ type: "button",
1550
+ className: `dsh-pu-btn is-tiny${saved === "" ? " is-ghost" : ""}`,
1551
+ disabled: busy || saved === "",
1552
+ onClick: () => {
1553
+ void apply2("");
1554
+ }
1555
+ },
1556
+ saved === "" ? t("sourceApplied") : t("registryFollowPnpm")
1557
+ )
1558
+ )
1559
+ )
1560
+ ) : null
1561
+ );
1562
+ }
1563
+
875
1564
  // src/client/components/Tab.ts
876
1565
  var SELF_NAME = "dsh-plugin-upgrader";
877
1566
  var APPEARANCE_RELOAD_DELAY_MS = 1200;
878
1567
  function Tab(props) {
879
1568
  const h = getH();
880
1569
  const React = getReact();
881
- const { t, locale } = props;
1570
+ const { t, locale, configForms } = props;
882
1571
  const revision = useLocaleRevision(locale);
883
1572
  const localeId = React.useMemo(() => activeLocaleId(locale), [locale, revision]);
884
1573
  const { data, error, loading, reload } = useOverview(localeId);
1574
+ const registry = useRegistrySetting(configForms, "plugin-upgrader");
885
1575
  const [query, setQuery] = React.useState("");
886
1576
  const [picked, setPicked] = React.useState(() => /* @__PURE__ */ new Set());
887
1577
  const [job, setJob] = React.useState(void 0);
@@ -963,6 +1653,12 @@ window.__ModuleLoader__.load({
963
1653
  (row) => row.name.toLowerCase().includes(needle) || String(row.title ?? "").toLowerCase().includes(needle)
964
1654
  );
965
1655
  const busy = busyName !== void 0 || job !== void 0 && (job.status === "running" || job.status === "queued");
1656
+ const knownRegistries = [
1657
+ data?.registry,
1658
+ data?.registries?.registry,
1659
+ ...data?.registries?.fallbackRegistries ?? [],
1660
+ data?.registries?.resolved
1661
+ ];
966
1662
  const pickedCount = upgradable.reduce((count, row) => picked.has(row.name) ? count + 1 : count, 0);
967
1663
  const stepOf = (row, version, action, approveBuilds) => ({
968
1664
  name: row.name,
@@ -1042,7 +1738,14 @@ window.__ModuleLoader__.load({
1042
1738
  " ",
1043
1739
  h("b", null, String(upgradable.length))
1044
1740
  ),
1045
- data?.registry === void 0 ? null : h("span", { className: "dsh-pu-mono" }, `${t("registry")}: ${data.registry}`)
1741
+ data?.registry === void 0 ? null : h(RegistrySourceButton, {
1742
+ t,
1743
+ active: data.registry,
1744
+ known: knownRegistries,
1745
+ setting: registry,
1746
+ disabled: busy,
1747
+ onChanged: () => reload(true)
1748
+ })
1046
1749
  )
1047
1750
  ),
1048
1751
  h(
@@ -1184,11 +1887,12 @@ window.__ModuleLoader__.load({
1184
1887
  }
1185
1888
  }
1186
1889
  const t = makeT(bound, active);
1890
+ const configForms = ctx.get("configForms", false);
1187
1891
  ctx.slots.inject(
1188
1892
  "plugins.tab",
1189
1893
  () => ctx.slots.register(
1190
1894
  { name: "plugins.tab", id: name, order: 5, label: () => t("tab") },
1191
- (ownerProps) => getH()(Tab, { ...ownerProps, t, locale })
1895
+ (ownerProps) => getH()(Tab, { ...ownerProps, t, locale, configForms })
1192
1896
  )
1193
1897
  );
1194
1898
  }