@lvce-editor/extension-detail-view 4.6.0 → 4.8.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.
@@ -30,6 +30,7 @@ const Id = 'ID';
30
30
  const Identifier = 'Identifier';
31
31
  const ImportTime = 'Import Time: ';
32
32
  const Installation = 'Installation';
33
+ const InvalidLink = 'Invalid link';
33
34
  const Issues = 'Issues';
34
35
  const JsonValidation$1 = 'Json Validation';
35
36
  const Label = 'Label';
@@ -42,18 +43,21 @@ const NoReadmeFound = 'No Readme Found.';
42
43
  const OpenImageInNewTab = 'Open Image in New Tab';
43
44
  const OpenInNewTab = 'Open in New Tab';
44
45
  const ProgrammingLanguages$1 = 'Programming Languages';
46
+ const PropertyMustBeOfTypeString = 'Property must be a string';
45
47
  const Published = 'Published';
46
48
  const Repository = 'Repository';
47
49
  const Resources$1 = 'Resources';
48
50
  const RuntimeStatus$1 = 'Runtime Status';
49
51
  const SaveImageAs = 'Save Image as';
50
52
  const Schema = 'Schema';
53
+ const SchemaNotFound = 'Schema not found';
51
54
  const ScrollToTop$1 = 'Scroll to top';
52
55
  const SelectedFeatureUnknownOrUnsupported = 'Selected feature is unknown or unsupported';
53
56
  const Selector = 'Selector';
54
57
  const SetColorTheme$1 = 'Set Color Theme';
55
58
  const Settings$1 = 'Settings';
56
59
  const Size = 'Size';
60
+ const StringMustNotBeEmpty = 'String must not be empty';
57
61
  const Theme$1 = 'Theme';
58
62
  const Uninstall$1 = 'Uninstall';
59
63
  const UnsupportedFeature = 'Unsupported Feature';
@@ -207,11 +211,50 @@ const repository = () => {
207
211
  const license = () => {
208
212
  return i18nString(License);
209
213
  };
214
+ const propertyMustBeOfTypeString = () => {
215
+ return i18nString(PropertyMustBeOfTypeString);
216
+ };
217
+ const stringMustNotBeEmpty = () => {
218
+ return i18nString(StringMustNotBeEmpty);
219
+ };
220
+ const schemaNotFound = () => {
221
+ return i18nString(SchemaNotFound);
222
+ };
223
+ const invalidLink = () => {
224
+ return i18nString(InvalidLink);
225
+ };
226
+
227
+ const getActivationEntry = value => {
228
+ if (typeof value !== 'string') {
229
+ return {
230
+ isValid: false,
231
+ stringValue: JSON.stringify(value),
232
+ errorMessage: propertyMustBeOfTypeString()
233
+ };
234
+ }
235
+ if (!value) {
236
+ return {
237
+ isValid: false,
238
+ stringValue: '',
239
+ errorMessage: stringMustNotBeEmpty()
240
+ };
241
+ }
242
+ return {
243
+ isValid: true,
244
+ stringValue: value,
245
+ errorMessage: ''
246
+ };
247
+ };
248
+ const getActivationEntries = activation => {
249
+ return activation.map(getActivationEntry);
250
+ };
210
251
 
211
252
  const getActivationEventsDetails = async extension => {
212
253
  const activationEvents = extension.activation || [];
254
+ const activationEntries = getActivationEntries(activationEvents);
213
255
  return {
214
- activationEvents
256
+ activationEvents,
257
+ activationEntries
215
258
  };
216
259
  };
217
260
 
@@ -336,7 +379,20 @@ const code = {
336
379
  childCount: 1
337
380
  };
338
381
  const getActivationEventVirtualDom = event => {
339
- return [li, code, text(event)];
382
+ const {
383
+ stringValue,
384
+ errorMessage,
385
+ isValid
386
+ } = event;
387
+ if (!isValid) {
388
+ return [{
389
+ type: Li,
390
+ childCount: 1,
391
+ title: errorMessage,
392
+ className: 'ListItem ListItemInvalid'
393
+ }, code, text(stringValue)];
394
+ }
395
+ return [li, code, text(stringValue)];
340
396
  };
341
397
 
342
398
  const getFeatureContentHeadingVirtualDom = heading => {
@@ -359,7 +415,7 @@ const getFeatureActivationEventsVirtualDom = activationEvents$1 => {
359
415
  };
360
416
 
361
417
  const getActivationEventsVirtualDom = state => {
362
- return getFeatureActivationEventsVirtualDom(state.activationEvents);
418
+ return getFeatureActivationEventsVirtualDom(state.activationEntries);
363
419
  };
364
420
 
365
421
  const Text = 1;
@@ -441,16 +497,19 @@ const getCellCodeVirtualDom = (value, props) => {
441
497
  }, text(value)];
442
498
  };
443
499
 
444
- const getCellLinkVirtualDom = (value, {
445
- href
446
- }) => {
500
+ const getCellLinkVirtualDom = (value, props) => {
501
+ const tdClassName = props?.className ? `${TableCell} ${props.className}` : TableCell;
447
502
  return [{
448
503
  type: Td,
449
- className: TableCell,
450
- childCount: 1
504
+ className: tdClassName,
505
+ childCount: 1,
506
+ ...(props?.title ? {
507
+ title: props.title
508
+ } : {})
451
509
  }, {
452
510
  type: A,
453
- href,
511
+ className: Link$1,
512
+ href: props?.href,
454
513
  childCount: 1
455
514
  }, text(value)];
456
515
  };
@@ -536,94 +595,187 @@ const getCommandsVirtualDom = state => {
536
595
  return getFeatureCommandsVirtualDom(state.commands);
537
596
  };
538
597
 
539
- const stringifyValue = value => {
540
- return JSON.stringify(value);
598
+ const isExternalLink = schema => {
599
+ return schema.startsWith('http://') || schema.startsWith('https://');
541
600
  };
542
- const getLinkOrTextEntry = (schema, schemaLinkUrl) => {
543
- if (typeof schema !== 'string') {
544
- return {
545
- type: Text,
546
- value: stringifyValue(schema)
547
- };
548
- }
549
- if (schemaLinkUrl) {
550
- return {
551
- type: Link,
552
- value: schema,
553
- href: schemaLinkUrl
554
- };
601
+ const hasWhitespace = value => {
602
+ return /\s/.test(value);
603
+ };
604
+ const isOnlyDotsOrEmpty = value => {
605
+ const trimmed = value.trim();
606
+ return trimmed === '' || /^\.*$/.test(trimmed);
607
+ };
608
+ const isValidHttpUrl = value => {
609
+ try {
610
+ const url = new URL(value);
611
+ return (url.protocol === 'http:' || url.protocol === 'https:') && !!url.hostname;
612
+ } catch {
613
+ return false;
555
614
  }
556
- return {
557
- type: Text,
558
- value: schema
559
- };
560
615
  };
561
-
562
- const isExternalLink = schema => {
563
- return schema.startsWith('http://') || schema.startsWith('https://');
616
+ const isValidRelativePath = value => {
617
+ // Disallow schemes and whitespace
618
+ if (hasWhitespace(value)) {
619
+ return false;
620
+ }
621
+ if (value.includes('://')) {
622
+ return false;
623
+ }
624
+ if (isOnlyDotsOrEmpty(value)) {
625
+ return false;
626
+ }
627
+ // Allow paths like ./a.json, ../a.json, /a.json, schemas/a.json, a/b.json
628
+ if (!/^[./]?[A-Za-z0-9._\-/]+$/.test(value)) {
629
+ return false;
630
+ }
631
+ // Must contain at least one alphanumeric character
632
+ if (!/[A-Za-z0-9]/.test(value)) {
633
+ return false;
634
+ }
635
+ return true;
564
636
  };
565
637
  const getSchemaLinkUrl = (schema, extensionUri) => {
566
- if (!schema) {
638
+ if (!schema || typeof schema !== 'string') {
567
639
  return '';
568
640
  }
569
- if (typeof schema !== 'string') {
641
+ const trimmed = schema.trim();
642
+ if (trimmed !== schema) {
570
643
  return '';
571
644
  }
572
645
  if (isExternalLink(schema)) {
573
- return schema;
646
+ return isValidHttpUrl(schema) ? schema : '';
647
+ }
648
+ if (!isValidRelativePath(schema)) {
649
+ return '';
650
+ }
651
+ try {
652
+ return new URL(schema, extensionUri).toString();
653
+ } catch {
654
+ return '';
574
655
  }
575
- return new URL(schema, extensionUri).toString();
576
656
  };
577
657
 
578
- const stringify = value => {
658
+ const existsJson = async schemaUrl => {
579
659
  try {
580
- return JSON.stringify(value);
660
+ // TODO verify that response header is json
661
+ const response = await fetch(schemaUrl, {
662
+ method: 'HEAD'
663
+ });
664
+ return response.ok;
581
665
  } catch {
582
- return String(value);
666
+ return false;
583
667
  }
584
668
  };
585
- const getJsonValidationTableEntry = (validation, extensionUri) => {
586
- const invalidProps = {
587
- className: TableCellInvalid,
588
- title: 'property must be a string'
589
- };
590
- if (!validation || typeof validation !== 'object' || Array.isArray(validation)) {
591
- const shown = stringify(validation);
669
+ const getJsonValidationInfos = async (extensionUri, validations) => {
670
+ const validationInfos = [];
671
+ for (const validation of validations) {
672
+ const schema = validation.schema ?? validation.url;
673
+ const schemaLinkUrl = getSchemaLinkUrl(schema, extensionUri);
674
+ const {
675
+ fileMatch
676
+ } = validation;
677
+ if (typeof schema !== 'string') {
678
+ validationInfos.push({
679
+ isValid: false,
680
+ stringValue: JSON.stringify(schema),
681
+ schemaUrl: '',
682
+ errorMessage: propertyMustBeOfTypeString(),
683
+ fileMatch
684
+ });
685
+ } else if (schema && !schemaLinkUrl) {
686
+ validationInfos.push({
687
+ isValid: false,
688
+ stringValue: schema,
689
+ schemaUrl: schemaLinkUrl,
690
+ errorMessage: invalidLink(),
691
+ fileMatch
692
+ });
693
+ } else if (schemaLinkUrl) {
694
+ if (await existsJson(schemaLinkUrl)) {
695
+ validationInfos.push({
696
+ isValid: true,
697
+ stringValue: schema,
698
+ schemaUrl: schemaLinkUrl,
699
+ errorMessage: '',
700
+ fileMatch
701
+ });
702
+ } else {
703
+ validationInfos.push({
704
+ isValid: false,
705
+ stringValue: schema,
706
+ schemaUrl: schemaLinkUrl,
707
+ errorMessage: schemaNotFound(),
708
+ fileMatch
709
+ });
710
+ }
711
+ } else {
712
+ validationInfos.push({
713
+ isValid: true,
714
+ stringValue: schema,
715
+ schemaUrl: schemaLinkUrl,
716
+ errorMessage: '',
717
+ fileMatch
718
+ });
719
+ }
720
+ }
721
+ return validationInfos;
722
+ };
723
+
724
+ const getJsonValidationTableEntry = validationInfo => {
725
+ const {
726
+ isValid,
727
+ errorMessage,
728
+ schemaUrl,
729
+ stringValue,
730
+ fileMatch
731
+ } = validationInfo;
732
+ if (!isValid && schemaUrl) {
733
+ return [{
734
+ type: Code,
735
+ value: fileMatch
736
+ }, {
737
+ type: Link,
738
+ value: stringValue,
739
+ href: schemaUrl,
740
+ className: TableCellInvalid,
741
+ title: errorMessage
742
+ }];
743
+ }
744
+ if (!isValid) {
592
745
  return [{
593
746
  type: Text,
594
- value: shown,
595
- ...invalidProps
747
+ value: fileMatch
596
748
  }, {
597
749
  type: Text,
598
- value: shown,
599
- ...invalidProps
750
+ value: stringValue,
751
+ className: TableCellInvalid,
752
+ title: errorMessage
600
753
  }];
601
754
  }
602
- const {
603
- fileMatch
604
- } = validation;
605
- const schema = validation.schema ?? validation.url;
606
- const schemaLinkUrl = getSchemaLinkUrl(schema, extensionUri);
607
- const leftCell = typeof fileMatch === 'string' || Array.isArray(fileMatch) ? {
755
+ if (schemaUrl) {
756
+ return [{
757
+ type: Code,
758
+ value: fileMatch
759
+ }, {
760
+ type: Link,
761
+ value: stringValue,
762
+ href: schemaUrl
763
+ }];
764
+ }
765
+ return [{
608
766
  type: Code,
609
767
  value: fileMatch
610
- } : {
611
- type: Text,
612
- value: stringify(fileMatch),
613
- ...invalidProps
614
- };
615
- const rightEntry = typeof schema === 'string' ? getLinkOrTextEntry(schema, schemaLinkUrl) : {
768
+ }, {
616
769
  type: Text,
617
- value: stringify(schema),
618
- ...invalidProps
619
- };
620
- return [leftCell, rightEntry];
770
+ value: stringValue
771
+ }];
621
772
  };
622
773
 
623
774
  const getJsonValidationDetails = async extension => {
624
775
  const validations = extension.jsonValidation || [];
625
776
  const extensionUri = extension.uri || '';
626
- const rows = validations.map(validation => getJsonValidationTableEntry(validation, extensionUri));
777
+ const validationInfos = await getJsonValidationInfos(extensionUri, validations);
778
+ const rows = validationInfos.map(getJsonValidationTableEntry);
627
779
  return {
628
780
  jsonValidation: rows
629
781
  };
@@ -835,7 +987,7 @@ class AssertionError extends Error {
835
987
  const Object$1 = 1;
836
988
  const Number$1 = 2;
837
989
  const Array$1 = 3;
838
- const String$1 = 4;
990
+ const String = 4;
839
991
  const Boolean$1 = 5;
840
992
  const Function = 6;
841
993
  const Null = 7;
@@ -847,7 +999,7 @@ const getType = value => {
847
999
  case 'function':
848
1000
  return Function;
849
1001
  case 'string':
850
- return String$1;
1002
+ return String;
851
1003
  case 'object':
852
1004
  if (value === null) {
853
1005
  return Null;
@@ -864,7 +1016,7 @@ const getType = value => {
864
1016
  };
865
1017
  const string = value => {
866
1018
  const type = getType(value);
867
- if (type !== String$1) {
1019
+ if (type !== String) {
868
1020
  throw new AssertionError('expected value to be of type string');
869
1021
  }
870
1022
  };
@@ -3112,7 +3264,8 @@ const create = (uid, uri, x, y, width, height, platform, assetDir) => {
3112
3264
  paddingRight: 0,
3113
3265
  showSideBar: true,
3114
3266
  sideBarWidth: 0,
3115
- locationProtocol: ''
3267
+ locationProtocol: '',
3268
+ activationEntries: []
3116
3269
  };
3117
3270
  set(uid, state, state);
3118
3271
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/extension-detail-view",
3
- "version": "4.6.0",
3
+ "version": "4.8.0",
4
4
  "description": "Extension Detail View Worker",
5
5
  "repository": {
6
6
  "type": "git",