diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/MissingRecommendedColumnNotice.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/MissingRecommendedColumnNotice.java new file mode 100644 index 0000000000..926186dbfc --- /dev/null +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/MissingRecommendedColumnNotice.java @@ -0,0 +1,38 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mobilitydata.gtfsvalidator.notice; + +import static org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice.SectionRef.TERM_DEFINITIONS; +import static org.mobilitydata.gtfsvalidator.notice.SeverityLevel.WARNING; + +import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice; +import org.mobilitydata.gtfsvalidator.annotation.GtfsValidationNotice.SectionRefs; + +/** A recommended column is missing in the input file. */ +@GtfsValidationNotice(severity = WARNING, sections = @SectionRefs(TERM_DEFINITIONS)) +public class MissingRecommendedColumnNotice extends ValidationNotice { + + /** The name of the faulty file. */ + private final String filename; + + /** The name of the missing column. */ + private final String fieldName; + + public MissingRecommendedColumnNotice(String filename, String fieldName) { + this.filename = filename; + this.fieldName = fieldName; + } +} diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/NoticeContainer.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/NoticeContainer.java index 997325c411..c5231b5316 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/NoticeContainer.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/NoticeContainer.java @@ -58,6 +58,7 @@ public class NoticeContainer { private final List> systemErrors = new ArrayList<>(); private final Map noticesCountPerTypeAndSeverity = new HashMap<>(); private boolean hasValidationErrors = false; + private boolean hasValidationWarnings = false; /** * Used to specify limits on amount of notices in this {@code NoticeContainer}. @@ -99,6 +100,10 @@ public void addValidationNoticeWithSeverity( if (resolved.isError()) { hasValidationErrors = true; } + if (resolved.isWarning()) { + hasValidationWarnings = true; + } + updateNoticeCount(resolved); if (validationNotices.size() >= maxTotalValidationNotices || noticesCountPerTypeAndSeverity.get(resolved.getMappingKey()) @@ -147,6 +152,7 @@ public void addAll(NoticeContainer otherContainer) { validationNotices.addAll(otherContainer.validationNotices); systemErrors.addAll(otherContainer.systemErrors); hasValidationErrors |= otherContainer.hasValidationErrors; + hasValidationWarnings |= otherContainer.hasValidationWarnings; for (Entry entry : otherContainer.noticesCountPerTypeAndSeverity.entrySet()) { int count = noticesCountPerTypeAndSeverity.getOrDefault(entry.getKey(), 0); noticesCountPerTypeAndSeverity.put(entry.getKey(), count + entry.getValue()); @@ -158,6 +164,11 @@ public boolean hasValidationErrors() { return hasValidationErrors; } + /** Tells if this container has any {@code ValidationNotice} that is a warning. */ + public boolean hasValidationWarnings() { + return hasValidationWarnings; + } + public List> getResolvedValidationNotices() { return validationNotices; } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/ResolvedNotice.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/ResolvedNotice.java index feb26b2271..04a61be24d 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/ResolvedNotice.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/notice/ResolvedNotice.java @@ -81,4 +81,16 @@ public int hashCode() { public boolean isError() { return getSeverityLevel().ordinal() >= SeverityLevel.ERROR.ordinal(); } + + /** + * Tells if this notice is a {@code WARNING}. + * + *

This method is preferred to checking {@code severityLevel} directly since more levels may be + * added in the future. + * + * @return true if this notice is a warning, false otherwise + */ + public boolean isWarning() { + return getSeverityLevel() == SeverityLevel.WARNING; + } } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoader.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoader.java index ed2baa720b..78dd9abfec 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoader.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoader.java @@ -152,6 +152,10 @@ private static NoticeContainer validateHeaders( .filter(GtfsColumnDescriptor::headerRequired) .map(GtfsColumnDescriptor::columnName) .collect(Collectors.toSet()), + columnDescriptors.stream() + .filter(GtfsColumnDescriptor::headerRecommended) + .map(GtfsColumnDescriptor::columnName) + .collect(Collectors.toSet()), headerNotices); return headerNotices; } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsColumnDescriptor.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsColumnDescriptor.java index d75c87933d..0546e479d1 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsColumnDescriptor.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsColumnDescriptor.java @@ -11,6 +11,8 @@ public abstract class GtfsColumnDescriptor { public abstract boolean headerRequired(); + public abstract boolean headerRecommended(); + public abstract FieldLevelEnum fieldLevel(); public abstract Optional numberBounds(); @@ -33,6 +35,8 @@ public abstract static class Builder { public abstract Builder setHeaderRequired(boolean value); + public abstract Builder setHeaderRecommended(boolean value); + public abstract Builder setFieldLevel(FieldLevelEnum value); public abstract Builder setNumberBounds(Optional value); diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultTableHeaderValidator.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultTableHeaderValidator.java index 6f9b993270..3ef0f174c6 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultTableHeaderValidator.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/DefaultTableHeaderValidator.java @@ -23,6 +23,7 @@ import java.util.TreeSet; import org.mobilitydata.gtfsvalidator.notice.DuplicatedColumnNotice; import org.mobilitydata.gtfsvalidator.notice.EmptyColumnNameNotice; +import org.mobilitydata.gtfsvalidator.notice.MissingRecommendedColumnNotice; import org.mobilitydata.gtfsvalidator.notice.MissingRequiredColumnNotice; import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; import org.mobilitydata.gtfsvalidator.notice.UnknownColumnNotice; @@ -36,14 +37,20 @@ public void validate( CsvHeader actualHeader, Set supportedColumns, Set requiredColumns, + Set recommendedColumns, NoticeContainer noticeContainer) { if (actualHeader.getColumnCount() == 0) { // This is an empty file. return; } Map columnIndices = new HashMap<>(); - // Sorted tree set for stable order of notices. + // Sorted tree set of all the columns for stable order of notices. + // We remove the columns that are properly present and well formed from that set, and at the + // end only the missing required columns are left in the set. TreeSet missingColumns = new TreeSet<>(requiredColumns); + // We also want to find the recommended columns that are absent. We use the same scheme for + // these. + TreeSet missingRecommendedColumns = new TreeSet<>(recommendedColumns); for (int i = 0; i < actualHeader.getColumnCount(); ++i) { String column = actualHeader.getColumnName(i); // Column indices are zero-based. We add 1 to make them 1-based. @@ -59,12 +66,23 @@ public void validate( if (!supportedColumns.contains(column)) { noticeContainer.addValidationNotice(new UnknownColumnNotice(filename, column, i + 1)); } + + // If the column is present, it should not be in the missing required columns set missingColumns.remove(column); + + // If the column is present, it should not be in the missing recommended columns set + missingRecommendedColumns.remove(column); } if (!missingColumns.isEmpty()) { for (String column : missingColumns) { noticeContainer.addValidationNotice(new MissingRequiredColumnNotice(filename, column)); } } + + if (!missingRecommendedColumns.isEmpty()) { + for (String column : missingRecommendedColumns) { + noticeContainer.addValidationNotice(new MissingRecommendedColumnNotice(filename, column)); + } + } } } diff --git a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/TableHeaderValidator.java b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/TableHeaderValidator.java index ef42d43770..3cc5732d2d 100644 --- a/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/TableHeaderValidator.java +++ b/core/src/main/java/org/mobilitydata/gtfsvalidator/validator/TableHeaderValidator.java @@ -28,5 +28,6 @@ void validate( CsvHeader actualHeader, Set supportedHeaders, Set requiredHeaders, + Set recommendedHeaders, NoticeContainer noticeContainer); } diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/parsing/RowParserTest.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/parsing/RowParserTest.java index c3c0db2cf7..260f3da2e2 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/parsing/RowParserTest.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/parsing/RowParserTest.java @@ -56,6 +56,11 @@ public boolean headerRequired() { return false; } + @Override + public boolean headerRecommended() { + return false; + } + @Override public FieldLevelEnum fieldLevel() { return FieldLevelEnum.REQUIRED; @@ -141,6 +146,7 @@ public void asString_recommended_missing() { GtfsColumnDescriptor.builder() .setColumnName("column name") .setHeaderRequired(false) + .setHeaderRecommended(false) .setFieldLevel(FieldLevelEnum.RECOMMENDED) .setIsMixedCase(false) .setIsCached(false) diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoaderTest.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoaderTest.java index c715a38e2f..29802babef 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoaderTest.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/table/AnyTableLoaderTest.java @@ -88,6 +88,7 @@ public void validate( CsvHeader actualHeader, Set supportedHeaders, Set requiredHeaders, + Set recommendedHeaders, NoticeContainer noticeContainer) { noticeContainer.addValidationNotice(headerValidationNotice); } @@ -144,6 +145,7 @@ public void missingRequiredField() { GtfsColumnDescriptor.builder() .setColumnName(GtfsTestEntity.ID_FIELD_NAME) .setHeaderRequired(true) + .setHeaderRecommended(false) .setFieldLevel(FieldLevelEnum.REQUIRED) .setIsMixedCase(false) .setIsCached(false) @@ -151,6 +153,7 @@ public void missingRequiredField() { GtfsColumnDescriptor.builder() .setColumnName(GtfsTestEntity.CODE_FIELD_NAME) .setHeaderRequired(false) + .setHeaderRecommended(false) .setFieldLevel(FieldLevelEnum.REQUIRED) .setIsMixedCase(false) .setIsCached(false) diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor.java index eb42741abc..4e6d73b0b2 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/testgtfs/GtfsTestTableDescriptor.java @@ -41,6 +41,7 @@ public ImmutableList getColumns() { GtfsColumnDescriptor.builder() .setColumnName(GtfsTestEntity.ID_FIELD_NAME) .setHeaderRequired(true) + .setHeaderRecommended(false) .setFieldLevel(FieldLevelEnum.REQUIRED) .setIsMixedCase(false) .setIsCached(false) @@ -49,6 +50,7 @@ public ImmutableList getColumns() { GtfsColumnDescriptor.builder() .setColumnName(GtfsTestEntity.CODE_FIELD_NAME) .setHeaderRequired(false) + .setHeaderRecommended(false) .setFieldLevel(FieldLevelEnum.OPTIONAL) .setIsMixedCase(false) .setIsCached(false) diff --git a/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/TableHeaderValidatorTest.java b/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/TableHeaderValidatorTest.java index a26323d187..1946ecd024 100644 --- a/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/TableHeaderValidatorTest.java +++ b/core/src/test/java/org/mobilitydata/gtfsvalidator/validator/TableHeaderValidatorTest.java @@ -19,11 +19,13 @@ import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableSet; +import java.util.Set; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mobilitydata.gtfsvalidator.notice.DuplicatedColumnNotice; import org.mobilitydata.gtfsvalidator.notice.EmptyColumnNameNotice; +import org.mobilitydata.gtfsvalidator.notice.MissingRecommendedColumnNotice; import org.mobilitydata.gtfsvalidator.notice.MissingRequiredColumnNotice; import org.mobilitydata.gtfsvalidator.notice.NoticeContainer; import org.mobilitydata.gtfsvalidator.notice.UnknownColumnNotice; @@ -40,6 +42,7 @@ public void expectedColumns() { new CsvHeader(new String[] {"stop_id", "stop_name"}), ImmutableSet.of("stop_id", "stop_name", "stop_lat", "stop_lon"), ImmutableSet.of("stop_id"), + Set.of(), container); assertThat(container.getValidationNotices()).isEmpty(); @@ -55,6 +58,7 @@ public void unknownColumnShouldGenerateNotice() { new CsvHeader(new String[] {"stop_id", "stop_name", "stop_extra"}), ImmutableSet.of("stop_id", "stop_name"), ImmutableSet.of("stop_id"), + Set.of(), container); assertThat(container.getValidationNotices()) @@ -71,11 +75,27 @@ public void missingRequiredColumnShouldGenerateNotice() { new CsvHeader(new String[] {"stop_name"}), ImmutableSet.of("stop_id", "stop_name"), ImmutableSet.of("stop_id"), + Set.of(), container); assertThat(container.getValidationNotices()) .containsExactly(new MissingRequiredColumnNotice("stops.txt", "stop_id")); - assertThat(container.hasValidationErrors()).isTrue(); + } + + @Test + public void missingRecommendedColumnShouldGenerateNotice() { + NoticeContainer container = new NoticeContainer(); + new DefaultTableHeaderValidator() + .validate( + "stops.txt", + new CsvHeader(new String[] {"stop_name"}), + Set.of("stop_id", "stop_name"), + Set.of(), + Set.of("stop_id"), + container); + + assertThat(container.getValidationNotices()) + .containsExactly(new MissingRecommendedColumnNotice("stops.txt", "stop_id")); } @Test @@ -87,6 +107,7 @@ public void duplicatedColumnShouldGenerateNotice() { new CsvHeader(new String[] {"stop_id", "stop_name", "stop_id"}), ImmutableSet.of("stop_id", "stop_name"), ImmutableSet.of("stop_id"), + Set.of(), container); assertThat(container.getValidationNotices()) @@ -103,6 +124,7 @@ public void emptyFileShouldNotGenerateNotice() { CsvHeader.EMPTY, ImmutableSet.of("stop_id", "stop_name"), ImmutableSet.of("stop_id"), + Set.of(), container); assertThat(container.getValidationNotices()).isEmpty(); @@ -118,6 +140,7 @@ public void emptyColumnNameShouldGenerateNotice() { new CsvHeader(new String[] {"stop_id", null, "stop_name", ""}), ImmutableSet.of("stop_id", "stop_name"), ImmutableSet.of("stop_id"), + Set.of(), container); assertThat(container.getValidationNotices()) diff --git a/main/build.gradle b/main/build.gradle index df34c56bb0..b20e9cb31b 100644 --- a/main/build.gradle +++ b/main/build.gradle @@ -52,16 +52,4 @@ dependencies { testImplementation 'com.google.truth:truth:1.0.1' testImplementation 'com.google.truth.extensions:truth-java8-extension:1.0.1' testImplementation 'org.mockito:mockito-core:4.5.1' -} - -// A custom task to copy RULES.md into the test resource directory so that we can reference it in -// unit tests. See NoticeDocumentationTest for more details. -tasks.register('copyRulesMarkdown', Copy) { - from "$rootDir/RULES.md" - into "$projectDir/build/resources/test" -} - -test { - // Make sure `copyRulesMarkdown` runs before we run any tests. - dependsOn 'copyRulesMarkdown' } \ No newline at end of file diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/NoticeView.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/NoticeView.java index 1075a64c62..2bb68c0130 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/NoticeView.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/report/model/NoticeView.java @@ -23,7 +23,7 @@ public NoticeView(ResolvedNotice notice) { this.notice = notice; this.json = notice.getContext().toJsonTree().getAsJsonObject(); this.fields = new ArrayList<>(json.keySet()); - this.comments = NoticeSchemaGenerator.loadComments(notice.getClass()); + this.comments = NoticeSchemaGenerator.loadComments(notice.getContext().getClass()); } /** diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsStopTimeSchema.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsStopTimeSchema.java index 731479a1c2..f35ce24ff9 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsStopTimeSchema.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/table/GtfsStopTimeSchema.java @@ -29,6 +29,7 @@ import org.mobilitydata.gtfsvalidator.annotation.Index; import org.mobilitydata.gtfsvalidator.annotation.NonNegative; import org.mobilitydata.gtfsvalidator.annotation.PrimaryKey; +import org.mobilitydata.gtfsvalidator.annotation.RecommendedColumn; import org.mobilitydata.gtfsvalidator.annotation.Required; import org.mobilitydata.gtfsvalidator.type.GtfsTime; @@ -77,5 +78,6 @@ public interface GtfsStopTimeSchema extends GtfsEntity { double shapeDistTraveled(); @DefaultValue("1") + @RecommendedColumn GtfsStopTimeTimepoint timepoint(); } diff --git a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TimepointTimeValidator.java b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TimepointTimeValidator.java index b8db497c69..972a35d8f0 100644 --- a/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TimepointTimeValidator.java +++ b/main/src/main/java/org/mobilitydata/gtfsvalidator/validator/TimepointTimeValidator.java @@ -38,7 +38,6 @@ *

  • {@link StopTimeTimepointWithoutTimesNotice} - a timepoint does not specifies arrival_time * or departure_time *
  • {@link MissingTimepointValueNotice} - value for {@code stop_times.timepoint} is missing - *
  • {@link MissingTimepointColumnNotice} - field {@code stop_times.timepoint} is missing * */ @GtfsValidator @@ -55,10 +54,9 @@ public class TimepointTimeValidator extends FileValidator { public void validate(NoticeContainer noticeContainer) { if (!stopTimes.hasColumn(GtfsStopTime.TIMEPOINT_FIELD_NAME)) { // legacy datasets do not use timepoint column in stop_times.txt as a result: - // - this should be flagged; + // - this should be flagged in the header tests. // - but also no notice regarding the absence of arrival_time or departure_time should be // generated - noticeContainer.addValidationNotice(new MissingTimepointColumnNotice()); return; } for (GtfsStopTime stopTime : stopTimes.getEntities()) { @@ -139,19 +137,4 @@ static class MissingTimepointValueNotice extends ValidationNotice { this.stopSequence = stopTime.stopSequence(); } } - - /** `timepoint` column is missing for a dataset. */ - @GtfsValidationNotice( - severity = WARNING, - files = @FileRefs(GtfsStopTimeSchema.class), - bestPractices = @FileRefs(GtfsStopTimeSchema.class)) - static class MissingTimepointColumnNotice extends ValidationNotice { - - /** The name of the affected file. */ - private final String filename; - - MissingTimepointColumnNotice() { - this.filename = GtfsStopTime.FILENAME; - } - } } diff --git a/main/src/main/resources/report.html b/main/src/main/resources/report.html index bf5637a664..b1d3d1eb40 100644 --- a/main/src/main/resources/report.html +++ b/main/src/main/resources/report.html @@ -73,6 +73,7 @@ .summary-cell { padding: 5px; box-sizing: border-box; + flex: 1; } .summary h4 { @@ -129,10 +130,19 @@ position: absolute; z-index: 1; bottom: 100%; - left: 50%; - margin-left: -60px; + transform: translateX(-50%); opacity: 0; transition: opacity 0.3s; + max-width: 400px; + min-width: 100px; + width: max-content; + white-space: normal; + } + + .tooltip { + position: relative; + display: inline-block; + cursor: help; } .tooltip:hover .tooltiptext { @@ -262,7 +272,12 @@

    Counts

    -

    GTFS Components included

    +

    + GTFS Components included + (?) + GTFS components provide a standardized vocabulary to define and describe features that are officially adopted in GTFS. + +


    diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NoticeDocumentationTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NoticeDocumentationTest.java index 72fd32375e..37ad6656f4 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NoticeDocumentationTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/NoticeDocumentationTest.java @@ -1,20 +1,11 @@ package org.mobilitydata.gtfsvalidator.validator; -import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import com.google.common.collect.ImmutableList; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import org.junit.Test; @@ -26,37 +17,6 @@ @RunWith(JUnit4.class) public class NoticeDocumentationTest { - private static Pattern MARKDOWN_NOTICE_SIMPLE_CLASS_NAME_ANCHOR_PATTERN = - Pattern.compile("^"); - - private static Pattern MARKDOWN_NOTICE_CODE_HEADER_PATTERN = - Pattern.compile("^### (([a-z0-9]+_)*[a-z0-9]+)"); - - /** - * If this test is failing, it likely means you need to update RULES.md in the project root - * directory to include an entry for a new notice. - */ - @Test - public void testThatRulesMarkdownContainsAnchorsForAllValidationNotices() throws IOException { - Set fromMarkdown = readNoticeSimpleClassNamesFromRulesMarkdown(); - Set fromSource = - discoverValidationNoticeClasses().map(Class::getSimpleName).collect(Collectors.toSet()); - - assertThat(fromMarkdown).isEqualTo(fromSource); - } - - /** - * If this test is failing, it likely means you need to update RULES.md in the project root - * directory to include an entry for a new notice. - */ - @Test - public void testThatRulesMarkdownContainsHeadersForAllValidationNotices() throws IOException { - Set fromMarkdown = readNoticeCodesFromRulesMarkdown(); - Set fromSource = - discoverValidationNoticeClasses().map(Notice::getCode).collect(Collectors.toSet()); - - assertThat(fromMarkdown).isEqualTo(fromSource); - } @Test public void testThatAllValidationNoticesAreDocumented() { @@ -164,35 +124,4 @@ private static Stream> discoverValidationNoticeClasses() { return ClassGraphDiscovery.discoverNoticeSubclasses(ClassGraphDiscovery.DEFAULT_NOTICE_PACKAGES) .stream(); } - - private static Set readNoticeSimpleClassNamesFromRulesMarkdown() throws IOException { - return readValuesFromRulesMarkdown(MARKDOWN_NOTICE_SIMPLE_CLASS_NAME_ANCHOR_PATTERN); - } - - private static Set readNoticeCodesFromRulesMarkdown() throws IOException { - return readValuesFromRulesMarkdown(MARKDOWN_NOTICE_CODE_HEADER_PATTERN); - } - - private static Set readValuesFromRulesMarkdown(Pattern pattern) throws IOException { - // RULES.md is copied into the main/build/resources/test resource directory by a custom copy - // rule in the main/build.gradle file. - try (InputStream in = NoticeDocumentationTest.class.getResourceAsStream("/RULES.md")) { - // Scan lines from the markdown file, find those that match our regex pattern, and pull out - // the matching group. - return new BufferedReader(new InputStreamReader(in)) - .lines() - .map(line -> maybeMatchAndExtract(pattern, line)) - .flatMap(Optional::stream) - .collect(Collectors.toSet()); - } - } - - private static Optional maybeMatchAndExtract(Pattern p, String line) { - Matcher m = p.matcher(line); - if (m.matches()) { - return Optional.of(m.group(1)); - } else { - return Optional.empty(); - } - } } diff --git a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/TimepointTimeValidatorTest.java b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/TimepointTimeValidatorTest.java index 9e3a9aed2c..159105a2af 100644 --- a/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/TimepointTimeValidatorTest.java +++ b/main/src/test/java/org/mobilitydata/gtfsvalidator/validator/TimepointTimeValidatorTest.java @@ -39,7 +39,6 @@ import org.mobilitydata.gtfsvalidator.table.GtfsStopTime; import org.mobilitydata.gtfsvalidator.table.GtfsStopTimeTableContainer; import org.mobilitydata.gtfsvalidator.type.GtfsTime; -import org.mobilitydata.gtfsvalidator.validator.TimepointTimeValidator.MissingTimepointColumnNotice; import org.mobilitydata.gtfsvalidator.validator.TimepointTimeValidator.MissingTimepointValueNotice; import org.mobilitydata.gtfsvalidator.validator.TimepointTimeValidator.StopTimeTimepointWithoutTimesNotice; @@ -59,7 +58,8 @@ private static List generateNotices( return noticeContainer.getValidationNotices(); } - // using this header will trigger a MissingTimepointColumnNotice + // Using this header will trigger a MissingRecommendedColumnNotice since the timepoint column is + // missing. private static CsvHeader createLegacyHeader() { return new CsvHeader( new String[] { @@ -95,58 +95,6 @@ private static CsvHeader createHeaderWithTimepointColumn() { }); } - @Test - public void noTimepointColumn_noTimeProvided_shouldGenerateNotice() { - // Using createLegacyHeader() that omits the timestamp column will trigger the - // MissingTimepointColumnNotice. - // .setTimepoint(null) is used to indicate that no value is provided, although it has no effect - // in this test. - List stopTimes = new ArrayList<>(); - stopTimes.add( - new GtfsStopTime.Builder() - .setCsvRowNumber(1) - .setTripId("first trip id") - .setArrivalTime(null) - .setDepartureTime(null) - .setStopId("stop id 0") - .setStopSequence(2) - .setTimepoint((Integer) null) - .build()); - stopTimes.add( - new GtfsStopTime.Builder() - .setCsvRowNumber(4) - .setTripId("second trip id") - .setArrivalTime(null) - .setDepartureTime(null) - .setStopId("stop id 1") - .setStopSequence(2) - .setTimepoint((Integer) null) - .build()); - assertThat(generateNotices(createLegacyHeader(), stopTimes)) - .containsExactly(new MissingTimepointColumnNotice()); - } - - @Test - public void noTimepointColumn_timesProvided_shouldGenerateNotice() { - // Using createLegacyHeader() that omits the timestamp column will trigger the - // MissingTimepointColumnNotice. - // .setTimepoint(null) is used to indicate that no value is provided, although it has no effect - // in this test. - List stopTimes = new ArrayList<>(); - stopTimes.add( - new GtfsStopTime.Builder() - .setCsvRowNumber(1) - .setTripId("first trip id") - .setArrivalTime(GtfsTime.fromSecondsSinceMidnight(450)) - .setDepartureTime(GtfsTime.fromSecondsSinceMidnight(580)) - .setStopId("stop id") - .setStopSequence(2) - .setTimepoint((Integer) null) - .build()); - assertThat(generateNotices(createLegacyHeader(), stopTimes)) - .containsExactly(new MissingTimepointColumnNotice()); - } - @Test public void timepointWithNoTimeShouldGenerateNotices() { List stopTimes = new ArrayList<>(); diff --git a/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/RecommendedColumn.java b/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/RecommendedColumn.java new file mode 100644 index 0000000000..f292ebd22d --- /dev/null +++ b/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/RecommendedColumn.java @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.mobilitydata.gtfsvalidator.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Adds a validation that it's recommended that a column be present. A value for the field may be + * optional. + * + *

    Example. + * + *

    + *   {@literal @}GtfsTable("stop_times.txt")
    + *    public interface GtfsStopTimeSchema extends GtfsEntity {
    + *
    + *     {@literal @}DefaultValue("1")
    + *     {@literal @}RecommendedColumn
    + *      GtfsStopTimeTimepoint timepoint();
    + *   }
    + * 
    + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.SOURCE) +public @interface RecommendedColumn {} diff --git a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/Analyser.java b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/Analyser.java index f2d52b825c..0a15d5577b 100644 --- a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/Analyser.java +++ b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/Analyser.java @@ -71,6 +71,7 @@ public GtfsFileDescriptor analyzeGtfsFileType(TypeElement type) { fieldBuilder.setRecommended(method.getAnnotation(Recommended.class) != null); fieldBuilder.setColumnRequired(method.getAnnotation(RequiredColumn.class) != null); fieldBuilder.setValueRequired(method.getAnnotation(Required.class) != null); + fieldBuilder.setColumnRecommended(method.getAnnotation(RecommendedColumn.class) != null); fieldBuilder.setMixedCase(method.getAnnotation(MixedCase.class) != null); PrimaryKey primaryKey = method.getAnnotation(PrimaryKey.class); if (primaryKey != null) { diff --git a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/GtfsFieldDescriptor.java b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/GtfsFieldDescriptor.java index d94b62c8ef..c5c6374fac 100644 --- a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/GtfsFieldDescriptor.java +++ b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/GtfsFieldDescriptor.java @@ -59,6 +59,8 @@ public static GtfsFieldDescriptor.Builder builder() { public abstract boolean columnRequired(); + public abstract boolean columnRecommended(); + public boolean isHeaderRequired() { return columnRequired() || valueRequired(); } @@ -81,6 +83,8 @@ public abstract static class Builder { public abstract Builder setColumnRequired(boolean value); + public abstract Builder setColumnRecommended(boolean value); + public abstract Builder setMixedCase(boolean value); public abstract Builder setPrimaryKey(PrimaryKey annotation); diff --git a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableDescriptorGenerator.java b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableDescriptorGenerator.java index 825ff03e2c..304bc93c83 100644 --- a/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableDescriptorGenerator.java +++ b/processor/src/main/java/org/mobilitydata/gtfsvalidator/processor/TableDescriptorGenerator.java @@ -186,12 +186,14 @@ private MethodSpec generateGetColumnsMethod() { "GtfsColumnDescriptor.builder()\n" + ".setColumnName($T.$L)\n" + ".setHeaderRequired($L)\n" + + ".setHeaderRecommended($L)\n" + ".setFieldLevel($T.$L)\n" + ".setIsMixedCase($L)\n" + ".setIsCached($L)\n", gtfsEntityType, fieldNameField(field.name()), field.isHeaderRequired(), + field.columnRecommended(), FieldLevelEnum.class, getFieldLevel(field), field.mixedCase(), diff --git a/processor/tests/src/main/java/org/mobilitydata/gtfsvalidator/processor/tests/RecommendedColumnAnnotationSchema.java b/processor/tests/src/main/java/org/mobilitydata/gtfsvalidator/processor/tests/RecommendedColumnAnnotationSchema.java new file mode 100644 index 0000000000..dff043ff42 --- /dev/null +++ b/processor/tests/src/main/java/org/mobilitydata/gtfsvalidator/processor/tests/RecommendedColumnAnnotationSchema.java @@ -0,0 +1,11 @@ +package org.mobilitydata.gtfsvalidator.processor.tests; + +import org.mobilitydata.gtfsvalidator.annotation.GtfsTable; +import org.mobilitydata.gtfsvalidator.annotation.RecommendedColumn; + +@GtfsTable("recommended_column.txt") +public interface RecommendedColumnAnnotationSchema { + + @RecommendedColumn + String columnRecommended(); +} diff --git a/processor/tests/src/test/java/org/mobilitydata/gtfsvalidator/processor/tests/RecommendedColumnAnnotationSchemaTest.java b/processor/tests/src/test/java/org/mobilitydata/gtfsvalidator/processor/tests/RecommendedColumnAnnotationSchemaTest.java new file mode 100644 index 0000000000..fd600103e2 --- /dev/null +++ b/processor/tests/src/test/java/org/mobilitydata/gtfsvalidator/processor/tests/RecommendedColumnAnnotationSchemaTest.java @@ -0,0 +1,51 @@ +package org.mobilitydata.gtfsvalidator.processor.tests; + +import static com.google.common.truth.Truth.assertThat; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mobilitydata.gtfsvalidator.notice.MissingRequiredColumnNotice; +import org.mobilitydata.gtfsvalidator.table.RecommendedColumnAnnotationTableDescriptor; +import org.mobilitydata.gtfsvalidator.testing.LoadingHelper; +import org.mobilitydata.gtfsvalidator.validator.ValidatorLoaderException; + +@RunWith(JUnit4.class) +public class RecommendedColumnAnnotationSchemaTest { + private RecommendedColumnAnnotationTableDescriptor tableDescriptor; + private LoadingHelper helper; + + @Before + public void setup() throws ValidatorLoaderException { + tableDescriptor = new RecommendedColumnAnnotationTableDescriptor(); + helper = new LoadingHelper(); + } + + @Test + public void includingRecommendedColumnHeaderWithoutValueShouldNotGenerateNotice() + throws ValidatorLoaderException { + + helper.load(tableDescriptor, "some_column,column_recommended", "value,"); + + assertThat( + !helper + .getValidationNotices() + .contains( + new MissingRequiredColumnNotice("recommended_column.txt", "column_recommended"))); + } + + @Test + public void missingRecommendedColumnHeaderShouldGenerateNotice() throws ValidatorLoaderException { + + helper.load(tableDescriptor, "column", "value"); + // Since we use an unknown column ("column") we have to expect at least one unknown_column + // notice along with the + // missing_recommended_column notice. + assertThat( + helper + .getValidationNotices() + .contains( + new MissingRequiredColumnNotice("recommended_column.txt", "column_recommended"))); + } +} diff --git a/web/client/.gitignore b/web/client/.gitignore index 8d3365a39b..3d4439f65b 100644 --- a/web/client/.gitignore +++ b/web/client/.gitignore @@ -8,4 +8,4 @@ node_modules !.env.example vite.config.js.timestamp-* vite.config.ts.timestamp-* -/static/RULES.md +/static/rules.json diff --git a/web/client/build.gradle b/web/client/build.gradle index e986d4dd20..a7bca3ce82 100644 --- a/web/client/build.gradle +++ b/web/client/build.gradle @@ -9,9 +9,29 @@ task webClientNodeModules (type: Exec) { commandLine 'npm', 'install' } +task webClientNoticeSchema (type: Exec){ + dependsOn ':cli:build' + outputs.file(file('./notice_schema.json')) + def javaCmd = System.getenv('JAVA_HOME') == null ? 'java' : "${System.getenv('JAVA_HOME')}/bin/java" + commandLine ( + javaCmd, + '-jar', + "../../cli/build/libs/gtfs-validator-${project.version}-cli.jar", + '--export_notices_schema', + '-o', + '.' + ) +} + +task webClientRulesJSON (type: Exec) { + dependsOn webClientNoticeSchema + outputs.file(file('./static/rules.json')) + commandLine 'mv', webClientNoticeSchema.outputs.files[0], outputs.files[0] +} + task webClientSetup { dependsOn webClientNodeModules - dependsOn ':copyRulesToWebClient' + dependsOn webClientRulesJSON } task webTest (type: Exec) { diff --git a/web/client/package-lock.json b/web/client/package-lock.json index 8103e03156..18a9571554 100644 --- a/web/client/package-lock.json +++ b/web/client/package-lock.json @@ -10,12 +10,14 @@ "devDependencies": { "@sveltejs/adapter-static": "^1.0.0", "@sveltejs/kit": "^1.15.1", + "@types/lodash": "^4.14.194", "@types/marked": "^4.0.8", "autoprefixer": "^10.4.13", "cypress": "^12.10.0", "eslint": "^8.28.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-svelte3": "^4.0.0", + "lodash": "^4.17.21", "marked": "^4.2.12", "postcss": "^8.4.20", "postcss-import": "^15.1.0", @@ -87,9 +89,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.10.tgz", - "integrity": "sha512-RmJjQTRrO6VwUWDrzTBLmV4OJZTarYsiepLGlF2rYTVB701hSorPywPGvP6d8HCuuRibyXa5JX4s3jN2kHEtjQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", "cpu": [ "arm" ], @@ -103,9 +105,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.10.tgz", - "integrity": "sha512-47Y+NwVKTldTlDhSgJHZ/RpvBQMUDG7eKihqaF/u6g7s0ZPz4J1vy8A3rwnnUOF2CuDn7w7Gj/QcMoWz3U3SJw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", "cpu": [ "arm64" ], @@ -119,9 +121,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.10.tgz", - "integrity": "sha512-C4PfnrBMcuAcOurQzpF1tTtZz94IXO5JmICJJ3NFJRHbXXsQUg9RFG45KvydKqtFfBaFLCHpduUkUfXwIvGnRg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", "cpu": [ "x64" ], @@ -135,9 +137,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.10.tgz", - "integrity": "sha512-bH/bpFwldyOKdi9HSLCLhhKeVgRYr9KblchwXgY2NeUHBB/BzTUHtUSBgGBmpydB1/4E37m+ggXXfSrnD7/E7g==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", "cpu": [ "arm64" ], @@ -151,9 +153,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.10.tgz", - "integrity": "sha512-OXt7ijoLuy+AjDSKQWu+KdDFMBbdeaL6wtgMKtDUXKWHiAMKHan5+R1QAG6HD4+K0nnOvEJXKHeA9QhXNAjOTQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", "cpu": [ "x64" ], @@ -167,9 +169,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.10.tgz", - "integrity": "sha512-shSQX/3GHuspE3Uxtq5kcFG/zqC+VuMnJkqV7LczO41cIe6CQaXHD3QdMLA4ziRq/m0vZo7JdterlgbmgNIAlQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", "cpu": [ "arm64" ], @@ -183,9 +185,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.10.tgz", - "integrity": "sha512-5YVc1zdeaJGASijZmTzSO4h6uKzsQGG3pkjI6fuXvolhm3hVRhZwnHJkforaZLmzvNv5Tb7a3QL2FAVmrgySIA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", "cpu": [ "x64" ], @@ -199,9 +201,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.10.tgz", - "integrity": "sha512-c360287ZWI2miBnvIj23bPyVctgzeMT2kQKR+x94pVqIN44h3GF8VMEs1SFPH1UgyDr3yBbx3vowDS1SVhyVhA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", "cpu": [ "arm" ], @@ -215,9 +217,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.10.tgz", - "integrity": "sha512-2aqeNVxIaRfPcIaMZIFoblLh588sWyCbmj1HHCCs9WmeNWm+EIN0SmvsmPvTa/TsNZFKnxTcvkX2eszTcCqIrA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", "cpu": [ "arm64" ], @@ -231,9 +233,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.10.tgz", - "integrity": "sha512-sqMIEWeyrLGU7J5RB5fTkLRIFwsgsQ7ieWXlDLEmC2HblPYGb3AucD7inw2OrKFpRPKsec1l+lssiM3+NV5aOw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", "cpu": [ "ia32" ], @@ -247,9 +249,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.10.tgz", - "integrity": "sha512-O7Pd5hLEtTg37NC73pfhUOGTjx/+aXu5YoSq3ahCxcN7Bcr2F47mv+kG5t840thnsEzrv0oB70+LJu3gUgchvg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", "cpu": [ "loong64" ], @@ -263,9 +265,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.10.tgz", - "integrity": "sha512-FN8mZOH7531iPHM0kaFhAOqqNHoAb6r/YHW2ZIxNi0a85UBi2DO4Vuyn7t1p4UN8a4LoAnLOT1PqNgHkgBJgbA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", "cpu": [ "mips64el" ], @@ -279,9 +281,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.10.tgz", - "integrity": "sha512-Dg9RiqdvHOAWnOKIOTsIx8dFX9EDlY2IbPEY7YFzchrCiTZmMkD7jWA9UdZbNUygPjdmQBVPRCrLydReFlX9yg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", "cpu": [ "ppc64" ], @@ -295,9 +297,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.10.tgz", - "integrity": "sha512-XMqtpjwzbmlar0BJIxmzu/RZ7EWlfVfH68Vadrva0Wj5UKOdKvqskuev2jY2oPV3aoQUyXwnMbMrFmloO2GfAw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", "cpu": [ "riscv64" ], @@ -311,9 +313,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.10.tgz", - "integrity": "sha512-fu7XtnoeRNFMx8DjK3gPWpFBDM2u5ba+FYwg27SjMJwKvJr4bDyKz5c+FLXLUSSAkMAt/UL+cUbEbra+rYtUgw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", "cpu": [ "s390x" ], @@ -327,9 +329,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.10.tgz", - "integrity": "sha512-61lcjVC/RldNNMUzQQdyCWjCxp9YLEQgIxErxU9XluX7juBdGKb0pvddS0vPNuCvotRbzijZ1pzII+26haWzbA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", "cpu": [ "x64" ], @@ -343,9 +345,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.10.tgz", - "integrity": "sha512-JeZXCX3viSA9j4HqSoygjssdqYdfHd6yCFWyfSekLbz4Ef+D2EjvsN02ZQPwYl5a5gg/ehdHgegHhlfOFP0HCA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", "cpu": [ "x64" ], @@ -359,9 +361,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.10.tgz", - "integrity": "sha512-3qpxQKuEVIIg8SebpXsp82OBrqjPV/OwNWmG+TnZDr3VGyChNnGMHccC1xkbxCHDQNnnXjxhMQNyHmdFJbmbRA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", "cpu": [ "x64" ], @@ -375,9 +377,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.10.tgz", - "integrity": "sha512-z+q0xZ+et/7etz7WoMyXTHZ1rB8PMSNp/FOqURLJLOPb3GWJ2aj4oCqFCjPwEbW1rsT7JPpxeH/DwGAWk/I1Bg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", "cpu": [ "x64" ], @@ -391,9 +393,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.10.tgz", - "integrity": "sha512-+YYu5sbQ9npkNT9Dec+tn1F/kjg6SMgr6bfi/6FpXYZvCRfu2YFPZGb+3x8K30s8eRxFpoG4sGhiSUkr1xbHEw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", "cpu": [ "arm64" ], @@ -407,9 +409,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.10.tgz", - "integrity": "sha512-Aw7Fupk7XNehR1ftHGYwUteyJ2q+em/aE+fVU3YMTBN2V5A7Z4aVCSV+SvCp9HIIHZavPFBpbdP3VfjQpdf6Xg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", "cpu": [ "ia32" ], @@ -423,9 +425,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.10.tgz", - "integrity": "sha512-qddWullt3sC1EIpfHvCRBq3H4g3L86DZpD6n8k2XFjFVyp01D++uNbN1hT/JRsHxTbyyemZcpwL5aRlJwc/zFw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", "cpu": [ "x64" ], @@ -570,13 +572,13 @@ } }, "node_modules/@sveltejs/kit": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.15.7.tgz", - "integrity": "sha512-dgdKExsMJ16X3q8tEcuDlv+QIWAlJcf7IqCU2HWV13nmtTzwSA2n4VtEx9Gy5OGhH0SUAGNIupmlf0TdFSMXbw==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.15.9.tgz", + "integrity": "sha512-Og+4WlguPVPS0PmAHefp4KxvTVZfyDN09aORVXIdKSzqzodSJiLs7Fhi/Q0z0YjmcoNLWF24tI0a6mTusL6Yfg==", "dev": true, "hasInstallScript": true, "dependencies": { - "@sveltejs/vite-plugin-svelte": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^2.1.1", "@types/cookie": "^0.5.1", "cookie": "^0.5.0", "devalue": "^4.3.0", @@ -588,7 +590,7 @@ "set-cookie-parser": "^2.5.1", "sirv": "^2.0.2", "tiny-glob": "^0.2.9", - "undici": "5.20.0" + "undici": "~5.22.0" }, "bin": { "svelte-kit": "svelte-kit.js" @@ -601,30 +603,18 @@ "vite": "^4.0.0" } }, - "node_modules/@sveltejs/kit/node_modules/magic-string": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", - "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.2.tgz", - "integrity": "sha512-xCEan0/NNpQuL0l5aS42FjwQ6wwskdxC3pW1OeFtEKNZwRg7Evro9lac9HesGP6TdFsTv2xMes5ASQVKbCacxg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.1.1.tgz", + "integrity": "sha512-7YeBDt4us0FiIMNsVXxyaP4Hwyn2/v9x3oqStkHU3ZdIc5O22pGwUwH33wUqYo+7Itdmo8zxJ45Qvfm3H7UUjQ==", "dev": true, "dependencies": { "debug": "^4.3.4", - "deepmerge": "^4.2.2", + "deepmerge": "^4.3.1", "kleur": "^4.1.5", - "magic-string": "^0.27.0", + "magic-string": "^0.30.0", "svelte-hmr": "^0.15.1", - "vitefu": "^0.2.3" + "vitefu": "^0.2.4" }, "engines": { "node": "^14.18.0 || >= 16" @@ -640,6 +630,12 @@ "integrity": "sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.194", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", + "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==", + "dev": true + }, "node_modules/@types/marked": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.8.tgz", @@ -1537,9 +1533,9 @@ "dev": true }, "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "engines": { "node": ">=0.10.0" @@ -1669,9 +1665,9 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.10.tgz", - "integrity": "sha512-z5dIViHoVnw2l+NCJ3zj5behdXjYvXne9gL18OOivCadXDUhyDkeSvEtLcGVAJW2fNmh33TDUpsi704XYlDodw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", "dev": true, "hasInstallScript": true, "bin": { @@ -1681,28 +1677,28 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.16.10", - "@esbuild/android-arm64": "0.16.10", - "@esbuild/android-x64": "0.16.10", - "@esbuild/darwin-arm64": "0.16.10", - "@esbuild/darwin-x64": "0.16.10", - "@esbuild/freebsd-arm64": "0.16.10", - "@esbuild/freebsd-x64": "0.16.10", - "@esbuild/linux-arm": "0.16.10", - "@esbuild/linux-arm64": "0.16.10", - "@esbuild/linux-ia32": "0.16.10", - "@esbuild/linux-loong64": "0.16.10", - "@esbuild/linux-mips64el": "0.16.10", - "@esbuild/linux-ppc64": "0.16.10", - "@esbuild/linux-riscv64": "0.16.10", - "@esbuild/linux-s390x": "0.16.10", - "@esbuild/linux-x64": "0.16.10", - "@esbuild/netbsd-x64": "0.16.10", - "@esbuild/openbsd-x64": "0.16.10", - "@esbuild/sunos-x64": "0.16.10", - "@esbuild/win32-arm64": "0.16.10", - "@esbuild/win32-ia32": "0.16.10", - "@esbuild/win32-x64": "0.16.10" + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" } }, "node_modules/escalade": { @@ -2894,9 +2890,9 @@ } }, "node_modules/magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.13" @@ -3054,10 +3050,16 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3302,9 +3304,9 @@ } }, "node_modules/postcss": { - "version": "8.4.20", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz", - "integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==", + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", "dev": true, "funding": [ { @@ -3314,10 +3316,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -3665,9 +3671,9 @@ } }, "node_modules/rollup": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.7.5.tgz", - "integrity": "sha512-z0ZbqHBtS/et2EEUKMrAl2CoSdwN7ZPzL17UMiKN9RjjqHShTlv7F9J6ZJZJNREYjBh3TvBrdfjkFDIXFNeuiQ==", + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.24.0.tgz", + "integrity": "sha512-OgraHOIg2YpHQTjl0/ymWfFNBEyPucB7lmhXrQUh38qNOegxLapSPFs9sNr0qKR75awW41D93XafoR2QfhBdUQ==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -4324,15 +4330,15 @@ } }, "node_modules/undici": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.20.0.tgz", - "integrity": "sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz", + "integrity": "sha512-fR9RXCc+6Dxav4P9VV/sp5w3eFiSdOjJYsbtWfd4s5L5C4ogyuVpdKIVHeW0vV1MloM65/f7W45nR9ZxwVdyiA==", "dev": true, "dependencies": { "busboy": "^1.6.0" }, "engines": { - "node": ">=12.18" + "node": ">=14.0" } }, "node_modules/universalify": { @@ -4418,15 +4424,14 @@ } }, "node_modules/vite": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.0.2.tgz", - "integrity": "sha512-QJaY3R+tFlTagH0exVqbgkkw45B+/bXVBzF2ZD1KB5Z8RiAoiKo60vSUf6/r4c2Vh9jfGBKM4oBI9b4/1ZJYng==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", + "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", "dev": true, "dependencies": { - "esbuild": "^0.16.3", - "postcss": "^8.4.20", - "resolve": "^1.22.1", - "rollup": "^3.7.0" + "esbuild": "^0.17.5", + "postcss": "^8.4.23", + "rollup": "^3.21.0" }, "bin": { "vite": "bin/vite.js" @@ -4630,156 +4635,156 @@ } }, "@esbuild/android-arm": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.10.tgz", - "integrity": "sha512-RmJjQTRrO6VwUWDrzTBLmV4OJZTarYsiepLGlF2rYTVB701hSorPywPGvP6d8HCuuRibyXa5JX4s3jN2kHEtjQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", "dev": true, "optional": true }, "@esbuild/android-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.10.tgz", - "integrity": "sha512-47Y+NwVKTldTlDhSgJHZ/RpvBQMUDG7eKihqaF/u6g7s0ZPz4J1vy8A3rwnnUOF2CuDn7w7Gj/QcMoWz3U3SJw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", "dev": true, "optional": true }, "@esbuild/android-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.10.tgz", - "integrity": "sha512-C4PfnrBMcuAcOurQzpF1tTtZz94IXO5JmICJJ3NFJRHbXXsQUg9RFG45KvydKqtFfBaFLCHpduUkUfXwIvGnRg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", "dev": true, "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.10.tgz", - "integrity": "sha512-bH/bpFwldyOKdi9HSLCLhhKeVgRYr9KblchwXgY2NeUHBB/BzTUHtUSBgGBmpydB1/4E37m+ggXXfSrnD7/E7g==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", "dev": true, "optional": true }, "@esbuild/darwin-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.10.tgz", - "integrity": "sha512-OXt7ijoLuy+AjDSKQWu+KdDFMBbdeaL6wtgMKtDUXKWHiAMKHan5+R1QAG6HD4+K0nnOvEJXKHeA9QhXNAjOTQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.10.tgz", - "integrity": "sha512-shSQX/3GHuspE3Uxtq5kcFG/zqC+VuMnJkqV7LczO41cIe6CQaXHD3QdMLA4ziRq/m0vZo7JdterlgbmgNIAlQ==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", "dev": true, "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.10.tgz", - "integrity": "sha512-5YVc1zdeaJGASijZmTzSO4h6uKzsQGG3pkjI6fuXvolhm3hVRhZwnHJkforaZLmzvNv5Tb7a3QL2FAVmrgySIA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", "dev": true, "optional": true }, "@esbuild/linux-arm": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.10.tgz", - "integrity": "sha512-c360287ZWI2miBnvIj23bPyVctgzeMT2kQKR+x94pVqIN44h3GF8VMEs1SFPH1UgyDr3yBbx3vowDS1SVhyVhA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", "dev": true, "optional": true }, "@esbuild/linux-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.10.tgz", - "integrity": "sha512-2aqeNVxIaRfPcIaMZIFoblLh588sWyCbmj1HHCCs9WmeNWm+EIN0SmvsmPvTa/TsNZFKnxTcvkX2eszTcCqIrA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", "dev": true, "optional": true }, "@esbuild/linux-ia32": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.10.tgz", - "integrity": "sha512-sqMIEWeyrLGU7J5RB5fTkLRIFwsgsQ7ieWXlDLEmC2HblPYGb3AucD7inw2OrKFpRPKsec1l+lssiM3+NV5aOw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", "dev": true, "optional": true }, "@esbuild/linux-loong64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.10.tgz", - "integrity": "sha512-O7Pd5hLEtTg37NC73pfhUOGTjx/+aXu5YoSq3ahCxcN7Bcr2F47mv+kG5t840thnsEzrv0oB70+LJu3gUgchvg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", "dev": true, "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.10.tgz", - "integrity": "sha512-FN8mZOH7531iPHM0kaFhAOqqNHoAb6r/YHW2ZIxNi0a85UBi2DO4Vuyn7t1p4UN8a4LoAnLOT1PqNgHkgBJgbA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", "dev": true, "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.10.tgz", - "integrity": "sha512-Dg9RiqdvHOAWnOKIOTsIx8dFX9EDlY2IbPEY7YFzchrCiTZmMkD7jWA9UdZbNUygPjdmQBVPRCrLydReFlX9yg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", "dev": true, "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.10.tgz", - "integrity": "sha512-XMqtpjwzbmlar0BJIxmzu/RZ7EWlfVfH68Vadrva0Wj5UKOdKvqskuev2jY2oPV3aoQUyXwnMbMrFmloO2GfAw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", "dev": true, "optional": true }, "@esbuild/linux-s390x": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.10.tgz", - "integrity": "sha512-fu7XtnoeRNFMx8DjK3gPWpFBDM2u5ba+FYwg27SjMJwKvJr4bDyKz5c+FLXLUSSAkMAt/UL+cUbEbra+rYtUgw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", "dev": true, "optional": true }, "@esbuild/linux-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.10.tgz", - "integrity": "sha512-61lcjVC/RldNNMUzQQdyCWjCxp9YLEQgIxErxU9XluX7juBdGKb0pvddS0vPNuCvotRbzijZ1pzII+26haWzbA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", "dev": true, "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.10.tgz", - "integrity": "sha512-JeZXCX3viSA9j4HqSoygjssdqYdfHd6yCFWyfSekLbz4Ef+D2EjvsN02ZQPwYl5a5gg/ehdHgegHhlfOFP0HCA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", "dev": true, "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.10.tgz", - "integrity": "sha512-3qpxQKuEVIIg8SebpXsp82OBrqjPV/OwNWmG+TnZDr3VGyChNnGMHccC1xkbxCHDQNnnXjxhMQNyHmdFJbmbRA==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", "dev": true, "optional": true }, "@esbuild/sunos-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.10.tgz", - "integrity": "sha512-z+q0xZ+et/7etz7WoMyXTHZ1rB8PMSNp/FOqURLJLOPb3GWJ2aj4oCqFCjPwEbW1rsT7JPpxeH/DwGAWk/I1Bg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", "dev": true, "optional": true }, "@esbuild/win32-arm64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.10.tgz", - "integrity": "sha512-+YYu5sbQ9npkNT9Dec+tn1F/kjg6SMgr6bfi/6FpXYZvCRfu2YFPZGb+3x8K30s8eRxFpoG4sGhiSUkr1xbHEw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", "dev": true, "optional": true }, "@esbuild/win32-ia32": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.10.tgz", - "integrity": "sha512-Aw7Fupk7XNehR1ftHGYwUteyJ2q+em/aE+fVU3YMTBN2V5A7Z4aVCSV+SvCp9HIIHZavPFBpbdP3VfjQpdf6Xg==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", "dev": true, "optional": true }, "@esbuild/win32-x64": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.10.tgz", - "integrity": "sha512-qddWullt3sC1EIpfHvCRBq3H4g3L86DZpD6n8k2XFjFVyp01D++uNbN1hT/JRsHxTbyyemZcpwL5aRlJwc/zFw==", + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", "dev": true, "optional": true }, @@ -4885,12 +4890,12 @@ "requires": {} }, "@sveltejs/kit": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.15.7.tgz", - "integrity": "sha512-dgdKExsMJ16X3q8tEcuDlv+QIWAlJcf7IqCU2HWV13nmtTzwSA2n4VtEx9Gy5OGhH0SUAGNIupmlf0TdFSMXbw==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-1.15.9.tgz", + "integrity": "sha512-Og+4WlguPVPS0PmAHefp4KxvTVZfyDN09aORVXIdKSzqzodSJiLs7Fhi/Q0z0YjmcoNLWF24tI0a6mTusL6Yfg==", "dev": true, "requires": { - "@sveltejs/vite-plugin-svelte": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^2.1.1", "@types/cookie": "^0.5.1", "cookie": "^0.5.0", "devalue": "^4.3.0", @@ -4902,32 +4907,21 @@ "set-cookie-parser": "^2.5.1", "sirv": "^2.0.2", "tiny-glob": "^0.2.9", - "undici": "5.20.0" - }, - "dependencies": { - "magic-string": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", - "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", - "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.13" - } - } + "undici": "~5.22.0" } }, "@sveltejs/vite-plugin-svelte": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.2.tgz", - "integrity": "sha512-xCEan0/NNpQuL0l5aS42FjwQ6wwskdxC3pW1OeFtEKNZwRg7Evro9lac9HesGP6TdFsTv2xMes5ASQVKbCacxg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.1.1.tgz", + "integrity": "sha512-7YeBDt4us0FiIMNsVXxyaP4Hwyn2/v9x3oqStkHU3ZdIc5O22pGwUwH33wUqYo+7Itdmo8zxJ45Qvfm3H7UUjQ==", "dev": true, "requires": { "debug": "^4.3.4", - "deepmerge": "^4.2.2", + "deepmerge": "^4.3.1", "kleur": "^4.1.5", - "magic-string": "^0.27.0", + "magic-string": "^0.30.0", "svelte-hmr": "^0.15.1", - "vitefu": "^0.2.3" + "vitefu": "^0.2.4" } }, "@types/cookie": { @@ -4936,6 +4930,12 @@ "integrity": "sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==", "dev": true }, + "@types/lodash": { + "version": "4.14.194", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", + "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==", + "dev": true + }, "@types/marked": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.8.tgz", @@ -5570,9 +5570,9 @@ "dev": true }, "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true }, "defined": { @@ -5678,33 +5678,33 @@ "dev": true }, "esbuild": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.10.tgz", - "integrity": "sha512-z5dIViHoVnw2l+NCJ3zj5behdXjYvXne9gL18OOivCadXDUhyDkeSvEtLcGVAJW2fNmh33TDUpsi704XYlDodw==", - "dev": true, - "requires": { - "@esbuild/android-arm": "0.16.10", - "@esbuild/android-arm64": "0.16.10", - "@esbuild/android-x64": "0.16.10", - "@esbuild/darwin-arm64": "0.16.10", - "@esbuild/darwin-x64": "0.16.10", - "@esbuild/freebsd-arm64": "0.16.10", - "@esbuild/freebsd-x64": "0.16.10", - "@esbuild/linux-arm": "0.16.10", - "@esbuild/linux-arm64": "0.16.10", - "@esbuild/linux-ia32": "0.16.10", - "@esbuild/linux-loong64": "0.16.10", - "@esbuild/linux-mips64el": "0.16.10", - "@esbuild/linux-ppc64": "0.16.10", - "@esbuild/linux-riscv64": "0.16.10", - "@esbuild/linux-s390x": "0.16.10", - "@esbuild/linux-x64": "0.16.10", - "@esbuild/netbsd-x64": "0.16.10", - "@esbuild/openbsd-x64": "0.16.10", - "@esbuild/sunos-x64": "0.16.10", - "@esbuild/win32-arm64": "0.16.10", - "@esbuild/win32-ia32": "0.16.10", - "@esbuild/win32-x64": "0.16.10" + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" } }, "escalade": { @@ -6582,9 +6582,9 @@ } }, "magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", "dev": true, "requires": { "@jridgewell/sourcemap-codec": "^1.4.13" @@ -6694,9 +6694,9 @@ "dev": true }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true }, "natural-compare": { @@ -6873,12 +6873,12 @@ "dev": true }, "postcss": { - "version": "8.4.20", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.20.tgz", - "integrity": "sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g==", + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", "dev": true, "requires": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -7094,9 +7094,9 @@ } }, "rollup": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.7.5.tgz", - "integrity": "sha512-z0ZbqHBtS/et2EEUKMrAl2CoSdwN7ZPzL17UMiKN9RjjqHShTlv7F9J6ZJZJNREYjBh3TvBrdfjkFDIXFNeuiQ==", + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.24.0.tgz", + "integrity": "sha512-OgraHOIg2YpHQTjl0/ymWfFNBEyPucB7lmhXrQUh38qNOegxLapSPFs9sNr0qKR75awW41D93XafoR2QfhBdUQ==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -7539,9 +7539,9 @@ "dev": true }, "undici": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.20.0.tgz", - "integrity": "sha512-J3j60dYzuo6Eevbawwp1sdg16k5Tf768bxYK4TUJRH7cBM4kFCbf3mOnM/0E3vQYXvpxITbbWmBafaDbxLDz3g==", + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz", + "integrity": "sha512-fR9RXCc+6Dxav4P9VV/sp5w3eFiSdOjJYsbtWfd4s5L5C4ogyuVpdKIVHeW0vV1MloM65/f7W45nR9ZxwVdyiA==", "dev": true, "requires": { "busboy": "^1.6.0" @@ -7602,16 +7602,15 @@ } }, "vite": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.0.2.tgz", - "integrity": "sha512-QJaY3R+tFlTagH0exVqbgkkw45B+/bXVBzF2ZD1KB5Z8RiAoiKo60vSUf6/r4c2Vh9jfGBKM4oBI9b4/1ZJYng==", + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", + "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", "dev": true, "requires": { - "esbuild": "^0.16.3", + "esbuild": "^0.17.5", "fsevents": "~2.3.2", - "postcss": "^8.4.20", - "resolve": "^1.22.1", - "rollup": "^3.7.0" + "postcss": "^8.4.23", + "rollup": "^3.21.0" } }, "vitefu": { diff --git a/web/client/package.json b/web/client/package.json index 9c3cf0cda9..7c084fe301 100644 --- a/web/client/package.json +++ b/web/client/package.json @@ -15,12 +15,14 @@ "devDependencies": { "@sveltejs/adapter-static": "^1.0.0", "@sveltejs/kit": "^1.15.1", + "@types/lodash": "^4.14.194", "@types/marked": "^4.0.8", "autoprefixer": "^10.4.13", "cypress": "^12.10.0", "eslint": "^8.28.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-svelte3": "^4.0.0", + "lodash": "^4.17.21", "marked": "^4.2.12", "postcss": "^8.4.20", "postcss-import": "^15.1.0", diff --git a/web/client/src/css/components/markdown.css b/web/client/src/css/components/markdown.css index 62557ae39b..a541565c37 100644 --- a/web/client/src/css/components/markdown.css +++ b/web/client/src/css/components/markdown.css @@ -36,8 +36,13 @@ @apply rounded; } + p { + max-width: 80ch; + @apply mb-4; + } + ol, ul { - @apply my-3; + @apply my-4; @apply pl-6; } @@ -49,14 +54,36 @@ @apply list-disc; } + blockquote { + @apply border-l-2; + @apply my-4; + @apply pl-4; + @apply text-mobi-dark-blue/50; + } + table { @apply my-2; } th, td { @apply align-baseline; - @apply px-3 py-2; + @apply px-4 py-3; @apply text-left; + + p, table, ol, ul { + @apply mb-4; + + &:last-child { + @apply m-0; + } + } + } + + .table-sm table, + table table { + th, td { + @apply px-3 py-2; + } } th { @@ -72,6 +99,27 @@ @apply bg-mobi-dark-blue/5; } + .table-collapse-responsive { + @media (max-width: 1023px) { + > thead, > tfoot { + display: none; + } + + > tbody { + > tr { + @apply bg-transparent; + display: block; + + > td, > th { + @apply bg-none; + @apply px-0; + display: block; + } + } + } + } + } + a[href] { @apply text-mobi-light-blue; @apply no-underline hover:underline; diff --git a/web/client/src/routes/+layout.svelte b/web/client/src/routes/+layout.svelte index b691e7785b..d958279e98 100644 --- a/web/client/src/routes/+layout.svelte +++ b/web/client/src/routes/+layout.svelte @@ -27,6 +27,8 @@ {/if} +
    +
    diff --git a/web/client/src/routes/rules.html/+page.js b/web/client/src/routes/rules.html/+page.js index 0661cabfa2..3e7be6a3b9 100644 --- a/web/client/src/routes/rules.html/+page.js +++ b/web/client/src/routes/rules.html/+page.js @@ -1,30 +1,29 @@ /** @type {import('./$types').PageLoad} */ export const load = async ({ fetch }) => { - let rulesMd = ''; + let msgHeading, msgBody, rules = null; try { - // local copy of https://raw.githubusercontent.com/MobilityData/gtfs-validator/master/RULES.md - // we could fetch it directly instead, if desired - const response = await fetch('/RULES.md'); + const response = await fetch('/rules.json'); if (response.ok) { - rulesMd = await response.text(); + rules = await response.json(); } else { throw new Error(`HTTP Error: ${response.status}`); } } catch (error) { - let msg = 'Error'; + let errorMsg = ''; if (error instanceof Error && error.message) { - msg = error.message; + errorMsg = error.message; } - rulesMd = `# ${msg}\n\nThere was a problem loading the rules file. You can try accessing it directly at https://github.com/MobilityData/gtfs-validator/blob/master/RULES.md.`; + msgHeading = errorMsg ?? 'Error'; + msgBody = 'There was a problem loading the rules file.'; } - return { rulesMd }; + return { rules, msgHeading, msgBody }; }; export const prerender = true; diff --git a/web/client/src/routes/rules.html/+page.svelte b/web/client/src/routes/rules.html/+page.svelte index 3b5e1e25b4..d0b3c1bedb 100644 --- a/web/client/src/routes/rules.html/+page.svelte +++ b/web/client/src/routes/rules.html/+page.svelte @@ -1,56 +1,226 @@
    - - {@html marked.parse(massagedMarkdown)} + {#if $page.data.msgHeading} +

    {$page.data.msgHeading}

    + {:else} +

    Validator Rules

    + {/if} + + {#if $page.data.msgBody} +

    + {$page.data.msgBody} +

    + {:else} + +
    +
    +

    Contents

    + +
    +
    + +
    +

    Introduction

    + +

    This tool is designed to assist you in testing the compliance of a dataset against the GTFS (General Transit Feed Specification) Schedule Reference and the GTFS Schedule Best Practices.

    + +

    This validator generates a list of notices, each associated with a severity level, allowing you to identify and address potential issues in your dataset. This documentation will provide a detailed overview of the rules that the validator evaluates, and ways in which the dataset can be fixed if the notice is present.

    + +

    Severities

    + +

    Each notice generated by the this validator is associated with a severity level: INFO, WARNING, or ERROR. Understanding these severities helps in assessing the impact and urgency of addressing the identified issues.

    + +

    ERROR notices correspond to GTFS Schedule Reference violations. These violations represent items explicitly required or prohibited by the GTFS Schedule Reference, denoted by the use of the terms “Required” or “must.” Errors signify critical discrepancies that must be resolved to ensure compliance with the GTFS standard.

    + +

    WARNING notices correspond to GTFS Schedule Best Practices. These recommendations are either explicitly suggested by the GTFS Schedule Reference, using the term “recommend” or “should,” or mentioned in the official GTFS Schedule Best Practices. Although not mandatory, addressing these warnings can significantly improve the quality of the data and the rider’s experience.

    + +

    INFO notices highlight items that may affect the overall quality of the feed. These notices identify unexpected findings that warrant the user’s attention.

    +
    +
    + + {#each Object.entries(categories) as [category, rules]} +
    +

    + Top + +
    + Table of {category} notices + + + +
    +

    + +
    + + + + + + + + + {#each rules as rule} + + + + + {/each} + +
    Notice codeDescription
    + + {rule.code} + + +
    {@html marked.parse(rule.shortSummary ?? '')}
    + {@html marked.parse(rule.description ?? '')} +
    +
    +
    + {/each} + +

    Notice details

    + + {#each Object.entries(rules) as [code, rule]} +
    +

    + + Table + + +
    + {code} + + + +
    +

    + +
    + {@html marked.parse(rule.shortSummary ?? '')} +
    + + {#if rule.description} +
    + {@html marked.parse(rule.description)} +
    + {/if} + + {#if rule.references} +

    References

    +
      + + {#each rule.references?.sectionReferences ?? [] as ref} +
    • + +
    • + {/each} + {#each rule.references?.fileReferences ?? [] as ref} +
    • + + {ref} + +
    • + {/each} + {#each rule.references?.bestPracticesFileReferences ?? [] as ref} +
    • + + {ref} Best Practices + +
    • + {/each} + {#each rule.references?.urlReferences ?? [] as ref} +
    • + + {ref.label} + +
    • + {/each} +
    + {/if} + + {#if rule.properties} +
    + Fields +
    + + + + + + + + + + {#each Object.entries(rule.properties) as [name, property]} + + + + + + {/each} + +
    Field nameDescriptionType
    {property.fieldName} + {@html marked.parse(property.shortSummary ?? '')} + {@html marked.parse(property.description ?? '\u2014')} + {property.type ?? '\u2014'}
    +
    +
    + {/if} +
    + {/each} + {/if}
    diff --git a/web/client/src/routes/rules.html/SectionRefLink.svelte b/web/client/src/routes/rules.html/SectionRefLink.svelte new file mode 100644 index 0000000000..2f8c4ce27e --- /dev/null +++ b/web/client/src/routes/rules.html/SectionRefLink.svelte @@ -0,0 +1,48 @@ + + + + {sectionReference.label} +