From 2fbc22af0d030558f700fb30b787a392fa68e239 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Thu, 5 May 2022 22:21:43 +0200 Subject: [PATCH 1/7] Added new model. --- pom.xml | 2 +- .../railsignalapi/dao/BranchRepository.java | 18 ------ .../dao/ComponentRepository.java | 11 ++++ .../railsignalapi/dao/SegmentRepository.java | 11 ++++ .../dao/SignalBranchConnectionRepository.java | 9 --- .../railsignalapi/dao/SignalRepository.java | 26 -------- .../railsignalapi/dao/TrainRepository.java | 9 --- .../andrewl/railsignalapi/model/Branch.java | 38 ------------ .../railsignalapi/model/BranchStatus.java | 6 -- .../railsignalapi/model/Direction.java | 3 + .../railsignalapi/model/RailSystem.java | 6 ++ .../andrewl/railsignalapi/model/Segment.java | 50 +++++++++++++++ .../andrewl/railsignalapi/model/Signal.java | 40 ------------ .../model/SignalBranchConnection.java | 57 ----------------- .../andrewl/railsignalapi/model/Switch.java | 30 --------- .../nl/andrewl/railsignalapi/model/Train.java | 20 ------ .../model/component/Component.java | 62 +++++++++++++++++++ .../model/component/PathNode.java | 33 ++++++++++ .../model/{ => component}/Position.java | 5 +- .../model/component/SegmentBoundaryNode.java | 32 ++++++++++ .../railsignalapi/model/component/Signal.java | 43 +++++++++++++ .../railsignalapi/model/component/Switch.java | 29 +++++++++ .../model/component/SwitchConfiguration.java | 31 ++++++++++ .../rest/BranchesController.java | 38 ------------ .../rest/RailSystemsApiController.java | 2 +- .../rest/SignalsApiController.java | 44 ------------- .../rest/dto/BranchResponse.java | 13 ---- .../dto/SignalConnectionsUpdatePayload.java | 9 --- .../rest/dto/SignalCreationPayload.java | 13 ---- .../rest/dto/SignalResponse.java | 61 ------------------ .../service/RailSystemService.java | 14 ++--- .../railsignalapi/service/SignalService.java | 1 + 32 files changed, 324 insertions(+), 442 deletions(-) delete mode 100644 src/main/java/nl/andrewl/railsignalapi/dao/BranchRepository.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/dao/ComponentRepository.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/dao/SegmentRepository.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/dao/SignalBranchConnectionRepository.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/dao/SignalRepository.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/dao/TrainRepository.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/model/Branch.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/model/BranchStatus.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/model/Segment.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/model/Signal.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/model/SignalBranchConnection.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/model/Switch.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/model/Train.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/model/component/Component.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/model/component/PathNode.java rename src/main/java/nl/andrewl/railsignalapi/model/{ => component}/Position.java (68%) create mode 100644 src/main/java/nl/andrewl/railsignalapi/model/component/SegmentBoundaryNode.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/model/component/Signal.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/model/component/Switch.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/model/component/SwitchConfiguration.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/BranchesController.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/SignalsApiController.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/BranchResponse.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalConnectionsUpdatePayload.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalCreationPayload.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalResponse.java diff --git a/pom.xml b/pom.xml index 9ee5fa8..7c63610 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 2.6.0 + 2.6.6 nl.andrewl diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/BranchRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/BranchRepository.java deleted file mode 100644 index ef475fc..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/dao/BranchRepository.java +++ /dev/null @@ -1,18 +0,0 @@ -package nl.andrewl.railsignalapi.dao; - -import nl.andrewl.railsignalapi.model.Branch; -import nl.andrewl.railsignalapi.model.RailSystem; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Optional; - -@Repository -public interface BranchRepository extends JpaRepository { - Optional findByIdAndRailSystem(long id, RailSystem railSystem); - Optional findByIdAndRailSystemId(long id, long railSystemId); - Optional findByNameAndRailSystem(String name, RailSystem railSystem); - List findAllByRailSystemOrderByName(RailSystem railSystem); - List findAllByNameAndRailSystem(String name, RailSystem railSystem); -} diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/ComponentRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/ComponentRepository.java new file mode 100644 index 0000000..3e5e0c9 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/dao/ComponentRepository.java @@ -0,0 +1,11 @@ +package nl.andrewl.railsignalapi.dao; + +import nl.andrewl.railsignalapi.model.RailSystem; +import nl.andrewl.railsignalapi.model.component.Component; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ComponentRepository extends JpaRepository { + void deleteAllByRailSystem(RailSystem rs); +} diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/SegmentRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/SegmentRepository.java new file mode 100644 index 0000000..3cde24a --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/dao/SegmentRepository.java @@ -0,0 +1,11 @@ +package nl.andrewl.railsignalapi.dao; + +import nl.andrewl.railsignalapi.model.RailSystem; +import nl.andrewl.railsignalapi.model.Segment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface SegmentRepository extends JpaRepository { + void deleteAllByRailSystem(RailSystem rs); +} diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/SignalBranchConnectionRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/SignalBranchConnectionRepository.java deleted file mode 100644 index 2e1efa7..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/dao/SignalBranchConnectionRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package nl.andrewl.railsignalapi.dao; - -import nl.andrewl.railsignalapi.model.SignalBranchConnection; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface SignalBranchConnectionRepository extends JpaRepository { -} diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/SignalRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/SignalRepository.java deleted file mode 100644 index 414cb9e..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/dao/SignalRepository.java +++ /dev/null @@ -1,26 +0,0 @@ -package nl.andrewl.railsignalapi.dao; - -import nl.andrewl.railsignalapi.model.Branch; -import nl.andrewl.railsignalapi.model.RailSystem; -import nl.andrewl.railsignalapi.model.Signal; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Optional; - -@Repository -public interface SignalRepository extends JpaRepository { - Optional findByIdAndRailSystem(long id, RailSystem railSystem); - Optional findByIdAndRailSystemId(long id, long railSystemId); - boolean existsByNameAndRailSystem(String name, RailSystem railSystem); - - @Query("SELECT DISTINCT s FROM Signal s " + - "LEFT JOIN s.branchConnections bc " + - "WHERE bc.branch = :branch " + - "ORDER BY s.name") - List findAllConnectedToBranch(Branch branch); - - List findAllByRailSystemOrderByName(RailSystem railSystem); -} diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/TrainRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/TrainRepository.java deleted file mode 100644 index 621136f..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/dao/TrainRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package nl.andrewl.railsignalapi.dao; - -import nl.andrewl.railsignalapi.model.Train; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface TrainRepository extends JpaRepository { -} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/Branch.java b/src/main/java/nl/andrewl/railsignalapi/model/Branch.java deleted file mode 100644 index 68af2c3..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/model/Branch.java +++ /dev/null @@ -1,38 +0,0 @@ -package nl.andrewl.railsignalapi.model; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import javax.persistence.*; -import java.util.HashSet; -import java.util.Set; - -@Entity -@NoArgsConstructor -@Getter -public class Branch { - @Id - @GeneratedValue - private Long id; - - @ManyToOne(optional = false, fetch = FetchType.LAZY) - private RailSystem railSystem; - - @Column(nullable = false) - private String name; - - @Enumerated(EnumType.STRING) - @Setter - private BranchStatus status; - - @OneToMany(mappedBy = "branch", orphanRemoval = true) - private Set signalConnections; - - public Branch(RailSystem railSystem, String name, BranchStatus status) { - this.railSystem = railSystem; - this.name = name; - this.status = status; - this.signalConnections = new HashSet<>(); - } -} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/BranchStatus.java b/src/main/java/nl/andrewl/railsignalapi/model/BranchStatus.java deleted file mode 100644 index cbca38b..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/model/BranchStatus.java +++ /dev/null @@ -1,6 +0,0 @@ -package nl.andrewl.railsignalapi.model; - -public enum BranchStatus { - FREE, - OCCUPIED -} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/Direction.java b/src/main/java/nl/andrewl/railsignalapi/model/Direction.java index 8a12331..f73363b 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/Direction.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/Direction.java @@ -1,5 +1,8 @@ package nl.andrewl.railsignalapi.model; +/** + * A cardinal direction, useful for some components. + */ public enum Direction { NORTH, SOUTH, diff --git a/src/main/java/nl/andrewl/railsignalapi/model/RailSystem.java b/src/main/java/nl/andrewl/railsignalapi/model/RailSystem.java index 6401810..92afd4d 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/RailSystem.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/RailSystem.java @@ -8,6 +8,9 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +/** + * Represents a closed system that contains a collection of components. + */ @Entity @Getter @NoArgsConstructor @@ -16,6 +19,9 @@ public class RailSystem { @GeneratedValue private Long id; + /** + * The name of this system. + */ @Column(nullable = false, unique = true) private String name; diff --git a/src/main/java/nl/andrewl/railsignalapi/model/Segment.java b/src/main/java/nl/andrewl/railsignalapi/model/Segment.java new file mode 100644 index 0000000..efe0b8f --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/model/Segment.java @@ -0,0 +1,50 @@ +package nl.andrewl.railsignalapi.model; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import nl.andrewl.railsignalapi.model.component.SegmentBoundaryNode; +import nl.andrewl.railsignalapi.model.component.Signal; + +import javax.persistence.*; +import java.util.Set; + +/** + * Represents a traversable segment of a rail system that components can + * connect to. + */ +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Segment { + @Id + @GeneratedValue + private Long id; + + @ManyToOne(optional = false, fetch = FetchType.LAZY) + private RailSystem railSystem; + + /** + * A unique name for this segment. + */ + @Column(unique = true) + private String name; + + /** + * The signals that are connected to this branch. + */ + @OneToMany(mappedBy = "segment") + private Set signals; + + /** + * The set of nodes from which trains can enter and exit this segment. + */ + @ManyToMany(mappedBy = "segments", cascade = CascadeType.ALL) + private Set boundaryNodes; + + @Override + public boolean equals(Object o) { + if (o == this) return true; + return o instanceof Segment s && this.id != null && this.id.equals(s.id); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/Signal.java b/src/main/java/nl/andrewl/railsignalapi/model/Signal.java deleted file mode 100644 index 9847f9a..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/model/Signal.java +++ /dev/null @@ -1,40 +0,0 @@ -package nl.andrewl.railsignalapi.model; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import javax.persistence.*; -import java.util.Set; - -@Entity -@Getter -@NoArgsConstructor -public class Signal { - @Id - @GeneratedValue - private Long id; - - @ManyToOne(optional = false, fetch = FetchType.LAZY) - private RailSystem railSystem; - - @Column(nullable = false) - private String name; - - @OneToMany(mappedBy = "signal", orphanRemoval = true, cascade = CascadeType.ALL) - private Set branchConnections; - - @Embedded - private Position position; - - @Column(nullable = false) - @Setter - private boolean online = false; - - public Signal(RailSystem railSystem, String name, Position position, Set branchConnections) { - this.railSystem = railSystem; - this.name = name; - this.position = position; - this.branchConnections = branchConnections; - } -} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/SignalBranchConnection.java b/src/main/java/nl/andrewl/railsignalapi/model/SignalBranchConnection.java deleted file mode 100644 index e4eabc6..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/model/SignalBranchConnection.java +++ /dev/null @@ -1,57 +0,0 @@ -package nl.andrewl.railsignalapi.model; - -import lombok.Getter; -import lombok.NoArgsConstructor; - -import javax.persistence.*; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -@Entity -@NoArgsConstructor -@Getter -public class SignalBranchConnection implements Comparable { - @Id - @GeneratedValue - private Long id; - - @ManyToOne(optional = false) - private Signal signal; - - @ManyToOne(optional = false, cascade = CascadeType.PERSIST) - private Branch branch; - - @Enumerated(EnumType.STRING) - private Direction direction; - - @ManyToMany - private Set reachableSignalConnections; - - public SignalBranchConnection(Signal signal, Branch branch, Direction direction) { - this.signal = signal; - this.branch = branch; - this.direction = direction; - reachableSignalConnections = new HashSet<>(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - return this.id != null && - o instanceof SignalBranchConnection sbc && sbc.getId() != null && - this.id.equals(sbc.getId()); - } - - @Override - public int hashCode() { - return Objects.hashCode(id); - } - - @Override - public int compareTo(SignalBranchConnection o) { - int c = Long.compare(this.getSignal().getId(), o.getSignal().getId()); - if (c != 0) return c; - return this.direction.compareTo(o.getDirection()); - } -} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/Switch.java b/src/main/java/nl/andrewl/railsignalapi/model/Switch.java deleted file mode 100644 index 720e85e..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/model/Switch.java +++ /dev/null @@ -1,30 +0,0 @@ -package nl.andrewl.railsignalapi.model; - -import lombok.Getter; -import lombok.NoArgsConstructor; - -import javax.persistence.*; - -@Entity -@Getter -@NoArgsConstructor -public class Switch { - @Id - @GeneratedValue - private Long id; - - @ManyToOne(optional = false, fetch = FetchType.LAZY) - private RailSystem railSystem; - - @Column(nullable = false) - private String name; - - @Embedded - private Position position; - - @Column(nullable = false) - private int state; - - @Column(nullable = false) - private int maxStates; -} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/Train.java b/src/main/java/nl/andrewl/railsignalapi/model/Train.java deleted file mode 100644 index 09a05b8..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/model/Train.java +++ /dev/null @@ -1,20 +0,0 @@ -package nl.andrewl.railsignalapi.model; - -import lombok.Getter; -import lombok.NoArgsConstructor; - -import javax.persistence.*; - -@Entity -@NoArgsConstructor -@Getter -public class Train { - @Id - @GeneratedValue - private Long id; - - private String name; - - @ManyToOne(fetch = FetchType.LAZY) - private Branch currentBranch; -} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/Component.java b/src/main/java/nl/andrewl/railsignalapi/model/component/Component.java new file mode 100644 index 0000000..a0ff76c --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/Component.java @@ -0,0 +1,62 @@ +package nl.andrewl.railsignalapi.model.component; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import nl.andrewl.railsignalapi.model.RailSystem; + +import javax.persistence.*; + +/** + * Represents a physical component of the rail system that the API can interact + * with, and send or receive data from. For example, a signal, switch, or + * detector. + */ +@Entity +@Inheritance(strategy = InheritanceType.JOINED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public abstract class Component { + @Id + @GeneratedValue + private Long id; + + /** + * The rail system that this component belongs to. + */ + @ManyToOne(optional = false, fetch = FetchType.LAZY) + private RailSystem railSystem; + + /** + * The position of this component in the system. + */ + @Embedded + private Position position; + + /** + * A human-readable name for the component. + */ + @Column(unique = true) + private String name; + + /** + * Whether this component is online, meaning that an in-world device is + * currently connected to relay information regarding this component. + */ + @Column(nullable = false) + @Setter + private boolean online = false; + + public Component(RailSystem railSystem, Position position, String name) { + this.railSystem = railSystem; + this.position = position; + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Component c && this.id != null && this.id.equals(c.id); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/PathNode.java b/src/main/java/nl/andrewl/railsignalapi/model/component/PathNode.java new file mode 100644 index 0000000..6d82422 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/PathNode.java @@ -0,0 +1,33 @@ +package nl.andrewl.railsignalapi.model.component; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import nl.andrewl.railsignalapi.model.RailSystem; + +import javax.persistence.Entity; +import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; +import javax.persistence.ManyToMany; +import java.util.Set; + +/** + * A node that, together with other nodes, forms a path that trains can follow + * to traverse through segments in the rail system. + */ +@Entity +@Inheritance(strategy = InheritanceType.JOINED) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class PathNode extends Component { + /** + * The set of nodes that this one is connected to. + */ + @ManyToMany + private Set connectedNodes; + + public PathNode(RailSystem railSystem, Position position, String name, Set connectedNodes) { + super(railSystem, position, name); + this.connectedNodes = connectedNodes; + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/Position.java b/src/main/java/nl/andrewl/railsignalapi/model/component/Position.java similarity index 68% rename from src/main/java/nl/andrewl/railsignalapi/model/Position.java rename to src/main/java/nl/andrewl/railsignalapi/model/component/Position.java index 9060feb..f7bb43f 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/Position.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/Position.java @@ -1,4 +1,4 @@ -package nl.andrewl.railsignalapi.model; +package nl.andrewl.railsignalapi.model.component; import lombok.AllArgsConstructor; import lombok.Data; @@ -6,6 +6,9 @@ import lombok.NoArgsConstructor; import javax.persistence.Embeddable; +/** + * A three-dimensional position for a component within a system. + */ @Embeddable @Data @AllArgsConstructor diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/SegmentBoundaryNode.java b/src/main/java/nl/andrewl/railsignalapi/model/component/SegmentBoundaryNode.java new file mode 100644 index 0000000..0b9923d --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/SegmentBoundaryNode.java @@ -0,0 +1,32 @@ +package nl.andrewl.railsignalapi.model.component; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import nl.andrewl.railsignalapi.model.RailSystem; +import nl.andrewl.railsignalapi.model.Segment; + +import javax.persistence.Entity; +import javax.persistence.ManyToMany; +import java.util.Set; + +/** + * Component that relays information about trains traversing from one segment + * to another. It links exactly two segments together at a specific point. + */ +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class SegmentBoundaryNode extends PathNode { + /** + * The set of segments that this boundary node connects. This should + * generally always have exactly two segments. + */ + @ManyToMany + private Set segments; + + public SegmentBoundaryNode(RailSystem railSystem, Position position, String name, Set connectedNodes, Set segments) { + super(railSystem, position, name, connectedNodes); + this.segments = segments; + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/Signal.java b/src/main/java/nl/andrewl/railsignalapi/model/component/Signal.java new file mode 100644 index 0000000..591542d --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/Signal.java @@ -0,0 +1,43 @@ +package nl.andrewl.railsignalapi.model.component; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import nl.andrewl.railsignalapi.model.Direction; +import nl.andrewl.railsignalapi.model.RailSystem; +import nl.andrewl.railsignalapi.model.Segment; + +import javax.persistence.*; + +/** + * A signal is a component that relays the status of a connected segment to + * some sort of in-world representation, whether that be a certain light + * color, or electrical signal. + */ +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Signal extends Component { + /** + * The direction this signal is facing. This is the direction in which the + * signal connects to a branch. + *
+	 *     |-segment A-|-segment B-|
+	 *     ===================== <- Rail
+	 *                ]+  --->      Signal is facing East, and shows status on
+	 *                              its western side. It is connected to segment B.
+	 * 
+ */ + @Enumerated(EnumType.STRING) + private Direction direction; + + /** + * The segment that this signal connects to. + */ + @ManyToOne(optional = false, fetch = FetchType.LAZY) + private Segment segment; + + public Signal(RailSystem railSystem, Position position, String name, Segment segment, Direction direction) { + super(railSystem, position, name); + this.segment = segment; + this.direction = direction; + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/Switch.java b/src/main/java/nl/andrewl/railsignalapi/model/component/Switch.java new file mode 100644 index 0000000..afa8c80 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/Switch.java @@ -0,0 +1,29 @@ +package nl.andrewl.railsignalapi.model.component; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.Set; + +/** + * A switch is a component that directs traffic between several connected + * segments. + */ +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class Switch extends PathNode { + /** + * The set of all possible configurations that this switch can be in. + */ + @OneToMany(mappedBy = "switchComponent", orphanRemoval = true, cascade = CascadeType.ALL) + private Set possibleConfigurations; + + /** + * The switch configuration that this switch is currently in. + */ + @OneToOne(optional = false, fetch = FetchType.LAZY) + private SwitchConfiguration activeConfiguration; +} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/SwitchConfiguration.java b/src/main/java/nl/andrewl/railsignalapi/model/component/SwitchConfiguration.java new file mode 100644 index 0000000..261f81c --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/SwitchConfiguration.java @@ -0,0 +1,31 @@ +package nl.andrewl.railsignalapi.model.component; + +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.Set; + +/** + * A possible connection that can be made between path nodes, if this is set + * as an active configuration in the linked switch component. + */ +@Entity +@NoArgsConstructor +public class SwitchConfiguration { + @Id + @GeneratedValue + private Long id; + + /** + * The switch component that this configuration belongs to. + */ + @ManyToOne(optional = false, fetch = FetchType.LAZY) + private Switch switchComponent; + + /** + * The set of nodes that this switch configuration connects. This should + * be almost always a set of two nodes. + */ + @ManyToMany(fetch = FetchType.EAGER) + private Set nodes; +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/BranchesController.java b/src/main/java/nl/andrewl/railsignalapi/rest/BranchesController.java deleted file mode 100644 index e4df17c..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/rest/BranchesController.java +++ /dev/null @@ -1,38 +0,0 @@ -package nl.andrewl.railsignalapi.rest; - -import lombok.RequiredArgsConstructor; -import nl.andrewl.railsignalapi.rest.dto.BranchResponse; -import nl.andrewl.railsignalapi.rest.dto.SignalResponse; -import nl.andrewl.railsignalapi.service.BranchService; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequestMapping(path = "/api/railSystems/{rsId}/branches") -@RequiredArgsConstructor -public class BranchesController { - private final BranchService branchService; - - @GetMapping - public List getAllBranches(@PathVariable long rsId) { - return branchService.getAllBranches(rsId); - } - - @GetMapping(path = "/{branchId}") - public BranchResponse getBranch(@PathVariable long rsId, @PathVariable long branchId) { - return branchService.getBranch(rsId, branchId); - } - - @GetMapping(path = "/{branchId}/signals") - public List getBranchSignals(@PathVariable long rsId, @PathVariable long branchId) { - return branchService.getConnectedSignals(rsId, branchId); - } - - @DeleteMapping(path = "/{branchId}") - public ResponseEntity deleteBranch(@PathVariable long rsId, @PathVariable long branchId) { - branchService.deleteBranch(rsId, branchId); - return ResponseEntity.noContent().build(); - } -} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/RailSystemsApiController.java b/src/main/java/nl/andrewl/railsignalapi/rest/RailSystemsApiController.java index 160c2dc..cbe1362 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/RailSystemsApiController.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/RailSystemsApiController.java @@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; @RestController -@RequestMapping(path = "/api/railSystems") +@RequestMapping(path = "/api/rs") @RequiredArgsConstructor public class RailSystemsApiController { private final RailSystemService railSystemService; diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/SignalsApiController.java b/src/main/java/nl/andrewl/railsignalapi/rest/SignalsApiController.java deleted file mode 100644 index e44ca0f..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/rest/SignalsApiController.java +++ /dev/null @@ -1,44 +0,0 @@ -package nl.andrewl.railsignalapi.rest; - -import lombok.RequiredArgsConstructor; -import nl.andrewl.railsignalapi.rest.dto.SignalConnectionsUpdatePayload; -import nl.andrewl.railsignalapi.rest.dto.SignalCreationPayload; -import nl.andrewl.railsignalapi.rest.dto.SignalResponse; -import nl.andrewl.railsignalapi.service.SignalService; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequestMapping(path = "/api/railSystems/{rsId}/signals") -@RequiredArgsConstructor -public class SignalsApiController { - private final SignalService signalService; - - @PostMapping - public SignalResponse createSignal(@PathVariable long rsId, @RequestBody SignalCreationPayload payload) { - return signalService.createSignal(rsId, payload); - } - - @GetMapping - public List getSignals(@PathVariable long rsId) { - return signalService.getAllSignals(rsId); - } - - @GetMapping(path = "/{sigId}") - public SignalResponse getSignal(@PathVariable long rsId, @PathVariable long sigId) { - return signalService.getSignal(rsId, sigId); - } - - @PostMapping(path = "/{sigId}/signalConnections") - public SignalResponse updateSignalConnections(@PathVariable long rsId, @PathVariable long sigId, @RequestBody SignalConnectionsUpdatePayload payload) { - return signalService.updateSignalBranchConnections(rsId, sigId, payload); - } - - @DeleteMapping(path = "/{sigId}") - public ResponseEntity deleteSignal(@PathVariable long rsId, @PathVariable long sigId) { - signalService.deleteSignal(rsId, sigId); - return ResponseEntity.noContent().build(); - } -} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/BranchResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/BranchResponse.java deleted file mode 100644 index 413fa7c..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/BranchResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package nl.andrewl.railsignalapi.rest.dto; - -import nl.andrewl.railsignalapi.model.Branch; - -public record BranchResponse( - long id, - String name, - String status -) { - public BranchResponse(Branch branch) { - this(branch.getId(), branch.getName(), branch.getStatus().name()); - } -} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalConnectionsUpdatePayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalConnectionsUpdatePayload.java deleted file mode 100644 index 321f931..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalConnectionsUpdatePayload.java +++ /dev/null @@ -1,9 +0,0 @@ -package nl.andrewl.railsignalapi.rest.dto; - -import java.util.List; - -public record SignalConnectionsUpdatePayload( - List connections -) { - public static record ConnectionData(long from, long to) {} -} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalCreationPayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalCreationPayload.java deleted file mode 100644 index b0ace7b..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalCreationPayload.java +++ /dev/null @@ -1,13 +0,0 @@ -package nl.andrewl.railsignalapi.rest.dto; - -import nl.andrewl.railsignalapi.model.Position; - -import java.util.List; - -public record SignalCreationPayload( - String name, - Position position, - List branchConnections -) { - public static record BranchData(String direction, String name, Long id) {} -} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalResponse.java deleted file mode 100644 index b71a040..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/SignalResponse.java +++ /dev/null @@ -1,61 +0,0 @@ -package nl.andrewl.railsignalapi.rest.dto; - -import nl.andrewl.railsignalapi.model.Position; -import nl.andrewl.railsignalapi.model.Signal; -import nl.andrewl.railsignalapi.model.SignalBranchConnection; - -import java.util.Comparator; -import java.util.List; - -public record SignalResponse( - long id, - String name, - Position position, - List branchConnections, - boolean online -) { - public SignalResponse(Signal signal) { - this( - signal.getId(), - signal.getName(), - signal.getPosition(), - signal.getBranchConnections().stream() - .sorted(Comparator.comparing(SignalBranchConnection::getDirection)) - .map(ConnectionData::new) - .toList(), - signal.isOnline() - ); - } - - public static record ConnectionData( - long id, - String direction, - BranchResponse branch, - List reachableSignalConnections - ) { - public ConnectionData(SignalBranchConnection c) { - this( - c.getId(), - c.getDirection().name(), - new BranchResponse(c.getBranch()), - c.getReachableSignalConnections().stream() - .map(cc -> new ReachableConnectionData( - cc.getId(), - cc.getDirection().name(), - cc.getSignal().getId(), - cc.getSignal().getName(), - cc.getSignal().getPosition() - )) - .toList() - ); - } - - public static record ReachableConnectionData( - long connectionId, - String direction, - long signalId, - String signalName, - Position signalPosition - ) {} - } -} diff --git a/src/main/java/nl/andrewl/railsignalapi/service/RailSystemService.java b/src/main/java/nl/andrewl/railsignalapi/service/RailSystemService.java index fafbb73..2e9e1f1 100644 --- a/src/main/java/nl/andrewl/railsignalapi/service/RailSystemService.java +++ b/src/main/java/nl/andrewl/railsignalapi/service/RailSystemService.java @@ -1,9 +1,9 @@ package nl.andrewl.railsignalapi.service; import lombok.RequiredArgsConstructor; -import nl.andrewl.railsignalapi.dao.BranchRepository; +import nl.andrewl.railsignalapi.dao.ComponentRepository; import nl.andrewl.railsignalapi.dao.RailSystemRepository; -import nl.andrewl.railsignalapi.dao.SignalRepository; +import nl.andrewl.railsignalapi.dao.SegmentRepository; import nl.andrewl.railsignalapi.model.RailSystem; import nl.andrewl.railsignalapi.rest.dto.RailSystemCreationPayload; import nl.andrewl.railsignalapi.rest.dto.RailSystemResponse; @@ -19,8 +19,8 @@ import java.util.List; @RequiredArgsConstructor public class RailSystemService { private final RailSystemRepository railSystemRepository; - private final SignalRepository signalRepository; - private final BranchRepository branchRepository; + private final SegmentRepository segmentRepository; + private final ComponentRepository componentRepository; @Transactional public List getRailSystems() { @@ -40,10 +40,8 @@ public class RailSystemService { public void delete(long rsId) { var rs = railSystemRepository.findById(rsId) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - var signals = signalRepository.findAllByRailSystemOrderByName(rs); - signalRepository.deleteAll(signals); - var branches = branchRepository.findAllByRailSystemOrderByName(rs); - branchRepository.deleteAll(branches); + componentRepository.deleteAllByRailSystem(rs); + segmentRepository.deleteAllByRailSystem(rs); railSystemRepository.delete(rs); } diff --git a/src/main/java/nl/andrewl/railsignalapi/service/SignalService.java b/src/main/java/nl/andrewl/railsignalapi/service/SignalService.java index 43e2d61..570bae2 100644 --- a/src/main/java/nl/andrewl/railsignalapi/service/SignalService.java +++ b/src/main/java/nl/andrewl/railsignalapi/service/SignalService.java @@ -9,6 +9,7 @@ import nl.andrewl.railsignalapi.dao.RailSystemRepository; import nl.andrewl.railsignalapi.dao.SignalBranchConnectionRepository; import nl.andrewl.railsignalapi.dao.SignalRepository; import nl.andrewl.railsignalapi.model.*; +import nl.andrewl.railsignalapi.model.component.SignalBranchConnection; import nl.andrewl.railsignalapi.rest.dto.SignalConnectionsUpdatePayload; import nl.andrewl.railsignalapi.rest.dto.SignalCreationPayload; import nl.andrewl.railsignalapi.rest.dto.SignalResponse; From e608e2ba8c24eddfa3f3c76e971bb860521b0eb5 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Fri, 6 May 2022 22:43:02 +0200 Subject: [PATCH 2/7] added stuff --- pom.xml | 1 + railsignal-app/.env.development | 1 + railsignal-app/.eslintrc.cjs | 11 + railsignal-app/.gitignore | 28 + railsignal-app/README.md | 35 + railsignal-app/index.html | 13 + railsignal-app/package-lock.json | 3192 +++++++++++++++++ railsignal-app/package.json | 23 + railsignal-app/public/favicon.ico | Bin 0 -> 4286 bytes railsignal-app/src/App.vue | 37 + railsignal-app/src/components/RailSystem.vue | 31 + .../src/components/RailSystemsManager.vue | 56 + .../src/components/railsystem/AddSegment.vue | 24 + .../src/components/railsystem/Component.vue | 23 + .../src/components/railsystem/MapView.vue | 26 + railsignal-app/src/main.js | 9 + railsignal-app/src/stores/railSystemsStore.js | 48 + railsignal-app/vite.config.js | 14 + .../dao/ComponentRepository.java | 19 +- .../railsignalapi/dao/SegmentRepository.java | 9 + .../railsignalapi/dao/SignalRepository.java | 8 + .../dao/SwitchConfigurationRepository.java | 11 + .../railsignalapi/dao/SwitchRepository.java | 8 + .../andrewl/railsignalapi/model/Segment.java | 12 +- .../model/component/Component.java | 20 +- .../model/component/ComponentType.java | 7 + .../model/component/PathNode.java | 6 +- .../model/component/SegmentBoundaryNode.java | 2 +- .../railsignalapi/model/component/Signal.java | 25 +- .../railsignalapi/model/component/Switch.java | 14 +- .../model/component/SwitchConfiguration.java | 18 +- .../rest/ComponentsApiController.java | 39 + .../railsignalapi/rest/SegmentPayload.java | 4 + .../rest/SegmentsApiController.java | 31 + .../andrewl/railsignalapi/rest/WebConfig.java | 10 + .../rest/dto/FullSegmentResponse.java | 18 + .../rest/dto/SegmentResponse.java | 17 + .../rest/dto/component/ComponentResponse.java | 27 + .../rest/dto/component/PathNodeResponse.java | 14 + .../SegmentBoundaryNodeResponse.java | 15 + .../rest/dto/component/SignalResponse.java | 12 + .../component/SimpleComponentResponse.java | 22 + .../SwitchConfigurationResponse.java | 17 + .../rest/dto/component/SwitchResponse.java | 16 + .../railsignalapi/service/BranchService.java | 57 - .../service/ComponentService.java | 129 + .../service/RailSystemService.java | 9 +- .../railsignalapi/service/SegmentService.java | 57 + .../railsignalapi/service/SignalService.java | 246 -- .../websocket/SignalWebSocketHandler.java | 8 +- src/main/resources/static/js/drawing.js | 144 - src/main/resources/static/js/main.js | 388 -- src/main/resources/static/style/main.css | 0 src/main/resources/templates/index.html | 209 -- 54 files changed, 4138 insertions(+), 1082 deletions(-) create mode 100644 railsignal-app/.env.development create mode 100644 railsignal-app/.eslintrc.cjs create mode 100644 railsignal-app/.gitignore create mode 100644 railsignal-app/README.md create mode 100644 railsignal-app/index.html create mode 100644 railsignal-app/package-lock.json create mode 100644 railsignal-app/package.json create mode 100644 railsignal-app/public/favicon.ico create mode 100644 railsignal-app/src/App.vue create mode 100644 railsignal-app/src/components/RailSystem.vue create mode 100644 railsignal-app/src/components/RailSystemsManager.vue create mode 100644 railsignal-app/src/components/railsystem/AddSegment.vue create mode 100644 railsignal-app/src/components/railsystem/Component.vue create mode 100644 railsignal-app/src/components/railsystem/MapView.vue create mode 100644 railsignal-app/src/main.js create mode 100644 railsignal-app/src/stores/railSystemsStore.js create mode 100644 railsignal-app/vite.config.js create mode 100644 src/main/java/nl/andrewl/railsignalapi/dao/SignalRepository.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/dao/SwitchConfigurationRepository.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/dao/SwitchRepository.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/model/component/ComponentType.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/ComponentsApiController.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/SegmentPayload.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/SegmentsApiController.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/FullSegmentResponse.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/SegmentResponse.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/component/ComponentResponse.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/component/PathNodeResponse.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SegmentBoundaryNodeResponse.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SignalResponse.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SimpleComponentResponse.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchConfigurationResponse.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchResponse.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/service/BranchService.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/service/ComponentService.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java delete mode 100644 src/main/java/nl/andrewl/railsignalapi/service/SignalService.java delete mode 100644 src/main/resources/static/js/drawing.js delete mode 100644 src/main/resources/static/js/main.js delete mode 100644 src/main/resources/static/style/main.css delete mode 100644 src/main/resources/templates/index.html diff --git a/pom.xml b/pom.xml index 7c63610..34f4cc6 100644 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ com.h2database h2 + 2.1.212 diff --git a/railsignal-app/.env.development b/railsignal-app/.env.development new file mode 100644 index 0000000..430da52 --- /dev/null +++ b/railsignal-app/.env.development @@ -0,0 +1 @@ +VITE_API_URL=http://localhost:8080/api \ No newline at end of file diff --git a/railsignal-app/.eslintrc.cjs b/railsignal-app/.eslintrc.cjs new file mode 100644 index 0000000..deed24f --- /dev/null +++ b/railsignal-app/.eslintrc.cjs @@ -0,0 +1,11 @@ +/* eslint-env node */ +module.exports = { + "root": true, + "extends": [ + "plugin:vue/vue3-essential", + "eslint:recommended" + ], + "env": { + "vue/setup-compiler-macros": true + } +} diff --git a/railsignal-app/.gitignore b/railsignal-app/.gitignore new file mode 100644 index 0000000..38adffa --- /dev/null +++ b/railsignal-app/.gitignore @@ -0,0 +1,28 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/railsignal-app/README.md b/railsignal-app/README.md new file mode 100644 index 0000000..6e6e3ef --- /dev/null +++ b/railsignal-app/README.md @@ -0,0 +1,35 @@ +# railsignal-app + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin). + +## Customize configuration + +See [Vite Configuration Reference](https://vitejs.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Compile and Minify for Production + +```sh +npm run build +``` + +### Lint with [ESLint](https://eslint.org/) + +```sh +npm run lint +``` diff --git a/railsignal-app/index.html b/railsignal-app/index.html new file mode 100644 index 0000000..e6c5ec9 --- /dev/null +++ b/railsignal-app/index.html @@ -0,0 +1,13 @@ + + + + + + + RailSignal + + +
+ + + diff --git a/railsignal-app/package-lock.json b/railsignal-app/package-lock.json new file mode 100644 index 0000000..98c6a16 --- /dev/null +++ b/railsignal-app/package-lock.json @@ -0,0 +1,3192 @@ +{ + "name": "railsignal-app", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "railsignal-app", + "version": "0.0.0", + "dependencies": { + "axios": "^0.27.2", + "pinia": "^2.0.14", + "three": "^0.140.0", + "vue": "^3.2.33", + "vue-router": "^4.0.14" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^2.3.1", + "eslint": "^8.5.0", + "eslint-plugin-vue": "^8.2.0", + "vite": "^2.9.5" + } + }, + "node_modules/@babel/parser": { + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", + "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", + "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.3.2.tgz", + "integrity": "sha512-umyypfSHS4kQLdYAnJHhaASq7FRzNCdvcRoQ3uYGNk1/M4a+hXUd7ysN7BLhCrWH6uBokyCkFeUAaFDzSaaSrQ==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "vite": "^2.5.10", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.33.tgz", + "integrity": "sha512-AAmr52ji3Zhk7IKIuigX2osWWsb2nQE5xsdFYjdnmtQ4gymmqXbjLvkSE174+fF3A3kstYrTgGkqgOEbsdLDpw==", + "dependencies": { + "@babel/parser": "^7.16.4", + "@vue/shared": "3.2.33", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.33.tgz", + "integrity": "sha512-GhiG1C8X98Xz9QUX/RlA6/kgPBWJkjq0Rq6//5XTAGSYrTMBgcLpP9+CnlUg1TFxnnCVughAG+KZl28XJqw8uQ==", + "dependencies": { + "@vue/compiler-core": "3.2.33", + "@vue/shared": "3.2.33" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.33.tgz", + "integrity": "sha512-H8D0WqagCr295pQjUYyO8P3IejM3vEzeCO1apzByAEaAR/WimhMYczHfZVvlCE/9yBaEu/eu9RdiWr0kF8b71Q==", + "dependencies": { + "@babel/parser": "^7.16.4", + "@vue/compiler-core": "3.2.33", + "@vue/compiler-dom": "3.2.33", + "@vue/compiler-ssr": "3.2.33", + "@vue/reactivity-transform": "3.2.33", + "@vue/shared": "3.2.33", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.33.tgz", + "integrity": "sha512-XQh1Xdk3VquDpXsnoCd7JnMoWec9CfAzQDQsaMcSU79OrrO2PNR0ErlIjm/mGq3GmBfkQjzZACV+7GhfRB8xMQ==", + "dependencies": { + "@vue/compiler-dom": "3.2.33", + "@vue/shared": "3.2.33" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.1.4.tgz", + "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ==" + }, + "node_modules/@vue/reactivity": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.33.tgz", + "integrity": "sha512-62Sq0mp9/0bLmDuxuLD5CIaMG2susFAGARLuZ/5jkU1FCf9EDbwUuF+BO8Ub3Rbodx0ziIecM/NsmyjardBxfQ==", + "dependencies": { + "@vue/shared": "3.2.33" + } + }, + "node_modules/@vue/reactivity-transform": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.33.tgz", + "integrity": "sha512-4UL5KOIvSQb254aqenW4q34qMXbfZcmEsV/yVidLUgvwYQQ/D21bGX3DlgPUGI3c4C+iOnNmDCkIxkILoX/Pyw==", + "dependencies": { + "@babel/parser": "^7.16.4", + "@vue/compiler-core": "3.2.33", + "@vue/shared": "3.2.33", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.33.tgz", + "integrity": "sha512-N2D2vfaXsBPhzCV3JsXQa2NECjxP3eXgZlFqKh4tgakp3iX6LCGv76DLlc+IfFZq+TW10Y8QUfeihXOupJ1dGw==", + "dependencies": { + "@vue/reactivity": "3.2.33", + "@vue/shared": "3.2.33" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.33.tgz", + "integrity": "sha512-LSrJ6W7CZTSUygX5s8aFkraDWlO6K4geOwA3quFF2O+hC3QuAMZt/0Xb7JKE3C4JD4pFwCSO7oCrZmZ0BIJUnw==", + "dependencies": { + "@vue/runtime-core": "3.2.33", + "@vue/shared": "3.2.33", + "csstype": "^2.6.8" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.33.tgz", + "integrity": "sha512-4jpJHRD4ORv8PlbYi+/MfP8ec1okz6rybe36MdpkDrGIdEItHEUyaHSKvz+ptNEyQpALmmVfRteHkU9F8vxOew==", + "dependencies": { + "@vue/compiler-ssr": "3.2.33", + "@vue/shared": "3.2.33" + }, + "peerDependencies": { + "vue": "3.2.33" + } + }, + "node_modules/@vue/shared": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.33.tgz", + "integrity": "sha512-UBc1Pg1T3yZ97vsA2ueER0F6GbJebLHYlEi4ou1H5YL4KWvMOOWwpYo9/QpWq93wxKG6Wo13IY74Hcn/f7c7Bg==" + }, + "node_modules/acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "2.6.20", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", + "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/esbuild": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz", + "integrity": "sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "esbuild-android-64": "0.14.38", + "esbuild-android-arm64": "0.14.38", + "esbuild-darwin-64": "0.14.38", + "esbuild-darwin-arm64": "0.14.38", + "esbuild-freebsd-64": "0.14.38", + "esbuild-freebsd-arm64": "0.14.38", + "esbuild-linux-32": "0.14.38", + "esbuild-linux-64": "0.14.38", + "esbuild-linux-arm": "0.14.38", + "esbuild-linux-arm64": "0.14.38", + "esbuild-linux-mips64le": "0.14.38", + "esbuild-linux-ppc64le": "0.14.38", + "esbuild-linux-riscv64": "0.14.38", + "esbuild-linux-s390x": "0.14.38", + "esbuild-netbsd-64": "0.14.38", + "esbuild-openbsd-64": "0.14.38", + "esbuild-sunos-64": "0.14.38", + "esbuild-windows-32": "0.14.38", + "esbuild-windows-64": "0.14.38", + "esbuild-windows-arm64": "0.14.38" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz", + "integrity": "sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz", + "integrity": "sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz", + "integrity": "sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz", + "integrity": "sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz", + "integrity": "sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz", + "integrity": "sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz", + "integrity": "sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz", + "integrity": "sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz", + "integrity": "sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz", + "integrity": "sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz", + "integrity": "sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz", + "integrity": "sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz", + "integrity": "sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz", + "integrity": "sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz", + "integrity": "sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz", + "integrity": "sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz", + "integrity": "sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz", + "integrity": "sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz", + "integrity": "sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz", + "integrity": "sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", + "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.2.2", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-vue": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz", + "integrity": "sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.0.1", + "postcss-selector-parser": "^6.0.9", + "semver": "^7.3.5", + "vue-eslint-parser": "^8.0.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "dependencies": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", + "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "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==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/pinia": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.14.tgz", + "integrity": "sha512-0nPuZR4TetT/WcLN+feMSjWJku3SQU7dBbXC6uw+R6FLQJCsg+/0pzXyD82T1FmAYe0lsx+jnEDQ1BLgkRKlxA==", + "dependencies": { + "@vue/devtools-api": "^6.1.4", + "vue-demi": "*" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "@vue/composition-api": "^1.4.0", + "typescript": ">=4.4.4", + "vue": "^2.6.14 || ^3.2.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz", + "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.4.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz", + "integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.3", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.72.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.72.0.tgz", + "integrity": "sha512-KqtR2YcO35/KKijg4nx4STO3569aqCUeGRkKWnJ6r+AvBBrVY9L4pmf4NHVrQr4mTOq6msbohflxr2kpihhaOA==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/three": { + "version": "0.140.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.140.0.tgz", + "integrity": "sha512-jcHjbnYspPLDdsDQChmzyAoZ5KhJbgFk6pNGlAIc9fQMvsfPGjF5H9glrngqvb2CR/qXcClMyp5PYdF996lldA==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/vite": { + "version": "2.9.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.8.tgz", + "integrity": "sha512-zsBGwn5UT3YS0NLSJ7hnR54+vUKfgzMUh/Z9CxF1YKEBVIe213+63jrFLmZphgGI5zXwQCSmqIdbPuE8NJywPw==", + "dev": true, + "dependencies": { + "esbuild": "^0.14.27", + "postcss": "^8.4.13", + "resolve": "^1.22.0", + "rollup": "^2.59.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": ">=12.2.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "less": "*", + "sass": "*", + "stylus": "*" + }, + "peerDependenciesMeta": { + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.33.tgz", + "integrity": "sha512-si1ExAlDUrLSIg/V7D/GgA4twJwfsfgG+t9w10z38HhL/HA07132pUQ2KuwAo8qbCyMJ9e6OqrmWrOCr+jW7ZQ==", + "dependencies": { + "@vue/compiler-dom": "3.2.33", + "@vue/compiler-sfc": "3.2.33", + "@vue/runtime-dom": "3.2.33", + "@vue/server-renderer": "3.2.33", + "@vue/shared": "3.2.33" + } + }, + "node_modules/vue-eslint-parser": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", + "dev": true, + "dependencies": { + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-router": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.15.tgz", + "integrity": "sha512-xa+pIN9ZqORdIW1MkN2+d9Ui2pCM1b/UMgwYUCZOiFYHAvz/slKKBDha8DLrh5aCG/RibtrpyhKjKOZ85tYyWg==", + "dependencies": { + "@vue/devtools-api": "^6.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + }, + "dependencies": { + "@babel/parser": { + "version": "7.17.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.10.tgz", + "integrity": "sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ==" + }, + "@eslint/eslintrc": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", + "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.1", + "globals": "^13.9.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + } + }, + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@vitejs/plugin-vue": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.3.2.tgz", + "integrity": "sha512-umyypfSHS4kQLdYAnJHhaASq7FRzNCdvcRoQ3uYGNk1/M4a+hXUd7ysN7BLhCrWH6uBokyCkFeUAaFDzSaaSrQ==", + "dev": true, + "requires": {} + }, + "@vue/compiler-core": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.33.tgz", + "integrity": "sha512-AAmr52ji3Zhk7IKIuigX2osWWsb2nQE5xsdFYjdnmtQ4gymmqXbjLvkSE174+fF3A3kstYrTgGkqgOEbsdLDpw==", + "requires": { + "@babel/parser": "^7.16.4", + "@vue/shared": "3.2.33", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-dom": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.33.tgz", + "integrity": "sha512-GhiG1C8X98Xz9QUX/RlA6/kgPBWJkjq0Rq6//5XTAGSYrTMBgcLpP9+CnlUg1TFxnnCVughAG+KZl28XJqw8uQ==", + "requires": { + "@vue/compiler-core": "3.2.33", + "@vue/shared": "3.2.33" + } + }, + "@vue/compiler-sfc": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.33.tgz", + "integrity": "sha512-H8D0WqagCr295pQjUYyO8P3IejM3vEzeCO1apzByAEaAR/WimhMYczHfZVvlCE/9yBaEu/eu9RdiWr0kF8b71Q==", + "requires": { + "@babel/parser": "^7.16.4", + "@vue/compiler-core": "3.2.33", + "@vue/compiler-dom": "3.2.33", + "@vue/compiler-ssr": "3.2.33", + "@vue/reactivity-transform": "3.2.33", + "@vue/shared": "3.2.33", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + } + }, + "@vue/compiler-ssr": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.33.tgz", + "integrity": "sha512-XQh1Xdk3VquDpXsnoCd7JnMoWec9CfAzQDQsaMcSU79OrrO2PNR0ErlIjm/mGq3GmBfkQjzZACV+7GhfRB8xMQ==", + "requires": { + "@vue/compiler-dom": "3.2.33", + "@vue/shared": "3.2.33" + } + }, + "@vue/devtools-api": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.1.4.tgz", + "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ==" + }, + "@vue/reactivity": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.33.tgz", + "integrity": "sha512-62Sq0mp9/0bLmDuxuLD5CIaMG2susFAGARLuZ/5jkU1FCf9EDbwUuF+BO8Ub3Rbodx0ziIecM/NsmyjardBxfQ==", + "requires": { + "@vue/shared": "3.2.33" + } + }, + "@vue/reactivity-transform": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.33.tgz", + "integrity": "sha512-4UL5KOIvSQb254aqenW4q34qMXbfZcmEsV/yVidLUgvwYQQ/D21bGX3DlgPUGI3c4C+iOnNmDCkIxkILoX/Pyw==", + "requires": { + "@babel/parser": "^7.16.4", + "@vue/compiler-core": "3.2.33", + "@vue/shared": "3.2.33", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7" + } + }, + "@vue/runtime-core": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.33.tgz", + "integrity": "sha512-N2D2vfaXsBPhzCV3JsXQa2NECjxP3eXgZlFqKh4tgakp3iX6LCGv76DLlc+IfFZq+TW10Y8QUfeihXOupJ1dGw==", + "requires": { + "@vue/reactivity": "3.2.33", + "@vue/shared": "3.2.33" + } + }, + "@vue/runtime-dom": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.33.tgz", + "integrity": "sha512-LSrJ6W7CZTSUygX5s8aFkraDWlO6K4geOwA3quFF2O+hC3QuAMZt/0Xb7JKE3C4JD4pFwCSO7oCrZmZ0BIJUnw==", + "requires": { + "@vue/runtime-core": "3.2.33", + "@vue/shared": "3.2.33", + "csstype": "^2.6.8" + } + }, + "@vue/server-renderer": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.33.tgz", + "integrity": "sha512-4jpJHRD4ORv8PlbYi+/MfP8ec1okz6rybe36MdpkDrGIdEItHEUyaHSKvz+ptNEyQpALmmVfRteHkU9F8vxOew==", + "requires": { + "@vue/compiler-ssr": "3.2.33", + "@vue/shared": "3.2.33" + } + }, + "@vue/shared": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.33.tgz", + "integrity": "sha512-UBc1Pg1T3yZ97vsA2ueER0F6GbJebLHYlEi4ou1H5YL4KWvMOOWwpYo9/QpWq93wxKG6Wo13IY74Hcn/f7c7Bg==" + }, + "acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "csstype": { + "version": "2.6.20", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.20.tgz", + "integrity": "sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==" + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "esbuild": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.38.tgz", + "integrity": "sha512-12fzJ0fsm7gVZX1YQ1InkOE5f9Tl7cgf6JPYXRJtPIoE0zkWAbHdPHVPPaLi9tYAcEBqheGzqLn/3RdTOyBfcA==", + "dev": true, + "requires": { + "esbuild-android-64": "0.14.38", + "esbuild-android-arm64": "0.14.38", + "esbuild-darwin-64": "0.14.38", + "esbuild-darwin-arm64": "0.14.38", + "esbuild-freebsd-64": "0.14.38", + "esbuild-freebsd-arm64": "0.14.38", + "esbuild-linux-32": "0.14.38", + "esbuild-linux-64": "0.14.38", + "esbuild-linux-arm": "0.14.38", + "esbuild-linux-arm64": "0.14.38", + "esbuild-linux-mips64le": "0.14.38", + "esbuild-linux-ppc64le": "0.14.38", + "esbuild-linux-riscv64": "0.14.38", + "esbuild-linux-s390x": "0.14.38", + "esbuild-netbsd-64": "0.14.38", + "esbuild-openbsd-64": "0.14.38", + "esbuild-sunos-64": "0.14.38", + "esbuild-windows-32": "0.14.38", + "esbuild-windows-64": "0.14.38", + "esbuild-windows-arm64": "0.14.38" + } + }, + "esbuild-android-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.38.tgz", + "integrity": "sha512-aRFxR3scRKkbmNuGAK+Gee3+yFxkTJO/cx83Dkyzo4CnQl/2zVSurtG6+G86EQIZ+w+VYngVyK7P3HyTBKu3nw==", + "dev": true, + "optional": true + }, + "esbuild-android-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.38.tgz", + "integrity": "sha512-L2NgQRWuHFI89IIZIlpAcINy9FvBk6xFVZ7xGdOwIm8VyhX1vNCEqUJO3DPSSy945Gzdg98cxtNt8Grv1CsyhA==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.38.tgz", + "integrity": "sha512-5JJvgXkX87Pd1Og0u/NJuO7TSqAikAcQQ74gyJ87bqWRVeouky84ICoV4sN6VV53aTW+NE87qLdGY4QA2S7KNA==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.38.tgz", + "integrity": "sha512-eqF+OejMI3mC5Dlo9Kdq/Ilbki9sQBw3QlHW3wjLmsLh+quNfHmGMp3Ly1eWm981iGBMdbtSS9+LRvR2T8B3eQ==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.38.tgz", + "integrity": "sha512-epnPbhZUt93xV5cgeY36ZxPXDsQeO55DppzsIgWM8vgiG/Rz+qYDLmh5ts3e+Ln1wA9dQ+nZmVHw+RjaW3I5Ig==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.38.tgz", + "integrity": "sha512-/9icXUYJWherhk+y5fjPI5yNUdFPtXHQlwP7/K/zg8t8lQdHVj20SqU9/udQmeUo5pDFHMYzcEFfJqgOVeKNNQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.38.tgz", + "integrity": "sha512-QfgfeNHRFvr2XeHFzP8kOZVnal3QvST3A0cgq32ZrHjSMFTdgXhMhmWdKzRXP/PKcfv3e2OW9tT9PpcjNvaq6g==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.38.tgz", + "integrity": "sha512-uuZHNmqcs+Bj1qiW9k/HZU3FtIHmYiuxZ/6Aa+/KHb/pFKr7R3aVqvxlAudYI9Fw3St0VCPfv7QBpUITSmBR1Q==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.38.tgz", + "integrity": "sha512-FiFvQe8J3VKTDXG01JbvoVRXQ0x6UZwyrU4IaLBZeq39Bsbatd94Fuc3F1RGqPF5RbIWW7RvkVQjn79ejzysnA==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.38.tgz", + "integrity": "sha512-HlMGZTEsBrXrivr64eZ/EO0NQM8H8DuSENRok9d+Jtvq8hOLzrxfsAT9U94K3KOGk2XgCmkaI2KD8hX7F97lvA==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.38.tgz", + "integrity": "sha512-qd1dLf2v7QBiI5wwfil9j0HG/5YMFBAmMVmdeokbNAMbcg49p25t6IlJFXAeLzogv1AvgaXRXvgFNhScYEUXGQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.38.tgz", + "integrity": "sha512-mnbEm7o69gTl60jSuK+nn+pRsRHGtDPfzhrqEUXyCl7CTOCLtWN2bhK8bgsdp6J/2NyS/wHBjs1x8aBWwP2X9Q==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.38.tgz", + "integrity": "sha512-+p6YKYbuV72uikChRk14FSyNJZ4WfYkffj6Af0/Tw63/6TJX6TnIKE+6D3xtEc7DeDth1fjUOEqm+ApKFXbbVQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.38.tgz", + "integrity": "sha512-0zUsiDkGJiMHxBQ7JDU8jbaanUY975CdOW1YDrurjrM0vWHfjv9tLQsW9GSyEb/heSK1L5gaweRjzfUVBFoybQ==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.38.tgz", + "integrity": "sha512-cljBAApVwkpnJZfnRVThpRBGzCi+a+V9Ofb1fVkKhtrPLDYlHLrSYGtmnoTVWDQdU516qYI8+wOgcGZ4XIZh0Q==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.38.tgz", + "integrity": "sha512-CDswYr2PWPGEPpLDUO50mL3WO/07EMjnZDNKpmaxUPsrW+kVM3LoAqr/CE8UbzugpEiflYqJsGPLirThRB18IQ==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.38.tgz", + "integrity": "sha512-2mfIoYW58gKcC3bck0j7lD3RZkqYA7MmujFYmSn9l6TiIcAMpuEvqksO+ntBgbLep/eyjpgdplF7b+4T9VJGOA==", + "dev": true, + "optional": true + }, + "esbuild-windows-32": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.38.tgz", + "integrity": "sha512-L2BmEeFZATAvU+FJzJiRLFUP+d9RHN+QXpgaOrs2klshoAm1AE6Us4X6fS9k33Uy5SzScn2TpcgecbqJza1Hjw==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.38.tgz", + "integrity": "sha512-Khy4wVmebnzue8aeSXLC+6clo/hRYeNIm0DyikoEqX+3w3rcvrhzpoix0S+MF9vzh6JFskkIGD7Zx47ODJNyCw==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.14.38", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.38.tgz", + "integrity": "sha512-k3FGCNmHBkqdJXuJszdWciAH77PukEyDsdIryEHn9cKLQFxzhT39dSumeTuggaQcXY57UlmLGIkklWZo2qzHpw==", + "dev": true, + "optional": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", + "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.2.2", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.6.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + } + }, + "eslint-plugin-vue": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz", + "integrity": "sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==", + "dev": true, + "requires": { + "eslint-utils": "^3.0.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.0.1", + "postcss-selector-parser": "^6.0.9", + "semver": "^7.3.5", + "vue-eslint-parser": "^8.0.1" + } + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "follow-redirects": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.0.tgz", + "integrity": "sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", + "integrity": "sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "pinia": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.14.tgz", + "integrity": "sha512-0nPuZR4TetT/WcLN+feMSjWJku3SQU7dBbXC6uw+R6FLQJCsg+/0pzXyD82T1FmAYe0lsx+jnEDQ1BLgkRKlxA==", + "requires": { + "@vue/devtools-api": "^6.1.4", + "vue-demi": "*" + }, + "dependencies": { + "vue-demi": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.12.5.tgz", + "integrity": "sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==", + "requires": {} + } + } + }, + "postcss": { + "version": "8.4.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz", + "integrity": "sha512-jtL6eTBrza5MPzy8oJLFuUscHDXTV5KcLlqAWHl5q5WYRfnNRGSmOZmOZ1T6Gy7A99mOZfqungmZMpMmCVJ8ZA==", + "requires": { + "nanoid": "^3.3.3", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "resolve": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", + "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, + "requires": { + "is-core-module": "^2.8.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rollup": { + "version": "2.72.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.72.0.tgz", + "integrity": "sha512-KqtR2YcO35/KKijg4nx4STO3569aqCUeGRkKWnJ6r+AvBBrVY9L4pmf4NHVrQr4mTOq6msbohflxr2kpihhaOA==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "three": { + "version": "0.140.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.140.0.tgz", + "integrity": "sha512-jcHjbnYspPLDdsDQChmzyAoZ5KhJbgFk6pNGlAIc9fQMvsfPGjF5H9glrngqvb2CR/qXcClMyp5PYdF996lldA==" + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "vite": { + "version": "2.9.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.8.tgz", + "integrity": "sha512-zsBGwn5UT3YS0NLSJ7hnR54+vUKfgzMUh/Z9CxF1YKEBVIe213+63jrFLmZphgGI5zXwQCSmqIdbPuE8NJywPw==", + "dev": true, + "requires": { + "esbuild": "^0.14.27", + "fsevents": "~2.3.2", + "postcss": "^8.4.13", + "resolve": "^1.22.0", + "rollup": "^2.59.0" + } + }, + "vue": { + "version": "3.2.33", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.33.tgz", + "integrity": "sha512-si1ExAlDUrLSIg/V7D/GgA4twJwfsfgG+t9w10z38HhL/HA07132pUQ2KuwAo8qbCyMJ9e6OqrmWrOCr+jW7ZQ==", + "requires": { + "@vue/compiler-dom": "3.2.33", + "@vue/compiler-sfc": "3.2.33", + "@vue/runtime-dom": "3.2.33", + "@vue/server-renderer": "3.2.33", + "@vue/shared": "3.2.33" + } + }, + "vue-eslint-parser": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", + "dev": true, + "requires": { + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.5" + } + }, + "vue-router": { + "version": "4.0.15", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.15.tgz", + "integrity": "sha512-xa+pIN9ZqORdIW1MkN2+d9Ui2pCM1b/UMgwYUCZOiFYHAvz/slKKBDha8DLrh5aCG/RibtrpyhKjKOZ85tYyWg==", + "requires": { + "@vue/devtools-api": "^6.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } +} diff --git a/railsignal-app/package.json b/railsignal-app/package.json new file mode 100644 index 0000000..0e3ecc6 --- /dev/null +++ b/railsignal-app/package.json @@ -0,0 +1,23 @@ +{ + "name": "railsignal-app", + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview --port 5050", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore" + }, + "dependencies": { + "axios": "^0.27.2", + "pinia": "^2.0.14", + "three": "^0.140.0", + "vue": "^3.2.33", + "vue-router": "^4.0.14" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^2.3.1", + "eslint": "^8.5.0", + "eslint-plugin-vue": "^8.2.0", + "vite": "^2.9.5" + } +} diff --git a/railsignal-app/public/favicon.ico b/railsignal-app/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..df36fcfb72584e00488330b560ebcf34a41c64c2 GIT binary patch literal 4286 zcmds*O-Phc6o&64GDVCEQHxsW(p4>LW*W<827=Unuo8sGpRux(DN@jWP-e29Wl%wj zY84_aq9}^Am9-cWTD5GGEo#+5Fi2wX_P*bo+xO!)p*7B;iKlbFd(U~_d(U?#hLj56 zPhFkj-|A6~Qk#@g^#D^U0XT1cu=c-vu1+SElX9NR;kzAUV(q0|dl0|%h|dI$%VICy zJnu2^L*Te9JrJMGh%-P79CL0}dq92RGU6gI{v2~|)p}sG5x0U*z<8U;Ij*hB9z?ei z@g6Xq-pDoPl=MANPiR7%172VA%r)kevtV-_5H*QJKFmd;8yA$98zCxBZYXTNZ#QFk2(TX0;Y2dt&WitL#$96|gJY=3xX zpCoi|YNzgO3R`f@IiEeSmKrPSf#h#Qd<$%Ej^RIeeYfsxhPMOG`S`Pz8q``=511zm zAm)MX5AV^5xIWPyEu7u>qYs?pn$I4nL9J!=K=SGlKLXpE<5x+2cDTXq?brj?n6sp= zphe9;_JHf40^9~}9i08r{XM$7HB!`{Ys~TK0kx<}ZQng`UPvH*11|q7&l9?@FQz;8 zx!=3<4seY*%=OlbCbcae?5^V_}*K>Uo6ZWV8mTyE^B=DKy7-sdLYkR5Z?paTgK-zyIkKjIcpyO z{+uIt&YSa_$QnN_@t~L014dyK(fOOo+W*MIxbA6Ndgr=Y!f#Tokqv}n<7-9qfHkc3 z=>a|HWqcX8fzQCT=dqVbogRq!-S>H%yA{1w#2Pn;=e>JiEj7Hl;zdt-2f+j2%DeVD zsW0Ab)ZK@0cIW%W7z}H{&~yGhn~D;aiP4=;m-HCo`BEI+Kd6 z={Xwx{TKxD#iCLfl2vQGDitKtN>z|-AdCN|$jTFDg0m3O`WLD4_s#$S literal 0 HcmV?d00001 diff --git a/railsignal-app/src/App.vue b/railsignal-app/src/App.vue new file mode 100644 index 0000000..10a2fad --- /dev/null +++ b/railsignal-app/src/App.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/railsignal-app/src/components/RailSystem.vue b/railsignal-app/src/components/RailSystem.vue new file mode 100644 index 0000000..a7d1c7b --- /dev/null +++ b/railsignal-app/src/components/RailSystem.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/railsignal-app/src/components/RailSystemsManager.vue b/railsignal-app/src/components/RailSystemsManager.vue new file mode 100644 index 0000000..568c206 --- /dev/null +++ b/railsignal-app/src/components/RailSystemsManager.vue @@ -0,0 +1,56 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/AddSegment.vue b/railsignal-app/src/components/railsystem/AddSegment.vue new file mode 100644 index 0000000..9e4f637 --- /dev/null +++ b/railsignal-app/src/components/railsystem/AddSegment.vue @@ -0,0 +1,24 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/Component.vue b/railsignal-app/src/components/railsystem/Component.vue new file mode 100644 index 0000000..0a4f80e --- /dev/null +++ b/railsignal-app/src/components/railsystem/Component.vue @@ -0,0 +1,23 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/MapView.vue b/railsignal-app/src/components/railsystem/MapView.vue new file mode 100644 index 0000000..c0c1d0f --- /dev/null +++ b/railsignal-app/src/components/railsystem/MapView.vue @@ -0,0 +1,26 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/main.js b/railsignal-app/src/main.js new file mode 100644 index 0000000..f00dcd1 --- /dev/null +++ b/railsignal-app/src/main.js @@ -0,0 +1,9 @@ +import { createApp } from 'vue'; +import { createPinia } from 'pinia'; +import App from './App.vue' + +const app = createApp(App) +const pinia = createPinia(); +app.use(pinia); + +app.mount('#app') diff --git a/railsignal-app/src/stores/railSystemsStore.js b/railsignal-app/src/stores/railSystemsStore.js new file mode 100644 index 0000000..d9747b1 --- /dev/null +++ b/railsignal-app/src/stores/railSystemsStore.js @@ -0,0 +1,48 @@ +import { defineStore } from "pinia"; +import axios from "axios"; + +export const useRailSystemsStore = defineStore('RailSystemsStore', { + state: () => ({ + railSystems: [], + selectedRailSystem: null, + selectedComponent: null + }), + actions: { + refreshRailSystems() { + return new Promise((resolve, reject) => { + axios.get(import.meta.env.VITE_API_URL + "/rs") + .then(response => { + this.railSystems = response.data; + resolve(); + }) + .catch(error => { + reject(error); + }); + }); + }, + createRailSystem(name) { + return new Promise((resolve, reject) => { + axios.post( + import.meta.env.VITE_API_URL + "/rs", + {name: name} + ) + .then(() => { + this.refreshRailSystems() + .then(() => resolve()) + .catch(error => reject(error)); + }) + .catch(error => reject(error)); + }); + }, + removeRailSystem(rs) { + return new Promise((resolve, reject) => { + axios.delete(import.meta.env.VITE_API_URL + "/rs/" + rs.id) + .then(() => { + this.refreshRailSystems() + .then(() => resolve) + .catch(error => reject(error)); + }) + }) + } + } +}); \ No newline at end of file diff --git a/railsignal-app/vite.config.js b/railsignal-app/vite.config.js new file mode 100644 index 0000000..116273f --- /dev/null +++ b/railsignal-app/vite.config.js @@ -0,0 +1,14 @@ +import { fileURLToPath, URL } from 'url' + +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + } + } +}) diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/ComponentRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/ComponentRepository.java index 3e5e0c9..862c122 100644 --- a/src/main/java/nl/andrewl/railsignalapi/dao/ComponentRepository.java +++ b/src/main/java/nl/andrewl/railsignalapi/dao/ComponentRepository.java @@ -2,10 +2,27 @@ package nl.andrewl.railsignalapi.dao; import nl.andrewl.railsignalapi.model.RailSystem; import nl.andrewl.railsignalapi.model.component.Component; +import nl.andrewl.railsignalapi.model.component.Position; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + @Repository -public interface ComponentRepository extends JpaRepository { +public interface ComponentRepository extends JpaRepository { + Optional findByIdAndRailSystemId(long id, long rsId); + + boolean existsByNameAndRailSystem(String name, RailSystem rs); + + @Query("SELECT c FROM Component c " + + "WHERE c.railSystem = :rs AND " + + "c.position.x >= :#{#lower.x} AND c.position.y >= :#{#lower.y} AND c.position.z >= :#{#lower.z} AND " + + "c.position.x <= :#{#upper.x} AND c.position.y <= :#{#upper.y} AND c.position.z <= :#{#upper.z}") + List findAllInBounds(RailSystem rs, Position lower, Position upper); + + List findAllByRailSystem(RailSystem rs); + void deleteAllByRailSystem(RailSystem rs); } diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/SegmentRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/SegmentRepository.java index 3cde24a..ebba876 100644 --- a/src/main/java/nl/andrewl/railsignalapi/dao/SegmentRepository.java +++ b/src/main/java/nl/andrewl/railsignalapi/dao/SegmentRepository.java @@ -5,7 +5,16 @@ import nl.andrewl.railsignalapi.model.Segment; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + @Repository public interface SegmentRepository extends JpaRepository { + boolean existsByNameAndRailSystem(String name, RailSystem rs); + + List findAllByRailSystemId(long rsId); + + Optional findByIdAndRailSystemId(long id, long rsId); + void deleteAllByRailSystem(RailSystem rs); } diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/SignalRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/SignalRepository.java new file mode 100644 index 0000000..0bea913 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/dao/SignalRepository.java @@ -0,0 +1,8 @@ +package nl.andrewl.railsignalapi.dao; + +import nl.andrewl.railsignalapi.model.component.Signal; +import org.springframework.stereotype.Repository; + +@Repository +public interface SignalRepository extends ComponentRepository { +} diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/SwitchConfigurationRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/SwitchConfigurationRepository.java new file mode 100644 index 0000000..5c5e52f --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/dao/SwitchConfigurationRepository.java @@ -0,0 +1,11 @@ +package nl.andrewl.railsignalapi.dao; + +import nl.andrewl.railsignalapi.model.component.PathNode; +import nl.andrewl.railsignalapi.model.component.SwitchConfiguration; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface SwitchConfigurationRepository extends JpaRepository { + void deleteAllByNodesContaining(PathNode p); +} diff --git a/src/main/java/nl/andrewl/railsignalapi/dao/SwitchRepository.java b/src/main/java/nl/andrewl/railsignalapi/dao/SwitchRepository.java new file mode 100644 index 0000000..2738d8d --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/dao/SwitchRepository.java @@ -0,0 +1,8 @@ +package nl.andrewl.railsignalapi.dao; + +import nl.andrewl.railsignalapi.model.component.Switch; +import org.springframework.stereotype.Repository; + +@Repository +public interface SwitchRepository extends ComponentRepository { +} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/Segment.java b/src/main/java/nl/andrewl/railsignalapi/model/Segment.java index efe0b8f..f05b9cf 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/Segment.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/Segment.java @@ -7,6 +7,7 @@ import nl.andrewl.railsignalapi.model.component.SegmentBoundaryNode; import nl.andrewl.railsignalapi.model.component.Signal; import javax.persistence.*; +import java.util.HashSet; import java.util.Set; /** @@ -27,7 +28,7 @@ public class Segment { /** * A unique name for this segment. */ - @Column(unique = true) + @Column private String name; /** @@ -39,9 +40,16 @@ public class Segment { /** * The set of nodes from which trains can enter and exit this segment. */ - @ManyToMany(mappedBy = "segments", cascade = CascadeType.ALL) + @ManyToMany(mappedBy = "segments") private Set boundaryNodes; + public Segment(RailSystem railSystem, String name) { + this.railSystem = railSystem; + this.name = name; + this.signals = new HashSet<>(); + this.boundaryNodes = new HashSet<>(); + } + @Override public boolean equals(Object o) { if (o == this) return true; diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/Component.java b/src/main/java/nl/andrewl/railsignalapi/model/component/Component.java index a0ff76c..3bbe652 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/component/Component.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/Component.java @@ -37,9 +37,15 @@ public abstract class Component { /** * A human-readable name for the component. */ - @Column(unique = true) + @Column private String name; + /** + * The type of this component. + */ + @Enumerated(EnumType.ORDINAL) + private ComponentType type; + /** * Whether this component is online, meaning that an in-world device is * currently connected to relay information regarding this component. @@ -48,10 +54,11 @@ public abstract class Component { @Setter private boolean online = false; - public Component(RailSystem railSystem, Position position, String name) { + public Component(RailSystem railSystem, Position position, String name, ComponentType type) { this.railSystem = railSystem; this.position = position; this.name = name; + this.type = type; } @Override @@ -59,4 +66,13 @@ public abstract class Component { if (this == o) return true; return o instanceof Component c && this.id != null && this.id.equals(c.id); } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(id); + if (name != null) sb.append('[').append(name).append(']'); + sb.append(String.format("@[x=%.1f,y=%.1f,z=%.1f]", position.getX(), position.getY(), position.getZ())); + return sb.toString(); + } } diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/ComponentType.java b/src/main/java/nl/andrewl/railsignalapi/model/component/ComponentType.java new file mode 100644 index 0000000..af9baeb --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/ComponentType.java @@ -0,0 +1,7 @@ +package nl.andrewl.railsignalapi.model.component; + +public enum ComponentType { + SIGNAL, + SWITCH, + SEGMENT_BOUNDARY +} diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/PathNode.java b/src/main/java/nl/andrewl/railsignalapi/model/component/PathNode.java index 6d82422..975e9bf 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/component/PathNode.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/PathNode.java @@ -19,15 +19,15 @@ import java.util.Set; @Inheritance(strategy = InheritanceType.JOINED) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter -public class PathNode extends Component { +public abstract class PathNode extends Component { /** * The set of nodes that this one is connected to. */ @ManyToMany private Set connectedNodes; - public PathNode(RailSystem railSystem, Position position, String name, Set connectedNodes) { - super(railSystem, position, name); + public PathNode(RailSystem railSystem, Position position, String name, ComponentType type, Set connectedNodes) { + super(railSystem, position, name, type); this.connectedNodes = connectedNodes; } } diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/SegmentBoundaryNode.java b/src/main/java/nl/andrewl/railsignalapi/model/component/SegmentBoundaryNode.java index 0b9923d..969e727 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/component/SegmentBoundaryNode.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/SegmentBoundaryNode.java @@ -26,7 +26,7 @@ public class SegmentBoundaryNode extends PathNode { private Set segments; public SegmentBoundaryNode(RailSystem railSystem, Position position, String name, Set connectedNodes, Set segments) { - super(railSystem, position, name, connectedNodes); + super(railSystem, position, name, ComponentType.SEGMENT_BOUNDARY, connectedNodes); this.segments = segments; } } diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/Signal.java b/src/main/java/nl/andrewl/railsignalapi/model/component/Signal.java index 591542d..2e1446f 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/component/Signal.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/Signal.java @@ -1,12 +1,14 @@ package nl.andrewl.railsignalapi.model.component; import lombok.AccessLevel; +import lombok.Getter; import lombok.NoArgsConstructor; -import nl.andrewl.railsignalapi.model.Direction; import nl.andrewl.railsignalapi.model.RailSystem; import nl.andrewl.railsignalapi.model.Segment; -import javax.persistence.*; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ManyToOne; /** * A signal is a component that relays the status of a connected segment to @@ -15,29 +17,16 @@ import javax.persistence.*; */ @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter public class Signal extends Component { - /** - * The direction this signal is facing. This is the direction in which the - * signal connects to a branch. - *
-	 *     |-segment A-|-segment B-|
-	 *     ===================== <- Rail
-	 *                ]+  --->      Signal is facing East, and shows status on
-	 *                              its western side. It is connected to segment B.
-	 * 
- */ - @Enumerated(EnumType.STRING) - private Direction direction; - /** * The segment that this signal connects to. */ @ManyToOne(optional = false, fetch = FetchType.LAZY) private Segment segment; - public Signal(RailSystem railSystem, Position position, String name, Segment segment, Direction direction) { - super(railSystem, position, name); + public Signal(RailSystem railSystem, Position position, String name, Segment segment) { + super(railSystem, position, name, ComponentType.SIGNAL); this.segment = segment; - this.direction = direction; } } diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/Switch.java b/src/main/java/nl/andrewl/railsignalapi/model/component/Switch.java index afa8c80..ce3b1e9 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/component/Switch.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/Switch.java @@ -3,6 +3,8 @@ package nl.andrewl.railsignalapi.model.component; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; +import nl.andrewl.railsignalapi.model.RailSystem; import javax.persistence.*; import java.util.Set; @@ -22,8 +24,16 @@ public class Switch extends PathNode { private Set possibleConfigurations; /** - * The switch configuration that this switch is currently in. + * The switch configuration that this switch is currently in. If null, then + * we don't know what configuration the switch is in. */ - @OneToOne(optional = false, fetch = FetchType.LAZY) + @OneToOne(fetch = FetchType.LAZY) + @Setter private SwitchConfiguration activeConfiguration; + + public Switch(RailSystem railSystem, Position position, String name, Set connectedNodes, Set possibleConfigurations, SwitchConfiguration activeConfiguration) { + super(railSystem, position, name, ComponentType.SWITCH, connectedNodes); + this.possibleConfigurations = possibleConfigurations; + this.activeConfiguration = activeConfiguration; + } } diff --git a/src/main/java/nl/andrewl/railsignalapi/model/component/SwitchConfiguration.java b/src/main/java/nl/andrewl/railsignalapi/model/component/SwitchConfiguration.java index 261f81c..b6a4f23 100644 --- a/src/main/java/nl/andrewl/railsignalapi/model/component/SwitchConfiguration.java +++ b/src/main/java/nl/andrewl/railsignalapi/model/component/SwitchConfiguration.java @@ -1,5 +1,7 @@ package nl.andrewl.railsignalapi.model.component; +import lombok.AccessLevel; +import lombok.Getter; import lombok.NoArgsConstructor; import javax.persistence.*; @@ -10,7 +12,8 @@ import java.util.Set; * as an active configuration in the linked switch component. */ @Entity -@NoArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter public class SwitchConfiguration { @Id @GeneratedValue @@ -28,4 +31,17 @@ public class SwitchConfiguration { */ @ManyToMany(fetch = FetchType.EAGER) private Set nodes; + + public SwitchConfiguration(Switch switchComponent, Set nodes) { + this.switchComponent = switchComponent; + this.nodes = nodes; + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + return o instanceof SwitchConfiguration sc && + sc.switchComponent.equals(this.switchComponent) && + sc.nodes.equals(this.nodes); + } } diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/ComponentsApiController.java b/src/main/java/nl/andrewl/railsignalapi/rest/ComponentsApiController.java new file mode 100644 index 0000000..1a83b3c --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/ComponentsApiController.java @@ -0,0 +1,39 @@ +package nl.andrewl.railsignalapi.rest; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.RequiredArgsConstructor; +import nl.andrewl.railsignalapi.rest.dto.component.ComponentResponse; +import nl.andrewl.railsignalapi.rest.dto.component.SimpleComponentResponse; +import nl.andrewl.railsignalapi.service.ComponentService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping(path = "/api/rs/{rsId}/c") +@RequiredArgsConstructor +public class ComponentsApiController { + private final ComponentService componentService; + + @GetMapping + public List getAllComponents(@PathVariable long rsId) { + return componentService.getComponents(rsId); + } + + @GetMapping(path = "/{cId}") + public ComponentResponse getComponent(@PathVariable long rsId, @PathVariable long cId) { + return componentService.getComponent(rsId, cId); + } + + @PostMapping + public ComponentResponse createComponent(@PathVariable long rsId, @RequestBody ObjectNode data) { + return componentService.create(rsId, data); + } + + @DeleteMapping(path = "/{cId}") + public ResponseEntity removeComponent(@PathVariable long rsId, @PathVariable long cId) { + componentService.removeComponent(rsId, cId); + return ResponseEntity.noContent().build(); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/SegmentPayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/SegmentPayload.java new file mode 100644 index 0000000..9bda7f6 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/SegmentPayload.java @@ -0,0 +1,4 @@ +package nl.andrewl.railsignalapi.rest; + +public record SegmentPayload(String name) { +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/SegmentsApiController.java b/src/main/java/nl/andrewl/railsignalapi/rest/SegmentsApiController.java new file mode 100644 index 0000000..80418a1 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/SegmentsApiController.java @@ -0,0 +1,31 @@ +package nl.andrewl.railsignalapi.rest; + +import lombok.RequiredArgsConstructor; +import nl.andrewl.railsignalapi.rest.dto.FullSegmentResponse; +import nl.andrewl.railsignalapi.rest.dto.SegmentResponse; +import nl.andrewl.railsignalapi.service.SegmentService; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping(path = "/api/rs/{rsId}/s") +@RequiredArgsConstructor +public class SegmentsApiController { + private final SegmentService segmentService; + + @GetMapping + public List getSegments(@PathVariable long rsId) { + return segmentService.getSegments(rsId); + } + + @GetMapping(path = "/{sId}") + public FullSegmentResponse getSegment(@PathVariable long rsId, @PathVariable long sId) { + return segmentService.getSegment(rsId, sId); + } + + @PostMapping + public FullSegmentResponse createSegment(@PathVariable long rsId, @RequestBody SegmentPayload payload) { + return segmentService.create(rsId, payload); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/WebConfig.java b/src/main/java/nl/andrewl/railsignalapi/rest/WebConfig.java index 1be14a6..22a3e05 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/WebConfig.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/WebConfig.java @@ -1,6 +1,7 @@ package nl.andrewl.railsignalapi.rest; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -13,4 +14,13 @@ public class WebConfig implements WebMvcConfigurer { registry.addResourceHandler("/static/**") .addResourceLocations("classpath:/static/"); } + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowedMethods("*"); + } + + } diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/FullSegmentResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/FullSegmentResponse.java new file mode 100644 index 0000000..ae8cbde --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/FullSegmentResponse.java @@ -0,0 +1,18 @@ +package nl.andrewl.railsignalapi.rest.dto; + +import nl.andrewl.railsignalapi.model.Segment; +import nl.andrewl.railsignalapi.rest.dto.component.SegmentBoundaryNodeResponse; +import nl.andrewl.railsignalapi.rest.dto.component.SignalResponse; + +import java.util.List; + +public class FullSegmentResponse extends SegmentResponse { + public List signals; + public List boundaryNodes; + + public FullSegmentResponse(Segment s) { + super(s); + this.signals = s.getSignals().stream().map(SignalResponse::new).toList(); + this.boundaryNodes = s.getBoundaryNodes().stream().map(SegmentBoundaryNodeResponse::new).toList(); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/SegmentResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/SegmentResponse.java new file mode 100644 index 0000000..617774b --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/SegmentResponse.java @@ -0,0 +1,17 @@ +package nl.andrewl.railsignalapi.rest.dto; + +import nl.andrewl.railsignalapi.model.Segment; + +public class SegmentResponse { + public long id; + public String name; + + public SegmentResponse(long id, String name) { + this.id = id; + this.name = name; + } + + public SegmentResponse(Segment s) { + this(s.getId(), s.getName()); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/ComponentResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/ComponentResponse.java new file mode 100644 index 0000000..7fd74f7 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/ComponentResponse.java @@ -0,0 +1,27 @@ +package nl.andrewl.railsignalapi.rest.dto.component; + +import nl.andrewl.railsignalapi.model.component.*; + +public abstract class ComponentResponse { + public long id; + public Position position; + public String name; + public String type; + public boolean online; + + public ComponentResponse(Component c) { + this.id = c.getId(); + this.position = c.getPosition(); + this.name = c.getName(); + this.type = c.getType().name(); + this.online = c.isOnline(); + } + + public static ComponentResponse of(Component c) { + return switch (c.getType()) { + case SIGNAL -> new SignalResponse((Signal) c); + case SWITCH -> new SwitchResponse((Switch) c); + case SEGMENT_BOUNDARY -> new SegmentBoundaryNodeResponse((SegmentBoundaryNode) c); + }; + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/PathNodeResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/PathNodeResponse.java new file mode 100644 index 0000000..1c83196 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/PathNodeResponse.java @@ -0,0 +1,14 @@ +package nl.andrewl.railsignalapi.rest.dto.component; + +import nl.andrewl.railsignalapi.model.component.PathNode; + +import java.util.List; + +public abstract class PathNodeResponse extends ComponentResponse { + public List connectedNodes; + + public PathNodeResponse(PathNode p) { + super(p); + this.connectedNodes = p.getConnectedNodes().stream().map(SimpleComponentResponse::new).toList(); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SegmentBoundaryNodeResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SegmentBoundaryNodeResponse.java new file mode 100644 index 0000000..ba09712 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SegmentBoundaryNodeResponse.java @@ -0,0 +1,15 @@ +package nl.andrewl.railsignalapi.rest.dto.component; + +import nl.andrewl.railsignalapi.model.component.SegmentBoundaryNode; +import nl.andrewl.railsignalapi.rest.dto.SegmentResponse; + +import java.util.List; + +public class SegmentBoundaryNodeResponse extends PathNodeResponse { + public List segments; + + public SegmentBoundaryNodeResponse(SegmentBoundaryNode n) { + super(n); + this.segments = n.getSegments().stream().map(SegmentResponse::new).toList(); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SignalResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SignalResponse.java new file mode 100644 index 0000000..56dd8d5 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SignalResponse.java @@ -0,0 +1,12 @@ +package nl.andrewl.railsignalapi.rest.dto.component; + +import nl.andrewl.railsignalapi.model.component.Signal; +import nl.andrewl.railsignalapi.rest.dto.SegmentResponse; + +public class SignalResponse extends ComponentResponse { + public SegmentResponse segment; + public SignalResponse(Signal s) { + super(s); + this.segment = new SegmentResponse(s.getSegment()); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SimpleComponentResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SimpleComponentResponse.java new file mode 100644 index 0000000..6b6a9da --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SimpleComponentResponse.java @@ -0,0 +1,22 @@ +package nl.andrewl.railsignalapi.rest.dto.component; + +import nl.andrewl.railsignalapi.model.component.Component; +import nl.andrewl.railsignalapi.model.component.Position; + +public record SimpleComponentResponse ( + long id, + Position position, + String name, + String type, + boolean online +) { + public SimpleComponentResponse(Component c) { + this( + c.getId(), + c.getPosition(), + c.getName(), + c.getType().name(), + c.isOnline() + ); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchConfigurationResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchConfigurationResponse.java new file mode 100644 index 0000000..f65a5b6 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchConfigurationResponse.java @@ -0,0 +1,17 @@ +package nl.andrewl.railsignalapi.rest.dto.component; + +import nl.andrewl.railsignalapi.model.component.SwitchConfiguration; + +import java.util.List; + +public record SwitchConfigurationResponse ( + long id, + List nodes +) { + public SwitchConfigurationResponse(SwitchConfiguration sc) { + this( + sc.getId(), + sc.getNodes().stream().map(SimpleComponentResponse::new).toList() + ); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchResponse.java new file mode 100644 index 0000000..b854bc0 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchResponse.java @@ -0,0 +1,16 @@ +package nl.andrewl.railsignalapi.rest.dto.component; + +import nl.andrewl.railsignalapi.model.component.Switch; + +import java.util.List; + +public class SwitchResponse extends PathNodeResponse { + public List possibleConfigurations; + public SwitchConfigurationResponse activeConfiguration; + + public SwitchResponse(Switch s) { + super(s); + this.possibleConfigurations = s.getPossibleConfigurations().stream().map(SwitchConfigurationResponse::new).toList(); + this.activeConfiguration = new SwitchConfigurationResponse(s.getActiveConfiguration()); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/service/BranchService.java b/src/main/java/nl/andrewl/railsignalapi/service/BranchService.java deleted file mode 100644 index 3a3dc08..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/service/BranchService.java +++ /dev/null @@ -1,57 +0,0 @@ -package nl.andrewl.railsignalapi.service; - -import lombok.RequiredArgsConstructor; -import nl.andrewl.railsignalapi.dao.BranchRepository; -import nl.andrewl.railsignalapi.dao.RailSystemRepository; -import nl.andrewl.railsignalapi.dao.SignalRepository; -import nl.andrewl.railsignalapi.rest.dto.BranchResponse; -import nl.andrewl.railsignalapi.rest.dto.SignalResponse; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.server.ResponseStatusException; - -import java.util.List; - -@Service -@RequiredArgsConstructor -public class BranchService { - private final BranchRepository branchRepository; - private final RailSystemRepository railSystemRepository; - private final SignalRepository signalRepository; - - @Transactional - public void deleteBranch(long rsId, long branchId) { - var branch = branchRepository.findByIdAndRailSystemId(branchId, rsId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - if (!branch.getSignalConnections().isEmpty()) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Branch should not be connected to any signals."); - } - branchRepository.delete(branch); - } - - @Transactional(readOnly = true) - public List getAllBranches(long rsId) { - var rs = railSystemRepository.findById(rsId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - return branchRepository.findAllByRailSystemOrderByName(rs).stream() - .map(BranchResponse::new) - .toList(); - } - - @Transactional(readOnly = true) - public BranchResponse getBranch(long rsId, long branchId) { - var branch = branchRepository.findByIdAndRailSystemId(branchId, rsId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - return new BranchResponse(branch); - } - - @Transactional(readOnly = true) - public List getConnectedSignals(long rsId, long branchId) { - var branch = branchRepository.findByIdAndRailSystemId(branchId, rsId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - return signalRepository.findAllConnectedToBranch(branch).stream() - .map(SignalResponse::new) - .toList(); - } -} diff --git a/src/main/java/nl/andrewl/railsignalapi/service/ComponentService.java b/src/main/java/nl/andrewl/railsignalapi/service/ComponentService.java new file mode 100644 index 0000000..c39803c --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/service/ComponentService.java @@ -0,0 +1,129 @@ +package nl.andrewl.railsignalapi.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.RequiredArgsConstructor; +import nl.andrewl.railsignalapi.dao.ComponentRepository; +import nl.andrewl.railsignalapi.dao.RailSystemRepository; +import nl.andrewl.railsignalapi.dao.SegmentRepository; +import nl.andrewl.railsignalapi.dao.SwitchConfigurationRepository; +import nl.andrewl.railsignalapi.model.RailSystem; +import nl.andrewl.railsignalapi.model.Segment; +import nl.andrewl.railsignalapi.model.component.*; +import nl.andrewl.railsignalapi.rest.dto.component.ComponentResponse; +import nl.andrewl.railsignalapi.rest.dto.component.SimpleComponentResponse; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Service +@RequiredArgsConstructor +public class ComponentService { + private final ComponentRepository componentRepository; + private final RailSystemRepository railSystemRepository; + private final SegmentRepository segmentRepository; + private final SwitchConfigurationRepository switchConfigurationRepository; + + @Transactional(readOnly = true) + public List getComponents(long rsId) { + var rs = railSystemRepository.findById(rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + return componentRepository.findAllByRailSystem(rs).stream().map(SimpleComponentResponse::new).toList(); + } + + @Transactional(readOnly = true) + public ComponentResponse getComponent(long rsId, long componentId) { + var c = componentRepository.findByIdAndRailSystemId(componentId, rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + return ComponentResponse.of(c); + } + + @Transactional + public void removeComponent(long rsId, long componentId) { + var c = componentRepository.findByIdAndRailSystemId(componentId, rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + // If this is a path node, check for and remove any switch configurations that use it. + if (c instanceof PathNode p) { + switchConfigurationRepository.deleteAllByNodesContaining(p); + } + componentRepository.delete(c); + } + + @Transactional + public ComponentResponse create(long rsId, ObjectNode data) { + RailSystem rs = railSystemRepository.findById(rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + String type = data.get("type").asText(); + Position pos = new Position(); + pos.setX(data.get("position").get("x").asDouble()); + pos.setY(data.get("position").get("y").asDouble()); + pos.setZ(data.get("position").get("z").asDouble()); + String name = data.get("name").asText(); + + if (name != null && componentRepository.existsByNameAndRailSystem(name, rs)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Component with that name already exists."); + } + + Component c = switch (type) { + case "SIGNAL" -> createSignal(rs, pos, name, data); + case "SWITCH" -> createSwitch(rs, pos, name, data); + case "SEGMENT_BOUNDARY" -> createSegmentBoundary(rs, pos, name, data); + default -> throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Unsupported component type: " + type); + }; + c = componentRepository.save(c); + return ComponentResponse.of(c); + } + + private Component createSignal(RailSystem rs, Position pos, String name, ObjectNode data) { + long segmentId = data.get("segment").get("id").asLong(); + Segment segment = segmentRepository.findById(segmentId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + return new Signal(rs, pos, name, segment); + } + + private Component createSwitch(RailSystem rs, Position pos, String name, ObjectNode data) { + Switch s = new Switch(rs, pos, name, new HashSet<>(), new HashSet<>(), null); + for (JsonNode configJson : data.withArray("possibleConfigurations")) { + Set pathNodes = new HashSet<>(); + for (JsonNode pathNodeJson : configJson.withArray("nodes")) { + long pathNodeId = pathNodeJson.get("id").asLong(); + Component c = componentRepository.findById(pathNodeId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + if (c instanceof PathNode pathNode) { + pathNodes.add(pathNode); + s.getConnectedNodes().add(pathNode); + } else { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Id " + pathNodeId + " does not refer to a PathNode component."); + } + } + s.getPossibleConfigurations().add(new SwitchConfiguration(s, pathNodes)); + } + return s; + } + + private Component createSegmentBoundary(RailSystem rs, Position pos, String name, ObjectNode data) { + ArrayNode segmentsNode = data.withArray("segments"); + Set segments = new HashSet<>(); + for (JsonNode segmentNode : segmentsNode) { + long segmentId = segmentNode.get("id").asLong(); + Segment segment = segmentRepository.findById(segmentId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + segments.add(segment); + } + if (segments.size() < 1 || segments.size() > 2) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid number of segments."); + } + return new SegmentBoundaryNode(rs, pos, name, new HashSet<>(), segments); + } + + @Transactional + public void remove(long rsId, long componentId) { + + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/service/RailSystemService.java b/src/main/java/nl/andrewl/railsignalapi/service/RailSystemService.java index 2e9e1f1..b5d7267 100644 --- a/src/main/java/nl/andrewl/railsignalapi/service/RailSystemService.java +++ b/src/main/java/nl/andrewl/railsignalapi/service/RailSystemService.java @@ -20,7 +20,7 @@ import java.util.List; public class RailSystemService { private final RailSystemRepository railSystemRepository; private final SegmentRepository segmentRepository; - private final ComponentRepository componentRepository; + private final ComponentRepository componentRepository; @Transactional public List getRailSystems() { @@ -29,10 +29,13 @@ public class RailSystemService { @Transactional public RailSystemResponse createRailSystem(RailSystemCreationPayload payload) { - if (railSystemRepository.existsByName(payload.name())) { + if (payload.name() == null) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Missing required name."); + if (payload.name().isBlank()) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Name cannot be blank."); + String name = payload.name().trim(); + if (railSystemRepository.existsByName(name)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "A rail system with that name already exists."); } - RailSystem rs = new RailSystem(payload.name()); + RailSystem rs = new RailSystem(name); return new RailSystemResponse(railSystemRepository.save(rs)); } diff --git a/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java b/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java new file mode 100644 index 0000000..3b7dfe7 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java @@ -0,0 +1,57 @@ +package nl.andrewl.railsignalapi.service; + +import lombok.RequiredArgsConstructor; +import nl.andrewl.railsignalapi.dao.ComponentRepository; +import nl.andrewl.railsignalapi.dao.RailSystemRepository; +import nl.andrewl.railsignalapi.dao.SegmentRepository; +import nl.andrewl.railsignalapi.model.Segment; +import nl.andrewl.railsignalapi.model.component.Component; +import nl.andrewl.railsignalapi.rest.SegmentPayload; +import nl.andrewl.railsignalapi.rest.dto.FullSegmentResponse; +import nl.andrewl.railsignalapi.rest.dto.SegmentResponse; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.server.ResponseStatusException; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class SegmentService { + private final SegmentRepository segmentRepository; + private final RailSystemRepository railSystemRepository; + private final ComponentRepository componentRepository; + + @Transactional(readOnly = true) + public List getSegments(long rsId) { + return segmentRepository.findAllByRailSystemId(rsId).stream().map(SegmentResponse::new).toList(); + } + + @Transactional(readOnly = true) + public FullSegmentResponse getSegment(long rsId, long segmentId) { + var segment = segmentRepository.findByIdAndRailSystemId(segmentId, rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + return new FullSegmentResponse(segment); + } + + @Transactional + public FullSegmentResponse create(long rsId, SegmentPayload payload) { + var rs = railSystemRepository.findById(rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + String name = payload.name(); + if (segmentRepository.existsByNameAndRailSystem(name, rs)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Segment with that name already exists."); + } + Segment segment = segmentRepository.save(new Segment(rs, name)); + return new FullSegmentResponse(segment); + } + + @Transactional + public void remove(long rsId, long segmentId) { + var segment = segmentRepository.findByIdAndRailSystemId(segmentId, rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + componentRepository.deleteAll(segment.getSignals()); + componentRepository.deleteAll(segment.getBoundaryNodes()); + } +} diff --git a/src/main/java/nl/andrewl/railsignalapi/service/SignalService.java b/src/main/java/nl/andrewl/railsignalapi/service/SignalService.java deleted file mode 100644 index 570bae2..0000000 --- a/src/main/java/nl/andrewl/railsignalapi/service/SignalService.java +++ /dev/null @@ -1,246 +0,0 @@ -package nl.andrewl.railsignalapi.service; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import nl.andrewl.railsignalapi.dao.BranchRepository; -import nl.andrewl.railsignalapi.dao.RailSystemRepository; -import nl.andrewl.railsignalapi.dao.SignalBranchConnectionRepository; -import nl.andrewl.railsignalapi.dao.SignalRepository; -import nl.andrewl.railsignalapi.model.*; -import nl.andrewl.railsignalapi.model.component.SignalBranchConnection; -import nl.andrewl.railsignalapi.rest.dto.SignalConnectionsUpdatePayload; -import nl.andrewl.railsignalapi.rest.dto.SignalCreationPayload; -import nl.andrewl.railsignalapi.rest.dto.SignalResponse; -import nl.andrewl.railsignalapi.websocket.BranchUpdateMessage; -import nl.andrewl.railsignalapi.websocket.SignalUpdateMessage; -import nl.andrewl.railsignalapi.websocket.SignalUpdateType; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.server.ResponseStatusException; -import org.springframework.web.socket.TextMessage; -import org.springframework.web.socket.WebSocketMessage; -import org.springframework.web.socket.WebSocketSession; - -import java.io.IOException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -@Service -@RequiredArgsConstructor -@Slf4j -public class SignalService { - private final RailSystemRepository railSystemRepository; - private final SignalRepository signalRepository; - private final BranchRepository branchRepository; - private final SignalBranchConnectionRepository signalBranchConnectionRepository; - - private final ObjectMapper mapper = new ObjectMapper(); - private final Map> signalWebSocketSessions = new ConcurrentHashMap<>(); - - @Transactional - public SignalResponse createSignal(long rsId, SignalCreationPayload payload) { - var rs = railSystemRepository.findById(rsId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Rail system not found.")); - if (signalRepository.existsByNameAndRailSystem(payload.name(), rs)) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Signal " + payload.name() + " already exists."); - } - if (payload.branchConnections().size() != 2) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Exactly two branch connections must be provided."); - } - // Ensure that the directions of the connections are opposite each other. - Direction dir1 = Direction.parse(payload.branchConnections().get(0).direction()); - Direction dir2 = Direction.parse(payload.branchConnections().get(1).direction()); - if (!dir1.isOpposite(dir2)) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Branch connections must be opposite each other."); - - Set branchConnections = new HashSet<>(); - Signal signal = new Signal(rs, payload.name(), payload.position(), branchConnections); - for (var branchData : payload.branchConnections()) { - Branch branch; - if (branchData.id() != null) { - branch = this.branchRepository.findByIdAndRailSystem(branchData.id(), rs) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Branch id " + branchData.id() + " is invalid.")); - } else { - branch = this.branchRepository.findByNameAndRailSystem(branchData.name(), rs) - .orElse(new Branch(rs, branchData.name(), BranchStatus.FREE)); - } - Direction dir = Direction.parse(branchData.direction()); - branchConnections.add(new SignalBranchConnection(signal, branch, dir)); - } - signal = signalRepository.save(signal); - return new SignalResponse(signal); - } - - @Transactional - public SignalResponse updateSignalBranchConnections(long rsId, long sigId, SignalConnectionsUpdatePayload payload) { - var signal = signalRepository.findByIdAndRailSystemId(sigId, rsId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - for (var c : payload.connections()) { - var fromConnection = signalBranchConnectionRepository.findById(c.from()) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Could not find signal branch connection: " + c.from())); - if (!fromConnection.getSignal().getId().equals(signal.getId())) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Can only update signal branch connections originating from the specified signal."); - } - var toConnection = signalBranchConnectionRepository.findById(c.to()) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Could not find signal branch connection: " + c.to())); - if (!fromConnection.getBranch().getId().equals(toConnection.getBranch().getId())) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Signal branch connections can only path via a mutual branch."); - } - fromConnection.getReachableSignalConnections().add(toConnection); - signalBranchConnectionRepository.save(fromConnection); - } - for (var con : signal.getBranchConnections()) { - Set connectionsToRemove = new HashSet<>(); - for (var reachableCon : con.getReachableSignalConnections()) { - if (!payload.connections().contains(new SignalConnectionsUpdatePayload.ConnectionData(con.getId(), reachableCon.getId()))) { - connectionsToRemove.add(reachableCon); - } - } - con.getReachableSignalConnections().removeAll(connectionsToRemove); - signalBranchConnectionRepository.save(con); - } - // Reload the signal. - signal = signalRepository.findById(signal.getId()).orElseThrow(); - return new SignalResponse(signal); - } - - @Transactional - public void registerSignalWebSocketSession(Set signalIds, WebSocketSession session) { - this.signalWebSocketSessions.put(session, signalIds); - // Instantly send a data packet so that the signals are up-to-date. - RailSystem rs = null; - for (var signalId : signalIds) { - var signal = signalRepository.findById(signalId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid signal id.")); - if (rs == null) { - rs = signal.getRailSystem(); - } else if (!rs.getId().equals(signal.getRailSystem().getId())) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot open signal websocket session for signals from different rail systems."); - } - for (var branchConnection : signal.getBranchConnections()) { - try { - session.sendMessage(new TextMessage(mapper.writeValueAsString( - new BranchUpdateMessage( - branchConnection.getBranch().getId(), - branchConnection.getBranch().getStatus().name() - ) - ))); - } catch (IOException e) { - e.printStackTrace(); - } - } - signal.setOnline(true); - signalRepository.save(signal); - } - } - - @Transactional - public void deregisterSignalWebSocketSession(WebSocketSession session) { - var ids = this.signalWebSocketSessions.remove(session); - if (ids != null) { - for (var signalId : ids) { - signalRepository.findById(signalId).ifPresent(signal -> { - signal.setOnline(false); - signalRepository.save(signal); - }); - } - } - } - - public WebSocketSession getSignalWebSocketSession(long signalId) { - for (var entry : signalWebSocketSessions.entrySet()) { - if (entry.getValue().contains(signalId)) return entry.getKey(); - } - return null; - } - - @Transactional - public void handleSignalUpdate(SignalUpdateMessage updateMessage) { - var signal = signalRepository.findById(updateMessage.signalId()) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - Branch fromBranch = null; - Branch toBranch = null; - for (var con : signal.getBranchConnections()) { - if (con.getBranch().getId() == updateMessage.fromBranchId()) { - fromBranch = con.getBranch(); - } - if (con.getBranch().getId() == updateMessage.toBranchId()) { - toBranch = con.getBranch(); - } - } - if (fromBranch == null || toBranch == null) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid branches."); - } - SignalUpdateType updateType = SignalUpdateType.valueOf(updateMessage.type().trim().toUpperCase()); - if (updateType == SignalUpdateType.BEGIN && toBranch.getStatus() != BranchStatus.FREE) { - log.warn("Warning! Train is entering a non-free branch {}.", toBranch.getName()); - } - if (toBranch.getStatus() != BranchStatus.OCCUPIED) { - log.info("Updating branch {} status from {} to {}.", toBranch.getName(), toBranch.getStatus(), BranchStatus.OCCUPIED); - toBranch.setStatus(BranchStatus.OCCUPIED); - branchRepository.save(toBranch); - broadcastToConnectedSignals(toBranch); - } - if (updateType == SignalUpdateType.END) { - if (fromBranch.getStatus() != BranchStatus.FREE) { - log.info("Updating branch {} status from {} to {}.", fromBranch.getName(), fromBranch.getStatus(), BranchStatus.FREE); - fromBranch.setStatus(BranchStatus.FREE); - branchRepository.save(fromBranch); - broadcastToConnectedSignals(fromBranch); - } - } else if (updateType == SignalUpdateType.BEGIN) { - if (fromBranch.getStatus() != BranchStatus.OCCUPIED) { - log.info("Updating branch {} status from {} to {}.", fromBranch.getName(), fromBranch.getStatus(), BranchStatus.OCCUPIED); - fromBranch.setStatus(BranchStatus.OCCUPIED); - branchRepository.save(fromBranch); - broadcastToConnectedSignals(fromBranch); - } - } - } - - private void broadcastToConnectedSignals(Branch branch) { - try { - WebSocketMessage msg = new TextMessage(mapper.writeValueAsString( - new BranchUpdateMessage(branch.getId(), branch.getStatus().name()) - )); - signalRepository.findAllConnectedToBranch(branch).stream() - .map(s -> getSignalWebSocketSession(s.getId())) - .filter(Objects::nonNull).distinct() - .forEach(session -> { - try { - session.sendMessage(msg); - } catch (IOException e) { - e.printStackTrace(); - } - }); - } catch (JsonProcessingException e) { - e.printStackTrace(); - } - - } - - @Transactional(readOnly = true) - public SignalResponse getSignal(long rsId, long sigId) { - var s = signalRepository.findByIdAndRailSystemId(sigId, rsId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - return new SignalResponse(s); - } - - @Transactional(readOnly = true) - public List getAllSignals(long rsId) { - var rs = railSystemRepository.findById(rsId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Rail system not found.")); - return signalRepository.findAllByRailSystemOrderByName(rs).stream() - .map(SignalResponse::new) - .toList(); - } - - @Transactional - public void deleteSignal(long rsId, long sigId) { - var s = signalRepository.findByIdAndRailSystemId(sigId, rsId) - .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - signalRepository.delete(s); - } -} diff --git a/src/main/java/nl/andrewl/railsignalapi/websocket/SignalWebSocketHandler.java b/src/main/java/nl/andrewl/railsignalapi/websocket/SignalWebSocketHandler.java index daf690e..81b2b1e 100644 --- a/src/main/java/nl/andrewl/railsignalapi/websocket/SignalWebSocketHandler.java +++ b/src/main/java/nl/andrewl/railsignalapi/websocket/SignalWebSocketHandler.java @@ -3,7 +3,6 @@ package nl.andrewl.railsignalapi.websocket; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import nl.andrewl.railsignalapi.service.SignalService; import org.springframework.stereotype.Component; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; @@ -18,7 +17,6 @@ import java.util.Set; @Slf4j public class SignalWebSocketHandler extends TextWebSocketHandler { private final ObjectMapper mapper = new ObjectMapper(); - private final SignalService signalService; @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { @@ -31,19 +29,19 @@ public class SignalWebSocketHandler extends TextWebSocketHandler { for (var idStr : signalIdHeader.split(",")) { ids.add(Long.parseLong(idStr.trim())); } - signalService.registerSignalWebSocketSession(ids, session); + //signalService.registerSignalWebSocketSession(ids, session); log.info("Connection established with signals {}.", ids); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { var msg = mapper.readValue(message.getPayload(), SignalUpdateMessage.class); - signalService.handleSignalUpdate(msg); + //signalService.handleSignalUpdate(msg); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { - signalService.deregisterSignalWebSocketSession(session); + //signalService.deregisterSignalWebSocketSession(session); log.info("Closed connection {}. Status: {}", session.getId(), status.toString()); } } diff --git a/src/main/resources/static/js/drawing.js b/src/main/resources/static/js/drawing.js deleted file mode 100644 index 384a667..0000000 --- a/src/main/resources/static/js/drawing.js +++ /dev/null @@ -1,144 +0,0 @@ -function worldTransform() { - const canvasRect = railMapCanvas[0].getBoundingClientRect(); - const scale = SCALE_VALUES[canvasScaleIndex]; - let tx = new DOMMatrix(); - tx.translateSelf(canvasRect.width / 2, canvasRect.height / 2, 0); - tx.scaleSelf(scale, scale, scale); - tx.translateSelf(canvasTranslation.x, canvasTranslation.y, 0); - if (canvasDragOrigin !== null && canvasDragTranslation !== null) { - tx.translateSelf(canvasDragTranslation.x, canvasDragTranslation.y, 0); - } - return tx; -} - -function canvasPointToWorld(p) { - return worldTransform().invertSelf().transformPoint(p); -} - -function worldPointToCanvas(p) { - return worldTransform().transformPoint(p); -} - -function signalTransform(worldTx, signal) { - let tx = DOMMatrix.fromMatrix(worldTx); - tx.translateSelf(signal.position.x, signal.position.z, 0); - let direction = signal.branchConnections[0].direction; - if (direction === "EAST" || direction === "SOUTH" || direction === "SOUTH_EAST" || direction === "SOUTH_WEST") { - direction = signal.branchConnections[1].direction; - } - if (direction === undefined || direction === null || direction === "") { - direction = "NORTH"; - } - let angle = 0; - if (direction === "NORTH") { - angle = 90; - } else if (direction === "NORTH_WEST") { - angle = 45; - } else if (direction === "NORTH_EAST") { - angle = 135; - } - tx.rotateSelf(0, 0, angle); - return tx; -} - -function drawRailSystem() { - let ctx = railMapCanvas[0].getContext("2d"); - ctx.resetTransform(); - ctx.clearRect(0, 0, railMapCanvas.width(), railMapCanvas.height()); - const worldTx = worldTransform(); - ctx.setTransform(worldTx); - railSystem.signals.forEach(signal => { - drawReachableConnections(ctx, signal); - }); - railSystem.signals.forEach(signal => { - ctx.setTransform(signalTransform(worldTx, signal)); - drawSignal(ctx, signal); - }); - ctx.resetTransform(); - ctx.fillStyle = 'black'; - ctx.strokeStyle = 'black'; - ctx.font = '24px Serif'; - let textLine = 0; - hoveredElements.forEach(element => { - ctx.strokeText(element.name, 10, 20 + textLine * 20); - ctx.fillText(element.name, 10, 20 + textLine * 20); - textLine += 1; - }); -} - -function drawSignal(ctx, signal) { - if (signal.online) { - ctx.fillStyle = 'black'; - } else { - ctx.fillStyle = 'gray'; - } - ctx.scale(2, 2); - ctx.fillRect(-0.5, -0.5, 1, 1); - let northWesterlyCon = signal.branchConnections[0]; - let southEasterlyCon = signal.branchConnections[1]; - if (northWesterlyCon.direction === "EAST" || northWesterlyCon.direction === "SOUTH" || northWesterlyCon.direction === "SOUTH_WEST" || northWesterlyCon.direction === "SOUTH_EAST") { - let tmp = northWesterlyCon; - northWesterlyCon = southEasterlyCon; - southEasterlyCon = tmp; - } - - ctx.fillStyle = getSignalColor(signal, southEasterlyCon.branch.status); - ctx.fillRect(-0.75, -0.4, 0.3, 0.8); - ctx.fillStyle = getSignalColor(signal, northWesterlyCon.branch.status); - ctx.fillRect(0.45, -0.4, 0.3, 0.8); -} - -function getSignalColor(signal, branchStatus) { - if (!signal.online) return 'rgb(0, 0, 255)'; - if (branchStatus === "FREE") { - return 'rgb(0, 255, 0)'; - } else if (branchStatus === "OCCUPIED") { - return 'rgb(255, 0, 0)'; - } else { - return 'rgb(0, 0, 255)'; - } -} - -// Draws lines indicating reachable paths between this signal and others, with arrows for directionality. -function drawReachableConnections(ctx, signal) { - ctx.strokeStyle = 'black'; - ctx.lineWidth = 0.25; - signal.branchConnections.forEach(connection => { - ctx.resetTransform(); - connection.reachableSignalConnections.forEach(reachableCon => { - const dx = reachableCon.signalPosition.x - signal.position.x; - const dy = reachableCon.signalPosition.z - signal.position.z; - const dist = Math.sqrt(dx * dx + dy * dy); - let tx = worldTransform(); - tx.translateSelf(signal.position.x, signal.position.z, 0); - const angle = Math.atan2(dy, dx) * 180 / Math.PI - 90; - tx.rotateSelf(0, 0, angle); - ctx.setTransform(tx); - ctx.beginPath(); - ctx.moveTo(0, 0); - ctx.lineTo(0, dist); - const arrowEnd = 5; - const arrowWidth = 0.5; - const arrowLength = 1; - ctx.lineTo(0, arrowEnd); - ctx.lineTo(arrowWidth, arrowEnd - arrowLength); - ctx.lineTo(-arrowWidth, arrowEnd - arrowLength); - ctx.lineTo(0, arrowEnd); - ctx.stroke(); - ctx.fill(); - }); - }); -} - -function getRailSystemBoundingBox() { - let min = {x: Number.MAX_SAFE_INTEGER, z: Number.MAX_SAFE_INTEGER}; - let max = {x: Number.MIN_SAFE_INTEGER, z: Number.MIN_SAFE_INTEGER}; - railSystem.signals.forEach(signal => { - let p = signal.position; - if (p.x < min.x) min.x = p.x; - if (p.z < min.z) min.z = p.z; - if (p.x > max.x) max.x = p.x; - if (p.z > max.z) max.z = p.z; - }); - return {x: min.x, y: min.z, width: Math.abs(max.x - min.x), height: Math.abs(max.z - min.z)}; -} \ No newline at end of file diff --git a/src/main/resources/static/js/main.js b/src/main/resources/static/js/main.js deleted file mode 100644 index 481ccf4..0000000 --- a/src/main/resources/static/js/main.js +++ /dev/null @@ -1,388 +0,0 @@ -const $ = jQuery; - -let railSystemSelect; -let railMapCanvas; -let railSystem = null; -let detailPanel = null; - -let canvasTranslation = {x: 0, y: 0}; -let canvasDragOrigin = null; -let canvasDragTranslation = null; -let hoveredElements = []; - -const SCALE_VALUES = [0.01, 0.1, 0.25, 0.5, 1.0, 1.25, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 10.0, 12.0, 16.0, 20.0, 30.0, 45.0, 60.0, 80.0, 100.0]; -const SCALE_INDEX_NORMAL = 7; -let canvasScaleIndex = SCALE_INDEX_NORMAL; - -$(document).ready(() => { - railSystemSelect = $('#railSystemSelect'); - railSystemSelect.change(railSystemChanged); - - railMapCanvas = $('#railMapCanvas'); - railMapCanvas.on('wheel', onCanvasMouseWheel); - railMapCanvas.mousedown(onCanvasMouseDown); - railMapCanvas.mouseup(onCanvasMouseUp); - railMapCanvas.mousemove(onCanvasMouseMove); - - $('#addRailSystemInput').on("input", () => { - $('#addRailSystemButton').prop("disabled", $('#addRailSystemInput').val() === ""); - }); - $('#addRailSystemButton').click(addRailSystem); - $('#removeRailSystemButton').click(deleteRailSystem); - - detailPanel = $('#railMapDetailPanel'); - - refreshRailSystems(true); -}); - -// Handle mouse scrolling within the context of the canvas. -function onCanvasMouseWheel(event) { - let s = event.originalEvent.deltaY; - if (s > 0) { - canvasScaleIndex = Math.max(0, canvasScaleIndex - 1); - } else if (s < 0) { - canvasScaleIndex = Math.min(SCALE_VALUES.length - 1, canvasScaleIndex + 1); - } - drawRailSystem(); - event.stopPropagation(); -} - -// Handle mouse clicks on the canvas. -function onCanvasMouseDown(event) { - const p = getMousePoint(event); - canvasDragOrigin = {x: p.x, y: p.y}; -} - -// Handle mouse release on the canvas, which stops dragging or indicates that the user may have clicked on something. -function onCanvasMouseUp(event) { - if (canvasDragTranslation !== null) { - canvasTranslation.x += canvasDragTranslation.x; - canvasTranslation.y += canvasDragTranslation.y; - } else { - const p = getMousePoint(event); - let signalClicked = false; - railSystem.signals.forEach(signal => { - const sp = new DOMPoint(signal.position.x, signal.position.z, 0, 1); - const canvasSp = worldPointToCanvas(sp); - const dist = Math.sqrt(Math.pow(p.x - canvasSp.x, 2) + Math.pow(p.y - canvasSp.y, 2)); - if (dist < 5) { - console.log(signal); - onSignalSelected(signal); - signalClicked = true; - } - }); - if (!signalClicked) { - onSignalSelected(null); - } - } - canvasDragOrigin = null; - canvasDragTranslation = null; -} - -// Handle mouse motion over the canvas. This is for dragging and hovering over items. -function onCanvasMouseMove(event) { - const rect = railMapCanvas[0].getBoundingClientRect(); - const x = event.clientX - rect.left; - const y = event.clientY - rect.top; - if (canvasDragOrigin !== null) { - const scale = SCALE_VALUES[canvasScaleIndex]; - const dx = x - canvasDragOrigin.x; - const dy = y - canvasDragOrigin.y; - canvasDragTranslation = {x: dx / scale, y: dy / scale}; - drawRailSystem(); - } else { - hoveredElements = []; - const p = getMousePoint(event); - railSystem.signals.forEach(signal => { - const sp = new DOMPoint(signal.position.x, signal.position.z, 0, 1); - const canvasSp = worldPointToCanvas(sp); - const dist = Math.sqrt(Math.pow(p.x - canvasSp.x, 2) + Math.pow(p.y - canvasSp.y, 2)); - if (dist < 5) { - hoveredElements.push(signal); - } - }); - drawRailSystem(); - } -} - -function getMousePoint(event) { - const rect = railMapCanvas[0].getBoundingClientRect(); - const x = event.clientX - rect.left; - const y = event.clientY - rect.top; - return new DOMPoint(x, y, 0, 1); -} - -function railSystemChanged() { - detailPanel.empty(); - railSystem = {}; - railSystem.id = railSystemSelect.val(); - $.get("/api/railSystems/" + railSystem.id + "/signals") - .done(signals => { - railSystem.signals = signals; - let bb = getRailSystemBoundingBox(); - canvasTranslation.x = -1 * (bb.x + (bb.width / 2)); - canvasTranslation.y = -1 * (bb.y + (bb.height / 2)); - canvasScaleIndex = SCALE_INDEX_NORMAL; - drawRailSystem(); - window.setInterval(railSystemUpdated, 1000); - }); - $.get("/api/railSystems/" + railSystem.id + "/branches") - .done(branches => { - railSystem.branches = branches; - const branchSelects = $('.js_branch_list'); - branchSelects.empty(); - railSystem.branches.forEach(branch => { - branchSelects.append($('')) - }); - }); -} - -// Updates the current rail system's information from the API. -function railSystemUpdated() { - $.get("/api/railSystems/" + railSystem.id + "/signals") - .done(signals => { - railSystem.signals = signals; - drawRailSystem(); - }); - $.get("/api/railSystems/" + railSystem.id + "/branches") - .done(branches => { - railSystem.branches = branches; - const branchSelects = $('.js_branch_list'); - branchSelects.empty(); - railSystem.branches.forEach(branch => { - branchSelects.append($('')) - }); - }); -} - -function selectSignalById(id) { - railSystem.signals.forEach(signal => { - if (signal.id === id) { - onSignalSelected(signal); - } - }); -} - -function onSignalSelected(signal) { - detailPanel.empty(); - if (signal !== null) { - const tpl = Handlebars.compile($('#signalTemplate').html()); - detailPanel.html(tpl(signal)); - signal.branchConnections.forEach(con => { - const select = $('#signalPotentialConnectionsSelect-' + con.id); - $.get("/api/railSystems/" + railSystem.id + "/branches/" + con.branch.id + "/signals") - .done(signals => { - signals = signals.filter(s => s.id !== signal.id); - let connections = []; - signals.forEach(s => { - s.branchConnections - .filter(c => c.branch.id === con.branch.id && !con.reachableSignalConnections.some(rc => rc.connectionId === c.id)) - .forEach(potentialConnection => { - potentialConnection.signalName = s.name; - potentialConnection.signalId = s.id; - connections.push(potentialConnection); - }); - }); - select.empty(); - const row = $('#signalPotentialConnectionsRow-' + con.id); - row.toggle(connections.length > 0); - connections.forEach(c => { - select.append($(``)) - }); - }); - }); - } -} - -function addRailSystem() { - let name = $('#addRailSystemInput').val().trim(); - $.post({ - url: "/api/railSystems", - data: JSON.stringify({name: name}), - contentType: "application/json" - }) - .done((response) => { - refreshRailSystems(); - }) - .always(() => { - $('#addRailSystemInput').val(""); - }); -} - -function deleteRailSystem() { - if (railSystem !== null && railSystem.id) { - confirm("Are you sure you want to permanently remove rail system " + railSystem.id + "?") - .then(() => { - $.ajax({ - url: "/api/railSystems/" + railSystem.id, - type: "DELETE" - }) - .always(() => { - refreshRailSystems(true); - }); - }); - } -} - -function refreshRailSystems(selectFirst) { - $.get("/api/railSystems") - .done(railSystems => { - railSystemSelect.empty(); - railSystems.forEach(railSystem => { - let option = $(``) - railSystemSelect.append(option); - }); - if (selectFirst) { - railSystemSelect.val(railSystems[0].id); - railSystemSelect.change(); - } - }); -} - -function addNewSignal() { - const modalElement = $('#addSignalModal'); - const form = $('#addSignalForm'); - form.validate(); - if (!form.valid()) return; - const data = { - name: $('#addSignalName').val().trim(), - position: { - x: $('#addSignalPositionX').val(), - y: $('#addSignalPositionY').val(), - z: $('#addSignalPositionZ').val() - }, - branchConnections: [ - { - direction: $('#addSignalFirstConnectionDirection').val(), - name: $('#addSignalFirstConnectionBranch').val() - }, - { - direction: $('#addSignalSecondConnectionDirection').val(), - name: $('#addSignalSecondConnectionBranch').val() - } - ] - }; - const modal = bootstrap.Modal.getInstance(modalElement[0]); - modal.hide(); - modalElement.on("hidden.bs.modal", () => { - confirm("Are you sure you want to add this new signal to the system?") - .then(() => { - $.post({ - url: "/api/railSystems/" + railSystem.id + "/signals", - data: JSON.stringify(data), - contentType: "application/json" - }) - .done(() => { - form.trigger("reset"); - railSystemUpdated(); - }) - .fail((response) => { - console.error(response); - $('#addSignalAlertsContainer').append($('
An error occurred.
')); - modal.show(); - }); - }) - .catch(() => { - form.trigger("reset"); - }); - }); -} - -function removeSignal(signalId) { - confirm(`Are you sure you want to remove signal ${signalId}? This cannot be undone.`) - .then(() => { - $.ajax({ - url: `/api/railSystems/${railSystem.id}/signals/${signalId}`, - type: "DELETE" - }) - .always(() => { - railSystemUpdated(); - }) - .fail((response) => { - console.error(response); - }) - }) -} - -function removeReachableConnection(signalId, fromId, toId) { - confirm(`Are you sure you want to remove the connection from ${fromId} to ${toId} from signal ${signalId}?`) - .then(() => { - $.get(`/api/railSystems/${railSystem.id}/signals/${signalId}`) - .done(signal => { - let connections = []; - signal.branchConnections.forEach(con => { - con.reachableSignalConnections.forEach(reachableCon => { - connections.push({from: con.id, to: reachableCon.connectionId}); - }); - }); - connections = connections.filter(c => !(c.from === fromId && c.to === toId)); - $.post({ - url: `/api/railSystems/${railSystem.id}/signals/${signal.id}/signalConnections`, - data: JSON.stringify({connections: connections}), - contentType: "application/json" - }) - .done(() => { - railSystemUpdated(); - }) - .fail((response) => { - console.error(response); - }); - }); - }); -} - -function addReachableConnectionBtn(signalId, fromId) { - const select = $('#signalPotentialConnectionsSelect-' + fromId); - const toId = select.val(); - if (toId) { - addReachableConnection(signalId, fromId, toId); - } -} - -function addReachableConnection(signalId, fromId, toId) { - confirm(`Are you sure you want to add a connection from ${fromId} to ${toId} from signal ${signalId}?`) - .then(() => { - $.get(`/api/railSystems/${railSystem.id}/signals/${signalId}`) - .done(signal => { - let connections = []; - signal.branchConnections.forEach(con => { - con.reachableSignalConnections.forEach(reachableCon => { - connections.push({from: con.id, to: reachableCon.connectionId}); - }); - }); - if (!connections.find(c => c.from === fromId && c.to === toId)) { - connections.push({from: fromId, to: toId}); - $.post({ - url: `/api/railSystems/${railSystem.id}/signals/${signal.id}/signalConnections`, - data: JSON.stringify({connections: connections}), - contentType: "application/json" - }) - .done(() => { - railSystemChanged(); - }) - .fail((response) => { - console.error(response); - }); - } - }); - }); -} - -function confirm(message) { - const modalElement = $('#confirmModal'); - if (message) { - $('#confirmModalBody').html(message); - } else { - $('#confirmModalBody').html("Are you sure you want to continue?"); - } - const modal = new bootstrap.Modal(modalElement[0], {keyboard: false}); - modal.show(); - return new Promise((resolve, reject) => { - $('#confirmModalOkButton').click(() => { - modalElement.on("hidden.bs.modal", () => resolve()); - }); - $('#confirmModalCancelButton').click(() => { - modalElement.on("hidden.bs.modal", () => reject()); - }); - }); -} diff --git a/src/main/resources/static/style/main.css b/src/main/resources/static/style/main.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/main/resources/templates/index.html b/src/main/resources/templates/index.html deleted file mode 100644 index 7e80408..0000000 --- a/src/main/resources/templates/index.html +++ /dev/null @@ -1,209 +0,0 @@ - - - - - RailSignal - - - - - - - - -
-

RailSignal

-

Stay in control of your rails.

- -
-
- -
-
- -
-
-
- - -
-
-
- -
-
- -
-
- -
-
- -
- -
-
- -
-
-
-
-
- -
- - - - - -
- - - - - - - - - - \ No newline at end of file From 3ac886feebee0e3fb47ffb455ad24bfca1447cb4 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sat, 7 May 2022 20:31:15 +0200 Subject: [PATCH 3/7] added stuff --- railsignal-app/package-lock.json | 49 ++++ railsignal-app/src/App.vue | 5 +- railsignal-app/src/components/RailSystem.vue | 42 +-- .../src/components/RailSystemsManager.vue | 6 + .../src/components/railsystem/AddSegment.vue | 13 +- .../src/components/railsystem/Component.vue | 23 -- .../src/components/railsystem/MapView.vue | 30 ++- .../components/railsystem/SegmentsView.vue | 31 +++ .../component/AddSegmentBoundary.vue | 76 ++++++ .../railsystem/component/AddSignal.vue | 62 +++++ .../railsystem/component/ComponentView.vue | 55 ++++ .../component/PathNodeComponentView.vue | 27 ++ .../SegmentBoundaryNodeComponentView.vue | 24 ++ .../component/SignalComponentView.vue | 21 ++ .../src/components/railsystem/mapRenderer.js | 248 ++++++++++++++++++ railsignal-app/src/stores/railSystemsStore.js | 70 ++++- .../rest/ComponentsApiController.java | 11 +- .../rest/SegmentsApiController.java | 8 + .../rest/dto/FullSegmentResponse.java | 4 +- .../rest/dto/PathNodeUpdatePayload.java | 5 + .../rest/{ => dto}/SegmentPayload.java | 2 +- .../dto/component/in/ComponentPayload.java | 9 + .../rest/dto/component/in/SignalPayload.java | 5 + .../{ => out}/ComponentResponse.java | 2 +- .../component/{ => out}/PathNodeResponse.java | 2 +- .../SegmentBoundaryNodeResponse.java | 2 +- .../component/{ => out}/SignalResponse.java | 2 +- .../{ => out}/SimpleComponentResponse.java | 2 +- .../SwitchConfigurationResponse.java | 2 +- .../component/{ => out}/SwitchResponse.java | 2 +- .../service/ComponentService.java | 27 +- .../railsignalapi/service/SegmentService.java | 3 +- 32 files changed, 784 insertions(+), 86 deletions(-) delete mode 100644 railsignal-app/src/components/railsystem/Component.vue create mode 100644 railsignal-app/src/components/railsystem/SegmentsView.vue create mode 100644 railsignal-app/src/components/railsystem/component/AddSegmentBoundary.vue create mode 100644 railsignal-app/src/components/railsystem/component/AddSignal.vue create mode 100644 railsignal-app/src/components/railsystem/component/ComponentView.vue create mode 100644 railsignal-app/src/components/railsystem/component/PathNodeComponentView.vue create mode 100644 railsignal-app/src/components/railsystem/component/SegmentBoundaryNodeComponentView.vue create mode 100644 railsignal-app/src/components/railsystem/component/SignalComponentView.vue create mode 100644 railsignal-app/src/components/railsystem/mapRenderer.js create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/PathNodeUpdatePayload.java rename src/main/java/nl/andrewl/railsignalapi/rest/{ => dto}/SegmentPayload.java (52%) create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/ComponentPayload.java create mode 100644 src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/SignalPayload.java rename src/main/java/nl/andrewl/railsignalapi/rest/dto/component/{ => out}/ComponentResponse.java (92%) rename src/main/java/nl/andrewl/railsignalapi/rest/dto/component/{ => out}/PathNodeResponse.java (86%) rename src/main/java/nl/andrewl/railsignalapi/rest/dto/component/{ => out}/SegmentBoundaryNodeResponse.java (88%) rename src/main/java/nl/andrewl/railsignalapi/rest/dto/component/{ => out}/SignalResponse.java (84%) rename src/main/java/nl/andrewl/railsignalapi/rest/dto/component/{ => out}/SimpleComponentResponse.java (87%) rename src/main/java/nl/andrewl/railsignalapi/rest/dto/component/{ => out}/SwitchConfigurationResponse.java (86%) rename src/main/java/nl/andrewl/railsignalapi/rest/dto/component/{ => out}/SwitchResponse.java (90%) diff --git a/railsignal-app/package-lock.json b/railsignal-app/package-lock.json index 98c6a16..2f97e8a 100644 --- a/railsignal-app/package-lock.json +++ b/railsignal-app/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "axios": "^0.27.2", + "bootstrap": "^4.6.1", "pinia": "^2.0.14", "three": "^0.140.0", "vue": "^3.2.33", @@ -285,6 +286,19 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, + "node_modules/bootstrap": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz", + "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + }, + "peerDependencies": { + "jquery": "1.9.1 - 3", + "popper.js": "^1.16.1" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1248,6 +1262,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "node_modules/jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==", + "peer": true + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1500,6 +1520,17 @@ } } }, + "node_modules/popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/postcss": { "version": "8.4.13", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz", @@ -2130,6 +2161,12 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, + "bootstrap": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz", + "integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==", + "requires": {} + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2744,6 +2781,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==", + "peer": true + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2924,6 +2967,12 @@ } } }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", + "peer": true + }, "postcss": { "version": "8.4.13", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.13.tgz", diff --git a/railsignal-app/src/App.vue b/railsignal-app/src/App.vue index 10a2fad..c2becb2 100644 --- a/railsignal-app/src/App.vue +++ b/railsignal-app/src/App.vue @@ -4,10 +4,7 @@ - + diff --git a/railsignal-app/src/components/RailSystemsManager.vue b/railsignal-app/src/components/RailSystemsManager.vue index 568c206..8664935 100644 --- a/railsignal-app/src/components/RailSystemsManager.vue +++ b/railsignal-app/src/components/RailSystemsManager.vue @@ -26,6 +26,12 @@ export default { name: "RailSystemsManager.vue", setup() { const rsStore = useRailSystemsStore(); + rsStore.$subscribe(mutation => { + const evt = mutation.events; + if (evt.key === "selectedRailSystem" && evt.newValue !== null) { + rsStore.fetchSelectedRailSystemData(); + } + }); return { rsStore }; diff --git a/railsignal-app/src/components/railsystem/AddSegment.vue b/railsignal-app/src/components/railsystem/AddSegment.vue index 9e4f637..f3c3aa1 100644 --- a/railsignal-app/src/components/railsystem/AddSegment.vue +++ b/railsignal-app/src/components/railsystem/AddSegment.vue @@ -1,14 +1,23 @@ - - - - \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/MapView.vue b/railsignal-app/src/components/railsystem/MapView.vue index c0c1d0f..8a61c5c 100644 --- a/railsignal-app/src/components/railsystem/MapView.vue +++ b/railsignal-app/src/components/railsystem/MapView.vue @@ -1,26 +1,32 @@ \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/SegmentsView.vue b/railsignal-app/src/components/railsystem/SegmentsView.vue new file mode 100644 index 0000000..65e5673 --- /dev/null +++ b/railsignal-app/src/components/railsystem/SegmentsView.vue @@ -0,0 +1,31 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/AddSegmentBoundary.vue b/railsignal-app/src/components/railsystem/component/AddSegmentBoundary.vue new file mode 100644 index 0000000..ae63966 --- /dev/null +++ b/railsignal-app/src/components/railsystem/component/AddSegmentBoundary.vue @@ -0,0 +1,76 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/AddSignal.vue b/railsignal-app/src/components/railsystem/component/AddSignal.vue new file mode 100644 index 0000000..6313c47 --- /dev/null +++ b/railsignal-app/src/components/railsystem/component/AddSignal.vue @@ -0,0 +1,62 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/ComponentView.vue b/railsignal-app/src/components/railsystem/component/ComponentView.vue new file mode 100644 index 0000000..5332df1 --- /dev/null +++ b/railsignal-app/src/components/railsystem/component/ComponentView.vue @@ -0,0 +1,55 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/PathNodeComponentView.vue b/railsignal-app/src/components/railsystem/component/PathNodeComponentView.vue new file mode 100644 index 0000000..4f4c1ed --- /dev/null +++ b/railsignal-app/src/components/railsystem/component/PathNodeComponentView.vue @@ -0,0 +1,27 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/SegmentBoundaryNodeComponentView.vue b/railsignal-app/src/components/railsystem/component/SegmentBoundaryNodeComponentView.vue new file mode 100644 index 0000000..b2f3ba9 --- /dev/null +++ b/railsignal-app/src/components/railsystem/component/SegmentBoundaryNodeComponentView.vue @@ -0,0 +1,24 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/SignalComponentView.vue b/railsignal-app/src/components/railsystem/component/SignalComponentView.vue new file mode 100644 index 0000000..a9b0833 --- /dev/null +++ b/railsignal-app/src/components/railsystem/component/SignalComponentView.vue @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/mapRenderer.js b/railsignal-app/src/components/railsystem/mapRenderer.js new file mode 100644 index 0000000..17007f8 --- /dev/null +++ b/railsignal-app/src/components/railsystem/mapRenderer.js @@ -0,0 +1,248 @@ +/* +This component is responsible for the rendering of a RailSystem in a 2d map +view. + */ + +const SCALE_VALUES = [0.01, 0.1, 0.25, 0.5, 1.0, 1.25, 1.5, 2.0, 3.0, 4.0, 6.0, 8.0, 10.0, 12.0, 16.0, 20.0, 30.0, 45.0, 60.0, 80.0, 100.0]; +const SCALE_INDEX_NORMAL = 7; +const HOVER_RADIUS = 10; + +let mapCanvas = null; +let railSystem = null; + +let mapScaleIndex = SCALE_INDEX_NORMAL; +let mapTranslation = {x: 0, y: 0}; +let mapDragOrigin = null; +let mapDragTranslation = null; +let lastMousePoint = new DOMPoint(0, 0, 0, 0); +const hoveredElements = []; + +export function initMap(rs) { + railSystem = rs; + console.log("Initializing map for rail system: " + rs.name); + hoveredElements.length = 0; + mapCanvas = document.getElementById("railSystemMapCanvas"); + mapCanvas.removeEventListener("wheel", onMouseWheel); + mapCanvas.addEventListener("wheel", onMouseWheel); + mapCanvas.removeEventListener("mousedown", onMouseDown); + mapCanvas.addEventListener("mousedown", onMouseDown); + mapCanvas.removeEventListener("mouseup", onMouseUp); + mapCanvas.addEventListener("mouseup", onMouseUp); + mapCanvas.removeEventListener("mousemove", onMouseMove); + mapCanvas.addEventListener("mousemove", onMouseMove); + + // Do an initial draw. + draw(); +} + +export function draw() { + if (!(mapCanvas && railSystem && railSystem.components)) { + console.warn("Attempted to draw map without canvas or railSystem."); + return; + } + const ctx = mapCanvas.getContext("2d"); + const width = mapCanvas.width; + const height = mapCanvas.height; + ctx.resetTransform(); + ctx.fillStyle = `rgb(240, 240, 240)`; + ctx.fillRect(0, 0, width, height); + const worldTx = getWorldTransform(); + ctx.setTransform(worldTx); + + for (let i = 0; i < railSystem.components.length; i++) { + drawComponent(ctx, worldTx, railSystem.components[i]); + } + + // Draw debug info. + ctx.resetTransform(); + ctx.fillStyle = "black"; + ctx.strokeStyle = "black"; + ctx.font = "10px Sans-Serif"; + const lastWorldPoint = mapPointToWorld(lastMousePoint); + const lines = [ + "Scale factor: " + getScaleFactor(), + `(x = ${lastWorldPoint.x.toFixed(2)}, y = ${lastWorldPoint.y.toFixed(2)}, z = ${lastWorldPoint.z.toFixed(2)})`, + `Components: ${railSystem.components.length}`, + `Hovered elements: ${hoveredElements.length}` + ] + for (let i = 0; i < lines.length; i++) { + ctx.fillText(lines[i], 10, 20 + (i * 15)); + } +} + +function drawComponent(ctx, worldTx, component) { + const tx = DOMMatrix.fromMatrix(worldTx); + tx.translateSelf(component.position.x, component.position.z, 0); + const s = getScaleFactor(); + tx.scaleSelf(1/s, 1/s, 1/s); + tx.scaleSelf(5, 5, 5); + ctx.setTransform(tx); + if (isComponentHovered(component)) { + ctx.fillStyle = `rgba(255, 255, 0, 32)`; + ctx.beginPath(); + ctx.ellipse(0, 0, 1.8, 1.8, 0, 0, Math.PI * 2); + ctx.fill(); + } + if (component.type === "SIGNAL") { + drawSignal(ctx, component); + } else if (component.type === "SEGMENT_BOUNDARY") { + drawSegmentBoundary(ctx, component); + } +} + +function drawSignal(ctx, signal) { + roundedRect(ctx, -0.7, -1, 1.4, 2, 0.25); + ctx.fillStyle = "black"; + ctx.fill(); + + // ctx.fillStyle = "green"; + // ctx.beginPath(); + // ctx.ellipse(0, 0, 0.8, 0.8, 0, 0, Math.PI * 2); + // ctx.fill(); + // + // ctx.strokeStyle = "black"; + // ctx.lineWidth = 0.5; + // ctx.beginPath(); + // ctx.ellipse(0, 0, 1, 1, 0, 0, Math.PI * 2); + // ctx.stroke(); +} + +function drawSegmentBoundary(ctx, segmentBoundary) { + ctx.fillStyle = "blue"; + ctx.beginPath(); + ctx.ellipse(0, 0, 1, 1, 0, 0, Math.PI * 2); + ctx.fill(); +} + +export function getScaleFactor() { + return SCALE_VALUES[mapScaleIndex]; +} + +function getWorldTransform() { + const canvasRect = mapCanvas.getBoundingClientRect(); + const scale = getScaleFactor(); + const tx = new DOMMatrix(); + tx.translateSelf(canvasRect.width / 2, canvasRect.height / 2, 0); + tx.scaleSelf(scale, scale, scale); + tx.translateSelf(mapTranslation.x, mapTranslation.y, 0); + if (mapDragOrigin !== null && mapDragTranslation !== null) { + tx.translateSelf(mapDragTranslation.x, mapDragTranslation.y, 0); + } + return tx; +} + +function isComponentHovered(component) { + for (let i = 0; i < hoveredElements.length; i++) { + if (hoveredElements[i].id === component.id) return true; + } + return false; +} + +/** + * Maps a point on the map coordinates to world coordinates. + * @param {DOMPoint} p + * @returns {DOMPoint} + */ +function mapPointToWorld(p) { + return getWorldTransform().invertSelf().transformPoint(p); +} + +/** + * Maps a point in the world to map coordinates. + * @param {DOMPoint} p + * @returns {DOMPoint} + */ +function worldPointToMap(p) { + return getWorldTransform().transformPoint(p); +} + +function roundedRect(ctx, x, y, w, h, r) { + if (w < 2 * r) r = w / 2; + if (h < 2 * r) r = h / 2; + ctx.beginPath(); + ctx.moveTo(x+r, y); + ctx.arcTo(x+w, y, x+w, y+h, r); + ctx.arcTo(x+w, y+h, x, y+h, r); + ctx.arcTo(x, y+h, x, y, r); + ctx.arcTo(x, y, x+w, y, r); + ctx.closePath(); +} + +/* +EVENT HANDLING +*/ + +/** + * @param {WheelEvent} event + */ +function onMouseWheel(event) { + const s = event.deltaY; + if (s > 0) { + mapScaleIndex = Math.max(0, mapScaleIndex - 1); + } else if (s < 0) { + mapScaleIndex = Math.min(SCALE_VALUES.length - 1, mapScaleIndex + 1); + } + draw(); + event.stopPropagation(); +} + +/** + * @param {MouseEvent} event + */ +function onMouseDown(event) { + const p = getMousePoint(event); + mapDragOrigin = {x: p.x, y: p.y}; +} + +function onMouseUp() { + if (mapDragTranslation !== null) { + mapTranslation.x += mapDragTranslation.x; + mapTranslation.y += mapDragTranslation.y; + } + if (hoveredElements.length === 1) { + railSystem.selectedComponent = hoveredElements[0]; + } else { + railSystem.selectedComponent = null; + } + mapDragOrigin = null; + mapDragTranslation = null; +} + +/** + * @param {MouseEvent} event + */ +function onMouseMove(event) { + const p = getMousePoint(event); + lastMousePoint = p; + if (mapDragOrigin !== null) { + const scale = getScaleFactor(); + const dx = p.x - mapDragOrigin.x; + const dy = p.y - mapDragOrigin.y; + mapDragTranslation = {x: dx / scale, y: dy / scale}; + } else { + hoveredElements.length = 0; + // Populate with list of hovered elements. + for (let i = 0; i < railSystem.components.length; i++) { + const c = railSystem.components[i]; + const componentPoint = new DOMPoint(c.position.x, c.position.z, 0, 1); + const mapComponentPoint = worldPointToMap(componentPoint); + const dist2 = (p.x - mapComponentPoint.x) * (p.x - mapComponentPoint.x) + (p.y - mapComponentPoint.y) * (p.y - mapComponentPoint.y); + if (dist2 < HOVER_RADIUS * HOVER_RADIUS) { + hoveredElements.push(c); + } + } + } + draw(); +} + +/** + * Gets the point at which the user clicked on the map. + * @param {MouseEvent} event + * @returns {DOMPoint} + */ +function getMousePoint(event) { + const rect = mapCanvas.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = event.clientY - rect.top; + return new DOMPoint(x, y, 0, 1); +} diff --git a/railsignal-app/src/stores/railSystemsStore.js b/railsignal-app/src/stores/railSystemsStore.js index d9747b1..ac9e411 100644 --- a/railsignal-app/src/stores/railSystemsStore.js +++ b/railsignal-app/src/stores/railSystemsStore.js @@ -4,13 +4,16 @@ import axios from "axios"; export const useRailSystemsStore = defineStore('RailSystemsStore', { state: () => ({ railSystems: [], + /** + * @type {{segments: [Object], components: [Object], selectedComponent: Object} | null} + */ selectedRailSystem: null, - selectedComponent: null + apiUrl: import.meta.env.VITE_API_URL }), actions: { refreshRailSystems() { return new Promise((resolve, reject) => { - axios.get(import.meta.env.VITE_API_URL + "/rs") + axios.get(this.apiUrl + "/rs") .then(response => { this.railSystems = response.data; resolve(); @@ -22,10 +25,7 @@ export const useRailSystemsStore = defineStore('RailSystemsStore', { }, createRailSystem(name) { return new Promise((resolve, reject) => { - axios.post( - import.meta.env.VITE_API_URL + "/rs", - {name: name} - ) + axios.post(this.apiUrl + "/rs", {name: name}) .then(() => { this.refreshRailSystems() .then(() => resolve()) @@ -36,13 +36,69 @@ export const useRailSystemsStore = defineStore('RailSystemsStore', { }, removeRailSystem(rs) { return new Promise((resolve, reject) => { - axios.delete(import.meta.env.VITE_API_URL + "/rs/" + rs.id) + axios.delete(this.apiUrl + "/rs/" + rs.id) .then(() => { + this.selectedRailSystem = null; this.refreshRailSystems() .then(() => resolve) .catch(error => reject(error)); }) }) + }, + refreshSegments(rs) { + return new Promise(resolve => { + axios.get(`${this.apiUrl}/rs/${rs.id}/s`) + .then(response => { + rs.segments = response.data; + resolve(); + }); + }); + }, + refreshComponents(rs) { + return new Promise(resolve => { + axios.get(`${this.apiUrl}/rs/${rs.id}/c`) + .then(response => { + rs.components = response.data; + resolve(); + }); + }); + }, + fetchSelectedRailSystemData() { + if (!this.selectedRailSystem) return; + this.refreshSegments(this.selectedRailSystem); + this.refreshComponents(this.selectedRailSystem); + }, + addSegment(name) { + const rs = this.selectedRailSystem; + axios.post(`${this.apiUrl}/rs/${rs.id}/s`, {name: name}) + .then(() => this.refreshSegments(rs)) + .catch(error => console.log(error)); + }, + removeSegment(id) { + const rs = this.selectedRailSystem; + axios.delete(`${this.apiUrl}/rs/${rs.id}/s/${id}`) + .then(() => this.refreshSegments(rs)) + .catch(error => console.log(error)); + }, + addComponent(data) { + const rs = this.selectedRailSystem; + axios.post(`${this.apiUrl}/rs/${rs.id}/c`, data) + .then(() => this.refreshComponents(rs)) + .catch(error => console.log(error)); + }, + removeComponent(id) { + const rs = this.selectedRailSystem; + axios.delete(`${this.apiUrl}/rs/${rs.id}/c/${id}`) + .then(() => this.refreshComponents(rs)) + .catch(error => console.log(error)); + }, + fetchComponentData(component) { + return new Promise(resolve => { + const rs = this.selectedRailSystem; + axios.get(`${this.apiUrl}/rs/${rs.id}/c/${component.id}`) + .then(response => resolve(response.data)) + .catch(error => console.log(error)); + }); } } }); \ No newline at end of file diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/ComponentsApiController.java b/src/main/java/nl/andrewl/railsignalapi/rest/ComponentsApiController.java index 1a83b3c..67b2763 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/ComponentsApiController.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/ComponentsApiController.java @@ -2,8 +2,8 @@ package nl.andrewl.railsignalapi.rest; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; -import nl.andrewl.railsignalapi.rest.dto.component.ComponentResponse; -import nl.andrewl.railsignalapi.rest.dto.component.SimpleComponentResponse; +import nl.andrewl.railsignalapi.rest.dto.PathNodeUpdatePayload; +import nl.andrewl.railsignalapi.rest.dto.component.out.ComponentResponse; import nl.andrewl.railsignalapi.service.ComponentService; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -17,7 +17,7 @@ public class ComponentsApiController { private final ComponentService componentService; @GetMapping - public List getAllComponents(@PathVariable long rsId) { + public List getAllComponents(@PathVariable long rsId) { return componentService.getComponents(rsId); } @@ -36,4 +36,9 @@ public class ComponentsApiController { componentService.removeComponent(rsId, cId); return ResponseEntity.noContent().build(); } + + @PatchMapping(path = "/{cId}/connectedNodes") + public ComponentResponse updateConnectedNodes(@PathVariable long rsId, @PathVariable long cId, @RequestBody PathNodeUpdatePayload payload) { + return componentService.updatePath(rsId, cId, payload); + } } diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/SegmentsApiController.java b/src/main/java/nl/andrewl/railsignalapi/rest/SegmentsApiController.java index 80418a1..46f7c40 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/SegmentsApiController.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/SegmentsApiController.java @@ -2,8 +2,10 @@ package nl.andrewl.railsignalapi.rest; import lombok.RequiredArgsConstructor; import nl.andrewl.railsignalapi.rest.dto.FullSegmentResponse; +import nl.andrewl.railsignalapi.rest.dto.SegmentPayload; import nl.andrewl.railsignalapi.rest.dto.SegmentResponse; import nl.andrewl.railsignalapi.service.SegmentService; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -28,4 +30,10 @@ public class SegmentsApiController { public FullSegmentResponse createSegment(@PathVariable long rsId, @RequestBody SegmentPayload payload) { return segmentService.create(rsId, payload); } + + @DeleteMapping(path = "/{sId}") + public ResponseEntity removeSegment(@PathVariable long rsId, @PathVariable long sId) { + segmentService.remove(rsId, sId); + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/FullSegmentResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/FullSegmentResponse.java index ae8cbde..d23c336 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/FullSegmentResponse.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/FullSegmentResponse.java @@ -1,8 +1,8 @@ package nl.andrewl.railsignalapi.rest.dto; import nl.andrewl.railsignalapi.model.Segment; -import nl.andrewl.railsignalapi.rest.dto.component.SegmentBoundaryNodeResponse; -import nl.andrewl.railsignalapi.rest.dto.component.SignalResponse; +import nl.andrewl.railsignalapi.rest.dto.component.out.SegmentBoundaryNodeResponse; +import nl.andrewl.railsignalapi.rest.dto.component.out.SignalResponse; import java.util.List; diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/PathNodeUpdatePayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/PathNodeUpdatePayload.java new file mode 100644 index 0000000..9afbb29 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/PathNodeUpdatePayload.java @@ -0,0 +1,5 @@ +package nl.andrewl.railsignalapi.rest.dto; + +public record PathNodeUpdatePayload ( + long[] connectedNodeIds +) {} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/SegmentPayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/SegmentPayload.java similarity index 52% rename from src/main/java/nl/andrewl/railsignalapi/rest/SegmentPayload.java rename to src/main/java/nl/andrewl/railsignalapi/rest/dto/SegmentPayload.java index 9bda7f6..ea99db5 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/SegmentPayload.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/SegmentPayload.java @@ -1,4 +1,4 @@ -package nl.andrewl.railsignalapi.rest; +package nl.andrewl.railsignalapi.rest.dto; public record SegmentPayload(String name) { } diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/ComponentPayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/ComponentPayload.java new file mode 100644 index 0000000..a9066b7 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/ComponentPayload.java @@ -0,0 +1,9 @@ +package nl.andrewl.railsignalapi.rest.dto.component.in; + +import nl.andrewl.railsignalapi.model.component.Position; + +public abstract class ComponentPayload { + public String name; + public String type; + public Position position; +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/SignalPayload.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/SignalPayload.java new file mode 100644 index 0000000..c29cbf2 --- /dev/null +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/in/SignalPayload.java @@ -0,0 +1,5 @@ +package nl.andrewl.railsignalapi.rest.dto.component.in; + +public class SignalPayload extends ComponentPayload { + public long segmentId; +} diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/ComponentResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/ComponentResponse.java similarity index 92% rename from src/main/java/nl/andrewl/railsignalapi/rest/dto/component/ComponentResponse.java rename to src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/ComponentResponse.java index 7fd74f7..2d78269 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/ComponentResponse.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/ComponentResponse.java @@ -1,4 +1,4 @@ -package nl.andrewl.railsignalapi.rest.dto.component; +package nl.andrewl.railsignalapi.rest.dto.component.out; import nl.andrewl.railsignalapi.model.component.*; diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/PathNodeResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/PathNodeResponse.java similarity index 86% rename from src/main/java/nl/andrewl/railsignalapi/rest/dto/component/PathNodeResponse.java rename to src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/PathNodeResponse.java index 1c83196..3d2bfee 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/PathNodeResponse.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/PathNodeResponse.java @@ -1,4 +1,4 @@ -package nl.andrewl.railsignalapi.rest.dto.component; +package nl.andrewl.railsignalapi.rest.dto.component.out; import nl.andrewl.railsignalapi.model.component.PathNode; diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SegmentBoundaryNodeResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SegmentBoundaryNodeResponse.java similarity index 88% rename from src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SegmentBoundaryNodeResponse.java rename to src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SegmentBoundaryNodeResponse.java index ba09712..1af60e4 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SegmentBoundaryNodeResponse.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SegmentBoundaryNodeResponse.java @@ -1,4 +1,4 @@ -package nl.andrewl.railsignalapi.rest.dto.component; +package nl.andrewl.railsignalapi.rest.dto.component.out; import nl.andrewl.railsignalapi.model.component.SegmentBoundaryNode; import nl.andrewl.railsignalapi.rest.dto.SegmentResponse; diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SignalResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SignalResponse.java similarity index 84% rename from src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SignalResponse.java rename to src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SignalResponse.java index 56dd8d5..f5c6dab 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SignalResponse.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SignalResponse.java @@ -1,4 +1,4 @@ -package nl.andrewl.railsignalapi.rest.dto.component; +package nl.andrewl.railsignalapi.rest.dto.component.out; import nl.andrewl.railsignalapi.model.component.Signal; import nl.andrewl.railsignalapi.rest.dto.SegmentResponse; diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SimpleComponentResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SimpleComponentResponse.java similarity index 87% rename from src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SimpleComponentResponse.java rename to src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SimpleComponentResponse.java index 6b6a9da..c26e66f 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SimpleComponentResponse.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SimpleComponentResponse.java @@ -1,4 +1,4 @@ -package nl.andrewl.railsignalapi.rest.dto.component; +package nl.andrewl.railsignalapi.rest.dto.component.out; import nl.andrewl.railsignalapi.model.component.Component; import nl.andrewl.railsignalapi.model.component.Position; diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchConfigurationResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SwitchConfigurationResponse.java similarity index 86% rename from src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchConfigurationResponse.java rename to src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SwitchConfigurationResponse.java index f65a5b6..eab8df2 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchConfigurationResponse.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SwitchConfigurationResponse.java @@ -1,4 +1,4 @@ -package nl.andrewl.railsignalapi.rest.dto.component; +package nl.andrewl.railsignalapi.rest.dto.component.out; import nl.andrewl.railsignalapi.model.component.SwitchConfiguration; diff --git a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchResponse.java b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SwitchResponse.java similarity index 90% rename from src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchResponse.java rename to src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SwitchResponse.java index b854bc0..efb04a1 100644 --- a/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/SwitchResponse.java +++ b/src/main/java/nl/andrewl/railsignalapi/rest/dto/component/out/SwitchResponse.java @@ -1,4 +1,4 @@ -package nl.andrewl.railsignalapi.rest.dto.component; +package nl.andrewl.railsignalapi.rest.dto.component.out; import nl.andrewl.railsignalapi.model.component.Switch; diff --git a/src/main/java/nl/andrewl/railsignalapi/service/ComponentService.java b/src/main/java/nl/andrewl/railsignalapi/service/ComponentService.java index c39803c..6d4ba3a 100644 --- a/src/main/java/nl/andrewl/railsignalapi/service/ComponentService.java +++ b/src/main/java/nl/andrewl/railsignalapi/service/ComponentService.java @@ -11,8 +11,8 @@ import nl.andrewl.railsignalapi.dao.SwitchConfigurationRepository; import nl.andrewl.railsignalapi.model.RailSystem; import nl.andrewl.railsignalapi.model.Segment; import nl.andrewl.railsignalapi.model.component.*; -import nl.andrewl.railsignalapi.rest.dto.component.ComponentResponse; -import nl.andrewl.railsignalapi.rest.dto.component.SimpleComponentResponse; +import nl.andrewl.railsignalapi.rest.dto.PathNodeUpdatePayload; +import nl.andrewl.railsignalapi.rest.dto.component.out.ComponentResponse; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -31,10 +31,10 @@ public class ComponentService { private final SwitchConfigurationRepository switchConfigurationRepository; @Transactional(readOnly = true) - public List getComponents(long rsId) { + public List getComponents(long rsId) { var rs = railSystemRepository.findById(rsId) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); - return componentRepository.findAllByRailSystem(rs).stream().map(SimpleComponentResponse::new).toList(); + return componentRepository.findAllByRailSystem(rs).stream().map(ComponentResponse::of).toList(); } @Transactional(readOnly = true) @@ -123,7 +123,22 @@ public class ComponentService { } @Transactional - public void remove(long rsId, long componentId) { - + public ComponentResponse updatePath(long rsId, long cId, PathNodeUpdatePayload payload) { + var c = componentRepository.findByIdAndRailSystemId(cId, rsId) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); + if (!(c instanceof PathNode p)) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Component is not a PathNode."); + Set newNodes = new HashSet<>(); + for (var id : payload.connectedNodeIds()) { + var c1 = componentRepository.findByIdAndRailSystemId(id, rsId); + if (c1.isPresent() && c1.get() instanceof PathNode pn) { + newNodes.add(pn); + } else { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Component with id " + id + " is not a PathNode in the same rail system."); + } + } + p.getConnectedNodes().retainAll(newNodes); + p.getConnectedNodes().addAll(newNodes); + p = componentRepository.save(p); + return ComponentResponse.of(p); } } diff --git a/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java b/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java index 3b7dfe7..fbad3a2 100644 --- a/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java +++ b/src/main/java/nl/andrewl/railsignalapi/service/SegmentService.java @@ -6,7 +6,7 @@ import nl.andrewl.railsignalapi.dao.RailSystemRepository; import nl.andrewl.railsignalapi.dao.SegmentRepository; import nl.andrewl.railsignalapi.model.Segment; import nl.andrewl.railsignalapi.model.component.Component; -import nl.andrewl.railsignalapi.rest.SegmentPayload; +import nl.andrewl.railsignalapi.rest.dto.SegmentPayload; import nl.andrewl.railsignalapi.rest.dto.FullSegmentResponse; import nl.andrewl.railsignalapi.rest.dto.SegmentResponse; import org.springframework.http.HttpStatus; @@ -53,5 +53,6 @@ public class SegmentService { .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND)); componentRepository.deleteAll(segment.getSignals()); componentRepository.deleteAll(segment.getBoundaryNodes()); + segmentRepository.delete(segment); } } From a02758ecd4e6baad2cd0aa7e74ec618b5fec277b Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sun, 8 May 2022 01:37:25 +0200 Subject: [PATCH 4/7] Added proper connection management. --- railsignal-app/src/components/RailSystem.vue | 2 +- .../src/components/railsystem/canvasUtils.js | 16 ++++ .../railsystem/component/ComponentView.vue | 8 +- .../component/PathNodeComponentView.vue | 48 ++++++++++ .../src/components/railsystem/drawing.js | 87 +++++++++++++++++++ .../src/components/railsystem/mapRenderer.js | 68 +++------------ railsignal-app/src/stores/railSystemsStore.js | 54 +++++++++++- .../model/component/PathNode.java | 7 +- .../rest/dto/PathNodeUpdatePayload.java | 11 ++- .../service/ComponentService.java | 22 ++++- 10 files changed, 248 insertions(+), 75 deletions(-) create mode 100644 railsignal-app/src/components/railsystem/canvasUtils.js create mode 100644 railsignal-app/src/components/railsystem/drawing.js diff --git a/railsignal-app/src/components/RailSystem.vue b/railsignal-app/src/components/RailSystem.vue index 079b94e..11a5da4 100644 --- a/railsignal-app/src/components/RailSystem.vue +++ b/railsignal-app/src/components/RailSystem.vue @@ -2,7 +2,7 @@

{{railSystem.name}}

- +
diff --git a/railsignal-app/src/components/railsystem/canvasUtils.js b/railsignal-app/src/components/railsystem/canvasUtils.js new file mode 100644 index 0000000..726b394 --- /dev/null +++ b/railsignal-app/src/components/railsystem/canvasUtils.js @@ -0,0 +1,16 @@ +export function roundedRect(ctx, x, y, w, h, r) { + if (w < 2 * r) r = w / 2; + if (h < 2 * r) r = h / 2; + ctx.beginPath(); + ctx.moveTo(x+r, y); + ctx.arcTo(x+w, y, x+w, y+h, r); + ctx.arcTo(x+w, y+h, x, y+h, r); + ctx.arcTo(x, y+h, x, y, r); + ctx.arcTo(x, y, x+w, y, r); + ctx.closePath(); +} + +export function circle(ctx, x, y, r) { + ctx.beginPath(); + ctx.arc(x, y, r, 0, Math.PI * 2); +} \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/ComponentView.vue b/railsignal-app/src/components/railsystem/component/ComponentView.vue index 5332df1..d6fc2ed 100644 --- a/railsignal-app/src/components/railsystem/component/ComponentView.vue +++ b/railsignal-app/src/components/railsystem/component/ComponentView.vue @@ -15,16 +15,16 @@

- + + + \ No newline at end of file diff --git a/railsignal-app/src/components/ConfirmModal.vue b/railsignal-app/src/components/ConfirmModal.vue new file mode 100644 index 0000000..9df94b8 --- /dev/null +++ b/railsignal-app/src/components/ConfirmModal.vue @@ -0,0 +1,77 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/RailSystem.vue b/railsignal-app/src/components/RailSystem.vue index 11a5da4..a7870e8 100644 --- a/railsignal-app/src/components/RailSystem.vue +++ b/railsignal-app/src/components/RailSystem.vue @@ -1,26 +1,25 @@ - - \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/AddRailSystemModal.vue b/railsignal-app/src/components/railsystem/AddRailSystemModal.vue new file mode 100644 index 0000000..d5e93fc --- /dev/null +++ b/railsignal-app/src/components/railsystem/AddRailSystemModal.vue @@ -0,0 +1,77 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/AddSegment.vue b/railsignal-app/src/components/railsystem/AddSegment.vue deleted file mode 100644 index f3c3aa1..0000000 --- a/railsignal-app/src/components/railsystem/AddSegment.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - - - \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/AddSegmentModal.vue b/railsignal-app/src/components/railsystem/AddSegmentModal.vue new file mode 100644 index 0000000..2133137 --- /dev/null +++ b/railsignal-app/src/components/railsystem/AddSegmentModal.vue @@ -0,0 +1,86 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/MapView.vue b/railsignal-app/src/components/railsystem/MapView.vue index 8a61c5c..3ce9e1a 100644 --- a/railsignal-app/src/components/railsystem/MapView.vue +++ b/railsignal-app/src/components/railsystem/MapView.vue @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/RailSystemPropertiesView.vue b/railsignal-app/src/components/railsystem/RailSystemPropertiesView.vue new file mode 100644 index 0000000..29c1edd --- /dev/null +++ b/railsignal-app/src/components/railsystem/RailSystemPropertiesView.vue @@ -0,0 +1,73 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/SegmentsView.vue b/railsignal-app/src/components/railsystem/SegmentsView.vue index 65e5673..e8237c9 100644 --- a/railsignal-app/src/components/railsystem/SegmentsView.vue +++ b/railsignal-app/src/components/railsystem/SegmentsView.vue @@ -1,22 +1,22 @@ - - \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/AddSegmentBoundaryModal.vue b/railsignal-app/src/components/railsystem/component/AddSegmentBoundaryModal.vue new file mode 100644 index 0000000..7b1add1 --- /dev/null +++ b/railsignal-app/src/components/railsystem/component/AddSegmentBoundaryModal.vue @@ -0,0 +1,112 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/AddSignal.vue b/railsignal-app/src/components/railsystem/component/AddSignal.vue deleted file mode 100644 index 6313c47..0000000 --- a/railsignal-app/src/components/railsystem/component/AddSignal.vue +++ /dev/null @@ -1,62 +0,0 @@ - - - - - \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/AddSignalModal.vue b/railsignal-app/src/components/railsystem/component/AddSignalModal.vue new file mode 100644 index 0000000..dcfe659 --- /dev/null +++ b/railsignal-app/src/components/railsystem/component/AddSignalModal.vue @@ -0,0 +1,98 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/AddSwitchModal.vue b/railsignal-app/src/components/railsystem/component/AddSwitchModal.vue new file mode 100644 index 0000000..0a95194 --- /dev/null +++ b/railsignal-app/src/components/railsystem/component/AddSwitchModal.vue @@ -0,0 +1,122 @@ + + + + + \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/ComponentView.vue b/railsignal-app/src/components/railsystem/component/ComponentView.vue index d6fc2ed..d830c21 100644 --- a/railsignal-app/src/components/railsystem/component/ComponentView.vue +++ b/railsignal-app/src/components/railsystem/component/ComponentView.vue @@ -1,23 +1,42 @@ \ No newline at end of file diff --git a/railsignal-app/src/components/railsystem/component/PathNodeComponentView.vue b/railsignal-app/src/components/railsystem/component/PathNodeComponentView.vue index 7fa0f01..6be36af 100644 --- a/railsignal-app/src/components/railsystem/component/PathNodeComponentView.vue +++ b/railsignal-app/src/components/railsystem/component/PathNodeComponentView.vue @@ -1,22 +1,39 @@ diff --git a/railsignal-app/src/components/railsystem/component/SegmentBoundaryNodeComponentView.vue b/railsignal-app/src/components/railsystem/component/SegmentBoundaryNodeComponentView.vue index b2f3ba9..18a2dfa 100644 --- a/railsignal-app/src/components/railsystem/component/SegmentBoundaryNodeComponentView.vue +++ b/railsignal-app/src/components/railsystem/component/SegmentBoundaryNodeComponentView.vue @@ -1,10 +1,17 @@