@lvce-editor/extension-detail-view 7.20.0 → 7.22.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/dist/extensionDetailViewWorkerMain.js +249 -179
- package/package.json +1 -1
|
@@ -544,6 +544,7 @@ const Web$1 = 1;
|
|
|
544
544
|
const Electron$1 = 2;
|
|
545
545
|
|
|
546
546
|
const ClipBoardWorker = 3400;
|
|
547
|
+
const DialogWorker = 7014;
|
|
547
548
|
const ExtensionHostWorker = 44;
|
|
548
549
|
const ExtensionManagementWorker = 9006;
|
|
549
550
|
const FileSystemWorker$1 = 209;
|
|
@@ -1205,12 +1206,26 @@ const existsJson = async schemaUrl => {
|
|
|
1205
1206
|
const isExternalLink$2 = schema => {
|
|
1206
1207
|
return schema.startsWith('http://') || schema.startsWith('https://');
|
|
1207
1208
|
};
|
|
1209
|
+
const isAlphanumeric = char => {
|
|
1210
|
+
const code = char.codePointAt(0) || 0;
|
|
1211
|
+
return code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122;
|
|
1212
|
+
};
|
|
1208
1213
|
const hasWhitespace = value => {
|
|
1209
|
-
|
|
1214
|
+
for (const char of value) {
|
|
1215
|
+
if (char.trim() === '') {
|
|
1216
|
+
return true;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
return false;
|
|
1210
1220
|
};
|
|
1211
1221
|
const isOnlyDotsOrEmpty = value => {
|
|
1212
1222
|
const trimmed = value.trim();
|
|
1213
|
-
|
|
1223
|
+
for (const char of trimmed) {
|
|
1224
|
+
if (char !== '.') {
|
|
1225
|
+
return false;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return true;
|
|
1214
1229
|
};
|
|
1215
1230
|
const isValidHttpUrl = value => {
|
|
1216
1231
|
try {
|
|
@@ -1232,11 +1247,17 @@ const isValidRelativePath = value => {
|
|
|
1232
1247
|
return false;
|
|
1233
1248
|
}
|
|
1234
1249
|
// Allow paths like ./a.json, ../a.json, /a.json, schemas/a.json, a/b.json
|
|
1235
|
-
|
|
1236
|
-
|
|
1250
|
+
let hasAlphanumeric = false;
|
|
1251
|
+
for (const char of value) {
|
|
1252
|
+
if (!isAlphanumeric(char) && char !== '.' && char !== '_' && char !== '-' && char !== '/') {
|
|
1253
|
+
return false;
|
|
1254
|
+
}
|
|
1255
|
+
if (isAlphanumeric(char)) {
|
|
1256
|
+
hasAlphanumeric = true;
|
|
1257
|
+
}
|
|
1237
1258
|
}
|
|
1238
1259
|
// Must contain at least one alphanumeric character
|
|
1239
|
-
return
|
|
1260
|
+
return hasAlphanumeric;
|
|
1240
1261
|
};
|
|
1241
1262
|
const getSchemaLinkUrl = (schema, extensionUri) => {
|
|
1242
1263
|
if (!schema || typeof schema !== 'string') {
|
|
@@ -2672,7 +2693,7 @@ const createMockRpc = ({
|
|
|
2672
2693
|
};
|
|
2673
2694
|
|
|
2674
2695
|
const rpcs = Object.create(null);
|
|
2675
|
-
const set$
|
|
2696
|
+
const set$d = (id, rpc) => {
|
|
2676
2697
|
rpcs[id] = rpc;
|
|
2677
2698
|
};
|
|
2678
2699
|
const get$3 = id => {
|
|
@@ -2705,7 +2726,7 @@ const create$2 = rpcId => {
|
|
|
2705
2726
|
const mockRpc = createMockRpc({
|
|
2706
2727
|
commandMap
|
|
2707
2728
|
});
|
|
2708
|
-
set$
|
|
2729
|
+
set$d(rpcId, mockRpc);
|
|
2709
2730
|
// @ts-ignore
|
|
2710
2731
|
mockRpc[Symbol.dispose] = () => {
|
|
2711
2732
|
remove(rpcId);
|
|
@@ -2714,22 +2735,27 @@ const create$2 = rpcId => {
|
|
|
2714
2735
|
return mockRpc;
|
|
2715
2736
|
},
|
|
2716
2737
|
set(rpc) {
|
|
2717
|
-
set$
|
|
2738
|
+
set$d(rpcId, rpc);
|
|
2718
2739
|
}
|
|
2719
2740
|
};
|
|
2720
2741
|
};
|
|
2721
2742
|
|
|
2722
2743
|
const {
|
|
2723
|
-
invoke: invoke$
|
|
2724
|
-
set: set$
|
|
2744
|
+
invoke: invoke$6,
|
|
2745
|
+
set: set$c
|
|
2725
2746
|
} = create$2(ClipBoardWorker);
|
|
2726
2747
|
const writeText$1 = async text => {
|
|
2727
|
-
return invoke$
|
|
2748
|
+
return invoke$6('ClipBoard.writeText', text);
|
|
2728
2749
|
};
|
|
2729
2750
|
const writeImage = async image => {
|
|
2730
|
-
return invoke$
|
|
2751
|
+
return invoke$6('ClipBoard.writeImage', image);
|
|
2731
2752
|
};
|
|
2732
2753
|
|
|
2754
|
+
const {
|
|
2755
|
+
invoke: invoke$5,
|
|
2756
|
+
set: set$b
|
|
2757
|
+
} = create$2(DialogWorker);
|
|
2758
|
+
|
|
2733
2759
|
const {
|
|
2734
2760
|
set: set$a
|
|
2735
2761
|
} = create$2(ExtensionHostWorker);
|
|
@@ -2812,6 +2838,10 @@ const sendMessagePortToClipBoardWorker = async (port, rpcId) => {
|
|
|
2812
2838
|
const command = 'ClipBoard.handleMessagePort';
|
|
2813
2839
|
await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToClipBoardWorker', port, command, rpcId);
|
|
2814
2840
|
};
|
|
2841
|
+
const sendMessagePortToDialogWorker = async port => {
|
|
2842
|
+
const command = 'HandleMessagePort.handleMessagePort';
|
|
2843
|
+
await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToDialogWorker', port, command);
|
|
2844
|
+
};
|
|
2815
2845
|
const sendMessagePortToMarkdownWorker$1 = async (port, rpcId) => {
|
|
2816
2846
|
const command = 'Markdown.handleMessagePort';
|
|
2817
2847
|
await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToMarkdownWorker', port, command, rpcId);
|
|
@@ -2824,10 +2854,6 @@ const sendMessagePortToExtensionHostWorker$1 = async (port, rpcId = 0) => {
|
|
|
2824
2854
|
const command = 'HandleMessagePort.handleMessagePort2';
|
|
2825
2855
|
await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker', port, command, rpcId);
|
|
2826
2856
|
};
|
|
2827
|
-
const confirm = async (message, options) => {
|
|
2828
|
-
const result = await invoke$1('ConfirmPrompt.prompt', message, options);
|
|
2829
|
-
return result;
|
|
2830
|
-
};
|
|
2831
2857
|
const sendMessagePortToExtensionManagementWorker = async (port, rpcId) => {
|
|
2832
2858
|
const command = 'Extensions.handleMessagePort';
|
|
2833
2859
|
await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionManagementWorker', port, command, rpcId);
|
|
@@ -3251,23 +3277,19 @@ const getCache = (cacheName, bucketName) => {
|
|
|
3251
3277
|
return cachedCaches[cacheName];
|
|
3252
3278
|
};
|
|
3253
3279
|
|
|
3254
|
-
|
|
3255
|
-
const
|
|
3256
|
-
cacheName$1 = `${applicationName}/markdown-cache`;
|
|
3257
|
-
};
|
|
3258
|
-
const has = async (key, bucketName, getCacheFunction = getCache) => {
|
|
3259
|
-
const cache = await getCacheFunction(cacheName$1, bucketName);
|
|
3280
|
+
const has = async (cacheName, key, bucketName, getCacheFunction = getCache) => {
|
|
3281
|
+
const cache = await getCacheFunction(cacheName, bucketName);
|
|
3260
3282
|
const response = await cache.match(key);
|
|
3261
3283
|
return Boolean(response);
|
|
3262
3284
|
};
|
|
3263
|
-
const get$2 = async (key, bucketName, getCacheFunction = getCache) => {
|
|
3264
|
-
const cache = await getCacheFunction(cacheName
|
|
3285
|
+
const get$2 = async (cacheName, key, bucketName, getCacheFunction = getCache) => {
|
|
3286
|
+
const cache = await getCacheFunction(cacheName, bucketName);
|
|
3265
3287
|
const response = await cache.match(key);
|
|
3266
3288
|
const text = await response?.text();
|
|
3267
3289
|
return text || '';
|
|
3268
3290
|
};
|
|
3269
|
-
const set$4 = async (key, bucketName, value, getCacheFunction = getCache) => {
|
|
3270
|
-
const cache = await getCacheFunction(cacheName
|
|
3291
|
+
const set$4 = async (cacheName, key, bucketName, value, getCacheFunction = getCache) => {
|
|
3292
|
+
const cache = await getCacheFunction(cacheName, bucketName);
|
|
3271
3293
|
await cache.put(key, new Response(value, {
|
|
3272
3294
|
headers: {
|
|
3273
3295
|
'Content-Length': `${value.length}`,
|
|
@@ -3276,25 +3298,25 @@ const set$4 = async (key, bucketName, value, getCacheFunction = getCache) => {
|
|
|
3276
3298
|
}));
|
|
3277
3299
|
};
|
|
3278
3300
|
|
|
3279
|
-
const renderMarkdownCached = async (markdown, options) => {
|
|
3301
|
+
const renderMarkdownCached = async (markdown, options, cacheName) => {
|
|
3280
3302
|
const cacheKey = await getMarkdownCacheKey(markdown, options);
|
|
3281
3303
|
const bucketName = `markdown-cache`;
|
|
3282
|
-
const hasItem = await has(cacheKey, bucketName);
|
|
3304
|
+
const hasItem = await has(cacheName, cacheKey, bucketName);
|
|
3283
3305
|
if (hasItem) {
|
|
3284
|
-
const value = await get$2(cacheKey, bucketName);
|
|
3306
|
+
const value = await get$2(cacheName, cacheKey, bucketName);
|
|
3285
3307
|
return value; // TODO validate if it's valid
|
|
3286
3308
|
}
|
|
3287
3309
|
const html = await render(markdown, options);
|
|
3288
|
-
await set$4(cacheKey, bucketName, html);
|
|
3310
|
+
await set$4(cacheName, cacheKey, bucketName, html);
|
|
3289
3311
|
return html;
|
|
3290
3312
|
};
|
|
3291
3313
|
|
|
3292
|
-
const renderMarkdown = async (markdown, options) => {
|
|
3293
|
-
const html = await renderMarkdownCached(markdown, options);
|
|
3314
|
+
const renderMarkdown = async (markdown, options, cacheName) => {
|
|
3315
|
+
const html = await renderMarkdownCached(markdown, options, cacheName);
|
|
3294
3316
|
return html;
|
|
3295
3317
|
};
|
|
3296
3318
|
|
|
3297
|
-
const getThemeDetails = async (extension, baseUrl, locationProtocol) => {
|
|
3319
|
+
const getThemeDetails = async (extension, baseUrl, locationProtocol, cacheName) => {
|
|
3298
3320
|
const {
|
|
3299
3321
|
colorThemes,
|
|
3300
3322
|
iconThemes,
|
|
@@ -3304,7 +3326,7 @@ const getThemeDetails = async (extension, baseUrl, locationProtocol) => {
|
|
|
3304
3326
|
const rendered = await renderMarkdown(markdown, {
|
|
3305
3327
|
baseUrl,
|
|
3306
3328
|
locationProtocol
|
|
3307
|
-
});
|
|
3329
|
+
}, cacheName);
|
|
3308
3330
|
const themesMarkdownDom = await getMarkdownVirtualDom(rendered);
|
|
3309
3331
|
return {
|
|
3310
3332
|
themesMarkdownDom
|
|
@@ -3772,7 +3794,10 @@ const writeText = async text => {
|
|
|
3772
3794
|
};
|
|
3773
3795
|
|
|
3774
3796
|
const copyExtensionId = async state => {
|
|
3775
|
-
|
|
3797
|
+
const {
|
|
3798
|
+
extensionId
|
|
3799
|
+
} = state;
|
|
3800
|
+
await writeText(extensionId);
|
|
3776
3801
|
return state;
|
|
3777
3802
|
};
|
|
3778
3803
|
|
|
@@ -3891,6 +3916,7 @@ const create = (uid, uri, x, y, width, height, platform, assetDir) => {
|
|
|
3891
3916
|
baseUrl: '',
|
|
3892
3917
|
builtinExtensionsBadgeEnabled: true,
|
|
3893
3918
|
buttons: [],
|
|
3919
|
+
cacheName: '',
|
|
3894
3920
|
categories: [],
|
|
3895
3921
|
changelogScrollTop: 0,
|
|
3896
3922
|
changelogVirtualDom: [],
|
|
@@ -4418,7 +4444,7 @@ const updateExtensionStatus = async (state, updateFunction) => {
|
|
|
4418
4444
|
} = state;
|
|
4419
4445
|
const error = await updateFunction(extensionId, platform);
|
|
4420
4446
|
if (error) {
|
|
4421
|
-
await
|
|
4447
|
+
await invoke$5('ConfirmPrompt.prompt', `${error}`);
|
|
4422
4448
|
}
|
|
4423
4449
|
const extension = await getExtension(extensionId, platform);
|
|
4424
4450
|
const disabled = extension?.disabled;
|
|
@@ -4451,6 +4477,7 @@ const selectFeature = async (state, name) => {
|
|
|
4451
4477
|
}
|
|
4452
4478
|
const {
|
|
4453
4479
|
baseUrl,
|
|
4480
|
+
cacheName,
|
|
4454
4481
|
extension,
|
|
4455
4482
|
features,
|
|
4456
4483
|
locationProtocol
|
|
@@ -4468,7 +4495,7 @@ const selectFeature = async (state, name) => {
|
|
|
4468
4495
|
};
|
|
4469
4496
|
});
|
|
4470
4497
|
const fn = getFeatureDetailsHandler(name);
|
|
4471
|
-
const partialNewState = await fn(extension, baseUrl, locationProtocol);
|
|
4498
|
+
const partialNewState = await fn(extension, baseUrl, locationProtocol, cacheName);
|
|
4472
4499
|
return {
|
|
4473
4500
|
...state,
|
|
4474
4501
|
...partialNewState,
|
|
@@ -4482,8 +4509,13 @@ const handleClickFeatures = async (state, name) => {
|
|
|
4482
4509
|
};
|
|
4483
4510
|
|
|
4484
4511
|
const handleClickScrollToTop = state => {
|
|
4485
|
-
|
|
4486
|
-
|
|
4512
|
+
const {
|
|
4513
|
+
changelogScrollTop,
|
|
4514
|
+
readmeScrollTop,
|
|
4515
|
+
selectedTab
|
|
4516
|
+
} = state;
|
|
4517
|
+
if (selectedTab === Changelog) {
|
|
4518
|
+
if (changelogScrollTop === 0) {
|
|
4487
4519
|
return state;
|
|
4488
4520
|
}
|
|
4489
4521
|
return {
|
|
@@ -4491,9 +4523,6 @@ const handleClickScrollToTop = state => {
|
|
|
4491
4523
|
changelogScrollTop: 0
|
|
4492
4524
|
};
|
|
4493
4525
|
}
|
|
4494
|
-
const {
|
|
4495
|
-
readmeScrollTop
|
|
4496
|
-
} = state;
|
|
4497
4526
|
if (readmeScrollTop === 0) {
|
|
4498
4527
|
return state;
|
|
4499
4528
|
}
|
|
@@ -4509,17 +4538,19 @@ const setColorTheme = id => {
|
|
|
4509
4538
|
|
|
4510
4539
|
const handleClickSetColorTheme = async state => {
|
|
4511
4540
|
const {
|
|
4512
|
-
|
|
4541
|
+
disabled,
|
|
4542
|
+
extension,
|
|
4543
|
+
hasColorTheme
|
|
4513
4544
|
} = state;
|
|
4514
4545
|
const colorThemeId = getColorThemeId(extension);
|
|
4515
4546
|
if (colorThemeId) {
|
|
4516
4547
|
const error = await setColorTheme(colorThemeId);
|
|
4517
4548
|
if (error) {
|
|
4518
|
-
await
|
|
4549
|
+
await invoke$5('ConfirmPrompt.prompt', `${error}`);
|
|
4519
4550
|
}
|
|
4520
4551
|
const isBuiltin = extension?.isBuiltin || extension?.builtin || false;
|
|
4521
4552
|
const colorThemeLabel = getColorThemeLabel(extension) || '';
|
|
4522
|
-
const buttons = getExtensionDetailButtons(
|
|
4553
|
+
const buttons = getExtensionDetailButtons(hasColorTheme, isBuiltin, disabled, colorThemeId, colorThemeLabel, colorThemeId);
|
|
4523
4554
|
return {
|
|
4524
4555
|
...state,
|
|
4525
4556
|
buttons,
|
|
@@ -4540,30 +4571,37 @@ const handleClickSettings = async (state, eventX, eventY) => {
|
|
|
4540
4571
|
};
|
|
4541
4572
|
|
|
4542
4573
|
const handleClickSize = async state => {
|
|
4574
|
+
const {
|
|
4575
|
+
extension
|
|
4576
|
+
} = state;
|
|
4543
4577
|
const {
|
|
4544
4578
|
uri
|
|
4545
|
-
} =
|
|
4579
|
+
} = extension;
|
|
4546
4580
|
await openNativeFolder(uri);
|
|
4547
4581
|
return state;
|
|
4548
4582
|
};
|
|
4549
4583
|
|
|
4550
4584
|
const handleClickUninstall = async state => {
|
|
4585
|
+
const {
|
|
4586
|
+
extension
|
|
4587
|
+
} = state;
|
|
4551
4588
|
const {
|
|
4552
4589
|
id
|
|
4553
|
-
} =
|
|
4590
|
+
} = extension;
|
|
4554
4591
|
await uninstallExtension(id);
|
|
4555
4592
|
return state;
|
|
4556
4593
|
};
|
|
4557
4594
|
|
|
4558
4595
|
const handleColorThemeChanged = (state, colorThemeId) => {
|
|
4559
|
-
if (state.currentColorThemeId === colorThemeId) {
|
|
4560
|
-
return state;
|
|
4561
|
-
}
|
|
4562
4596
|
const {
|
|
4597
|
+
currentColorThemeId,
|
|
4563
4598
|
disabled,
|
|
4564
4599
|
extension,
|
|
4565
4600
|
hasColorTheme
|
|
4566
4601
|
} = state;
|
|
4602
|
+
if (currentColorThemeId === colorThemeId) {
|
|
4603
|
+
return state;
|
|
4604
|
+
}
|
|
4567
4605
|
const extensionColorThemeId = getColorThemeId(extension) || '';
|
|
4568
4606
|
const extensionColorThemeLabel = getColorThemeLabel(extension) || '';
|
|
4569
4607
|
const isBuiltin = extension?.isBuiltin || extension?.builtin || false;
|
|
@@ -4590,6 +4628,10 @@ class ExtensionNotFoundError extends Error {
|
|
|
4590
4628
|
}
|
|
4591
4629
|
}
|
|
4592
4630
|
|
|
4631
|
+
const getApplicationName = async () => {
|
|
4632
|
+
return invoke$1('Layout.getApplicationName');
|
|
4633
|
+
};
|
|
4634
|
+
|
|
4593
4635
|
const getRemoteSrc = uri => {
|
|
4594
4636
|
const src = `/remote${uri}`;
|
|
4595
4637
|
return src;
|
|
@@ -4672,6 +4714,12 @@ const getExtensionUri = (uri, platform, origin) => {
|
|
|
4672
4714
|
};
|
|
4673
4715
|
|
|
4674
4716
|
const segmentRegex = /^[\w.-]+$/;
|
|
4717
|
+
const removeGitPrefix = value => {
|
|
4718
|
+
return value.startsWith('git+') ? value.slice(4) : value;
|
|
4719
|
+
};
|
|
4720
|
+
const removeGitSuffix = value => {
|
|
4721
|
+
return value.toLowerCase().endsWith('.git') ? value.slice(0, -4) : value;
|
|
4722
|
+
};
|
|
4675
4723
|
const getRepositoryUrl = extension => {
|
|
4676
4724
|
if (!extension || !hasProperty(extension, 'repository')) {
|
|
4677
4725
|
return '';
|
|
@@ -4688,7 +4736,7 @@ const getRepositoryUrl = extension => {
|
|
|
4688
4736
|
return '';
|
|
4689
4737
|
};
|
|
4690
4738
|
const getGithubRepository = extension => {
|
|
4691
|
-
const rawUrl = getRepositoryUrl(extension)
|
|
4739
|
+
const rawUrl = removeGitPrefix(getRepositoryUrl(extension));
|
|
4692
4740
|
try {
|
|
4693
4741
|
const url = new URL(rawUrl);
|
|
4694
4742
|
if (url.protocol !== 'https:' || url.hostname.toLowerCase() !== 'github.com' || url.username || url.password || url.port) {
|
|
@@ -4699,7 +4747,7 @@ const getGithubRepository = extension => {
|
|
|
4699
4747
|
return undefined;
|
|
4700
4748
|
}
|
|
4701
4749
|
const owner = segments[0];
|
|
4702
|
-
const repository = segments[1]
|
|
4750
|
+
const repository = removeGitSuffix(segments[1]);
|
|
4703
4751
|
if (!owner || !repository || !segmentRegex.test(owner) || !segmentRegex.test(repository)) {
|
|
4704
4752
|
return undefined;
|
|
4705
4753
|
}
|
|
@@ -4767,9 +4815,15 @@ const toSyntaxLanguage = value => {
|
|
|
4767
4815
|
if (typeof language.id !== 'string' || typeof language.tokenize !== 'string') {
|
|
4768
4816
|
return undefined;
|
|
4769
4817
|
}
|
|
4818
|
+
const aliases = getStringArray(language.aliases);
|
|
4819
|
+
const extensions = getStringArray(language.extensions);
|
|
4770
4820
|
return {
|
|
4771
|
-
aliases
|
|
4772
|
-
|
|
4821
|
+
...(aliases && {
|
|
4822
|
+
aliases
|
|
4823
|
+
}),
|
|
4824
|
+
...(extensions && {
|
|
4825
|
+
extensions
|
|
4826
|
+
}),
|
|
4773
4827
|
id: language.id,
|
|
4774
4828
|
tokenize: language.tokenize
|
|
4775
4829
|
};
|
|
@@ -5301,13 +5355,20 @@ const getIssuesLink = extension => {
|
|
|
5301
5355
|
const isGitHubRepository = url => {
|
|
5302
5356
|
return url.startsWith('https://github.com/');
|
|
5303
5357
|
};
|
|
5358
|
+
const removeTrailingSlashes = value => {
|
|
5359
|
+
let end = value.length;
|
|
5360
|
+
while (end > 0 && value[end - 1] === '/') {
|
|
5361
|
+
end--;
|
|
5362
|
+
}
|
|
5363
|
+
return value.slice(0, end);
|
|
5364
|
+
};
|
|
5304
5365
|
const getLicenseLink = extension => {
|
|
5305
5366
|
const repositoryLink = getRepositoryLink(extension);
|
|
5306
5367
|
if (!repositoryLink) {
|
|
5307
5368
|
return '#';
|
|
5308
5369
|
}
|
|
5309
5370
|
if (isGitHubRepository(repositoryLink)) {
|
|
5310
|
-
const normalizedLink = repositoryLink
|
|
5371
|
+
const normalizedLink = removeTrailingSlashes(repositoryLink);
|
|
5311
5372
|
return `${normalizedLink}/blob/main/LICENSE`;
|
|
5312
5373
|
}
|
|
5313
5374
|
return '#';
|
|
@@ -5461,6 +5522,7 @@ const loadContentInternal = async (state, platform, savedState, isTest = false)
|
|
|
5461
5522
|
savedState = undefined;
|
|
5462
5523
|
}
|
|
5463
5524
|
const {
|
|
5525
|
+
assetDir,
|
|
5464
5526
|
uri,
|
|
5465
5527
|
width
|
|
5466
5528
|
} = state;
|
|
@@ -5470,8 +5532,10 @@ const loadContentInternal = async (state, platform, savedState, isTest = false)
|
|
|
5470
5532
|
throw new ExtensionNotFoundError(id);
|
|
5471
5533
|
}
|
|
5472
5534
|
const currentColorThemeId = await getCurrentColorTheme();
|
|
5535
|
+
const applicationName = await getApplicationName();
|
|
5536
|
+
const cacheName = `${applicationName}/markdown-cache`;
|
|
5473
5537
|
const commit = await getCommit();
|
|
5474
|
-
const languages = await getSyntaxLanguages(platform,
|
|
5538
|
+
const languages = await getSyntaxLanguages(platform, assetDir);
|
|
5475
5539
|
const headerData = loadHeaderContent(state, platform, extension);
|
|
5476
5540
|
const {
|
|
5477
5541
|
badge,
|
|
@@ -5500,7 +5564,7 @@ const loadContentInternal = async (state, platform, savedState, isTest = false)
|
|
|
5500
5564
|
languages,
|
|
5501
5565
|
linksExternal: true,
|
|
5502
5566
|
locationProtocol
|
|
5503
|
-
});
|
|
5567
|
+
}, cacheName);
|
|
5504
5568
|
const detailsVirtualDom = await getMarkdownVirtualDom(readmeHtml, {
|
|
5505
5569
|
scrollToTopEnabled: true
|
|
5506
5570
|
});
|
|
@@ -5542,6 +5606,7 @@ const loadContentInternal = async (state, platform, savedState, isTest = false)
|
|
|
5542
5606
|
badge,
|
|
5543
5607
|
baseUrl,
|
|
5544
5608
|
buttons,
|
|
5609
|
+
cacheName,
|
|
5545
5610
|
categories,
|
|
5546
5611
|
changelogScrollTop,
|
|
5547
5612
|
commit,
|
|
@@ -5592,10 +5657,13 @@ const loadContentInternal = async (state, platform, savedState, isTest = false)
|
|
|
5592
5657
|
};
|
|
5593
5658
|
};
|
|
5594
5659
|
const loadContent = async (state, platform, savedState, isTest = false) => {
|
|
5660
|
+
const {
|
|
5661
|
+
uri
|
|
5662
|
+
} = state;
|
|
5595
5663
|
try {
|
|
5596
5664
|
return await loadContentInternal(state, platform, savedState, isTest);
|
|
5597
5665
|
} catch (error) {
|
|
5598
|
-
const extensionId = getExtensionIdFromUri(
|
|
5666
|
+
const extensionId = getExtensionIdFromUri(uri);
|
|
5599
5667
|
const errorMessage = error instanceof ExtensionNotFoundError ? extensionNotAvailable(extensionId) : unableToLoadExtensionWithError(getErrorMessage$1(error));
|
|
5600
5668
|
return {
|
|
5601
5669
|
...state,
|
|
@@ -5689,9 +5757,13 @@ const replaceMarkdownImageWithError = (dom, failedSrc) => {
|
|
|
5689
5757
|
};
|
|
5690
5758
|
|
|
5691
5759
|
const handleMarkdownImageError = (state, failedSrc) => {
|
|
5692
|
-
const
|
|
5693
|
-
|
|
5694
|
-
|
|
5760
|
+
const {
|
|
5761
|
+
changelogVirtualDom: oldChangelogVirtualDom,
|
|
5762
|
+
detailsVirtualDom: oldDetailsVirtualDom
|
|
5763
|
+
} = state;
|
|
5764
|
+
const detailsVirtualDom = replaceMarkdownImageWithError(oldDetailsVirtualDom, failedSrc);
|
|
5765
|
+
const changelogVirtualDom = replaceMarkdownImageWithError(oldChangelogVirtualDom, failedSrc);
|
|
5766
|
+
if (detailsVirtualDom === oldDetailsVirtualDom && changelogVirtualDom === oldChangelogVirtualDom) {
|
|
5695
5767
|
return state;
|
|
5696
5768
|
}
|
|
5697
5769
|
return {
|
|
@@ -5709,7 +5781,10 @@ const disablePreviewColorTheme = async () => {
|
|
|
5709
5781
|
};
|
|
5710
5782
|
|
|
5711
5783
|
const handleMouseEnterEnable = async state => {
|
|
5712
|
-
const
|
|
5784
|
+
const {
|
|
5785
|
+
extension
|
|
5786
|
+
} = state;
|
|
5787
|
+
const colorThemeId = getColorThemeId(extension);
|
|
5713
5788
|
if (!colorThemeId) {
|
|
5714
5789
|
return state;
|
|
5715
5790
|
}
|
|
@@ -5749,7 +5824,7 @@ const handleReadmeLinkClick = async (linkProtectionEnabled, platform, href) => {
|
|
|
5749
5824
|
// TODO what to do about mail links?
|
|
5750
5825
|
if (linkProtectionEnabled) {
|
|
5751
5826
|
const message = `Do you want to open this external link?\n\n${href}`;
|
|
5752
|
-
const confirmed = await
|
|
5827
|
+
const confirmed = await invoke$5('ConfirmPrompt.prompt', message);
|
|
5753
5828
|
if (!confirmed) {
|
|
5754
5829
|
return;
|
|
5755
5830
|
}
|
|
@@ -5804,8 +5879,11 @@ const handleResourceLinkClick = async (state, href) => {
|
|
|
5804
5879
|
};
|
|
5805
5880
|
|
|
5806
5881
|
const handleScroll = (state, scrollTop, scrollSource = Script) => {
|
|
5882
|
+
const {
|
|
5883
|
+
selectedTab
|
|
5884
|
+
} = state;
|
|
5807
5885
|
const newScrollTop = Math.max(0, scrollTop);
|
|
5808
|
-
if (
|
|
5886
|
+
if (selectedTab === Changelog) {
|
|
5809
5887
|
return {
|
|
5810
5888
|
...state,
|
|
5811
5889
|
changelogScrollTop: newScrollTop,
|
|
@@ -5957,7 +6035,7 @@ const getErrorMessage = async response => {
|
|
|
5957
6035
|
const getHttpError = async response => {
|
|
5958
6036
|
const apiMessage = await getErrorMessage(response);
|
|
5959
6037
|
const rateLimitRemaining = response.headers.get('x-ratelimit-remaining');
|
|
5960
|
-
if (response.status === 429 || response.status === 403 && (rateLimitRemaining === '0' ||
|
|
6038
|
+
if (response.status === 429 || response.status === 403 && (rateLimitRemaining === '0' || apiMessage.toLowerCase().includes('rate limit'))) {
|
|
5961
6039
|
const resetValue = Number(response.headers.get('x-ratelimit-reset'));
|
|
5962
6040
|
const retry = Number.isFinite(resetValue) && resetValue > 0 ? ` Try again after ${new Date(resetValue * 1000).toLocaleString()}.` : ' Please try again later.';
|
|
5963
6041
|
return new GithubReleasesError(`GitHub API rate limit exceeded.${retry}`);
|
|
@@ -6077,7 +6155,7 @@ const mergeMarkdownVirtualDoms = chunks => {
|
|
|
6077
6155
|
}
|
|
6078
6156
|
return root ? [root, ...children] : [];
|
|
6079
6157
|
};
|
|
6080
|
-
const renderGithubReleases = async (result, githubRepository, languages, locationProtocol) => {
|
|
6158
|
+
const renderGithubReleases = async (result, githubRepository, cacheName, languages, locationProtocol) => {
|
|
6081
6159
|
const chunks = [];
|
|
6082
6160
|
const {
|
|
6083
6161
|
isTruncated,
|
|
@@ -6097,7 +6175,7 @@ const renderGithubReleases = async (result, githubRepository, languages, locatio
|
|
|
6097
6175
|
languages,
|
|
6098
6176
|
linksExternal: true,
|
|
6099
6177
|
locationProtocol
|
|
6100
|
-
});
|
|
6178
|
+
}, cacheName);
|
|
6101
6179
|
chunks.push([...(await getMarkdownVirtualDom(html))]);
|
|
6102
6180
|
}
|
|
6103
6181
|
return addScrollToTopVirtualDom(mergeMarkdownVirtualDoms(chunks));
|
|
@@ -6105,6 +6183,7 @@ const renderGithubReleases = async (result, githubRepository, languages, locatio
|
|
|
6105
6183
|
const selectTabChangelog = async state => {
|
|
6106
6184
|
const {
|
|
6107
6185
|
baseUrl,
|
|
6186
|
+
cacheName,
|
|
6108
6187
|
extension,
|
|
6109
6188
|
extensionUri,
|
|
6110
6189
|
languages,
|
|
@@ -6116,7 +6195,7 @@ const selectTabChangelog = async state => {
|
|
|
6116
6195
|
if (githubRepository) {
|
|
6117
6196
|
try {
|
|
6118
6197
|
const result = await loadGithubReleases(githubRepository);
|
|
6119
|
-
changelogDom = await renderGithubReleases(result, githubRepository, languages, locationProtocol);
|
|
6198
|
+
changelogDom = await renderGithubReleases(result, githubRepository, cacheName, languages, locationProtocol);
|
|
6120
6199
|
} catch (error) {
|
|
6121
6200
|
const message = error instanceof GithubReleasesError ? error.message : 'GitHub releases could not be loaded. Please try again later.';
|
|
6122
6201
|
const html = await renderMarkdown(`# Changelog\n\n${message}`, {
|
|
@@ -6124,7 +6203,7 @@ const selectTabChangelog = async state => {
|
|
|
6124
6203
|
languages,
|
|
6125
6204
|
linksExternal: true,
|
|
6126
6205
|
locationProtocol
|
|
6127
|
-
});
|
|
6206
|
+
}, cacheName);
|
|
6128
6207
|
changelogDom = await getMarkdownVirtualDom(html, {
|
|
6129
6208
|
scrollToTopEnabled: true
|
|
6130
6209
|
});
|
|
@@ -6136,7 +6215,7 @@ const selectTabChangelog = async state => {
|
|
|
6136
6215
|
languages,
|
|
6137
6216
|
linksExternal: true,
|
|
6138
6217
|
locationProtocol
|
|
6139
|
-
});
|
|
6218
|
+
}, cacheName);
|
|
6140
6219
|
changelogDom = await getMarkdownVirtualDom(html, {
|
|
6141
6220
|
scrollToTopEnabled: true
|
|
6142
6221
|
});
|
|
@@ -6162,6 +6241,7 @@ const selectTabDefault = async state => {
|
|
|
6162
6241
|
const selectTabDetails = async state => {
|
|
6163
6242
|
const {
|
|
6164
6243
|
baseUrl,
|
|
6244
|
+
cacheName,
|
|
6165
6245
|
languages,
|
|
6166
6246
|
locationProtocol,
|
|
6167
6247
|
readmeUrl,
|
|
@@ -6173,7 +6253,7 @@ const selectTabDetails = async state => {
|
|
|
6173
6253
|
languages,
|
|
6174
6254
|
linksExternal: true,
|
|
6175
6255
|
locationProtocol
|
|
6176
|
-
});
|
|
6256
|
+
}, cacheName);
|
|
6177
6257
|
const detailsDom = await getMarkdownVirtualDom(readmeHtml);
|
|
6178
6258
|
const newTabs = tabs.map(tab => {
|
|
6179
6259
|
return {
|
|
@@ -6192,6 +6272,7 @@ const selectTabDetails = async state => {
|
|
|
6192
6272
|
const selectTabFeatures = async state => {
|
|
6193
6273
|
const {
|
|
6194
6274
|
baseUrl,
|
|
6275
|
+
cacheName,
|
|
6195
6276
|
extension,
|
|
6196
6277
|
features,
|
|
6197
6278
|
locationProtocol,
|
|
@@ -6203,7 +6284,7 @@ const selectTabFeatures = async state => {
|
|
|
6203
6284
|
}
|
|
6204
6285
|
const actualSelectedFeature = selectedFeature || Theme;
|
|
6205
6286
|
const fn = getFeatureDetailsHandler(actualSelectedFeature);
|
|
6206
|
-
const partialNewState = await fn(extension, baseUrl, locationProtocol);
|
|
6287
|
+
const partialNewState = await fn(extension, baseUrl, locationProtocol, cacheName);
|
|
6207
6288
|
const newTabs = tabs.map(tab => {
|
|
6208
6289
|
return {
|
|
6209
6290
|
...tab,
|
|
@@ -6272,112 +6353,11 @@ const hideSizeLink = state => {
|
|
|
6272
6353
|
};
|
|
6273
6354
|
};
|
|
6274
6355
|
|
|
6275
|
-
const createRpc = async () => {
|
|
6276
|
-
const rpc = await create$4({
|
|
6277
|
-
commandMap: {},
|
|
6278
|
-
send: async port => {
|
|
6279
|
-
await sendMessagePortToClipBoardWorker(port, 0);
|
|
6280
|
-
}
|
|
6281
|
-
});
|
|
6282
|
-
return rpc;
|
|
6283
|
-
};
|
|
6284
|
-
const initializeClipBoardWorker = async () => {
|
|
6285
|
-
const rpc = await createRpc();
|
|
6286
|
-
set$b(rpc);
|
|
6287
|
-
};
|
|
6288
|
-
|
|
6289
|
-
const sendMessagePortToExtensionHostWorker = async port => {
|
|
6290
|
-
await sendMessagePortToExtensionHostWorker$1(port, 0);
|
|
6291
|
-
};
|
|
6292
|
-
|
|
6293
|
-
const createExtensionHostWorkerRpc = async () => {
|
|
6294
|
-
try {
|
|
6295
|
-
const rpc = await create$5({
|
|
6296
|
-
commandMap: {},
|
|
6297
|
-
send: sendMessagePortToExtensionHostWorker
|
|
6298
|
-
});
|
|
6299
|
-
return rpc;
|
|
6300
|
-
} catch (error) {
|
|
6301
|
-
throw new VError(error, `Failed to create extension host rpc`);
|
|
6302
|
-
}
|
|
6303
|
-
};
|
|
6304
|
-
|
|
6305
|
-
const {
|
|
6306
|
-
set
|
|
6307
|
-
} = ExtensionHost;
|
|
6308
|
-
|
|
6309
|
-
const initializeExtensionHostWorker = async () => {
|
|
6310
|
-
const rpc = await createExtensionHostWorkerRpc();
|
|
6311
|
-
set(rpc);
|
|
6312
|
-
};
|
|
6313
|
-
|
|
6314
|
-
const createExtensionManagementWorkerRpc = async () => {
|
|
6315
|
-
try {
|
|
6316
|
-
const rpc = await create$5({
|
|
6317
|
-
commandMap: {},
|
|
6318
|
-
send: port => sendMessagePortToExtensionManagementWorker(port, 0)
|
|
6319
|
-
});
|
|
6320
|
-
return rpc;
|
|
6321
|
-
} catch (error) {
|
|
6322
|
-
throw new VError(error, `Failed to create extension management rpc`);
|
|
6323
|
-
}
|
|
6324
|
-
};
|
|
6325
|
-
|
|
6326
|
-
const initializeExtensionManagementWorker = async () => {
|
|
6327
|
-
try {
|
|
6328
|
-
const rpc = await createExtensionManagementWorkerRpc();
|
|
6329
|
-
set$9(rpc);
|
|
6330
|
-
} catch {
|
|
6331
|
-
// ignore
|
|
6332
|
-
}
|
|
6333
|
-
};
|
|
6334
|
-
|
|
6335
|
-
const sendMessagePortToFileSystemWorker = async port => {
|
|
6336
|
-
await sendMessagePortToFileSystemWorker$1(port, 0);
|
|
6337
|
-
};
|
|
6338
|
-
|
|
6339
|
-
const createFileSystemWorkerRpc = async () => {
|
|
6340
|
-
try {
|
|
6341
|
-
const rpc = await create$5({
|
|
6342
|
-
commandMap: {},
|
|
6343
|
-
send: sendMessagePortToFileSystemWorker
|
|
6344
|
-
});
|
|
6345
|
-
return rpc;
|
|
6346
|
-
} catch (error) {
|
|
6347
|
-
throw new VError(error, `Failed to create file system worker rpc`);
|
|
6348
|
-
}
|
|
6349
|
-
};
|
|
6350
|
-
|
|
6351
|
-
const initializeFileSystemWorker = async () => {
|
|
6352
|
-
const rpc = await createFileSystemWorkerRpc();
|
|
6353
|
-
set$3(rpc);
|
|
6354
|
-
};
|
|
6355
|
-
|
|
6356
|
-
const sendMessagePortToMarkdownWorker = async port => {
|
|
6357
|
-
await sendMessagePortToMarkdownWorker$1(port, 0);
|
|
6358
|
-
};
|
|
6359
|
-
|
|
6360
|
-
const createMarkdownWorkerRpc = async () => {
|
|
6361
|
-
const rpc = await create$4({
|
|
6362
|
-
commandMap: {},
|
|
6363
|
-
send: sendMessagePortToMarkdownWorker
|
|
6364
|
-
});
|
|
6365
|
-
return rpc;
|
|
6366
|
-
};
|
|
6367
|
-
|
|
6368
|
-
const initializeMarkdownWorker = async () => {
|
|
6369
|
-
const rpc = await createMarkdownWorkerRpc();
|
|
6370
|
-
set$5(rpc);
|
|
6371
|
-
};
|
|
6372
|
-
|
|
6373
|
-
const initialize = async applicationName => {
|
|
6374
|
-
setApplicationName(applicationName);
|
|
6375
|
-
// TODO load markdown worker only when needed
|
|
6376
|
-
await Promise.all([initializeMarkdownWorker(), initializeFileSystemWorker(), initializeExtensionHostWorker(), initializeExtensionManagementWorker(), initializeClipBoardWorker()]);
|
|
6377
|
-
};
|
|
6378
|
-
|
|
6379
6356
|
const loadContent2 = async (state, savedState, isTest = false) => {
|
|
6380
|
-
|
|
6357
|
+
const {
|
|
6358
|
+
platform
|
|
6359
|
+
} = state;
|
|
6360
|
+
return loadContent(state, platform, savedState, isTest);
|
|
6381
6361
|
};
|
|
6382
6362
|
|
|
6383
6363
|
const openImageInNewTab = async state => {
|
|
@@ -7210,7 +7190,10 @@ const renderEventListeners = () => {
|
|
|
7210
7190
|
};
|
|
7211
7191
|
|
|
7212
7192
|
const renderTitle = state => {
|
|
7213
|
-
|
|
7193
|
+
const {
|
|
7194
|
+
name
|
|
7195
|
+
} = state;
|
|
7196
|
+
return name;
|
|
7214
7197
|
};
|
|
7215
7198
|
|
|
7216
7199
|
const resize = (state, dimensions) => {
|
|
@@ -7289,7 +7272,6 @@ const commandMap = {
|
|
|
7289
7272
|
'ExtensionDetail.handleWheel': wrapCommand(handleScroll),
|
|
7290
7273
|
// deprecated
|
|
7291
7274
|
'ExtensionDetail.hideSizeLink': wrapCommand(hideSizeLink),
|
|
7292
|
-
'ExtensionDetail.initialize': initialize,
|
|
7293
7275
|
'ExtensionDetail.loadContent2': wrapCommand(loadContent2),
|
|
7294
7276
|
'ExtensionDetail.mockGithubApi': wrapCommand(handleMockGithubApi),
|
|
7295
7277
|
'ExtensionDetail.openImageInNewTab': wrapCommand(openImageInNewTab),
|
|
@@ -7303,12 +7285,100 @@ const commandMap = {
|
|
|
7303
7285
|
'ExtensionDetail.terminate': terminate
|
|
7304
7286
|
};
|
|
7305
7287
|
|
|
7288
|
+
const createRpc = async () => {
|
|
7289
|
+
const rpc = await create$4({
|
|
7290
|
+
commandMap: {},
|
|
7291
|
+
send: async port => {
|
|
7292
|
+
await sendMessagePortToClipBoardWorker(port, 0);
|
|
7293
|
+
}
|
|
7294
|
+
});
|
|
7295
|
+
return rpc;
|
|
7296
|
+
};
|
|
7297
|
+
const initializeClipBoardWorker = async () => {
|
|
7298
|
+
const rpc = await createRpc();
|
|
7299
|
+
set$c(rpc);
|
|
7300
|
+
};
|
|
7301
|
+
|
|
7302
|
+
const sendMessagePortToExtensionHostWorker = async port => {
|
|
7303
|
+
await sendMessagePortToExtensionHostWorker$1(port, 0);
|
|
7304
|
+
};
|
|
7305
|
+
|
|
7306
|
+
const createExtensionHostWorkerRpc = async () => {
|
|
7307
|
+
return create$4({
|
|
7308
|
+
commandMap: {},
|
|
7309
|
+
send: sendMessagePortToExtensionHostWorker
|
|
7310
|
+
});
|
|
7311
|
+
};
|
|
7312
|
+
|
|
7313
|
+
const {
|
|
7314
|
+
set
|
|
7315
|
+
} = ExtensionHost;
|
|
7316
|
+
|
|
7317
|
+
const initializeExtensionHostWorker = async () => {
|
|
7318
|
+
const rpc = await createExtensionHostWorkerRpc();
|
|
7319
|
+
set(rpc);
|
|
7320
|
+
};
|
|
7321
|
+
|
|
7322
|
+
const createExtensionManagementWorkerRpc = async () => {
|
|
7323
|
+
return create$4({
|
|
7324
|
+
commandMap: {},
|
|
7325
|
+
send: port => sendMessagePortToExtensionManagementWorker(port, 0)
|
|
7326
|
+
});
|
|
7327
|
+
};
|
|
7328
|
+
|
|
7329
|
+
const initializeExtensionManagementWorker = async () => {
|
|
7330
|
+
try {
|
|
7331
|
+
const rpc = await createExtensionManagementWorkerRpc();
|
|
7332
|
+
set$9(rpc);
|
|
7333
|
+
} catch {
|
|
7334
|
+
// ignore
|
|
7335
|
+
}
|
|
7336
|
+
};
|
|
7337
|
+
|
|
7338
|
+
const sendMessagePortToFileSystemWorker = async port => {
|
|
7339
|
+
await sendMessagePortToFileSystemWorker$1(port, 0);
|
|
7340
|
+
};
|
|
7341
|
+
|
|
7342
|
+
const createFileSystemWorkerRpc = async () => {
|
|
7343
|
+
return create$4({
|
|
7344
|
+
commandMap: {},
|
|
7345
|
+
send: sendMessagePortToFileSystemWorker
|
|
7346
|
+
});
|
|
7347
|
+
};
|
|
7348
|
+
|
|
7349
|
+
const initializeFileSystemWorker = async () => {
|
|
7350
|
+
const rpc = await createFileSystemWorkerRpc();
|
|
7351
|
+
set$3(rpc);
|
|
7352
|
+
};
|
|
7353
|
+
|
|
7354
|
+
const sendMessagePortToMarkdownWorker = async port => {
|
|
7355
|
+
await sendMessagePortToMarkdownWorker$1(port, 0);
|
|
7356
|
+
};
|
|
7357
|
+
|
|
7358
|
+
const createMarkdownWorkerRpc = async () => {
|
|
7359
|
+
const rpc = await create$4({
|
|
7360
|
+
commandMap: {},
|
|
7361
|
+
send: sendMessagePortToMarkdownWorker
|
|
7362
|
+
});
|
|
7363
|
+
return rpc;
|
|
7364
|
+
};
|
|
7365
|
+
|
|
7366
|
+
const initializeMarkdownWorker = async () => {
|
|
7367
|
+
const rpc = await createMarkdownWorkerRpc();
|
|
7368
|
+
set$5(rpc);
|
|
7369
|
+
};
|
|
7370
|
+
|
|
7306
7371
|
const listen = async () => {
|
|
7307
7372
|
registerCommands(commandMap);
|
|
7308
7373
|
const rpc = await create$3({
|
|
7309
7374
|
commandMap: commandMap
|
|
7310
7375
|
});
|
|
7311
7376
|
set$6(rpc);
|
|
7377
|
+
const [dialogRpc] = await Promise.all([create$4({
|
|
7378
|
+
commandMap: {},
|
|
7379
|
+
send: sendMessagePortToDialogWorker
|
|
7380
|
+
}), initializeMarkdownWorker(), initializeFileSystemWorker(), initializeExtensionHostWorker(), initializeExtensionManagementWorker(), initializeClipBoardWorker()]);
|
|
7381
|
+
set$b(dialogRpc);
|
|
7312
7382
|
};
|
|
7313
7383
|
|
|
7314
7384
|
const main = async () => {
|