claudeup 3.5.0 → 3.5.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/package.json +1 -1
- package/src/data/marketplaces.js +1 -0
- package/src/data/marketplaces.ts +1 -0
- package/src/prerunner/index.js +2 -2
- package/src/prerunner/index.ts +2 -2
- package/src/services/claude-settings.js +65 -48
- package/src/services/claude-settings.ts +68 -52
- package/src/ui/App.js +1 -1
- package/src/ui/App.tsx +1 -1
package/package.json
CHANGED
package/src/data/marketplaces.js
CHANGED
package/src/data/marketplaces.ts
CHANGED
package/src/prerunner/index.js
CHANGED
|
@@ -11,13 +11,13 @@ import { recoverMarketplaceSettings, migrateMarketplaceRename } from "../service
|
|
|
11
11
|
export async function prerunClaude(claudeArgs, options = {}) {
|
|
12
12
|
const cache = new UpdateCache();
|
|
13
13
|
try {
|
|
14
|
-
// STEP 0: Migrate
|
|
14
|
+
// STEP 0: Migrate old marketplace names → magus (idempotent, no-ops if already migrated)
|
|
15
15
|
const migration = await migrateMarketplaceRename();
|
|
16
16
|
const migTotal = migration.projectMigrated + migration.globalMigrated
|
|
17
17
|
+ migration.localMigrated + migration.registryMigrated
|
|
18
18
|
+ (migration.knownMarketplacesMigrated ? 1 : 0);
|
|
19
19
|
if (migTotal > 0) {
|
|
20
|
-
console.log(`✓ Migrated ${migTotal} plugin reference(s)
|
|
20
|
+
console.log(`✓ Migrated ${migTotal} plugin reference(s) → magus`);
|
|
21
21
|
}
|
|
22
22
|
// STEP 1: Check if we should update (time-based cache, or forced)
|
|
23
23
|
const shouldUpdate = options.force || (await cache.shouldCheckForUpdates());
|
package/src/prerunner/index.ts
CHANGED
|
@@ -24,13 +24,13 @@ export async function prerunClaude(
|
|
|
24
24
|
const cache = new UpdateCache();
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
// STEP 0: Migrate
|
|
27
|
+
// STEP 0: Migrate old marketplace names → magus (idempotent, no-ops if already migrated)
|
|
28
28
|
const migration = await migrateMarketplaceRename();
|
|
29
29
|
const migTotal = migration.projectMigrated + migration.globalMigrated
|
|
30
30
|
+ migration.localMigrated + migration.registryMigrated
|
|
31
31
|
+ (migration.knownMarketplacesMigrated ? 1 : 0);
|
|
32
32
|
if (migTotal > 0) {
|
|
33
|
-
console.log(`✓ Migrated ${migTotal} plugin reference(s)
|
|
33
|
+
console.log(`✓ Migrated ${migTotal} plugin reference(s) → magus`);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// STEP 1: Check if we should update (time-based cache, or forced)
|
|
@@ -414,13 +414,14 @@ export async function getMarketplaceAutoUpdate(marketplaceName) {
|
|
|
414
414
|
return known[marketplaceName]?.autoUpdate;
|
|
415
415
|
}
|
|
416
416
|
// =============================================================================
|
|
417
|
-
// MARKETPLACE RENAME MIGRATION: mag-claude-plugins → magus
|
|
417
|
+
// MARKETPLACE RENAME MIGRATION: mag-claude-plugins / MadAppGang-claude-code → magus
|
|
418
418
|
// =============================================================================
|
|
419
|
-
const
|
|
419
|
+
const OLD_MARKETPLACE_NAMES = ["mag-claude-plugins", "MadAppGang-claude-code"];
|
|
420
420
|
const NEW_MARKETPLACE_NAME = "magus";
|
|
421
421
|
/**
|
|
422
|
-
* Rename plugin keys in a Record from old marketplace to new.
|
|
422
|
+
* Rename plugin keys in a Record from any old marketplace name to new.
|
|
423
423
|
* e.g., "frontend@mag-claude-plugins" → "frontend@magus"
|
|
424
|
+
* "dev@MadAppGang-claude-code" → "dev@magus"
|
|
424
425
|
* Returns [migratedRecord, count] — count=0 means no changes.
|
|
425
426
|
*/
|
|
426
427
|
function migratePluginKeys(record) {
|
|
@@ -429,9 +430,14 @@ function migratePluginKeys(record) {
|
|
|
429
430
|
const migrated = {};
|
|
430
431
|
let count = 0;
|
|
431
432
|
for (const [key, value] of Object.entries(record)) {
|
|
432
|
-
|
|
433
|
+
const oldName = OLD_MARKETPLACE_NAMES.find((n) => key.endsWith(`@${n}`));
|
|
434
|
+
if (oldName) {
|
|
433
435
|
const pluginName = key.slice(0, key.lastIndexOf("@"));
|
|
434
|
-
|
|
436
|
+
const newKey = `${pluginName}@${NEW_MARKETPLACE_NAME}`;
|
|
437
|
+
// Don't overwrite if canonical key already exists
|
|
438
|
+
if (!record[newKey]) {
|
|
439
|
+
migrated[newKey] = value;
|
|
440
|
+
}
|
|
435
441
|
count++;
|
|
436
442
|
}
|
|
437
443
|
else {
|
|
@@ -456,12 +462,16 @@ function migrateSettingsObject(settings) {
|
|
|
456
462
|
settings.installedPluginVersions = iv;
|
|
457
463
|
total += ivCount;
|
|
458
464
|
}
|
|
459
|
-
// Migrate extraKnownMarketplaces
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
+
// Migrate extraKnownMarketplaces keys
|
|
466
|
+
for (const oldName of OLD_MARKETPLACE_NAMES) {
|
|
467
|
+
if (settings.extraKnownMarketplaces?.[oldName]) {
|
|
468
|
+
const entry = settings.extraKnownMarketplaces[oldName];
|
|
469
|
+
delete settings.extraKnownMarketplaces[oldName];
|
|
470
|
+
if (!settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME]) {
|
|
471
|
+
settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME] = entry;
|
|
472
|
+
}
|
|
473
|
+
total++;
|
|
474
|
+
}
|
|
465
475
|
}
|
|
466
476
|
return total;
|
|
467
477
|
}
|
|
@@ -519,33 +529,34 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
519
529
|
}
|
|
520
530
|
}
|
|
521
531
|
catch { /* skip if unreadable */ }
|
|
522
|
-
// 4. known_marketplaces.json — rename
|
|
532
|
+
// 4. known_marketplaces.json — rename old keys + physical directory cleanup
|
|
523
533
|
const pluginsDir = path.join(os.homedir(), ".claude", "plugins", "marketplaces");
|
|
524
|
-
const oldDir = path.join(pluginsDir, OLD_MARKETPLACE_NAME);
|
|
525
534
|
const newDir = path.join(pluginsDir, NEW_MARKETPLACE_NAME);
|
|
526
535
|
try {
|
|
527
536
|
const known = await readKnownMarketplaces();
|
|
528
537
|
let knownModified = false;
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
known[NEW_MARKETPLACE_NAME]
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
538
|
+
for (const oldName of OLD_MARKETPLACE_NAMES) {
|
|
539
|
+
if (known[oldName]) {
|
|
540
|
+
const oldEntry = known[oldName];
|
|
541
|
+
// If canonical entry doesn't exist yet, create it
|
|
542
|
+
if (!known[NEW_MARKETPLACE_NAME]) {
|
|
543
|
+
known[NEW_MARKETPLACE_NAME] = {
|
|
544
|
+
...oldEntry,
|
|
545
|
+
source: {
|
|
546
|
+
...oldEntry.source,
|
|
547
|
+
repo: "MadAppGang/magus",
|
|
548
|
+
},
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
delete known[oldName];
|
|
552
|
+
knownModified = true;
|
|
553
|
+
}
|
|
554
|
+
// Ensure installLocation doesn't reference old directory names
|
|
555
|
+
if (known[NEW_MARKETPLACE_NAME]?.installLocation?.includes(oldName)) {
|
|
556
|
+
known[NEW_MARKETPLACE_NAME].installLocation =
|
|
557
|
+
known[NEW_MARKETPLACE_NAME].installLocation.replace(oldName, NEW_MARKETPLACE_NAME);
|
|
558
|
+
knownModified = true;
|
|
540
559
|
}
|
|
541
|
-
delete known[OLD_MARKETPLACE_NAME];
|
|
542
|
-
knownModified = true;
|
|
543
|
-
}
|
|
544
|
-
// Ensure installLocation points to new directory name
|
|
545
|
-
if (known[NEW_MARKETPLACE_NAME]?.installLocation?.includes(OLD_MARKETPLACE_NAME)) {
|
|
546
|
-
known[NEW_MARKETPLACE_NAME].installLocation =
|
|
547
|
-
known[NEW_MARKETPLACE_NAME].installLocation.replace(OLD_MARKETPLACE_NAME, NEW_MARKETPLACE_NAME);
|
|
548
|
-
knownModified = true;
|
|
549
560
|
}
|
|
550
561
|
if (knownModified) {
|
|
551
562
|
await writeKnownMarketplaces(known);
|
|
@@ -553,31 +564,33 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
553
564
|
}
|
|
554
565
|
}
|
|
555
566
|
catch { /* skip if unreadable */ }
|
|
556
|
-
// 4b. Rename/remove
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
567
|
+
// 4b. Rename/remove old physical directories (runs even if keys were already migrated)
|
|
568
|
+
for (const oldName of OLD_MARKETPLACE_NAMES) {
|
|
569
|
+
const oldDir = path.join(pluginsDir, oldName);
|
|
570
|
+
try {
|
|
571
|
+
if (await fs.pathExists(oldDir)) {
|
|
572
|
+
if (!(await fs.pathExists(newDir))) {
|
|
573
|
+
await fs.rename(oldDir, newDir);
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
// Both exist — remove the old one (magus dir is canonical)
|
|
577
|
+
await fs.remove(oldDir);
|
|
578
|
+
}
|
|
565
579
|
}
|
|
566
580
|
}
|
|
581
|
+
catch { /* non-fatal: directory cleanup is best-effort */ }
|
|
567
582
|
}
|
|
568
|
-
catch { /* non-fatal: directory cleanup is best-effort */ }
|
|
569
583
|
// 4c. Update git remote URL in the marketplace clone (old → new repo)
|
|
570
584
|
try {
|
|
571
|
-
|
|
572
|
-
if (await fs.pathExists(path.join(marketplaceDir, ".git"))) {
|
|
585
|
+
if (await fs.pathExists(path.join(newDir, ".git"))) {
|
|
573
586
|
const { execSync } = await import("node:child_process");
|
|
574
587
|
const remote = execSync("git remote get-url origin", {
|
|
575
|
-
cwd:
|
|
588
|
+
cwd: newDir, encoding: "utf-8", timeout: 5000,
|
|
576
589
|
}).trim();
|
|
577
590
|
if (remote.includes("claude-code") && remote.includes("MadAppGang")) {
|
|
578
591
|
const newRemote = remote.replace("claude-code", NEW_MARKETPLACE_NAME);
|
|
579
592
|
execSync(`git remote set-url origin "${newRemote}"`, {
|
|
580
|
-
cwd:
|
|
593
|
+
cwd: newDir, encoding: "utf-8", timeout: 5000,
|
|
581
594
|
});
|
|
582
595
|
}
|
|
583
596
|
}
|
|
@@ -589,9 +602,13 @@ export async function migrateMarketplaceRename(projectPath) {
|
|
|
589
602
|
let regCount = 0;
|
|
590
603
|
const newPlugins = {};
|
|
591
604
|
for (const [pluginId, entries] of Object.entries(registry.plugins)) {
|
|
592
|
-
|
|
605
|
+
const oldName = OLD_MARKETPLACE_NAMES.find((n) => pluginId.endsWith(`@${n}`));
|
|
606
|
+
if (oldName) {
|
|
593
607
|
const pluginName = pluginId.slice(0, pluginId.lastIndexOf("@"));
|
|
594
|
-
|
|
608
|
+
const newKey = `${pluginName}@${NEW_MARKETPLACE_NAME}`;
|
|
609
|
+
if (!newPlugins[newKey]) {
|
|
610
|
+
newPlugins[newKey] = entries;
|
|
611
|
+
}
|
|
595
612
|
regCount++;
|
|
596
613
|
}
|
|
597
614
|
else {
|
|
@@ -630,15 +630,16 @@ export interface MarketplaceRecoveryResult {
|
|
|
630
630
|
}
|
|
631
631
|
|
|
632
632
|
// =============================================================================
|
|
633
|
-
// MARKETPLACE RENAME MIGRATION: mag-claude-plugins → magus
|
|
633
|
+
// MARKETPLACE RENAME MIGRATION: mag-claude-plugins / MadAppGang-claude-code → magus
|
|
634
634
|
// =============================================================================
|
|
635
635
|
|
|
636
|
-
const
|
|
636
|
+
const OLD_MARKETPLACE_NAMES = ["mag-claude-plugins", "MadAppGang-claude-code"];
|
|
637
637
|
const NEW_MARKETPLACE_NAME = "magus";
|
|
638
638
|
|
|
639
639
|
/**
|
|
640
|
-
* Rename plugin keys in a Record from old marketplace to new.
|
|
640
|
+
* Rename plugin keys in a Record from any old marketplace name to new.
|
|
641
641
|
* e.g., "frontend@mag-claude-plugins" → "frontend@magus"
|
|
642
|
+
* "dev@MadAppGang-claude-code" → "dev@magus"
|
|
642
643
|
* Returns [migratedRecord, count] — count=0 means no changes.
|
|
643
644
|
*/
|
|
644
645
|
function migratePluginKeys<T>(
|
|
@@ -648,9 +649,14 @@ function migratePluginKeys<T>(
|
|
|
648
649
|
const migrated: Record<string, T> = {};
|
|
649
650
|
let count = 0;
|
|
650
651
|
for (const [key, value] of Object.entries(record)) {
|
|
651
|
-
|
|
652
|
+
const oldName = OLD_MARKETPLACE_NAMES.find((n) => key.endsWith(`@${n}`));
|
|
653
|
+
if (oldName) {
|
|
652
654
|
const pluginName = key.slice(0, key.lastIndexOf("@"));
|
|
653
|
-
|
|
655
|
+
const newKey = `${pluginName}@${NEW_MARKETPLACE_NAME}`;
|
|
656
|
+
// Don't overwrite if canonical key already exists
|
|
657
|
+
if (!record[newKey]) {
|
|
658
|
+
migrated[newKey] = value;
|
|
659
|
+
}
|
|
654
660
|
count++;
|
|
655
661
|
} else {
|
|
656
662
|
migrated[key] = value;
|
|
@@ -678,12 +684,16 @@ function migrateSettingsObject(settings: ClaudeSettings): number {
|
|
|
678
684
|
total += ivCount;
|
|
679
685
|
}
|
|
680
686
|
|
|
681
|
-
// Migrate extraKnownMarketplaces
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
+
// Migrate extraKnownMarketplaces keys
|
|
688
|
+
for (const oldName of OLD_MARKETPLACE_NAMES) {
|
|
689
|
+
if (settings.extraKnownMarketplaces?.[oldName]) {
|
|
690
|
+
const entry = settings.extraKnownMarketplaces[oldName];
|
|
691
|
+
delete settings.extraKnownMarketplaces[oldName];
|
|
692
|
+
if (!settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME]) {
|
|
693
|
+
settings.extraKnownMarketplaces[NEW_MARKETPLACE_NAME] = entry;
|
|
694
|
+
}
|
|
695
|
+
total++;
|
|
696
|
+
}
|
|
687
697
|
}
|
|
688
698
|
|
|
689
699
|
return total;
|
|
@@ -748,40 +758,40 @@ export async function migrateMarketplaceRename(
|
|
|
748
758
|
}
|
|
749
759
|
} catch { /* skip if unreadable */ }
|
|
750
760
|
|
|
751
|
-
// 4. known_marketplaces.json — rename
|
|
761
|
+
// 4. known_marketplaces.json — rename old keys + physical directory cleanup
|
|
752
762
|
const pluginsDir = path.join(os.homedir(), ".claude", "plugins", "marketplaces");
|
|
753
|
-
const oldDir = path.join(pluginsDir, OLD_MARKETPLACE_NAME);
|
|
754
763
|
const newDir = path.join(pluginsDir, NEW_MARKETPLACE_NAME);
|
|
755
764
|
|
|
756
765
|
try {
|
|
757
766
|
const known = await readKnownMarketplaces();
|
|
758
767
|
let knownModified = false;
|
|
759
768
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
769
|
+
for (const oldName of OLD_MARKETPLACE_NAMES) {
|
|
770
|
+
if (known[oldName]) {
|
|
771
|
+
const oldEntry = known[oldName];
|
|
772
|
+
// If canonical entry doesn't exist yet, create it
|
|
773
|
+
if (!known[NEW_MARKETPLACE_NAME]) {
|
|
774
|
+
known[NEW_MARKETPLACE_NAME] = {
|
|
775
|
+
...oldEntry,
|
|
776
|
+
source: {
|
|
777
|
+
...oldEntry.source,
|
|
778
|
+
repo: "MadAppGang/magus",
|
|
779
|
+
},
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
delete known[oldName];
|
|
783
|
+
knownModified = true;
|
|
772
784
|
}
|
|
773
|
-
delete known[OLD_MARKETPLACE_NAME];
|
|
774
|
-
knownModified = true;
|
|
775
|
-
}
|
|
776
785
|
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
786
|
+
// Ensure installLocation doesn't reference old directory names
|
|
787
|
+
if (known[NEW_MARKETPLACE_NAME]?.installLocation?.includes(oldName)) {
|
|
788
|
+
known[NEW_MARKETPLACE_NAME].installLocation =
|
|
789
|
+
known[NEW_MARKETPLACE_NAME].installLocation.replace(
|
|
790
|
+
oldName,
|
|
791
|
+
NEW_MARKETPLACE_NAME,
|
|
792
|
+
);
|
|
793
|
+
knownModified = true;
|
|
794
|
+
}
|
|
785
795
|
}
|
|
786
796
|
|
|
787
797
|
if (knownModified) {
|
|
@@ -790,30 +800,32 @@ export async function migrateMarketplaceRename(
|
|
|
790
800
|
}
|
|
791
801
|
} catch { /* skip if unreadable */ }
|
|
792
802
|
|
|
793
|
-
// 4b. Rename/remove
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
803
|
+
// 4b. Rename/remove old physical directories (runs even if keys were already migrated)
|
|
804
|
+
for (const oldName of OLD_MARKETPLACE_NAMES) {
|
|
805
|
+
const oldDir = path.join(pluginsDir, oldName);
|
|
806
|
+
try {
|
|
807
|
+
if (await fs.pathExists(oldDir)) {
|
|
808
|
+
if (!(await fs.pathExists(newDir))) {
|
|
809
|
+
await fs.rename(oldDir, newDir);
|
|
810
|
+
} else {
|
|
811
|
+
// Both exist — remove the old one (magus dir is canonical)
|
|
812
|
+
await fs.remove(oldDir);
|
|
813
|
+
}
|
|
801
814
|
}
|
|
802
|
-
}
|
|
803
|
-
}
|
|
815
|
+
} catch { /* non-fatal: directory cleanup is best-effort */ }
|
|
816
|
+
}
|
|
804
817
|
|
|
805
818
|
// 4c. Update git remote URL in the marketplace clone (old → new repo)
|
|
806
819
|
try {
|
|
807
|
-
|
|
808
|
-
if (await fs.pathExists(path.join(marketplaceDir, ".git"))) {
|
|
820
|
+
if (await fs.pathExists(path.join(newDir, ".git"))) {
|
|
809
821
|
const { execSync } = await import("node:child_process");
|
|
810
822
|
const remote = execSync("git remote get-url origin", {
|
|
811
|
-
cwd:
|
|
823
|
+
cwd: newDir, encoding: "utf-8", timeout: 5000,
|
|
812
824
|
}).trim();
|
|
813
825
|
if (remote.includes("claude-code") && remote.includes("MadAppGang")) {
|
|
814
826
|
const newRemote = remote.replace("claude-code", NEW_MARKETPLACE_NAME);
|
|
815
827
|
execSync(`git remote set-url origin "${newRemote}"`, {
|
|
816
|
-
cwd:
|
|
828
|
+
cwd: newDir, encoding: "utf-8", timeout: 5000,
|
|
817
829
|
});
|
|
818
830
|
}
|
|
819
831
|
}
|
|
@@ -825,9 +837,13 @@ export async function migrateMarketplaceRename(
|
|
|
825
837
|
let regCount = 0;
|
|
826
838
|
const newPlugins: typeof registry.plugins = {};
|
|
827
839
|
for (const [pluginId, entries] of Object.entries(registry.plugins)) {
|
|
828
|
-
|
|
840
|
+
const oldName = OLD_MARKETPLACE_NAMES.find((n) => pluginId.endsWith(`@${n}`));
|
|
841
|
+
if (oldName) {
|
|
829
842
|
const pluginName = pluginId.slice(0, pluginId.lastIndexOf("@"));
|
|
830
|
-
|
|
843
|
+
const newKey = `${pluginName}@${NEW_MARKETPLACE_NAME}`;
|
|
844
|
+
if (!newPlugins[newKey]) {
|
|
845
|
+
newPlugins[newKey] = entries;
|
|
846
|
+
}
|
|
831
847
|
regCount++;
|
|
832
848
|
} else {
|
|
833
849
|
newPlugins[pluginId] = entries;
|
package/src/ui/App.js
CHANGED
|
@@ -182,7 +182,7 @@ function AppContentInner({ showDebug, onDebugToggle, updateInfo, onExit, }) {
|
|
|
182
182
|
type: "SHOW_PROGRESS",
|
|
183
183
|
state: { message: "Scanning marketplaces..." },
|
|
184
184
|
});
|
|
185
|
-
// Migrate
|
|
185
|
+
// Migrate old marketplace names → magus (idempotent), then repair plugin.json files
|
|
186
186
|
migrateMarketplaceRename()
|
|
187
187
|
.catch(() => { }); // non-blocking, best-effort
|
|
188
188
|
repairAllMarketplaces()
|
package/src/ui/App.tsx
CHANGED
|
@@ -265,7 +265,7 @@ function AppContentInner({
|
|
|
265
265
|
state: { message: "Scanning marketplaces..." },
|
|
266
266
|
});
|
|
267
267
|
|
|
268
|
-
// Migrate
|
|
268
|
+
// Migrate old marketplace names → magus (idempotent), then repair plugin.json files
|
|
269
269
|
migrateMarketplaceRename()
|
|
270
270
|
.catch(() => {}); // non-blocking, best-effort
|
|
271
271
|
|