diff --git a/.gitattributes b/.gitattributes index a499c9a48..18b79c477 100644 --- a/.gitattributes +++ b/.gitattributes @@ -10,6 +10,7 @@ *.bsp binary *.bpc binary *.jar binary +*.fit binary *.fits binary *.ttf binary *.xcf binary diff --git a/.gitignore b/.gitignore index 2fc15a57c..f1644f555 100644 --- a/.gitignore +++ b/.gitignore @@ -188,3 +188,6 @@ libobjectbox*.so libobjectbox*.dylib # End of https://www.toptal.com/developers/gitignore/api/intellij,kotlin,java,gradle + +**/saved/*.png +**/saved/*.jpg diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 63359c790..7b3e29349 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -2,50 +2,52 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar plugins { kotlin("jvm") - id("org.springframework.boot") version "3.1.5" - id("io.spring.dependency-management") version "1.1.3" + id("org.springframework.boot") version "3.2.0" + id("io.spring.dependency-management") version "1.1.4" kotlin("plugin.spring") kotlin("kapt") } dependencies { + implementation(project(":nebulosa-astap")) + implementation(project(":nebulosa-astrometrynet")) implementation(project(":nebulosa-common")) implementation(project(":nebulosa-guiding-phd2")) implementation(project(":nebulosa-hips2fits")) implementation(project(":nebulosa-horizons")) implementation(project(":nebulosa-imaging")) implementation(project(":nebulosa-indi-client")) - implementation(project(":nebulosa-json")) + implementation(project(":nebulosa-log")) implementation(project(":nebulosa-lx200-protocol")) implementation(project(":nebulosa-nova")) - implementation(project(":nebulosa-platesolving-astap")) - implementation(project(":nebulosa-platesolving-astrometrynet")) - implementation(project(":nebulosa-platesolving-watney")) implementation(project(":nebulosa-sbd")) implementation(project(":nebulosa-simbad")) implementation(project(":nebulosa-stellarium-protocol")) + implementation(project(":nebulosa-watney")) implementation(project(":nebulosa-wcs")) - implementation(project(":nebulosa-log")) + implementation(libs.apache.codec) implementation(libs.csv) + implementation(libs.eventbus) + implementation(libs.flyway) implementation(libs.okhttp) implementation(libs.oshi) - implementation(libs.eventbus) - implementation(libs.apache.codec) implementation(libs.rx) implementation(libs.sqlite) - implementation(libs.flyway) implementation("org.springframework.boot:spring-boot-starter") - implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-web") { + exclude(module = "spring-boot-starter-tomcat") + } implementation("org.springframework.boot:spring-boot-starter-validation") - implementation("org.springframework.boot:spring-boot-starter-websocket") + implementation("org.springframework.boot:spring-boot-starter-websocket") { + exclude(module = "spring-boot-starter-tomcat") + } implementation("org.springframework.boot:spring-boot-starter-batch") implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-undertow") implementation("org.hibernate.orm:hibernate-community-dialects") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") - kapt("org.springframework:spring-context-indexer:6.0.13") - testImplementation(project(":nebulosa-skycatalog-hyg")) - testImplementation(project(":nebulosa-skycatalog-stellarium")) + kapt("org.springframework:spring-context-indexer:6.1.1") testImplementation(project(":nebulosa-test")) } diff --git a/api/src/main/kotlin/nebulosa/api/Main.kt b/api/src/main/kotlin/nebulosa/api/Main.kt index 480962364..b9eb21aaf 100644 --- a/api/src/main/kotlin/nebulosa/api/Main.kt +++ b/api/src/main/kotlin/nebulosa/api/Main.kt @@ -1,8 +1,7 @@ package nebulosa.api +import com.sun.jna.Platform import org.springframework.boot.runApplication -import oshi.PlatformEnum -import oshi.SystemInfo import java.nio.file.Path import java.time.LocalDate import java.time.format.DateTimeFormatter @@ -13,23 +12,15 @@ import kotlin.io.path.deleteIfExists import kotlin.io.path.exists import kotlin.io.path.listDirectoryEntries -fun initAppDirectory(): Path? { - val appPath = when (SystemInfo.getCurrentPlatform()) { - PlatformEnum.LINUX -> { - val userHomeDir = Path.of(System.getProperty("user.home")) - Path.of("$userHomeDir", ".nebulosa") - } - PlatformEnum.WINDOWS -> { - val documentsDir = FileSystemView.getFileSystemView().defaultDirectory.path - Path.of(documentsDir, "Nebulosa") - } - else -> return null +fun initAppDirectory(): Path { + val appPath = when { + Platform.isLinux() -> Path.of(System.getProperty("user.home"), ".nebulosa") + Platform.isWindows() -> Path.of(FileSystemView.getFileSystemView().defaultDirectory.path, "Nebulosa") + else -> throw IllegalStateException("unsupported operating system") } appPath.createDirectories() - System.setProperty("app.dir", "$appPath") - return appPath } diff --git a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentEvent.kt b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentEvent.kt index 6c5e48f42..340487f0b 100644 --- a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentEvent.kt +++ b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentEvent.kt @@ -1,11 +1,14 @@ package nebulosa.api.alignment.polar.darv +import nebulosa.api.sequencer.SequenceJobEvent import nebulosa.indi.device.camera.Camera import nebulosa.indi.device.guide.GuideOutput -sealed interface DARVPolarAlignmentEvent { +sealed interface DARVPolarAlignmentEvent : SequenceJobEvent { val camera: Camera val guideOutput: GuideOutput + + val state: DARVPolarAlignmentState } diff --git a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentExecutor.kt b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentExecutor.kt index 62433be0d..730aba73e 100644 --- a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentExecutor.kt +++ b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentExecutor.kt @@ -1,9 +1,12 @@ package nebulosa.api.alignment.polar.darv import io.reactivex.rxjava3.functions.Consumer +import nebulosa.api.alignment.polar.darv.DARVPolarAlignmentState.BACKWARD +import nebulosa.api.alignment.polar.darv.DARVPolarAlignmentState.FORWARD import nebulosa.api.cameras.CameraCaptureEvent import nebulosa.api.cameras.CameraStartCaptureRequest -import nebulosa.api.guiding.* +import nebulosa.api.guiding.GuidePulseEvent +import nebulosa.api.guiding.GuidePulseRequest import nebulosa.api.sequencer.* import nebulosa.api.sequencer.tasklets.delay.DelayElapsed import nebulosa.api.services.MessageService @@ -24,7 +27,6 @@ import org.springframework.core.task.SimpleAsyncTaskExecutor import org.springframework.stereotype.Component import java.nio.file.Path import java.util.* -import kotlin.time.Duration.Companion.seconds /** * @see Reference @@ -58,7 +60,7 @@ class DARVPolarAlignmentExecutor( val cameraRequest = CameraStartCaptureRequest( camera = camera, - exposureInMicroseconds = (request.exposureInSeconds + request.initialPauseInSeconds).seconds.inWholeMicroseconds, + exposureTime = request.exposureTime + request.initialPause, savePath = Path.of("$capturesPath", "${camera.name}-DARV.fits") ) @@ -66,8 +68,8 @@ class DARVPolarAlignmentExecutor( cameraExposureTasklet.subscribe(this) val cameraExposureFlow = sequenceFlowFactory.cameraExposure(cameraExposureTasklet) - val guidePulseDuration = (request.exposureInSeconds / 2.0).seconds.inWholeMilliseconds - val initialPauseDelayTasklet = sequenceTaskletFactory.delay(request.initialPauseInSeconds.seconds) + val guidePulseDuration = request.exposureTime.dividedBy(2L) + val initialPauseDelayTasklet = sequenceTaskletFactory.delay(request.initialPause) initialPauseDelayTasklet.subscribe(this) val direction = if (request.reversed) request.direction.reversed else request.direction @@ -123,26 +125,13 @@ class DARVPolarAlignmentExecutor( val messageEvent = when (event) { // Initial pulse event. - is DelayElapsed -> { - DARVPolarAlignmentInitialPauseElapsed(camera, guideOutput, event) - } + is DelayElapsed -> DARVPolarAlignmentInitialPauseElapsed(camera, guideOutput, event) // Forward & backward guide pulse event. is GuidePulseEvent -> { val direction = event.tasklet.request.direction - val duration = event.tasklet.request.durationInMilliseconds - val forward = (direction == data.direction) != data.reversed - - when (event) { - is GuidePulseStarted -> { - DARVPolarAlignmentGuidePulseElapsed(camera, guideOutput, forward, direction, duration, 0.0) - } - is GuidePulseElapsed -> { - DARVPolarAlignmentGuidePulseElapsed(camera, guideOutput, forward, direction, event.remainingTime, event.progress) - } - is GuidePulseFinished -> { - DARVPolarAlignmentGuidePulseElapsed(camera, guideOutput, forward, direction, 0L, 1.0) - } - } + val duration = event.tasklet.request.duration + val state = if ((direction == data.direction) != data.reversed) FORWARD else BACKWARD + DARVPolarAlignmentGuidePulseElapsed(camera, guideOutput, state, direction, duration, event.progress, event.jobExecution) } is CameraCaptureEvent -> event else -> return @@ -153,12 +142,12 @@ class DARVPolarAlignmentExecutor( override fun beforeJob(jobExecution: JobExecution) { val (camera, guideOutput) = sequenceJobWithId(jobExecution.jobId) ?: return - messageService.sendMessage(DARVPolarAlignmentStarted(camera, guideOutput)) + messageService.sendMessage(DARVPolarAlignmentStarted(camera, guideOutput, jobExecution)) } override fun afterJob(jobExecution: JobExecution) { val (camera, guideOutput) = sequenceJobWithId(jobExecution.jobId) ?: return - messageService.sendMessage(DARVPolarAlignmentFinished(camera, guideOutput)) + messageService.sendMessage(DARVPolarAlignmentFinished(camera, guideOutput, jobExecution)) } override fun iterator(): Iterator { diff --git a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentFinished.kt b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentFinished.kt index 5e9f2d353..016a753ed 100644 --- a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentFinished.kt +++ b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentFinished.kt @@ -4,11 +4,17 @@ import com.fasterxml.jackson.annotation.JsonIgnore import nebulosa.api.services.MessageEvent import nebulosa.indi.device.camera.Camera import nebulosa.indi.device.guide.GuideOutput +import org.springframework.batch.core.JobExecution data class DARVPolarAlignmentFinished( override val camera: Camera, override val guideOutput: GuideOutput, + @JsonIgnore override val jobExecution: JobExecution, ) : MessageEvent, DARVPolarAlignmentEvent { - @JsonIgnore override val eventName = "DARV_POLAR_ALIGNMENT_FINISHED" + override val progress = 1.0 + + override val state = DARVPolarAlignmentState.IDLE + + override val eventName = "DARV_POLAR_ALIGNMENT_FINISHED" } diff --git a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentGuidePulseElapsed.kt b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentGuidePulseElapsed.kt index 6f13686a3..b06cc2d0c 100644 --- a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentGuidePulseElapsed.kt +++ b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentGuidePulseElapsed.kt @@ -5,15 +5,18 @@ import nebulosa.api.services.MessageEvent import nebulosa.guiding.GuideDirection import nebulosa.indi.device.camera.Camera import nebulosa.indi.device.guide.GuideOutput +import org.springframework.batch.core.JobExecution +import java.time.Duration data class DARVPolarAlignmentGuidePulseElapsed( override val camera: Camera, override val guideOutput: GuideOutput, - val forward: Boolean, + override val state: DARVPolarAlignmentState, val direction: GuideDirection, - val remainingTime: Long, - val progress: Double, + val remainingTime: Duration, + override val progress: Double, + @JsonIgnore override val jobExecution: JobExecution, ) : MessageEvent, DARVPolarAlignmentEvent { - @JsonIgnore override val eventName = "DARV_POLAR_ALIGNMENT_GUIDE_PULSE_ELAPSED" + override val eventName = "DARV_POLAR_ALIGNMENT_UPDATED" } diff --git a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentInitialPauseElapsed.kt b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentInitialPauseElapsed.kt index c8aff9dea..bebe3d285 100644 --- a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentInitialPauseElapsed.kt +++ b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentInitialPauseElapsed.kt @@ -1,23 +1,28 @@ package nebulosa.api.alignment.polar.darv import com.fasterxml.jackson.annotation.JsonIgnore -import nebulosa.api.sequencer.DelayEvent +import nebulosa.api.sequencer.tasklets.delay.DelayEvent import nebulosa.api.services.MessageEvent import nebulosa.indi.device.camera.Camera import nebulosa.indi.device.guide.GuideOutput +import org.springframework.batch.core.JobExecution +import java.time.Duration data class DARVPolarAlignmentInitialPauseElapsed( override val camera: Camera, override val guideOutput: GuideOutput, - val pauseTime: Long, - val remainingTime: Long, - val progress: Double, + val pauseTime: Duration, + val remainingTime: Duration, + override val progress: Double, + @JsonIgnore override val jobExecution: JobExecution, ) : MessageEvent, DARVPolarAlignmentEvent { constructor(camera: Camera, guideOutput: GuideOutput, delay: DelayEvent) : this( - camera, guideOutput, delay.waitTime.inWholeMicroseconds, - delay.remainingTime.inWholeMicroseconds, delay.progress + camera, guideOutput, delay.tasklet.duration, + delay.remainingTime, delay.progress, delay.jobExecution ) - @JsonIgnore override val eventName = "DARV_POLAR_ALIGNMENT_INITIAL_PAUSE_ELAPSED" + override val state = DARVPolarAlignmentState.INITIAL_PAUSE + + override val eventName = "DARV_POLAR_ALIGNMENT_UPDATED" } diff --git a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentStarted.kt b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentStarted.kt index 25d53a927..0ba1998f5 100644 --- a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentStarted.kt +++ b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentStarted.kt @@ -4,11 +4,17 @@ import com.fasterxml.jackson.annotation.JsonIgnore import nebulosa.api.services.MessageEvent import nebulosa.indi.device.camera.Camera import nebulosa.indi.device.guide.GuideOutput +import org.springframework.batch.core.JobExecution data class DARVPolarAlignmentStarted( override val camera: Camera, override val guideOutput: GuideOutput, + @JsonIgnore override val jobExecution: JobExecution, ) : MessageEvent, DARVPolarAlignmentEvent { - @JsonIgnore override val eventName = "DARV_POLAR_ALIGNMENT_STARTED" + override val progress = 0.0 + + override val state = DARVPolarAlignmentState.INITIAL_PAUSE + + override val eventName = "DARV_POLAR_ALIGNMENT_STARTED" } diff --git a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentState.kt b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentState.kt new file mode 100644 index 000000000..61ed4e749 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVPolarAlignmentState.kt @@ -0,0 +1,8 @@ +package nebulosa.api.alignment.polar.darv + +enum class DARVPolarAlignmentState { + IDLE, + INITIAL_PAUSE, + FORWARD, + BACKWARD, +} diff --git a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVStart.kt b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVStart.kt index 6d7bbc23d..cb5580384 100644 --- a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVStart.kt +++ b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVStart.kt @@ -4,13 +4,15 @@ import com.fasterxml.jackson.annotation.JsonIgnore import nebulosa.guiding.GuideDirection import nebulosa.indi.device.camera.Camera import nebulosa.indi.device.guide.GuideOutput -import org.hibernate.validator.constraints.Range +import org.hibernate.validator.constraints.time.DurationMax +import org.hibernate.validator.constraints.time.DurationMin +import java.time.Duration data class DARVStart( @JsonIgnore val camera: Camera? = null, @JsonIgnore val guideOutput: GuideOutput? = null, - @Range(min = 1, max = 600) val exposureInSeconds: Long = 0L, - @Range(min = 1, max = 60) val initialPauseInSeconds: Long = 0L, + @field:DurationMin(seconds = 1) @field:DurationMax(seconds = 600) val exposureTime: Duration = Duration.ZERO, + @field:DurationMin(seconds = 1) @field:DurationMax(seconds = 60) val initialPause: Duration = Duration.ZERO, val direction: GuideDirection = GuideDirection.NORTH, val reversed: Boolean = false, ) diff --git a/api/src/main/kotlin/nebulosa/api/atlas/BodyPosition.kt b/api/src/main/kotlin/nebulosa/api/atlas/BodyPosition.kt index bab53ff2f..a7d19bff8 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/BodyPosition.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/BodyPosition.kt @@ -1,22 +1,31 @@ package nebulosa.api.atlas +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import nebulosa.api.beans.converters.DegreeMinuteSecondSerializer +import nebulosa.api.beans.converters.HourMinuteSecondSerializer +import nebulosa.api.beans.converters.SignedDegreeMinuteSecondSerializer import nebulosa.constants.AU_KM import nebulosa.constants.SPEED_OF_LIGHT import nebulosa.horizons.HorizonsElement import nebulosa.horizons.HorizonsQuantity -import nebulosa.math.AngleFormatter +import nebulosa.math.Angle import nebulosa.math.deg -import nebulosa.math.format import nebulosa.nova.astrometry.Constellation import nebulosa.skycatalog.SkyObject data class BodyPosition( - val rightAscensionJ2000: String, - val declinationJ2000: String, - val rightAscension: String, - val declination: String, - val azimuth: String, - val altitude: String, + @JsonSerialize(using = HourMinuteSecondSerializer::class) + val rightAscensionJ2000: Angle, + @JsonSerialize(using = SignedDegreeMinuteSecondSerializer::class) + val declinationJ2000: Angle, + @JsonSerialize(using = HourMinuteSecondSerializer::class) + val rightAscension: Angle, + @JsonSerialize(using = SignedDegreeMinuteSecondSerializer::class) + val declination: Angle, + @JsonSerialize(using = DegreeMinuteSecondSerializer::class) + val azimuth: Angle, + @JsonSerialize(using = SignedDegreeMinuteSecondSerializer::class) + val altitude: Angle, val magnitude: Double, val constellation: Constellation, val distance: Double, @@ -42,12 +51,12 @@ data class BodyPosition( } return BodyPosition( - element.asDouble(HorizonsQuantity.ASTROMETRIC_RA).deg.format(AngleFormatter.HMS), - element.asDouble(HorizonsQuantity.ASTROMETRIC_DEC).deg.format(AngleFormatter.SIGNED_DMS), - element.asDouble(HorizonsQuantity.APPARENT_RA).deg.format(AngleFormatter.HMS), - element.asDouble(HorizonsQuantity.APPARENT_DEC).deg.format(AngleFormatter.SIGNED_DMS), - element.asDouble(HorizonsQuantity.APPARENT_AZ).deg.format(AngleFormatter.DMS), - element.asDouble(HorizonsQuantity.APPARENT_ALT).deg.format(AngleFormatter.SIGNED_DMS), + element.asDouble(HorizonsQuantity.ASTROMETRIC_RA).deg, + element.asDouble(HorizonsQuantity.ASTROMETRIC_DEC).deg, + element.asDouble(HorizonsQuantity.APPARENT_RA).deg, + element.asDouble(HorizonsQuantity.APPARENT_DEC).deg, + element.asDouble(HorizonsQuantity.APPARENT_AZ).deg, + element.asDouble(HorizonsQuantity.APPARENT_ALT).deg, element.asDouble(HorizonsQuantity.VISUAL_MAGNITUDE, SkyObject.UNKNOWN_MAGNITUDE), element.asEnum(HorizonsQuantity.CONSTELLATION, Constellation.AND), distance, distanceUnit, diff --git a/api/src/main/kotlin/nebulosa/api/atlas/DeepSkyObjectRepository.kt b/api/src/main/kotlin/nebulosa/api/atlas/DeepSkyObjectRepository.kt index 84b0c0c62..96ceb780e 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/DeepSkyObjectRepository.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/DeepSkyObjectRepository.kt @@ -2,13 +2,17 @@ package nebulosa.api.atlas import nebulosa.math.Angle import nebulosa.nova.astrometry.Constellation +import nebulosa.skycatalog.SkyObject import nebulosa.skycatalog.SkyObjectType import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository +import org.springframework.transaction.annotation.Isolation +import org.springframework.transaction.annotation.Transactional @Repository +@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE) interface DeepSkyObjectRepository : JpaRepository { @Query( @@ -20,15 +24,17 @@ interface DeepSkyObjectRepository : JpaRepository { "(:radius <= 0.0 OR acos(sin(dso.declinationJ2000) * sin(:declinationJ2000) + cos(dso.declinationJ2000) * cos(:declinationJ2000) * cos(dso.rightAscensionJ2000 - :rightAscensionJ2000)) <= :radius) " + "ORDER BY dso.magnitude ASC" ) + @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) fun search( text: String? = null, rightAscensionJ2000: Angle = 0.0, declinationJ2000: Angle = 0.0, radius: Angle = 0.0, constellation: Constellation? = null, - magnitudeMin: Double = -100.0, magnitudeMax: Double = 100.0, + magnitudeMin: Double = -SkyObject.UNKNOWN_MAGNITUDE, magnitudeMax: Double = SkyObject.UNKNOWN_MAGNITUDE, type: SkyObjectType? = null, pageable: Pageable = Pageable.unpaged(), ): List @Query("SELECT DISTINCT dso.type FROM DeepSkyObjectEntity dso") + @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) fun types(): List } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdater.kt similarity index 88% rename from api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt rename to api/src/main/kotlin/nebulosa/api/atlas/IERSUpdater.kt index 6d7262835..f607859d0 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdateTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/IERSUpdater.kt @@ -14,7 +14,7 @@ import kotlin.io.path.outputStream @Component @ThreadedTask -class IERSUpdateTask( +class IERSUpdater( private val dataPath: Path, private val httpClient: OkHttpClient, ) : Runnable { @@ -39,7 +39,7 @@ class IERSUpdateTask( LOG.info("downloading finals2000A.all") httpClient.newCall(request).execute().use { - it.body.byteStream().transferAndClose(outputStream()) + it.body!!.byteStream().transferAndClose(outputStream()) LOG.info("finals2000A.all loaded") } } catch (e: Throwable) { @@ -49,6 +49,6 @@ class IERSUpdateTask( companion object { - @JvmStatic private val LOG = loggerFor() + @JvmStatic private val LOG = loggerFor() } } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteRepository.kt b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteRepository.kt index 6c8d2da4f..fdb0f847a 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteRepository.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteRepository.kt @@ -4,8 +4,11 @@ import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository +import org.springframework.transaction.annotation.Isolation +import org.springframework.transaction.annotation.Transactional @Repository +@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE) interface SatelliteRepository : JpaRepository { @Query( @@ -14,6 +17,7 @@ interface SatelliteRepository : JpaRepository { " (:groupType = 0 OR s.group_type & :groupType != 0)", nativeQuery = true, ) + @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) fun search(text: String? = null, groupType: Long = 0L, page: Pageable): List fun search(text: String? = null, groups: List, page: Pageable): List { diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdater.kt similarity index 84% rename from api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt rename to api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdater.kt index 3ea1cc0cf..f80c548ee 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdateTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteUpdater.kt @@ -1,7 +1,6 @@ package nebulosa.api.atlas -import nebulosa.api.beans.annotations.ThreadedTask -import nebulosa.api.configs.ConfigRepository +import nebulosa.api.preferences.PreferenceService import nebulosa.log.loggerFor import okhttp3.OkHttpClient import okhttp3.Request @@ -9,10 +8,9 @@ import org.springframework.stereotype.Component import java.util.concurrent.CompletableFuture @Component -@ThreadedTask -class SatelliteUpdateTask( +class SatelliteUpdater( private val httpClient: OkHttpClient, - private val configRepository: ConfigRepository, + private val preferenceService: PreferenceService, private val satelliteRepository: SatelliteRepository, ) : Runnable { @@ -21,7 +19,7 @@ class SatelliteUpdateTask( } private fun isOutOfDate(): Boolean { - val updatedAt = configRepository.long(TLE_UPDATED_AT) ?: 0L + val updatedAt = preferenceService.satellitesUpdatedAt return System.currentTimeMillis() - updatedAt >= UPDATE_INTERVAL } @@ -30,7 +28,7 @@ class SatelliteUpdateTask( LOG.info("satellites is out of date") if (updateTLEs()) { - configRepository.save(TLE_UPDATED_AT, System.currentTimeMillis()) + preferenceService.satellitesUpdatedAt = System.currentTimeMillis() } else { LOG.warn("no satellites was updated") } @@ -75,7 +73,7 @@ class SatelliteUpdateTask( if (it.isSuccessful) { val lines = ArrayList(3) - for (line in it.body.byteStream().bufferedReader().lines()) { + for (line in it.body!!.byteStream().bufferedReader().lines()) { lines.add(line) if (lines.size == 3) { @@ -101,9 +99,8 @@ class SatelliteUpdateTask( companion object { - const val TLE_UPDATED_AT = "TLE_UPDATED_AT" const val UPDATE_INTERVAL = 1000L * 60 * 60 * 24 // 1 day - @JvmStatic private val LOG = loggerFor() + @JvmStatic private val LOG = loggerFor() } } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/AtlasController.kt b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasController.kt similarity index 65% rename from api/src/main/kotlin/nebulosa/api/atlas/AtlasController.kt rename to api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasController.kt index 60ea62d41..11756d57b 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/AtlasController.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasController.kt @@ -16,57 +16,47 @@ import java.time.LocalDateTime @RestController @RequestMapping("sky-atlas") -class AtlasController( - private val atlasService: AtlasService, +class SkyAtlasController( + private val skyAtlasService: SkyAtlasService, ) { @GetMapping("sun/image") fun imageOfSun(response: HttpServletResponse) { - atlasService.imageOfSun(response) + skyAtlasService.imageOfSun(response) } @GetMapping("sun/position") fun positionOfSun( @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, - ): BodyPosition { - return atlasService.positionOfSun(location, dateTime) - } + ) = skyAtlasService.positionOfSun(location, dateTime) @GetMapping("sun/altitude-points") fun altitudePointsOfSun( @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, @RequestParam(required = false, defaultValue = "1") @Valid @Min(1) stepSize: Int, - ): List { - return atlasService.altitudePointsOfSun(location, dateTime.toLocalDate(), stepSize) - } + ) = skyAtlasService.altitudePointsOfSun(location, dateTime.toLocalDate(), stepSize) @GetMapping("moon/position") fun positionOfMoon( @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, - ): BodyPosition { - return atlasService.positionOfMoon(location, dateTime) - } + ) = skyAtlasService.positionOfMoon(location, dateTime) @GetMapping("moon/altitude-points") fun altitudePointsOfMoon( @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, @RequestParam(required = false, defaultValue = "1") stepSize: Int, - ): List { - return atlasService.altitudePointsOfMoon(location, dateTime.toLocalDate(), stepSize) - } + ) = skyAtlasService.altitudePointsOfMoon(location, dateTime.toLocalDate(), stepSize) @GetMapping("planets/{code}/position") fun positionOfPlanet( @PathVariable code: String, @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, - ): BodyPosition { - return atlasService.positionOfPlanet(location, code, dateTime) - } + ) = skyAtlasService.positionOfPlanet(location, code, dateTime) @GetMapping("planets/{code}/altitude-points") fun altitudePointsOfPlanet( @@ -74,23 +64,17 @@ class AtlasController( @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, @RequestParam(required = false, defaultValue = "1") stepSize: Int, - ): List { - return atlasService.altitudePointsOfPlanet(location, code, dateTime.toLocalDate(), stepSize) - } + ) = skyAtlasService.altitudePointsOfPlanet(location, code, dateTime.toLocalDate(), stepSize) @GetMapping("minor-planets") - fun searchMinorPlanet(@RequestParam @Valid @NotBlank text: String): MinorPlanet { - return atlasService.searchMinorPlanet(text) - } + fun searchMinorPlanet(@RequestParam @Valid @NotBlank text: String) = skyAtlasService.searchMinorPlanet(text) @GetMapping("stars/{star}/position") fun positionOfStar( @EntityBy star: StarEntity, @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, - ): BodyPosition { - return atlasService.positionOfStar(location, star, dateTime) - } + ) = skyAtlasService.positionOfStar(location, star, dateTime) @GetMapping("stars/{star}/altitude-points") fun altitudePointsOfStar( @@ -98,9 +82,7 @@ class AtlasController( @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, @RequestParam(required = false, defaultValue = "1") stepSize: Int, - ): List { - return atlasService.altitudePointsOfStar(location, star, dateTime.toLocalDate(), stepSize) - } + ) = skyAtlasService.altitudePointsOfStar(location, star, dateTime.toLocalDate(), stepSize) @GetMapping("stars") fun searchStar( @@ -112,24 +94,20 @@ class AtlasController( @RequestParam(required = false, defaultValue = "-99.0") magnitudeMin: Double, @RequestParam(required = false, defaultValue = "99.0") magnitudeMax: Double, @RequestParam(required = false) type: SkyObjectType?, - ) = atlasService.searchStar( + ) = skyAtlasService.searchStar( text, rightAscension.hours, declination.deg, radius.deg, constellation, magnitudeMin, magnitudeMax, type, ) @GetMapping("stars/types") - fun starTypes(): List { - return atlasService.starTypes - } + fun starTypes() = skyAtlasService.starTypes @GetMapping("dsos/{dso}/position") fun positionOfDSO( @EntityBy dso: DeepSkyObjectEntity, @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, - ): BodyPosition { - return atlasService.positionOfDSO(location, dso, dateTime) - } + ) = skyAtlasService.positionOfDSO(location, dso, dateTime) @GetMapping("dsos/{dso}/altitude-points") fun altitudePointsOfDSO( @@ -137,9 +115,7 @@ class AtlasController( @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, @RequestParam(required = false, defaultValue = "1") stepSize: Int, - ): List { - return atlasService.altitudePointsOfDSO(location, dso, dateTime.toLocalDate(), stepSize) - } + ) = skyAtlasService.altitudePointsOfDSO(location, dso, dateTime.toLocalDate(), stepSize) @GetMapping("dsos") fun searchDSO( @@ -151,24 +127,53 @@ class AtlasController( @RequestParam(required = false, defaultValue = "-99.0") magnitudeMin: Double, @RequestParam(required = false, defaultValue = "99.0") magnitudeMax: Double, @RequestParam(required = false) type: SkyObjectType?, - ) = atlasService.searchDSO( + ) = skyAtlasService.searchDSO( text, rightAscension.hours, declination.deg, radius.deg, constellation, magnitudeMin, magnitudeMax, type, ) @GetMapping("dsos/types") - fun dsoTypes(): List { - return atlasService.dsoTypes - } + fun dsoTypes() = skyAtlasService.dsoTypes + + @GetMapping("simbad/{id}/position") + fun positionOfSimbad( + @PathVariable id: Long, + @EntityBy location: LocationEntity, + @DateAndTime dateTime: LocalDateTime, + ) = skyAtlasService.positionOfSimbad(location, id, dateTime) + + @GetMapping("simbad/{id}/altitude-points") + fun altitudePointsOfSimbad( + @PathVariable id: Long, + @EntityBy location: LocationEntity, + @DateAndTime dateTime: LocalDateTime, + @RequestParam(required = false, defaultValue = "1") stepSize: Int, + ) = skyAtlasService.altitudePointsOfSimbad(location, id, dateTime.toLocalDate(), stepSize) + + @GetMapping("simbad") + fun searchSimbad( + @RequestParam(required = false, defaultValue = "") text: String, + @RequestParam(required = false, defaultValue = "") rightAscension: String, + @RequestParam(required = false, defaultValue = "") declination: String, + @RequestParam(required = false, defaultValue = "0.0") radius: Double, + @RequestParam(required = false) constellation: Constellation?, + @RequestParam(required = false, defaultValue = "-99.0") magnitudeMin: Double, + @RequestParam(required = false, defaultValue = "99.0") magnitudeMax: Double, + @RequestParam(required = false) type: SkyObjectType?, + ) = skyAtlasService.searchSimbad( + text, rightAscension.hours, declination.deg, radius.deg, + constellation, magnitudeMin, magnitudeMax, type, + ) + + @GetMapping("simbad/types") + fun simbadTypes() = skyAtlasService.simbadTypes @GetMapping("satellites/{satellite}/position") fun positionOfSatellite( @EntityBy satellite: SatelliteEntity, @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, - ): BodyPosition { - return atlasService.positionOfSatellite(location, satellite, dateTime) - } + ) = skyAtlasService.positionOfSatellite(location, satellite, dateTime) @GetMapping("satellites/{satellite}/altitude-points") fun altitudePointsOfSatellite( @@ -176,23 +181,17 @@ class AtlasController( @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, @RequestParam(required = false, defaultValue = "1") stepSize: Int, - ): List { - return atlasService.altitudePointsOfSatellite(location, satellite, dateTime.toLocalDate(), stepSize) - } + ) = skyAtlasService.altitudePointsOfSatellite(location, satellite, dateTime.toLocalDate(), stepSize) @GetMapping("satellites") fun searchSatellites( @RequestParam(required = false, defaultValue = "") text: String, @RequestParam(name = "group", required = false) groups: List?, - ): List { - return atlasService.searchSatellites(text, groups ?: emptyList()) - } + ) = skyAtlasService.searchSatellites(text, groups ?: emptyList()) @GetMapping("twilight") fun twilight( @EntityBy location: LocationEntity, @DateAndTime dateTime: LocalDateTime, - ): Twilight { - return atlasService.twilight(location, dateTime.toLocalDate()) - } + ) = skyAtlasService.twilight(location, dateTime.toLocalDate()) } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/AtlasService.kt b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasService.kt similarity index 85% rename from api/src/main/kotlin/nebulosa/api/atlas/AtlasService.kt rename to api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasService.kt index 4d9aae469..8471ff9f7 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/AtlasService.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasService.kt @@ -13,14 +13,19 @@ import nebulosa.nova.astrometry.Body import nebulosa.nova.astrometry.Constellation import nebulosa.nova.position.GeographicPosition import nebulosa.sbd.SmallBodyDatabaseService +import nebulosa.simbad.SimbadEntry +import nebulosa.simbad.SimbadSearch +import nebulosa.simbad.SimbadService import nebulosa.skycatalog.SkyObject import nebulosa.skycatalog.SkyObjectType import okhttp3.OkHttpClient import okhttp3.Request import org.springframework.data.domain.Pageable +import org.springframework.http.HttpStatus import org.springframework.scheduling.annotation.EnableScheduling import org.springframework.scheduling.annotation.Scheduled import org.springframework.stereotype.Service +import org.springframework.web.server.ResponseStatusException import java.awt.image.BufferedImage import java.io.ByteArrayOutputStream import java.time.LocalDate @@ -33,23 +38,27 @@ import kotlin.math.hypot @Service @EnableScheduling -class AtlasService( +class SkyAtlasService( private val horizonsEphemerisProvider: HorizonsEphemerisProvider, private val bodyEphemerisProvider: BodyEphemerisProvider, private val smallBodyDatabaseService: SmallBodyDatabaseService, private val starRepository: StarRepository, private val deepSkyObjectRepository: DeepSkyObjectRepository, private val satelliteRepository: SatelliteRepository, + private val simbadService: SimbadService, private val httpClient: OkHttpClient, ) { private val positions = HashMap() + private val cachedSimbadEntries = HashMap() @Volatile private var sunImage = ByteArray(0) val starTypes by lazy { starRepository.types() } val dsoTypes by lazy { deepSkyObjectRepository.types() } + val simbadTypes by lazy { SkyObjectType.entries } + fun imageOfSun(output: HttpServletResponse) { output.contentType = "image/png" output.outputStream.write(sunImage) @@ -77,6 +86,14 @@ class AtlasService( .copy(magnitude = dso.magnitude, constellation = dso.constellation, distance = dso.distance.toLightYears, distanceUnit = "ly") } + fun positionOfSimbad(location: LocationEntity, id: Long, dateTime: LocalDateTime): BodyPosition { + val target = cachedSimbadEntries[id] ?: simbadService.search(SimbadSearch(id)).firstOrNull() + ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Cannot found simbad with id: [$id]") + cachedSimbadEntries[target.id] = target + return positionOfBody(target, location, dateTime)!! + .copy(magnitude = target.magnitude, constellation = target.constellation, distance = target.distance, distanceUnit = "ly") + } + fun positionOfSatellite(location: LocationEntity, satellite: SatelliteEntity, dateTime: LocalDateTime): BodyPosition { return positionOfBody("TLE@${satellite.tle}", location, dateTime)!! } @@ -157,6 +174,14 @@ class AtlasService( return altitudePointsOfBody(ephemeris, stepSize) } + fun altitudePointsOfSimbad(location: LocationEntity, id: Long, date: LocalDate, stepSize: Int): List { + val target = cachedSimbadEntries[id] ?: simbadService.search(SimbadSearch(id)).firstOrNull() + ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Cannot found simbad with id: [$id]") + cachedSimbadEntries[target.id] = target + val ephemeris = bodyEphemeris(target, location, LocalDateTime.of(date, LocalTime.now())) + return altitudePointsOfBody(ephemeris, stepSize) + } + fun altitudePointsOfSatellite(location: LocationEntity, satellite: SatelliteEntity, date: LocalDate, stepSize: Int): List { val ephemeris = bodyEphemeris("TLE@${satellite.tle}", location, LocalDateTime.of(date, LocalTime.now())) return altitudePointsOfBody(ephemeris, stepSize) @@ -206,6 +231,15 @@ class AtlasService( Pageable.ofSize(5000), ) + fun searchSimbad( + text: String? = null, + rightAscension: Angle = 0.0, declination: Angle = 0.0, radius: Angle = 0.0, + constellation: Constellation? = null, + magnitudeMin: Double = -SkyObject.UNKNOWN_MAGNITUDE, magnitudeMax: Double = SkyObject.UNKNOWN_MAGNITUDE, + type: SkyObjectType? = null, + ) = simbadService + .search(SimbadSearch(0, text, rightAscension, declination, radius, type?.let(::listOf), magnitudeMin, magnitudeMax, constellation, 5000)) + @Scheduled(fixedDelay = 15, timeUnit = TimeUnit.MINUTES) private fun refreshImageOfSun() { val request = Request.Builder() @@ -215,7 +249,7 @@ class AtlasService( val image = httpClient.newCall(request) .execute() .body - .use { ImageIO.read(it.byteStream()) } + .use { ImageIO.read(it!!.byteStream()) } .removeBackground() val bytes = ByteArrayOutputStream(1024 * 128) diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateFinished.kt b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateFinished.kt new file mode 100644 index 000000000..a351c304b --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateFinished.kt @@ -0,0 +1,8 @@ +package nebulosa.api.atlas + +import nebulosa.api.notification.NotificationEvent + +data class SkyAtlasUpdateFinished(override val body: String) : NotificationEvent { + + override val type = "SKY_ATLAS_UPDATE_FINISHED" +} diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdater.kt similarity index 75% rename from api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt rename to api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdater.kt index dcfbef515..e194de02c 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdateTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasUpdater.kt @@ -3,7 +3,8 @@ package nebulosa.api.atlas import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.ObjectMapper import nebulosa.api.beans.annotations.ThreadedTask -import nebulosa.api.configs.ConfigRepository +import nebulosa.api.preferences.PreferenceService +import nebulosa.api.services.MessageService import nebulosa.log.loggerFor import okhttp3.OkHttpClient import okhttp3.Request @@ -16,20 +17,26 @@ import kotlin.io.path.inputStream @Component @ThreadedTask -class SkyAtlasUpdateTask( +class SkyAtlasUpdater( private val objectMapper: ObjectMapper, - private val configRepository: ConfigRepository, + private val preferenceService: PreferenceService, private val starsRepository: StarRepository, private val deepSkyObjectRepository: DeepSkyObjectRepository, private val httpClient: OkHttpClient, private val dataPath: Path, + private val satelliteUpdater: SatelliteUpdater, + private val messageService: MessageService, ) : Runnable { override fun run() { - val databaseVersion = configRepository.text(DATABASE_VERSION_KEY) + satelliteUpdater.run() - if (databaseVersion != DATABASE_VERSION) { - LOG.info("Star/DSO database is out of date. currentVersion={}, newVersion={}", databaseVersion, DATABASE_VERSION) + val version = preferenceService.skyAtlasVersion + + if (version != DATABASE_VERSION) { + LOG.info("Star/DSO database is out of date. currentVersion={}, newVersion={}", version, DATABASE_VERSION) + + messageService.sendMessage(SkyAtlasUpdateFinished("Star/DSO database is being updated.")) starsRepository.deleteAllInBatch() deepSkyObjectRepository.deleteAllInBatch() @@ -37,7 +44,9 @@ class SkyAtlasUpdateTask( readStarsAndLoad() readDSOsAndLoad() - configRepository.save(DATABASE_VERSION_KEY, DATABASE_VERSION) + preferenceService.skyAtlasVersion = DATABASE_VERSION + + messageService.sendMessage(SkyAtlasUpdateFinished("Sky Atlas database was updated to version $DATABASE_VERSION.")) } else { LOG.info("Star/DSO database is up to date") } @@ -57,7 +66,7 @@ class SkyAtlasUpdateTask( .execute() .use { if (it.isSuccessful) { - loadStars(it.body.byteStream()) + loadStars(it.body!!.byteStream()) } } } @@ -84,7 +93,7 @@ class SkyAtlasUpdateTask( .execute() .use { if (it.isSuccessful) { - loadDSOs(it.body.byteStream()) + loadDSOs(it.body!!.byteStream()) } } } @@ -100,8 +109,7 @@ class SkyAtlasUpdateTask( companion object { const val DATABASE_VERSION = "2023.10.18" - const val DATABASE_VERSION_KEY = "DATABASE_VERSION" - @JvmStatic private val LOG = loggerFor() + @JvmStatic private val LOG = loggerFor() } } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/StarRepository.kt b/api/src/main/kotlin/nebulosa/api/atlas/StarRepository.kt index d32dcbb59..5b6160a59 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/StarRepository.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/StarRepository.kt @@ -8,8 +8,11 @@ import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository +import org.springframework.transaction.annotation.Isolation +import org.springframework.transaction.annotation.Transactional @Repository +@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE) interface StarRepository : JpaRepository { @Query( @@ -21,6 +24,7 @@ interface StarRepository : JpaRepository { "(:radius <= 0.0 OR acos(sin(star.declinationJ2000) * sin(:declinationJ2000) + cos(star.declinationJ2000) * cos(:declinationJ2000) * cos(star.rightAscensionJ2000 - :rightAscensionJ2000)) <= :radius) " + "ORDER BY star.magnitude ASC" ) + @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) fun search( text: String? = null, rightAscensionJ2000: Angle = 0.0, declinationJ2000: Angle = 0.0, radius: Angle = 0.0, @@ -31,5 +35,6 @@ interface StarRepository : JpaRepository { ): List @Query("SELECT DISTINCT star.type FROM StarEntity star") + @Transactional(readOnly = true, isolation = Isolation.READ_UNCOMMITTED) fun types(): List } diff --git a/api/src/main/kotlin/nebulosa/api/beans/EntityByMethodArgumentResolver.kt b/api/src/main/kotlin/nebulosa/api/beans/EntityByMethodArgumentResolver.kt index 2bd183e30..31f06c603 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/EntityByMethodArgumentResolver.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/EntityByMethodArgumentResolver.kt @@ -46,13 +46,12 @@ class EntityByMethodArgumentResolver( val parameterName = parameter.parameterName ?: "id" val parameterValue = webRequest.pathVariables()[parameterName] ?: webRequest.getParameter(parameterName) - ?: throw throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Parameter $parameterName is not mapped") val entity = entityByParameterValue(parameterType, parameterValue) if (entityBy.required && entity == null) { val message = "Cannot found a ${parameterType.simpleName} entity with name [$parameterValue]" - throw throw ResponseStatusException(HttpStatus.NOT_FOUND, message) + throw ResponseStatusException(HttpStatus.NOT_FOUND, message) } return entity diff --git a/api/src/main/kotlin/nebulosa/api/beans/ThreadedTaskBeanPostProcessor.kt b/api/src/main/kotlin/nebulosa/api/beans/ThreadedTaskBeanPostProcessor.kt index 7401a68e0..ac50ea599 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/ThreadedTaskBeanPostProcessor.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/ThreadedTaskBeanPostProcessor.kt @@ -8,14 +8,14 @@ import java.util.concurrent.CompletableFuture import java.util.concurrent.ExecutorService @Component -class ThreadedTaskBeanPostProcessor(private val singleTaskExecutorService: ExecutorService) : BeanPostProcessor { +class ThreadedTaskBeanPostProcessor(private val systemExecutorService: ExecutorService) : BeanPostProcessor { override fun postProcessAfterInitialization(bean: Any, beanName: String): Any { if (bean is Runnable && bean::class.java.isAnnotationPresent(ThreadedTask::class.java)) { LOG.info("threaded task scheduled. name={}", beanName) CompletableFuture - .runAsync(bean, singleTaskExecutorService) + .runAsync(bean, systemExecutorService) .whenComplete { _, e -> e?.printStackTrace() ?: LOG.info("threaded task finished. name={}", beanName) } } diff --git a/api/src/main/kotlin/nebulosa/api/beans/configurations/BeanConfiguration.kt b/api/src/main/kotlin/nebulosa/api/beans/configurations/BeanConfiguration.kt index f639f230b..b5afd2a0b 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/configurations/BeanConfiguration.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/configurations/BeanConfiguration.kt @@ -1,17 +1,20 @@ package nebulosa.api.beans.configurations import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.ser.std.StdSerializer import com.fasterxml.jackson.module.kotlin.kotlinModule import nebulosa.api.beans.DateAndTimeMethodArgumentResolver import nebulosa.api.beans.EntityByMethodArgumentResolver import nebulosa.common.concurrency.DaemonThreadFactory import nebulosa.common.concurrency.Incrementer +import nebulosa.common.json.PathDeserializer +import nebulosa.common.json.PathSerializer import nebulosa.guiding.Guider import nebulosa.guiding.phd2.PHD2Guider import nebulosa.hips2fits.Hips2FitsService import nebulosa.horizons.HorizonsService -import nebulosa.json.* -import nebulosa.json.converters.PathConverter import nebulosa.phd2.client.PHD2Client import nebulosa.sbd.SmallBodyDatabaseService import nebulosa.simbad.SimbadService @@ -58,13 +61,15 @@ class BeanConfiguration { fun cachePath(appPath: Path): Path = Path.of("$appPath", "cache").createDirectories() @Bean + @Suppress("UNCHECKED_CAST") fun kotlinModule( - serializers: List>, - deserializers: List>, - ) = kotlinModule() + serializers: List>, + deserializers: List>, + ): SimpleModule = kotlinModule() .apply { serializers.forEach { addSerializer(it) } } - .apply { deserializers.forEach { addDeserializer(it) } } - .addConverter(PathConverter) + .apply { deserializers.forEach { addDeserializer(it.handledType() as Class, it) } } + .addSerializer(PathSerializer) + .addDeserializer(Path::class.java, PathDeserializer) @Bean fun jackson2ObjectMapperBuilderCustomizer() = Jackson2ObjectMapperBuilderCustomizer { @@ -105,9 +110,6 @@ class BeanConfiguration { fun systemExecutorService(): ExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), DaemonThreadFactory) - @Bean - fun singleTaskExecutorService(): ExecutorService = Executors.newSingleThreadExecutor(DaemonThreadFactory) - @Bean fun eventBus(systemExecutorService: ExecutorService) = EventBus.builder() .sendNoSubscriberEvent(false) diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/AngleSerializer.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/AngleSerializer.kt new file mode 100644 index 000000000..cc670ef96 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/AngleSerializer.kt @@ -0,0 +1,15 @@ +package nebulosa.api.beans.converters + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import nebulosa.math.AngleFormatter +import nebulosa.math.format + +abstract class AngleSerializer(private val formatter: AngleFormatter) : StdSerializer(Double::class.java) { + + override fun serialize(value: Double?, gen: JsonGenerator, provider: SerializerProvider) { + if (value == null) gen.writeNull() + else gen.writeString(value.format(formatter)) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/DegreeMinuteSecondSerializer.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/DegreeMinuteSecondSerializer.kt new file mode 100644 index 000000000..a3b938465 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/DegreeMinuteSecondSerializer.kt @@ -0,0 +1,5 @@ +package nebulosa.api.beans.converters + +import nebulosa.math.AngleFormatter + +class DegreeMinuteSecondSerializer : AngleSerializer(AngleFormatter.DMS) diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/DurationInMicrosecondsDeserializer.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/DurationInMicrosecondsDeserializer.kt new file mode 100644 index 000000000..69820495a --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/DurationInMicrosecondsDeserializer.kt @@ -0,0 +1,19 @@ +package nebulosa.api.beans.converters + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.deser.std.NumberDeserializers +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import org.springframework.stereotype.Component +import java.time.Duration +import java.time.temporal.ChronoUnit + +@Component +class DurationInMicrosecondsDeserializer : StdDeserializer(Duration::class.java) { + + private val numberDeserializer = NumberDeserializers.LongDeserializer(Long::class.java, 0L) + + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Duration { + return Duration.of(numberDeserializer.deserialize(p, ctxt), ChronoUnit.MICROS) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/DurationInMicrosecondsSerializer.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/DurationInMicrosecondsSerializer.kt new file mode 100644 index 000000000..989ae9a02 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/DurationInMicrosecondsSerializer.kt @@ -0,0 +1,15 @@ +package nebulosa.api.beans.converters + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.ser.std.StdSerializer +import org.springframework.stereotype.Component +import java.time.Duration + +@Component +class DurationInMicrosecondsSerializer : StdSerializer(Duration::class.java) { + + override fun serialize(value: Duration?, gen: JsonGenerator, provider: SerializerProvider) { + value?.also { gen.writeNumber(it.toNanos() / 1000) } ?: gen.writeNull() + } +} diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/HourMinuteSecondSerializer.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/HourMinuteSecondSerializer.kt new file mode 100644 index 000000000..df0736d4b --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/HourMinuteSecondSerializer.kt @@ -0,0 +1,5 @@ +package nebulosa.api.beans.converters + +import nebulosa.math.AngleFormatter + +class HourMinuteSecondSerializer : AngleSerializer(AngleFormatter.HMS) diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertyConverter.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertySerializer.kt similarity index 79% rename from api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertyConverter.kt rename to api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertySerializer.kt index c3eb47f23..34631255b 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertyConverter.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertySerializer.kt @@ -2,14 +2,12 @@ package nebulosa.api.beans.converters import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.ser.std.StdSerializer import nebulosa.indi.device.Property -import nebulosa.json.ToJson import org.springframework.stereotype.Component @Component -class INDIPropertyConverter : ToJson> { - - override val type = Property::class.java +class INDIPropertySerializer : StdSerializer>(Property::class.java) { override fun serialize(value: Property<*>, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertyVectorConverter.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertyVectorSerializer.kt similarity index 86% rename from api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertyVectorConverter.kt rename to api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertyVectorSerializer.kt index 30a516fc9..ea511f9e0 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertyVectorConverter.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/INDIPropertyVectorSerializer.kt @@ -2,15 +2,13 @@ package nebulosa.api.beans.converters import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.ser.std.StdSerializer import nebulosa.indi.device.PropertyVector import nebulosa.indi.device.SwitchPropertyVector -import nebulosa.json.ToJson import org.springframework.stereotype.Component @Component -class INDIPropertyVectorConverter : ToJson> { - - override val type = PropertyVector::class.java +class INDIPropertyVectorSerializer : StdSerializer>(PropertyVector::class.java) { override fun serialize(value: PropertyVector<*, *>, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/PathAttributeConverter.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/PathAttributeConverter.kt new file mode 100644 index 000000000..243cefb6f --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/PathAttributeConverter.kt @@ -0,0 +1,13 @@ +package nebulosa.api.beans.converters + +import jakarta.persistence.AttributeConverter +import org.springframework.stereotype.Component +import java.nio.file.Path + +@Component +class PathAttributeConverter : AttributeConverter { + + override fun convertToDatabaseColumn(attribute: Path?) = attribute?.toString() + + override fun convertToEntityAttribute(dbData: String?) = dbData?.let(Path::of) +} diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/SignedDegreeMinuteSecondSerializer.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/SignedDegreeMinuteSecondSerializer.kt new file mode 100644 index 000000000..f76acdd5e --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/SignedDegreeMinuteSecondSerializer.kt @@ -0,0 +1,5 @@ +package nebulosa.api.beans.converters + +import nebulosa.math.AngleFormatter + +class SignedDegreeMinuteSecondSerializer : AngleSerializer(AngleFormatter.SIGNED_DMS) diff --git a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameController.kt b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameController.kt new file mode 100644 index 000000000..a694a7bce --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameController.kt @@ -0,0 +1,37 @@ +package nebulosa.api.calibration + +import nebulosa.api.beans.annotations.EntityBy +import nebulosa.indi.device.camera.Camera +import org.springframework.web.bind.annotation.* +import java.nio.file.Path + +@RestController +@RequestMapping("calibration-frames") +class CalibrationFrameController( + private val calibrationFrameService: CalibrationFrameService, +) { + + @GetMapping("{camera}") + fun groups(@EntityBy camera: Camera): List { + var id = 0 + val groupedFrames = calibrationFrameService.groupedCalibrationFrames(camera) + return groupedFrames.map { CalibrationFrameGroup(id++, it.key, it.value) } + } + + @PutMapping("{camera}") + fun upload(@EntityBy camera: Camera, @RequestParam path: Path): List { + return calibrationFrameService.upload(camera, path) + } + + @PatchMapping("{id}") + fun edit( + @PathVariable id: Long, + @RequestParam(required = false) path: String? = "", + @RequestParam enabled: Boolean, + ) = calibrationFrameService.edit(id, path, enabled) + + @DeleteMapping("{id}") + fun delete(@PathVariable id: Long) { + calibrationFrameService.delete(id) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameEntity.kt b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameEntity.kt new file mode 100644 index 000000000..b3cffb740 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameEntity.kt @@ -0,0 +1,22 @@ +package nebulosa.api.calibration + +import jakarta.persistence.* +import nebulosa.indi.device.camera.FrameType + +@Entity +@Table(name = "calibration_frames") +data class CalibrationFrameEntity( + @Id @Column(name = "id", columnDefinition = "INT8") var id: Long = 0L, + @Enumerated(EnumType.ORDINAL) @Column(name = "type", columnDefinition = "INT1") var type: FrameType = FrameType.LIGHT, + @Column(name = "camera", columnDefinition = "TEXT") var camera: String? = null, + @Column(name = "filter", columnDefinition = "TEXT") var filter: String? = null, + @Column(name = "exposure_time", columnDefinition = "INT8") var exposureTime: Long = 0L, + @Column(name = "temperature", columnDefinition = "REAL") var temperature: Double = 0.0, + @Column(name = "width", columnDefinition = "INT4") var width: Int = 0, + @Column(name = "height", columnDefinition = "INT4") var height: Int = 0, + @Column(name = "bin_x", columnDefinition = "INT1") var binX: Int = 0, + @Column(name = "bin_y", columnDefinition = "INT1") var binY: Int = 0, + @Column(name = "gain", columnDefinition = "REAL") var gain: Double = 0.0, + @Column(name = "path", columnDefinition = "TEXT") var path: String? = null, + @Column(name = "enabled", columnDefinition = "INT1") var enabled: Boolean = true, +) diff --git a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameGroup.kt b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameGroup.kt new file mode 100644 index 000000000..1320e3499 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameGroup.kt @@ -0,0 +1,7 @@ +package nebulosa.api.calibration + +data class CalibrationFrameGroup( + val id: Int, + val key: CalibrationGroupKey, + val frames: List, +) diff --git a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameRepository.kt b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameRepository.kt new file mode 100644 index 000000000..86c6455ba --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameRepository.kt @@ -0,0 +1,52 @@ +package nebulosa.api.calibration + +import nebulosa.indi.device.camera.Camera +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Modifying +import org.springframework.data.jpa.repository.Query +import org.springframework.stereotype.Repository +import org.springframework.transaction.annotation.Isolation +import org.springframework.transaction.annotation.Transactional + +@Repository +@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE) +interface CalibrationFrameRepository : JpaRepository { + + @Query("SELECT frame FROM CalibrationFrameEntity frame WHERE frame.camera = :#{#camera.name}") + fun findAll(camera: Camera): List + + @Modifying + @Query("DELETE FROM CalibrationFrameEntity frame WHERE frame.camera = :#{#camera.name} and frame.path = :path") + fun delete(camera: Camera, path: String) + + @Query( + "SELECT frame FROM CalibrationFrameEntity frame WHERE frame.type = 1 " + + "AND frame.enabled = TRUE AND frame.camera = :#{#camera.name} " + + "AND frame.width = :width " + + "AND frame.height = :height " + + "AND frame.binX = :bin AND frame.binY = :bin " + + "AND (:exposureTime <= 0 OR frame.exposureTime = :exposureTime) " + + "AND (:gain < 0.0 OR frame.gain = :gain)" + ) + @Transactional(readOnly = true, isolation = Isolation.READ_COMMITTED) + fun darkFrames(camera: Camera, width: Int, height: Int, bin: Int, exposureTime: Long, gain: Double): List + + @Query( + "SELECT frame FROM CalibrationFrameEntity frame WHERE frame.type = 3 " + + "AND frame.enabled = TRUE AND frame.camera = :#{#camera.name} " + + "AND frame.width = :width AND frame.height = :height " + + "AND frame.binX = :bin AND frame.binY = :bin " + + "AND (:gain < 0.0 OR frame.gain = :gain)" + ) + @Transactional(readOnly = true, isolation = Isolation.READ_COMMITTED) + fun biasFrames(camera: Camera, width: Int, height: Int, bin: Int, gain: Double): List + + @Query( + "SELECT frame FROM CalibrationFrameEntity frame WHERE frame.type = 2 " + + "AND frame.enabled = TRUE AND frame.camera = :#{#camera.name} AND frame.filter = :filter " + + "AND frame.width = :width AND frame.height = :height " + + "AND frame.binX = :bin AND frame.binY = :bin" + ) + @Transactional(readOnly = true, isolation = Isolation.READ_COMMITTED) + fun flatFrames(camera: Camera, filter: String, width: Int, height: Int, bin: Int): List +} diff --git a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameService.kt b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameService.kt new file mode 100644 index 000000000..ebf375059 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameService.kt @@ -0,0 +1,172 @@ +package nebulosa.api.calibration + +import nebulosa.fits.* +import nebulosa.imaging.Image +import nebulosa.imaging.algorithms.transformation.correction.BiasSubtraction +import nebulosa.imaging.algorithms.transformation.correction.DarkSubtraction +import nebulosa.imaging.algorithms.transformation.correction.FlatCorrection +import nebulosa.indi.device.camera.Camera +import nebulosa.indi.device.camera.FrameType +import nebulosa.log.loggerFor +import org.springframework.stereotype.Service +import java.nio.file.Path +import java.util.* +import kotlin.io.path.isDirectory +import kotlin.io.path.isRegularFile +import kotlin.io.path.listDirectoryEntries +import kotlin.math.abs +import kotlin.math.roundToInt + +// https://cdn.diffractionlimited.com/help/maximdl/Understanding_Calibration_Groups.htm +// https://www.astropy.org/ccd-reduction-and-photometry-guide/v/dev/notebooks/00-00-Preface.html + +@Service +class CalibrationFrameService( + private val calibrationFrameRepository: CalibrationFrameRepository, +) { + + fun calibrate(camera: Camera, image: Image, createNew: Boolean = false): Image { + val darkFrame = findBestDarkFrames(camera, image).firstOrNull() + val biasFrame = findBestBiasFrames(camera, image).firstOrNull() + val flatFrame = findBestFlatFrames(camera, image).firstOrNull() + + return if (darkFrame != null || biasFrame != null || flatFrame != null) { + var transformedImage = if (createNew) image.clone() else image + var calibrationImage = Image(transformedImage.width, transformedImage.height, Header(), transformedImage.mono) + + if (biasFrame != null) { + calibrationImage = Fits(biasFrame.path!!).also(Fits::read).use(calibrationImage::load) + transformedImage = transformedImage.transform(BiasSubtraction(calibrationImage)) + LOG.info("bias frame subtraction applied. frame={}", biasFrame) + } + + if (darkFrame != null) { + calibrationImage = Fits(darkFrame.path!!).also(Fits::read).use(calibrationImage::load) + transformedImage = transformedImage.transform(DarkSubtraction(calibrationImage)) + LOG.info("dark frame subtraction applied. frame={}", darkFrame) + } + + if (flatFrame != null) { + calibrationImage = Fits(flatFrame.path!!).also(Fits::read).use(calibrationImage::load) + transformedImage = transformedImage.transform(FlatCorrection(calibrationImage)) + LOG.info("flat frame correction applied. frame={}", flatFrame) + } + + transformedImage + } else { + image + } + } + + fun groupedCalibrationFrames(camera: Camera): Map> { + val frames = calibrationFrameRepository.findAll(camera) + return frames.groupBy(CalibrationGroupKey::from) + } + + fun upload(camera: Camera, path: Path): List { + val files = if (path.isRegularFile() && path.isFits) listOf(path) + else if (path.isDirectory()) path.listDirectoryEntries("*.{fits,fit}").filter { it.isRegularFile() } + else return emptyList() + + return upload(camera, files) + } + + @Synchronized + fun upload(camera: Camera, files: List): List { + val frames = ArrayList(files.size) + + for (file in files) { + calibrationFrameRepository.delete(camera, "$file") + + try { + Fits(file).also(Fits::read).use { fits -> + val (header) = fits.filterIsInstance().firstOrNull() ?: return@use + val frameType = header.frameType?.takeIf { it != FrameType.LIGHT } ?: return@use + + val exposureTime = if (frameType == FrameType.DARK) header.exposureTimeInMicroseconds else 0L + val temperature = if (frameType == FrameType.DARK) header.temperature else 999.0 + val gain = if (frameType != FrameType.FLAT) header.gain else 0.0 + val filter = if (frameType == FrameType.FLAT) header.filter else null + + val frame = CalibrationFrameEntity( + System.currentTimeMillis(), + frameType, camera.name, filter, + exposureTime, temperature, + header.width, header.height, header.binX, header.binY, + gain, "$file", + ) + + calibrationFrameRepository.saveAndFlush(frame) + .also(frames::add) + } + } catch (e: Throwable) { + LOG.error("cannot open FITS. path={}, message={}", file, e.message) + } + } + + return frames + } + + fun edit(id: Long, path: String?, enabled: Boolean): CalibrationFrameEntity { + return with(calibrationFrameRepository.findById(id).get()) { + if (!path.isNullOrBlank()) this.path = path + this.enabled = enabled + calibrationFrameRepository.saveAndFlush(this) + } + } + + fun delete(id: Long) { + calibrationFrameRepository.deleteById(id) + } + + // exposureTime, temperature, width, height, binX, binY, gain. + fun findBestDarkFrames(camera: Camera, image: Image): List { + val header = image.header + val temperature = header.temperature + + val frames = calibrationFrameRepository + .darkFrames(camera, image.width, image.height, header.binX, header.exposureTimeInMicroseconds, header.gain) + + if (frames.isEmpty()) return emptyList() + + // Closest temperature. + val groupedFrames = TreeMap>() + frames.groupByTo(groupedFrames) { abs(it.temperature - temperature).roundToInt() } + // TODO: Dont use if temperature is out of tolerance range + // TODO: Generate master from matched frames. + return groupedFrames.firstEntry().value + } + + // filter, width, height, binX, binY. + fun findBestFlatFrames(camera: Camera, image: Image): List { + val filter = image.header.filter ?: return emptyList() + + // TODO: Generate master from matched frames. + return calibrationFrameRepository + .flatFrames(camera, filter, image.width, image.height, image.header.binX) + } + + // width, height, binX, binY, gain. + fun findBestBiasFrames(camera: Camera, image: Image): List { + // TODO: Generate master from matched frames. + return calibrationFrameRepository + .biasFrames(camera, image.width, image.height, image.header.binX, image.header.gain) + } + + companion object { + + @JvmStatic private val LOG = loggerFor() + + @JvmStatic val Header.frameType + get() = frame?.uppercase()?.let { + if ("LIGHT" in it) FrameType.LIGHT + else if ("DARK" in it) FrameType.DARK + else if ("FLAT" in it) FrameType.FLAT + else if ("BIAS" in it) FrameType.BIAS + else null + } + + inline val Path.isFits + get() = "$this".let { it.endsWith(".fits") || it.endsWith(".fit") } + } +} diff --git a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationGroupKey.kt b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationGroupKey.kt new file mode 100644 index 000000000..dd659e464 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationGroupKey.kt @@ -0,0 +1,24 @@ +package nebulosa.api.calibration + +import nebulosa.indi.device.camera.FrameType +import kotlin.math.roundToInt + +data class CalibrationGroupKey( + val type: FrameType, val filter: String?, + val width: Int, val height: Int, + val binX: Int, val binY: Int, + val exposureTime: Long, + val temperature: Int, val gain: Double, +) { + + companion object { + + @JvmStatic + fun from(frame: CalibrationFrameEntity) = CalibrationGroupKey( + frame.type, frame.filter?.ifBlank { null }, + frame.width, frame.height, + frame.binX, frame.binY, frame.exposureTime, + frame.temperature.roundToInt(), frame.gain, + ) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureElapsed.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureElapsed.kt new file mode 100644 index 000000000..eae7f59d5 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureElapsed.kt @@ -0,0 +1,20 @@ +package nebulosa.api.cameras + +import com.fasterxml.jackson.annotation.JsonIgnore +import nebulosa.api.sequencer.SequenceStepEvent +import nebulosa.indi.device.camera.Camera +import org.springframework.batch.core.StepExecution +import java.time.Duration + +data class CameraCaptureElapsed( + override val camera: Camera, + val exposureCount: Int, + val remainingTime: Duration, + override val progress: Double, + val elapsedTime: Duration, + @JsonIgnore override val stepExecution: StepExecution, + @JsonIgnore override val tasklet: CameraExposureTasklet, +) : CameraCaptureEvent, SequenceStepEvent { + + override val eventName = "CAMERA_CAPTURE_ELAPSED" +} diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureEvent.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureEvent.kt index 6de16c81f..61e3d1e48 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureEvent.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureEvent.kt @@ -1,30 +1,11 @@ package nebulosa.api.cameras +import nebulosa.api.sequencer.SequenceJobEvent import nebulosa.api.sequencer.SequenceTaskletEvent import nebulosa.api.services.MessageEvent import nebulosa.indi.device.camera.Camera -sealed interface CameraCaptureEvent : MessageEvent, SequenceTaskletEvent { +sealed interface CameraCaptureEvent : MessageEvent, SequenceTaskletEvent, SequenceJobEvent { val camera: Camera - - companion object { - - const val WAIT_PROGRESS = "waitProgress" - const val WAIT_REMAINING_TIME = "waitRemainingTime" - const val WAIT_TIME = "waitTime" - - const val EXPOSURE_AMOUNT = "exposureAmount" - const val EXPOSURE_COUNT = "exposureCount" - const val EXPOSURE_TIME = "exposureTime" - const val EXPOSURE_REMAINING_TIME = "exposureRemainingTime" - const val EXPOSURE_PROGRESS = "exposureProgress" - - const val CAPTURE_TIME = "captureTime" - const val CAPTURE_REMAINING_TIME = "captureRemainingTime" - const val CAPTURE_PROGRESS = "captureProgress" - const val CAPTURE_IN_LOOP = "captureInLoop" - const val CAPTURE_IS_WAITING = "captureIsWaiting" - const val CAPTURE_ELAPSED_TIME = "captureElapsedTime" - } } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureEventConverter.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureEventConverter.kt deleted file mode 100644 index 2fc8c9c22..000000000 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureEventConverter.kt +++ /dev/null @@ -1,65 +0,0 @@ -package nebulosa.api.cameras - -import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.databind.SerializerProvider -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_ELAPSED_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_IN_LOOP -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_IS_WAITING -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_PROGRESS -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_REMAINING_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_AMOUNT -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_COUNT -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_PROGRESS -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_REMAINING_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.WAIT_PROGRESS -import nebulosa.api.cameras.CameraCaptureEvent.Companion.WAIT_REMAINING_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.WAIT_TIME -import nebulosa.api.sequencer.SequenceJobEvent -import nebulosa.api.sequencer.SequenceStepEvent -import nebulosa.json.ToJson -import org.springframework.stereotype.Component - -@Component -class CameraCaptureEventConverter : ToJson { - - override val type = CameraCaptureEvent::class.java - - override fun serialize(value: CameraCaptureEvent, gen: JsonGenerator, provider: SerializerProvider) { - gen.writeStartObject() - - gen.writeObjectField("camera", value.camera) - - val executionContext = when (value) { - is SequenceStepEvent -> value.stepExecution.executionContext - is SequenceJobEvent -> value.jobExecution.executionContext - else -> null - } - - if (executionContext != null) { - gen.writeNumberField(WAIT_PROGRESS, executionContext.getDouble(WAIT_PROGRESS, 0.0)) - gen.writeNumberField(WAIT_REMAINING_TIME, executionContext.getLong(WAIT_REMAINING_TIME, 0L)) - gen.writeNumberField(WAIT_TIME, executionContext.getLong(WAIT_TIME, 0L)) - - gen.writeNumberField(EXPOSURE_AMOUNT, executionContext.getInt(EXPOSURE_AMOUNT, 0)) - gen.writeNumberField(EXPOSURE_COUNT, executionContext.getInt(EXPOSURE_COUNT, 0)) - gen.writeNumberField(EXPOSURE_TIME, executionContext.getLong(EXPOSURE_TIME, 0L)) - gen.writeNumberField(EXPOSURE_REMAINING_TIME, executionContext.getLong(EXPOSURE_REMAINING_TIME, 0L)) - gen.writeNumberField(EXPOSURE_PROGRESS, executionContext.getDouble(EXPOSURE_PROGRESS, 0.0)) - - gen.writeNumberField(CAPTURE_TIME, executionContext.getLong(CAPTURE_TIME, 0L)) - gen.writeNumberField(CAPTURE_REMAINING_TIME, executionContext.getLong(CAPTURE_REMAINING_TIME, 0L)) - gen.writeNumberField(CAPTURE_PROGRESS, executionContext.getDouble(CAPTURE_PROGRESS, 0.0)) - gen.writeBooleanField(CAPTURE_IN_LOOP, executionContext.get(CAPTURE_IN_LOOP) == true) - gen.writeBooleanField(CAPTURE_IS_WAITING, executionContext.get(CAPTURE_IS_WAITING) == true) - gen.writeNumberField(CAPTURE_ELAPSED_TIME, executionContext.getLong(CAPTURE_ELAPSED_TIME, 0L)) - } - - if (value is CameraExposureFinished && value.savePath != null) { - gen.writeObjectField("savePath", value.savePath) - } - - gen.writeEndObject() - } -} diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureFinished.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureFinished.kt index 7bf488c1a..deeb6425a 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureFinished.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureFinished.kt @@ -1,7 +1,6 @@ package nebulosa.api.cameras import com.fasterxml.jackson.annotation.JsonIgnore -import nebulosa.api.sequencer.SequenceJobEvent import nebulosa.indi.device.camera.Camera import org.springframework.batch.core.JobExecution @@ -9,7 +8,9 @@ data class CameraCaptureFinished( override val camera: Camera, @JsonIgnore override val jobExecution: JobExecution, @JsonIgnore override val tasklet: CameraExposureTasklet, -) : CameraCaptureEvent, SequenceJobEvent { +) : CameraCaptureEvent { - @JsonIgnore override val eventName = "CAMERA_CAPTURE_FINISHED" + override val progress = 1.0 + + override val eventName = "CAMERA_CAPTURE_FINISHED" } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureUpdated.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureIsWaiting.kt similarity index 66% rename from api/src/main/kotlin/nebulosa/api/cameras/CameraExposureUpdated.kt rename to api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureIsWaiting.kt index 0ca8933c3..4af353c99 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureUpdated.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureIsWaiting.kt @@ -4,12 +4,16 @@ import com.fasterxml.jackson.annotation.JsonIgnore import nebulosa.api.sequencer.SequenceStepEvent import nebulosa.indi.device.camera.Camera import org.springframework.batch.core.StepExecution +import java.time.Duration -data class CameraExposureUpdated( +data class CameraCaptureIsWaiting( override val camera: Camera, + val waitDuration: Duration, + val remainingTime: Duration, + override val progress: Double, @JsonIgnore override val stepExecution: StepExecution, @JsonIgnore override val tasklet: CameraExposureTasklet, ) : CameraCaptureEvent, SequenceStepEvent { - @JsonIgnore override val eventName = "CAMERA_EXPOSURE_UPDATED" + override val eventName = "CAMERA_CAPTURE_WAITING" } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureStarted.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureStarted.kt index db6d16f17..370f38639 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureStarted.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureStarted.kt @@ -1,15 +1,25 @@ package nebulosa.api.cameras import com.fasterxml.jackson.annotation.JsonIgnore -import nebulosa.api.sequencer.SequenceJobEvent import nebulosa.indi.device.camera.Camera import org.springframework.batch.core.JobExecution +import java.time.Duration data class CameraCaptureStarted( override val camera: Camera, + val looping: Boolean, + val estimatedTime: Duration, @JsonIgnore override val jobExecution: JobExecution, @JsonIgnore override val tasklet: CameraExposureTasklet, -) : CameraCaptureEvent, SequenceJobEvent { +) : CameraCaptureEvent { - @JsonIgnore override val eventName = "CAMERA_CAPTURE_STARTED" + val exposureAmount + get() = tasklet.request.exposureAmount + + val exposureTime + get() = tasklet.request.exposureTime + + override val progress = 0.0 + + override val eventName = "CAMERA_CAPTURE_STARTED" } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraDeserializer.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraDeserializer.kt new file mode 100644 index 000000000..b79a4eb48 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraDeserializer.kt @@ -0,0 +1,28 @@ +package nebulosa.api.cameras + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import nebulosa.api.connection.ConnectionService +import nebulosa.indi.device.camera.Camera +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.annotation.Lazy +import org.springframework.stereotype.Component + +@Component +class CameraDeserializer : StdDeserializer(Camera::class.java) { + + @Autowired @Lazy private lateinit var connectionService: ConnectionService + + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Camera? { + val node = p.codec.readTree(p) + + val name = if (node.has("camera")) node.get("camera").asText() + else if (node.has("device")) node.get("device").asText() + else return null + + return if (name.isNullOrBlank()) null + else connectionService.camera(name) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraEventHandler.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraEventHandler.kt index a5cf51e88..98778f5c9 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraEventHandler.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraEventHandler.kt @@ -36,16 +36,16 @@ class CameraEventHandler( throttler.onNext(event) } is CameraAttached -> { - messageService.sendMessage(CAMERA_ATTACHED, event.device) + messageService.sendMessage(CameraMessageEvent(CAMERA_ATTACHED, event.device)) } is CameraDetached -> { - messageService.sendMessage(CAMERA_DETACHED, event.device) + messageService.sendMessage(CameraMessageEvent(CAMERA_DETACHED, event.device)) } } } fun sendUpdate(device: Camera) { - messageService.sendMessage(CAMERA_UPDATED, device) + messageService.sendMessage(CameraMessageEvent(CAMERA_UPDATED, device)) } companion object { diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureElapsed.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureElapsed.kt new file mode 100644 index 000000000..d34418b39 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureElapsed.kt @@ -0,0 +1,18 @@ +package nebulosa.api.cameras + +import com.fasterxml.jackson.annotation.JsonIgnore +import nebulosa.indi.device.camera.Camera +import org.springframework.batch.core.StepExecution +import java.time.Duration + +data class CameraExposureElapsed( + override val camera: Camera, + override val exposureCount: Int, + override val remainingTime: Duration, + override val progress: Double, + @JsonIgnore override val stepExecution: StepExecution, + @JsonIgnore override val tasklet: CameraExposureTasklet, +) : CameraExposureEvent { + + override val eventName = "CAMERA_EXPOSURE_ELAPSED" +} diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureEvent.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureEvent.kt new file mode 100644 index 000000000..aae3da0a4 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureEvent.kt @@ -0,0 +1,13 @@ +package nebulosa.api.cameras + +import nebulosa.api.sequencer.SequenceStepEvent +import java.time.Duration + +sealed interface CameraExposureEvent : CameraCaptureEvent, SequenceStepEvent { + + override val tasklet: CameraStartCaptureTasklet + + val exposureCount: Int + + val remainingTime: Duration +} diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureFinished.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureFinished.kt index 520213259..9e4b82d7b 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureFinished.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureFinished.kt @@ -1,18 +1,22 @@ package nebulosa.api.cameras import com.fasterxml.jackson.annotation.JsonIgnore -import nebulosa.api.sequencer.SequenceStepEvent -import nebulosa.imaging.Image import nebulosa.indi.device.camera.Camera import org.springframework.batch.core.StepExecution import java.nio.file.Path +import java.time.Duration data class CameraExposureFinished( override val camera: Camera, + override val exposureCount: Int, @JsonIgnore override val stepExecution: StepExecution, @JsonIgnore override val tasklet: CameraExposureTasklet, - val image: Image?, val savePath: Path?, -) : CameraCaptureEvent, SequenceStepEvent { + val savePath: Path?, +) : CameraExposureEvent { - @JsonIgnore override val eventName = "CAMERA_EXPOSURE_FINISHED" + override val remainingTime = Duration.ZERO!! + + override val progress = 1.0 + + override val eventName = "CAMERA_EXPOSURE_FINISHED" } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureStarted.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureStarted.kt index c18489ff5..94166488e 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureStarted.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureStarted.kt @@ -1,15 +1,20 @@ package nebulosa.api.cameras import com.fasterxml.jackson.annotation.JsonIgnore -import nebulosa.api.sequencer.SequenceStepEvent import nebulosa.indi.device.camera.Camera import org.springframework.batch.core.StepExecution data class CameraExposureStarted( override val camera: Camera, + override val exposureCount: Int, @JsonIgnore override val stepExecution: StepExecution, @JsonIgnore override val tasklet: CameraExposureTasklet, -) : CameraCaptureEvent, SequenceStepEvent { +) : CameraExposureEvent { - @JsonIgnore override val eventName = "CAMERA_EXPOSURE_STARTED" + override val remainingTime + get() = tasklet.request.exposureTime + + override val progress = 0.0 + + override val eventName = "CAMERA_EXPOSURE_STARTED" } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureTasklet.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureTasklet.kt index ade56d53a..2222b5538 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureTasklet.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureTasklet.kt @@ -1,24 +1,9 @@ package nebulosa.api.cameras import io.reactivex.rxjava3.functions.Consumer -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_ELAPSED_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_IN_LOOP -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_IS_WAITING -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_PROGRESS -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_REMAINING_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.CAPTURE_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_AMOUNT -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_COUNT -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_PROGRESS -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_REMAINING_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.EXPOSURE_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.WAIT_PROGRESS -import nebulosa.api.cameras.CameraCaptureEvent.Companion.WAIT_REMAINING_TIME -import nebulosa.api.cameras.CameraCaptureEvent.Companion.WAIT_TIME -import nebulosa.api.sequencer.SubjectSequenceTasklet -import nebulosa.api.sequencer.tasklets.delay.DelayElapsed +import nebulosa.api.sequencer.PublishSequenceTasklet +import nebulosa.api.sequencer.tasklets.delay.DelayEvent import nebulosa.common.concurrency.CountUpDownLatch -import nebulosa.imaging.Image import nebulosa.indi.device.camera.* import nebulosa.io.transferAndClose import nebulosa.log.loggerFor @@ -33,31 +18,29 @@ import org.springframework.batch.core.scope.context.ChunkContext import org.springframework.batch.repeat.RepeatStatus import java.io.InputStream import java.nio.file.Path +import java.time.Duration import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.concurrent.atomic.AtomicBoolean import kotlin.io.path.createParentDirectories import kotlin.io.path.outputStream -import kotlin.time.Duration -import kotlin.time.Duration.Companion.microseconds -import kotlin.time.Duration.Companion.seconds data class CameraExposureTasklet(override val request: CameraStartCaptureRequest) : - SubjectSequenceTasklet(), CameraStartCaptureTasklet, JobExecutionListener, Consumer { + PublishSequenceTasklet(), CameraStartCaptureTasklet, JobExecutionListener, Consumer { private val latch = CountUpDownLatch() private val aborted = AtomicBoolean() @Volatile private var exposureCount = 0 - @Volatile private var captureElapsedTime = 0L - @Volatile private var exposureElapsedTime = 0L + @Volatile private var captureElapsedTime = Duration.ZERO!! @Volatile private var stepExecution: StepExecution? = null private val camera = requireNotNull(request.camera) - private val exposureTime = request.exposureInMicroseconds.microseconds - private val exposureDelay = request.exposureDelayInSeconds.seconds - private val captureTime = if (request.isLoop) Duration.ZERO - else exposureTime * request.exposureAmount + exposureDelay * (request.exposureAmount - 1) + private val exposureTime = request.exposureTime + private val exposureDelay = request.exposureDelay + + private val estimatedTime = if (request.isLoop) Duration.ZERO + else Duration.ofNanos(exposureTime.toNanos() * request.exposureAmount + exposureDelay.toNanos() * (request.exposureAmount - 1)) @Subscribe(threadMode = ThreadMode.ASYNC) fun onCameraEvent(event: CameraEvent) { @@ -76,10 +59,8 @@ data class CameraExposureTasklet(override val request: CameraStartCaptureRequest is CameraExposureProgressChanged -> { val exposureRemainingTime = event.device.exposureTime val exposureElapsedTime = exposureTime - exposureRemainingTime - this.exposureElapsedTime = exposureElapsedTime.inWholeMicroseconds - - val exposureProgress = exposureElapsedTime / exposureTime - onCameraExposureUpdated(exposureRemainingTime, exposureProgress) + val exposureProgress = exposureElapsedTime.toNanos().toDouble() / exposureTime.toNanos() + onCameraExposureElapsed(exposureElapsedTime, exposureRemainingTime, exposureProgress) } } } @@ -88,9 +69,8 @@ data class CameraExposureTasklet(override val request: CameraStartCaptureRequest override fun beforeJob(jobExecution: JobExecution) { camera.enableBlob() EventBus.getDefault().register(this) - jobExecution.executionContext.put(CAPTURE_IN_LOOP, request.isLoop) - onNext(CameraCaptureStarted(camera, jobExecution, this)) - captureElapsedTime = 0L + onNext(CameraCaptureStarted(camera, request.isLoop, estimatedTime, jobExecution, this)) + captureElapsedTime = Duration.ZERO } override fun afterJob(jobExecution: JobExecution) { @@ -113,17 +93,10 @@ data class CameraExposureTasklet(override val request: CameraStartCaptureRequest latch.reset() } - override fun accept(event: DelayElapsed) { - captureElapsedTime += event.waitTime.inWholeMicroseconds - - with(stepExecution!!.executionContext) { - putDouble(WAIT_PROGRESS, event.progress) - putLong(WAIT_REMAINING_TIME, event.remainingTime.inWholeMicroseconds) - putLong(WAIT_TIME, event.waitTime.inWholeMicroseconds) - put(CAPTURE_IS_WAITING, true) - } - - onCameraExposureUpdated(Duration.ZERO, 1.0) + override fun accept(event: DelayEvent) { + captureElapsedTime += event.waitDuration + onNext(CameraCaptureIsWaiting(camera, event.waitDuration, event.remainingTime, event.progress, event.stepExecution, this)) + onCameraExposureElapsed(Duration.ZERO, Duration.ZERO, 1.0) } private fun executeCapture(contribution: StepContribution) { @@ -135,14 +108,7 @@ data class CameraExposureTasklet(override val request: CameraStartCaptureRequest exposureCount++ - with(contribution.stepExecution.executionContext) { - putInt(EXPOSURE_COUNT, exposureCount) - putInt(EXPOSURE_AMOUNT, request.exposureAmount) - put(CAPTURE_IN_LOOP, request.isLoop) - put(CAPTURE_IS_WAITING, false) - } - - onNext(CameraExposureStarted(camera, stepExecution!!, this)) + onNext(CameraExposureStarted(camera, exposureCount, stepExecution!!, this)) if (request.width > 0 && request.height > 0) { camera.frame(request.x, request.y, request.width, request.height) @@ -155,22 +121,17 @@ data class CameraExposureTasklet(override val request: CameraStartCaptureRequest camera.offset(request.offset) camera.startCapture(exposureTime) - exposureElapsedTime = 0L - latch.await() - exposureElapsedTime = 0L - captureElapsedTime += exposureTime.inWholeMicroseconds + captureElapsedTime += exposureTime LOG.info("camera exposure finished") } } } - private fun save(inputStream: InputStream, stepExecution: StepExecution) { - val savePath = if (request.saveInMemory) { - request.savePath - } else if (request.autoSave) { + private fun save(stream: InputStream, stepExecution: StepExecution) { + val savePath = if (request.autoSave) { val now = LocalDateTime.now() val fileName = "%s-%s.fits".format(now.format(DATE_TIME_FORMAT), request.frameType) Path.of("${request.savePath}", fileName) @@ -180,44 +141,30 @@ data class CameraExposureTasklet(override val request: CameraStartCaptureRequest } try { - if (request.saveInMemory) { - val image = Image.openFITS(inputStream) - onNext(CameraExposureFinished(camera, stepExecution, this, image, savePath)) - } else { - LOG.info("saving FITS at $savePath...") + LOG.info("saving FITS at $savePath...") - savePath!!.createParentDirectories() - inputStream.transferAndClose(savePath.outputStream()) + savePath.createParentDirectories() + stream.transferAndClose(savePath.outputStream()) - onNext(CameraExposureFinished(camera, stepExecution, this, null, savePath)) - } + onNext(CameraExposureFinished(camera, exposureCount, stepExecution, this, savePath)) } catch (e: Throwable) { LOG.error("failed to save FITS", e) aborted.set(true) } } - private fun onCameraExposureUpdated(exposureRemainingTime: Duration, exposureProgress: Double) { - val elapsedTime = (captureElapsedTime + exposureElapsedTime).microseconds + private fun onCameraExposureElapsed(elapsedTime: Duration, remainingTime: Duration, progress: Double) { + val totalElapsedTime = captureElapsedTime + elapsedTime var captureRemainingTime = Duration.ZERO var captureProgress = 0.0 if (!request.isLoop) { - captureRemainingTime = if (captureTime > elapsedTime) captureTime - elapsedTime else Duration.ZERO - captureProgress = (captureTime - captureRemainingTime) / captureTime - } - - with(stepExecution!!.executionContext) { - putLong(EXPOSURE_TIME, exposureTime.inWholeMicroseconds) - putLong(EXPOSURE_REMAINING_TIME, exposureRemainingTime.inWholeMicroseconds) - putDouble(EXPOSURE_PROGRESS, exposureProgress) - putLong(CAPTURE_TIME, captureTime.inWholeMicroseconds) - putLong(CAPTURE_REMAINING_TIME, captureRemainingTime.inWholeMicroseconds) - putDouble(CAPTURE_PROGRESS, captureProgress) - putLong(CAPTURE_ELAPSED_TIME, elapsedTime.inWholeMicroseconds) + captureRemainingTime = if (estimatedTime > totalElapsedTime) estimatedTime - totalElapsedTime else Duration.ZERO + captureProgress = (estimatedTime - captureRemainingTime).toNanos().toDouble() / estimatedTime.toNanos() } - onNext(CameraExposureUpdated(camera, stepExecution!!, this)) + onNext(CameraExposureElapsed(camera, exposureCount, remainingTime, progress, stepExecution!!, this)) + onNext(CameraCaptureElapsed(camera, exposureCount, captureRemainingTime, captureProgress, totalElapsedTime, stepExecution!!, this)) } companion object { diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraLoopExposureTasklet.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraLoopExposureTasklet.kt index f282bf592..69e95472a 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraLoopExposureTasklet.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraLoopExposureTasklet.kt @@ -1,19 +1,18 @@ package nebulosa.api.cameras -import nebulosa.api.sequencer.SubjectSequenceTasklet +import nebulosa.api.sequencer.PublishSequenceTasklet import nebulosa.api.sequencer.tasklets.delay.DelayTasklet import org.springframework.batch.core.JobExecution import org.springframework.batch.core.JobExecutionListener import org.springframework.batch.core.StepContribution import org.springframework.batch.core.scope.context.ChunkContext import org.springframework.batch.repeat.RepeatStatus -import kotlin.time.Duration.Companion.seconds data class CameraLoopExposureTasklet(override val request: CameraStartCaptureRequest) : - SubjectSequenceTasklet(), CameraStartCaptureTasklet, JobExecutionListener { + PublishSequenceTasklet(), CameraStartCaptureTasklet, JobExecutionListener { private val exposureTasklet = CameraExposureTasklet(request) - private val delayTasklet = DelayTasklet(request.exposureDelayInSeconds.seconds) + private val delayTasklet = DelayTasklet(request.exposureDelay) init { exposureTasklet.subscribe(this) diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraMessageEvent.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraMessageEvent.kt new file mode 100644 index 000000000..911a1fd85 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraMessageEvent.kt @@ -0,0 +1,9 @@ +package nebulosa.api.cameras + +import nebulosa.api.services.DeviceMessageEvent +import nebulosa.indi.device.camera.Camera + +data class CameraMessageEvent( + override val eventName: String, + override val device: Camera, +) : DeviceMessageEvent diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraConverter.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraSerializer.kt similarity index 73% rename from api/src/main/kotlin/nebulosa/api/cameras/CameraConverter.kt rename to api/src/main/kotlin/nebulosa/api/cameras/CameraSerializer.kt index 1078a59b3..162ca36df 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraConverter.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraSerializer.kt @@ -1,25 +1,14 @@ package nebulosa.api.cameras import com.fasterxml.jackson.core.JsonGenerator -import com.fasterxml.jackson.core.JsonParser -import com.fasterxml.jackson.databind.DeserializationContext -import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.SerializerProvider -import nebulosa.api.connection.ConnectionService +import com.fasterxml.jackson.databind.ser.std.StdSerializer import nebulosa.indi.device.camera.Camera -import nebulosa.json.FromJson -import nebulosa.json.ToJson -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.annotation.Lazy import org.springframework.stereotype.Component import java.nio.file.Path @Component -class CameraConverter(private val capturesPath: Path) : ToJson, FromJson { - - @Autowired @Lazy private lateinit var connectionService: ConnectionService - - override val type = Camera::class.java +class CameraSerializer(private val capturesPath: Path) : StdSerializer(Camera::class.java) { override fun serialize(value: Camera, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() @@ -36,10 +25,10 @@ class CameraConverter(private val capturesPath: Path) : ToJson, FromJson gen.writeNumberField("cfaOffsetX", value.cfaOffsetX) gen.writeNumberField("cfaOffsetY", value.cfaOffsetY) gen.writeStringField("cfaType", value.cfaType.name) - gen.writeNumberField("exposureMin", value.exposureMin.inWholeMicroseconds) - gen.writeNumberField("exposureMax", value.exposureMax.inWholeMicroseconds) + gen.writeNumberField("exposureMin", value.exposureMin.toNanos() / 1000L) + gen.writeNumberField("exposureMax", value.exposureMax.toNanos() / 1000L) gen.writeStringField("exposureState", value.exposureState.name) - gen.writeNumberField("exposureTime", value.exposureTime.inWholeMicroseconds) + gen.writeNumberField("exposureTime", value.exposureTime.toNanos() / 1000L) gen.writeBooleanField("hasCooler", value.hasCooler) gen.writeBooleanField("canSetTemperature", value.canSetTemperature) gen.writeBooleanField("canSubFrame", value.canSubFrame) @@ -76,15 +65,4 @@ class CameraConverter(private val capturesPath: Path) : ToJson, FromJson gen.writeObjectField("capturesPath", Path.of("$capturesPath", value.name)) gen.writeEndObject() } - - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Camera? { - val node = p.codec.readTree(p) - - val name = if (node.has("camera")) node.get("camera").asText() - else if (node.has("device")) node.get("device").asText() - else return null - - return if (name.isNullOrBlank()) null - else connectionService.camera(name) - } } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt index fe1da96d8..4eefe4b60 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt @@ -9,12 +9,13 @@ import nebulosa.indi.device.camera.Camera import nebulosa.indi.device.camera.FrameType import org.hibernate.validator.constraints.Range import java.nio.file.Path +import java.time.Duration data class CameraStartCaptureRequest( @JsonIgnore val camera: Camera? = null, - @field:Positive val exposureInMicroseconds: Long = 0L, + @field:Positive val exposureTime: Duration = Duration.ZERO, @field:Range(min = 0L, max = 1000L) val exposureAmount: Int = 1, // 0 = looping - @field:Range(min = 0L, max = 60L) val exposureDelayInSeconds: Long = 0L, + @field:Range(min = 0L, max = 60L) val exposureDelay: Duration = Duration.ZERO, @field:PositiveOrZero val x: Int = 0, @field:PositiveOrZero val y: Int = 0, @field:PositiveOrZero val width: Int = 0, @@ -28,7 +29,6 @@ data class CameraStartCaptureRequest( val autoSave: Boolean = false, val savePath: Path? = null, val autoSubFolderMode: AutoSubFolderMode = AutoSubFolderMode.OFF, - @JsonIgnore val saveInMemory: Boolean = savePath == null, @field:Valid val dither: DitherAfterExposureRequest = DitherAfterExposureRequest.DISABLED, ) { diff --git a/api/src/main/kotlin/nebulosa/api/configs/ConfigRepository.kt b/api/src/main/kotlin/nebulosa/api/configs/ConfigRepository.kt deleted file mode 100644 index 4e018408a..000000000 --- a/api/src/main/kotlin/nebulosa/api/configs/ConfigRepository.kt +++ /dev/null @@ -1,15 +0,0 @@ -package nebulosa.api.configs - -import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.repository.findByIdOrNull -import org.springframework.stereotype.Repository - -@Repository -interface ConfigRepository : JpaRepository { - - fun text(key: String) = findByIdOrNull(key)?.value - - fun long(key: String) = text(key)?.toLongOrNull() - - fun save(key: String, value: Any?) = saveAndFlush(ConfigEntity(key, value?.toString())) -} diff --git a/api/src/main/kotlin/nebulosa/api/focusers/FocuserEventHandler.kt b/api/src/main/kotlin/nebulosa/api/focusers/FocuserEventHandler.kt index 85749d55b..2b218cde3 100644 --- a/api/src/main/kotlin/nebulosa/api/focusers/FocuserEventHandler.kt +++ b/api/src/main/kotlin/nebulosa/api/focusers/FocuserEventHandler.kt @@ -36,16 +36,16 @@ class FocuserEventHandler( throttler.onNext(event) } is FocuserAttached -> { - messageService.sendMessage(FOCUSER_ATTACHED, event.device) + messageService.sendMessage(FocuserMessageEvent(FOCUSER_ATTACHED, event.device)) } is FocuserDetached -> { - messageService.sendMessage(FOCUSER_DETACHED, event.device) + messageService.sendMessage(FocuserMessageEvent(FOCUSER_DETACHED, event.device)) } } } fun sendUpdate(device: Focuser) { - messageService.sendMessage(FOCUSER_UPDATED, device) + messageService.sendMessage(FocuserMessageEvent(FOCUSER_UPDATED, device)) } companion object { diff --git a/api/src/main/kotlin/nebulosa/api/focusers/FocuserMessageEvent.kt b/api/src/main/kotlin/nebulosa/api/focusers/FocuserMessageEvent.kt new file mode 100644 index 000000000..d75cbcdbc --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/focusers/FocuserMessageEvent.kt @@ -0,0 +1,9 @@ +package nebulosa.api.focusers + +import nebulosa.api.services.DeviceMessageEvent +import nebulosa.indi.device.focuser.Focuser + +data class FocuserMessageEvent( + override val eventName: String, + override val device: Focuser, +) : DeviceMessageEvent diff --git a/api/src/main/kotlin/nebulosa/api/focusers/FocuserConverter.kt b/api/src/main/kotlin/nebulosa/api/focusers/FocuserSerializer.kt similarity index 90% rename from api/src/main/kotlin/nebulosa/api/focusers/FocuserConverter.kt rename to api/src/main/kotlin/nebulosa/api/focusers/FocuserSerializer.kt index 5f9bc8a89..aa0295dff 100644 --- a/api/src/main/kotlin/nebulosa/api/focusers/FocuserConverter.kt +++ b/api/src/main/kotlin/nebulosa/api/focusers/FocuserSerializer.kt @@ -2,14 +2,12 @@ package nebulosa.api.focusers import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.ser.std.StdSerializer import nebulosa.indi.device.focuser.Focuser -import nebulosa.json.ToJson import org.springframework.stereotype.Component @Component -class FocuserConverter : ToJson { - - override val type = Focuser::class.java +class FocuserSerializer : StdSerializer(Focuser::class.java) { override fun serialize(value: Focuser, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() diff --git a/api/src/main/kotlin/nebulosa/api/framing/FramingService.kt b/api/src/main/kotlin/nebulosa/api/framing/FramingService.kt index ecd9fcf19..cb4b24bbb 100644 --- a/api/src/main/kotlin/nebulosa/api/framing/FramingService.kt +++ b/api/src/main/kotlin/nebulosa/api/framing/FramingService.kt @@ -1,24 +1,29 @@ package nebulosa.api.framing +import nebulosa.fits.Fits import nebulosa.hips2fits.FormatOutputType import nebulosa.hips2fits.Hips2FitsService import nebulosa.imaging.Image +import nebulosa.io.transferAndCloseOutput import nebulosa.log.loggerFor import nebulosa.math.Angle -import nebulosa.platesolving.Calibration +import nebulosa.plate.solving.PlateSolution import org.springframework.stereotype.Service -import java.io.ByteArrayInputStream +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.outputStream @Service class FramingService(private val hips2FitsService: Hips2FitsService) { + @Synchronized fun frame( rightAscension: Angle, declination: Angle, width: Int, height: Int, fov: Angle, rotation: Angle = 0.0, hipsSurveyType: HipsSurveyType = HipsSurveyType.CDS_P_DSS2_COLOR, - ): Pair? { - val data = hips2FitsService.query( + ): Triple? { + val responseBody = hips2FitsService.query( hipsSurveyType.hipsSurvey, rightAscension, declination, width, height, @@ -26,15 +31,16 @@ class FramingService(private val hips2FitsService: Hips2FitsService) { format = FormatOutputType.FITS, ).execute().body() ?: return null - val image = Image.openFITS(ByteArrayInputStream(data)) - val calibration = Calibration.from(image.header) - LOG.info("framing file loaded. calibration={}", calibration) - return image to calibration + responseBody.use { it.byteStream().transferAndCloseOutput(DEFAULT_PATH.outputStream()) } + val image = Fits(DEFAULT_PATH).also(Fits::read).use(Image::open) + val solution = PlateSolution.from(image.header) + LOG.info("framing file loaded. calibration={}", solution) + return Triple(image, solution, DEFAULT_PATH) } companion object { - @JvmStatic - private val LOG = loggerFor() + @JvmStatic private val LOG = loggerFor() + @JvmStatic private val DEFAULT_PATH = Files.createTempFile("framing", ".fits") } } diff --git a/api/src/main/kotlin/nebulosa/api/guiding/DitherAfterExposureTasklet.kt b/api/src/main/kotlin/nebulosa/api/guiding/DitherAfterExposureTasklet.kt index 0624bb52c..059aca432 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/DitherAfterExposureTasklet.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/DitherAfterExposureTasklet.kt @@ -1,6 +1,7 @@ package nebulosa.api.guiding import nebulosa.common.concurrency.CountUpDownLatch +import nebulosa.guiding.GuideState import nebulosa.guiding.Guider import nebulosa.guiding.GuiderListener import org.springframework.batch.core.StepContribution @@ -18,7 +19,7 @@ data class DitherAfterExposureTasklet(val request: DitherAfterExposureRequest) : private val exposureCount = AtomicInteger() override fun execute(contribution: StepContribution, chunkContext: ChunkContext): RepeatStatus { - if (request.enabled) { + if (guider.canDither && request.enabled && guider.state == GuideState.GUIDING) { if (exposureCount.get() < request.afterExposures) { try { guider.registerGuiderListener(this) diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputController.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputController.kt index 6c4ddbfa3..ec75d6c4c 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputController.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputController.kt @@ -5,6 +5,7 @@ import nebulosa.api.connection.ConnectionService import nebulosa.guiding.GuideDirection import nebulosa.indi.device.guide.GuideOutput import org.springframework.web.bind.annotation.* +import java.time.Duration @RestController @RequestMapping("guide-outputs") @@ -36,8 +37,8 @@ class GuideOutputController( @PutMapping("{guideOutput}/pulse") fun pulse( @EntityBy guideOutput: GuideOutput, - @RequestParam direction: GuideDirection, @RequestParam duration: Int, + @RequestParam direction: GuideDirection, @RequestParam duration: Long, ) { - guideOutputService.pulse(guideOutput, direction, duration) + guideOutputService.pulse(guideOutput, direction, Duration.ofNanos(duration * 1000L)) } } diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputEventHandler.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputEventHandler.kt index 1ce92c7d7..081d041f3 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputEventHandler.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputEventHandler.kt @@ -39,17 +39,17 @@ class GuideOutputEventHandler( throttler.onNext(event) } is GuideOutputAttached -> { - messageService.sendMessage(GUIDE_OUTPUT_ATTACHED, event.device) + messageService.sendMessage(GuideOutputMessageEvent(GUIDE_OUTPUT_ATTACHED, event.device)) } is GuideOutputDetached -> { - messageService.sendMessage(GUIDE_OUTPUT_DETACHED, event.device) + messageService.sendMessage(GuideOutputMessageEvent(GUIDE_OUTPUT_DETACHED, event.device)) } } } } fun sendUpdate(device: GuideOutput) { - messageService.sendMessage(GUIDE_OUTPUT_UPDATED, device) + messageService.sendMessage(GuideOutputMessageEvent(GUIDE_OUTPUT_UPDATED, device)) } companion object { diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputMessageEvent.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputMessageEvent.kt new file mode 100644 index 000000000..9ea9e3673 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputMessageEvent.kt @@ -0,0 +1,9 @@ +package nebulosa.api.guiding + +import nebulosa.api.services.DeviceMessageEvent +import nebulosa.indi.device.guide.GuideOutput + +data class GuideOutputMessageEvent( + override val eventName: String, + override val device: GuideOutput, +) : DeviceMessageEvent diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputService.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputService.kt index 4d0d39822..4fd7e54cb 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputService.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputService.kt @@ -3,6 +3,7 @@ package nebulosa.api.guiding import nebulosa.guiding.GuideDirection import nebulosa.indi.device.guide.GuideOutput import org.springframework.stereotype.Service +import java.time.Duration @Service class GuideOutputService { @@ -15,7 +16,7 @@ class GuideOutputService { guideOutput.disconnect() } - fun pulse(guideOutput: GuideOutput, direction: GuideDirection, duration: Int) { + fun pulse(guideOutput: GuideOutput, direction: GuideDirection, duration: Duration) { if (guideOutput.canPulseGuide) { when (direction) { GuideDirection.NORTH -> guideOutput.guideNorth(duration) diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseElapsed.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseElapsed.kt index 26468f580..9e0728c62 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseElapsed.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseElapsed.kt @@ -1,17 +1,14 @@ package nebulosa.api.guiding import com.fasterxml.jackson.annotation.JsonIgnore -import nebulosa.api.sequencer.SequenceStepEvent import nebulosa.guiding.GuideDirection import org.springframework.batch.core.StepExecution +import java.time.Duration data class GuidePulseElapsed( - val remainingTime: Long, - val progress: Double, + val remainingTime: Duration, + override val progress: Double, val direction: GuideDirection, @JsonIgnore override val stepExecution: StepExecution, @JsonIgnore override val tasklet: GuidePulseTasklet, -) : GuidePulseEvent, SequenceStepEvent { - - @JsonIgnore override val eventName = "GUIDE_PULSE_ELAPSED" -} +) : GuidePulseEvent diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseEvent.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseEvent.kt index 583009260..57aaad25b 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseEvent.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseEvent.kt @@ -2,9 +2,8 @@ package nebulosa.api.guiding import nebulosa.api.sequencer.SequenceStepEvent import nebulosa.api.sequencer.SequenceTaskletEvent -import nebulosa.api.services.MessageEvent -sealed interface GuidePulseEvent : MessageEvent, SequenceTaskletEvent, SequenceStepEvent { +sealed interface GuidePulseEvent : SequenceTaskletEvent, SequenceStepEvent { override val tasklet: GuidePulseTasklet } diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseFinished.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseFinished.kt index 48d0913a0..c76953e06 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseFinished.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseFinished.kt @@ -1,13 +1,12 @@ package nebulosa.api.guiding import com.fasterxml.jackson.annotation.JsonIgnore -import nebulosa.api.sequencer.SequenceStepEvent import org.springframework.batch.core.StepExecution data class GuidePulseFinished( @JsonIgnore override val stepExecution: StepExecution, @JsonIgnore override val tasklet: GuidePulseTasklet, -) : GuidePulseEvent, SequenceStepEvent { +) : GuidePulseEvent { - @JsonIgnore override val eventName = "GUIDE_PULSE_FINISHED" + override val progress = 1.0 } diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseRequest.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseRequest.kt index c0e6a8e3a..3badd2dbe 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseRequest.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseRequest.kt @@ -1,12 +1,12 @@ package nebulosa.api.guiding import com.fasterxml.jackson.annotation.JsonIgnore -import jakarta.validation.constraints.Positive import nebulosa.guiding.GuideDirection import nebulosa.indi.device.guide.GuideOutput +import java.time.Duration data class GuidePulseRequest( @JsonIgnore val guideOutput: GuideOutput? = null, val direction: GuideDirection, - @field:Positive val durationInMilliseconds: Long, + val duration: Duration, ) diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseStarted.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseStarted.kt index 0037f1104..a275bcc08 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseStarted.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseStarted.kt @@ -8,5 +8,5 @@ data class GuidePulseStarted( @JsonIgnore override val tasklet: GuidePulseTasklet, ) : GuidePulseEvent { - @JsonIgnore override val eventName = "GUIDE_PULSE_STARTED" + override val progress = 0.0 } diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseTasklet.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseTasklet.kt index abd052e5a..40717cba0 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseTasklet.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidePulseTasklet.kt @@ -1,19 +1,19 @@ package nebulosa.api.guiding import io.reactivex.rxjava3.functions.Consumer -import nebulosa.api.sequencer.SubjectSequenceTasklet -import nebulosa.api.sequencer.tasklets.delay.DelayElapsed +import nebulosa.api.sequencer.PublishSequenceTasklet +import nebulosa.api.sequencer.tasklets.delay.DelayEvent import nebulosa.api.sequencer.tasklets.delay.DelayTasklet import nebulosa.guiding.GuideDirection import nebulosa.indi.device.guide.GuideOutput import org.springframework.batch.core.StepContribution import org.springframework.batch.core.scope.context.ChunkContext import org.springframework.batch.repeat.RepeatStatus -import kotlin.time.Duration.Companion.milliseconds +import java.time.Duration -data class GuidePulseTasklet(val request: GuidePulseRequest) : SubjectSequenceTasklet(), Consumer { +data class GuidePulseTasklet(val request: GuidePulseRequest) : PublishSequenceTasklet(), Consumer { - private val delayTasklet = DelayTasklet(request.durationInMilliseconds.milliseconds) + private val delayTasklet = DelayTasklet(request.duration) init { delayTasklet.subscribe(this) @@ -21,12 +21,12 @@ data class GuidePulseTasklet(val request: GuidePulseRequest) : SubjectSequenceTa override fun execute(contribution: StepContribution, chunkContext: ChunkContext): RepeatStatus { val guideOutput = requireNotNull(request.guideOutput) - val durationInMilliseconds = request.durationInMilliseconds + val durationInMilliseconds = request.duration // Force stop in reversed direction. - guideOutput.pulseGuide(0, request.direction.reversed) + guideOutput.pulseGuide(Duration.ZERO, request.direction.reversed) - if (guideOutput.pulseGuide(durationInMilliseconds.toInt(), request.direction)) { + if (guideOutput.pulseGuide(durationInMilliseconds, request.direction)) { delayTasklet.execute(contribution, chunkContext) } @@ -34,28 +34,27 @@ data class GuidePulseTasklet(val request: GuidePulseRequest) : SubjectSequenceTa } override fun stop() { - request.guideOutput?.pulseGuide(0, request.direction) + request.guideOutput?.pulseGuide(Duration.ZERO, request.direction) delayTasklet.stop() } - override fun accept(event: DelayElapsed) { - if (event.isStarted) onNext(GuidePulseStarted(event.stepExecution, this)) - else if (event.isFinished) onNext(GuidePulseFinished(event.stepExecution, this)) - else { - val remainingTime = event.remainingTime.inWholeMicroseconds - onNext(GuidePulseElapsed(remainingTime, event.progress, request.direction, event.stepExecution, this)) - } + override fun accept(event: DelayEvent) { + val guidePulseEvent = if (event.isStarted) GuidePulseStarted(event.stepExecution, this) + else if (event.isFinished) GuidePulseFinished(event.stepExecution, this) + else GuidePulseElapsed(event.remainingTime, event.progress, request.direction, event.stepExecution, this) + + onNext(guidePulseEvent) } companion object { @JvmStatic - private fun GuideOutput.pulseGuide(durationInMilliseconds: Int, direction: GuideDirection): Boolean { + private fun GuideOutput.pulseGuide(duration: Duration, direction: GuideDirection): Boolean { when (direction) { - GuideDirection.NORTH -> guideNorth(durationInMilliseconds) - GuideDirection.SOUTH -> guideSouth(durationInMilliseconds) - GuideDirection.WEST -> guideWest(durationInMilliseconds) - GuideDirection.EAST -> guideEast(durationInMilliseconds) + GuideDirection.NORTH -> guideNorth(duration) + GuideDirection.SOUTH -> guideSouth(duration) + GuideDirection.WEST -> guideWest(duration) + GuideDirection.EAST -> guideEast(duration) else -> return false } diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuideStepHistory.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuideStepHistory.kt index 3f22a9547..720306b58 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuideStepHistory.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuideStepHistory.kt @@ -3,8 +3,6 @@ package nebulosa.api.guiding import nebulosa.common.concurrency.Incrementer import nebulosa.guiding.GuideStep import java.util.* -import kotlin.math.max -import kotlin.math.min class GuideStepHistory private constructor(private val data: LinkedList) : List by data { @@ -13,30 +11,12 @@ class GuideStepHistory private constructor(private val data: LinkedList= maxHistorySize) { - data.pop() - } - data.add(step) } @Synchronized fun addGuideStep(guideStep: GuideStep): HistoryStep { - if (rms.size == maxHistorySize) { - while (isNotEmpty()) { - val removedStep = data.pop() - removedStep.guideStep ?: continue - rms.removeDataPoint(removedStep.guideStep.raDistance, removedStep.guideStep.decDistance) - break - } - } - rms.addDataPoint(guideStep.raDistance, guideStep.decDistance) return HistoryStep(id.increment(), rms.rightAscension, rms.declination, rms.total, guideStep) @@ -44,8 +24,8 @@ class GuideStepHistory private constructor(private val data: LinkedList { - return guidingService.history() + fun history(@RequestParam(required = false, defaultValue = "100") maxLength: Int): List { + return guidingService.history(maxLength) } @GetMapping("history/latest") @@ -54,12 +52,13 @@ class GuidingController(private val guidingService: GuidingService) { } @PutMapping("settle") - fun settle( - @RequestParam(required = false) @Valid @Range(min = 1, max = 25) amount: Double?, - @RequestParam(required = false) @Valid @Range(min = 1, max = 60) time: Long?, - @RequestParam(required = false) @Valid @Range(min = 1, max = 60) timeout: Long?, - ) { - guidingService.settle(amount, time?.seconds, timeout?.seconds) + fun settle(@RequestBody @Valid body: SettleInfo) { + guidingService.settle(body) + } + + @GetMapping("settle") + fun settle(): SettleInfo { + return guidingService.settle() } @PutMapping("dither") diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt index b9fd6763d..78bd71846 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt @@ -1,52 +1,68 @@ package nebulosa.api.guiding +import jakarta.annotation.PostConstruct import jakarta.annotation.PreDestroy +import nebulosa.api.preferences.PreferenceService import nebulosa.api.services.MessageService import nebulosa.guiding.GuideStar import nebulosa.guiding.GuideState import nebulosa.guiding.Guider import nebulosa.guiding.GuiderListener import nebulosa.phd2.client.PHD2Client -import nebulosa.phd2.client.PHD2EventListener -import nebulosa.phd2.client.commands.PHD2Command -import nebulosa.phd2.client.events.PHD2Event import org.springframework.stereotype.Service -import kotlin.time.Duration +import java.time.Duration +import kotlin.math.max +import kotlin.math.min @Service class GuidingService( + private val preferenceService: PreferenceService, private val messageService: MessageService, private val phd2Client: PHD2Client, private val guider: Guider, -) : PHD2EventListener, GuiderListener { +) : GuiderListener { private val guideHistory = GuideStepHistory() + val settleAmount + get() = guider.settleAmount + + val settleTime + get() = guider.settleTime + + val settleTimeout + get() = guider.settleTimeout + + @PostConstruct + private fun initialize() { + settle(preferenceService.getJSON("GUIDING.SETTLE") ?: SettleInfo.EMPTY) + } + @Synchronized fun connect(host: String, port: Int) { check(!phd2Client.isOpen) phd2Client.open(host, port) - phd2Client.registerListener(this) guider.registerGuiderListener(this) - messageService.sendMessage(GUIDER_CONNECTED) + messageService.sendMessage(GuiderMessageEvent(GUIDER_CONNECTED)) } @PreDestroy @Synchronized fun disconnect() { runCatching { guider.close() } - phd2Client.unregisterListener(this) - messageService.sendMessage(GUIDER_DISCONNECTED) + messageService.sendMessage(GuiderMessageEvent(GUIDER_DISCONNECTED)) } - fun status(): GuiderStatus { - return if (!phd2Client.isOpen) GuiderStatus.DISCONNECTED - else GuiderStatus(phd2Client.isOpen, guider.state, guider.isSettling, guider.pixelScale) + fun status(): GuiderInfo { + return if (!phd2Client.isOpen) GuiderInfo.DISCONNECTED + else GuiderInfo(phd2Client.isOpen, guider.state, guider.isSettling, guider.pixelScale) } - fun history(): List { - return guideHistory + fun history(maxLength: Int = 100): List { + val startIndex = max(0, guideHistory.size - 100) + val endIndex = min(guideHistory.size, startIndex + maxLength) + return guideHistory.subList(startIndex, endIndex) } fun latestHistory(): HistoryStep? { @@ -69,10 +85,16 @@ class GuidingService( } } - fun settle(settleAmount: Double?, settleTime: Duration?, settleTimeout: Duration?) { - if (settleAmount != null) guider.settleAmount = settleAmount - if (settleTime != null) guider.settleTime = settleTime - if (settleTimeout != null) guider.settleTimeout = settleTimeout + fun settle(settle: SettleInfo) { + guider.settleAmount = settle.amount + guider.settleTime = Duration.ofSeconds(settle.time) + guider.settleTimeout = Duration.ofSeconds(settle.timeout) + + preferenceService.putJSON("GUIDING.SETTLE", settle) + } + + fun settle(): SettleInfo { + return SettleInfo.from(guider) } fun dither(amount: Double, raOnly: Boolean = false) { @@ -88,27 +110,24 @@ class GuidingService( } override fun onStateChanged(state: GuideState, pixelScale: Double) { - val status = GuiderStatus(phd2Client.isOpen, state, guider.isSettling, pixelScale) - messageService.sendMessage(GUIDER_UPDATED, status) + val status = GuiderInfo(phd2Client.isOpen, state, guider.isSettling, pixelScale) + messageService.sendMessage(GuiderMessageEvent(GUIDER_UPDATED, status)) } override fun onGuideStepped(guideStar: GuideStar) { - val payload = guideStar.guideStep?.let(guideHistory::addGuideStep) ?: guideStar - messageService.sendMessage(GUIDER_STEPPED, payload) + val payload = guideStar.guideStep.let(guideHistory::addGuideStep) + messageService.sendMessage(GuiderMessageEvent(GUIDER_STEPPED, payload)) } override fun onDithered(dx: Double, dy: Double) { - guideHistory.addDither(dx, dy) + val payload = guideHistory.addDither(dx, dy) + messageService.sendMessage(GuiderMessageEvent(GUIDER_STEPPED, payload)) } override fun onMessageReceived(message: String) { - messageService.sendMessage(GUIDER_MESSAGE_RECEIVED, "message" to message) + messageService.sendMessage(GuiderMessageEvent(GUIDER_MESSAGE_RECEIVED, message)) } - override fun onEventReceived(event: PHD2Event) {} - - override fun onCommandProcessed(command: PHD2Command, result: T?, error: String?) {} - companion object { const val GUIDER_CONNECTED = "GUIDER_CONNECTED" diff --git a/api/src/main/kotlin/nebulosa/api/guiding/SettleInfo.kt b/api/src/main/kotlin/nebulosa/api/guiding/SettleInfo.kt new file mode 100644 index 000000000..f46d6986d --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/guiding/SettleInfo.kt @@ -0,0 +1,20 @@ +package nebulosa.api.guiding + +import nebulosa.guiding.Guider +import org.hibernate.validator.constraints.Range + +data class SettleInfo( + @Range(min = 1, max = 25) val amount: Double = 1.5, + @Range(min = 1, max = 60) val time: Long = 10, + @Range(min = 1, max = 60) val timeout: Long = 30, +) { + + companion object { + + @JvmStatic val EMPTY = SettleInfo() + + @JvmStatic + fun from(guider: Guider) = + SettleInfo(guider.settleAmount, guider.settleTime.toSeconds(), guider.settleTimeout.toSeconds()) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageBucket.kt b/api/src/main/kotlin/nebulosa/api/image/ImageBucket.kt index 39941ce23..69f177b70 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageBucket.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageBucket.kt @@ -1,33 +1,34 @@ package nebulosa.api.image +import nebulosa.fits.Fits import nebulosa.imaging.Image -import nebulosa.platesolving.Calibration +import nebulosa.plate.solving.PlateSolution import org.springframework.stereotype.Component import java.nio.file.Path @Component class ImageBucket { - private val bucket = HashMap>(256) + private val bucket = HashMap>(256) @Synchronized - fun put(path: Path, image: Image, calibration: Calibration? = null) { + fun put(path: Path, image: Image, solution: PlateSolution? = null) { remove(path) - bucket[path] = image to calibration + bucket[path] = image to solution } @Synchronized - fun put(path: Path, calibration: Calibration): Boolean { + fun put(path: Path, solution: PlateSolution): Boolean { val item = bucket[path] ?: return false - bucket[path] = item.first to calibration + bucket[path] = item.first to solution return true } @Synchronized - fun open(path: Path, debayer: Boolean = true, calibration: Calibration? = null): Image { + fun open(path: Path, debayer: Boolean = true, solution: PlateSolution? = null): Image { remove(path) - val image = Image.open(path, debayer) - put(path, image, calibration ?: Calibration.from(image.header)) + val image = Fits(path).also(Fits::read).use { Image.open(it, debayer) } + put(path, image, solution ?: PlateSolution.from(image.header)) return image } @@ -36,7 +37,7 @@ class ImageBucket { bucket.remove(path) } - operator fun get(path: Path): Pair? { + operator fun get(path: Path): Pair? { return bucket[path] } @@ -48,7 +49,7 @@ class ImageBucket { return bucket.any { it.value.first === image } } - operator fun contains(calibration: Calibration): Boolean { - return bucket.any { it.value.second === calibration } + operator fun contains(solution: PlateSolution): Boolean { + return bucket.any { it.value.second === solution } } } diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageCalibrated.kt b/api/src/main/kotlin/nebulosa/api/image/ImageCalibrated.kt index 7eee9e9d0..e2e207b53 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageCalibrated.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageCalibrated.kt @@ -1,7 +1,7 @@ package nebulosa.api.image import nebulosa.math.* -import nebulosa.platesolving.Calibration +import nebulosa.plate.solving.PlateSolution data class ImageCalibrated( val orientation: Double = 0.0, @@ -13,12 +13,12 @@ data class ImageCalibrated( val radius: Double = 0.0, ) { - constructor(calibration: Calibration) : this( - calibration.orientation.toDegrees, - calibration.scale.toArcsec, - calibration.rightAscension.format(AngleFormatter.HMS), - calibration.declination.format(AngleFormatter.SIGNED_DMS), - calibration.width.toArcmin, calibration.height.toArcmin, - calibration.radius.toDegrees, + constructor(solution: PlateSolution) : this( + solution.orientation.toDegrees, + solution.scale.toArcsec, + solution.rightAscension.format(AngleFormatter.HMS), + solution.declination.format(AngleFormatter.SIGNED_DMS), + solution.width.toArcmin, solution.height.toArcmin, + solution.radius.toDegrees, ) } diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageController.kt b/api/src/main/kotlin/nebulosa/api/image/ImageController.kt index a1a1cb63c..793e4117d 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageController.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageController.kt @@ -1,10 +1,11 @@ package nebulosa.api.image import jakarta.servlet.http.HttpServletResponse +import nebulosa.api.beans.annotations.EntityBy import nebulosa.imaging.ImageChannel -import nebulosa.imaging.algorithms.ProtectionMethod -import nebulosa.math.deg -import nebulosa.math.hours +import nebulosa.imaging.algorithms.transformation.ProtectionMethod +import nebulosa.indi.device.camera.Camera +import nebulosa.star.detection.ImageStar import org.springframework.web.bind.annotation.* import java.nio.file.Path @@ -17,7 +18,9 @@ class ImageController( @GetMapping fun openImage( @RequestParam path: Path, + @EntityBy(required = false) camera: Camera?, @RequestParam(required = false, defaultValue = "true") debayer: Boolean, + @RequestParam(required = false, defaultValue = "false") calibrate: Boolean, @RequestParam(required = false, defaultValue = "false") autoStretch: Boolean, @RequestParam(required = false, defaultValue = "0.0") shadow: Float, @RequestParam(required = false, defaultValue = "1.0") highlight: Float, @@ -30,15 +33,13 @@ class ImageController( @RequestParam(required = false, defaultValue = "0.5") scnrAmount: Float, @RequestParam(required = false, defaultValue = "AVERAGE_NEUTRAL") scnrProtectionMode: ProtectionMethod, output: HttpServletResponse, - ) { - imageService.openImage( - path, - debayer, autoStretch, shadow, highlight, midtone, - mirrorHorizontal, mirrorVertical, invert, - scnrEnabled, scnrChannel, scnrAmount, scnrProtectionMode, - output, - ) - } + ) = imageService.openImage( + path, camera, + debayer, calibrate, autoStretch, shadow, highlight, midtone, + mirrorHorizontal, mirrorVertical, invert, + scnrEnabled, scnrChannel, scnrAmount, scnrProtectionMode, + output, + ) @DeleteMapping fun closeImage(@RequestParam path: Path) { @@ -57,31 +58,15 @@ class ImageController( @RequestParam(required = false, defaultValue = "true") dsos: Boolean, @RequestParam(required = false, defaultValue = "false") minorPlanets: Boolean, @RequestParam(required = false, defaultValue = "12.0") minorPlanetMagLimit: Double, - ): List { - return imageService.annotations(path, stars, dsos, minorPlanets, minorPlanetMagLimit) - } - - @PutMapping("solve") - fun solveImage( - @RequestParam path: Path, - @RequestParam(required = false, defaultValue = "ASTROMETRY_NET_ONLINE") type: PlateSolverType, - @RequestParam(required = false, defaultValue = "true") blind: Boolean, - @RequestParam(required = false, defaultValue = "0.0") centerRA: String, - @RequestParam(required = false, defaultValue = "0.0") centerDEC: String, - @RequestParam(required = false, defaultValue = "8.0") radius: String, - @RequestParam(required = false, defaultValue = "1") downsampleFactor: Int, - @RequestParam(required = false, defaultValue = "") pathOrUrl: String, - @RequestParam(required = false, defaultValue = "") apiKey: String, - ): ImageCalibrated { - return imageService.solveImage( - path, type, blind, - centerRA.hours, centerDEC.deg, radius.deg, - downsampleFactor, pathOrUrl, apiKey, - ) - } + ) = imageService.annotations(path, stars, dsos, minorPlanets, minorPlanetMagLimit) @GetMapping("coordinate-interpolation") fun coordinateInterpolation(@RequestParam path: Path): CoordinateInterpolation? { return imageService.coordinateInterpolation(path) } + + @PutMapping("detect-stars") + fun detectStars(@RequestParam path: Path): List { + return imageService.detectStars(path) + } } diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt index 9d973db7b..c839caab1 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt @@ -2,31 +2,29 @@ package nebulosa.api.image import com.fasterxml.jackson.databind.ObjectMapper import jakarta.servlet.http.HttpServletResponse +import nebulosa.api.calibration.CalibrationFrameService import nebulosa.api.framing.FramingService import nebulosa.api.framing.HipsSurveyType -import nebulosa.astrometrynet.nova.NovaAstrometryNetService import nebulosa.fits.* import nebulosa.imaging.ImageChannel -import nebulosa.imaging.algorithms.* +import nebulosa.imaging.algorithms.transformation.* +import nebulosa.indi.device.camera.Camera import nebulosa.io.transferAndClose import nebulosa.log.loggerFor import nebulosa.math.* -import nebulosa.platesolving.astap.AstapPlateSolver -import nebulosa.platesolving.astrometrynet.LocalAstrometryNetPlateSolver -import nebulosa.platesolving.astrometrynet.NovaAstrometryNetPlateSolver -import nebulosa.platesolving.watney.WatneyPlateSolver import nebulosa.sbd.SmallBodyDatabaseService +import nebulosa.simbad.SimbadSearch import nebulosa.simbad.SimbadService -import nebulosa.simbad.SimbadSkyCatalog import nebulosa.skycatalog.ClassificationType import nebulosa.skycatalog.SkyObjectType +import nebulosa.star.detection.ImageStar +import nebulosa.watney.star.detection.WatneyStarDetector import nebulosa.wcs.WCSException import nebulosa.wcs.WCSTransform import org.springframework.http.HttpStatus import org.springframework.stereotype.Service import org.springframework.web.server.ResponseStatusException import java.nio.file.Path -import java.time.Duration import java.util.* import java.util.concurrent.CompletableFuture import java.util.concurrent.ExecutorService @@ -39,6 +37,7 @@ import kotlin.io.path.outputStream class ImageService( private val objectMapper: ObjectMapper, private val framingService: FramingService, + private val calibrationFrameService: CalibrationFrameService, private val smallBodyDatabaseService: SmallBodyDatabaseService, private val simbadService: SimbadService, private val imageBucket: ImageBucket, @@ -47,7 +46,7 @@ class ImageService( @Synchronized fun openImage( - path: Path, debayer: Boolean, + path: Path, camera: Camera?, debayer: Boolean = true, calibrate: Boolean = false, autoStretch: Boolean = false, shadow: Float = 0f, highlight: Float = 1f, midtone: Float = 0.5f, mirrorHorizontal: Boolean = false, mirrorVertical: Boolean = false, invert: Boolean = false, scnrEnabled: Boolean = false, scnrChannel: ImageChannel = ImageChannel.GREEN, scnrAmount: Float = 0.5f, @@ -65,8 +64,16 @@ class ImageService( var transformedImage = if (shouldBeTransformed) image.clone() else image - if (mirrorHorizontal) transformedImage = HorizontalFlip.transform(transformedImage) - if (mirrorVertical) transformedImage = VerticalFlip.transform(transformedImage) + if (calibrate && camera != null) { + transformedImage = calibrationFrameService.calibrate(camera, transformedImage, transformedImage === image) + } + + if (mirrorHorizontal) { + transformedImage = HorizontalFlip.transform(transformedImage) + } + if (mirrorVertical) { + transformedImage = VerticalFlip.transform(transformedImage) + } if (scnrEnabled) { transformedImage = SubtractiveChromaticNoiseReduction(scnrChannel, scnrAmount, scnrProtectionMode).transform(transformedImage) @@ -79,22 +86,20 @@ class ImageService( transformedImage = ScreenTransformFunction(stretchParams).transform(transformedImage) } - if (invert) transformedImage = Invert.transform(transformedImage) + if (invert) { + transformedImage = Invert.transform(transformedImage) + } val info = ImageInfo( path, - transformedImage.width, - transformedImage.height, - transformedImage.mono, - stretchParams.shadow, - stretchParams.highlight, - stretchParams.midtone, - transformedImage.header.ra.format(AngleFormatter.HMS), - transformedImage.header.dec.format(AngleFormatter.SIGNED_DMS), + transformedImage.width, transformedImage.height, transformedImage.mono, + stretchParams.shadow, stretchParams.highlight, stretchParams.midtone, + transformedImage.header.rightAscension.format(AngleFormatter.HMS), + transformedImage.header.declination.format(AngleFormatter.SIGNED_DMS), imageBucket[path]?.second != null, transformedImage.header.iterator().asSequence() - .filter { it.key.isNotBlank() && !it.value.isNullOrBlank() } - .map { ImageHeaderItem(it.key, it.value ?: "") } + .filter { it.key.isNotBlank() && it.value.isNotBlank() } + .map { ImageHeaderItem(it.key, it.value) } .toList(), ) @@ -119,7 +124,7 @@ class ImageService( ): List { val (image, calibration) = imageBucket[path] ?: return emptyList() - if (calibration == null || calibration.isEmpty || !calibration.solved) { + if (calibration.isNullOrEmpty() || !calibration.solved) { return emptyList() } @@ -178,8 +183,6 @@ class ImageService( CompletableFuture.runAsync({ LOG.info("finding star annotations. dateTime={}, calibration={}", dateTime, calibration) - val catalog = SimbadSkyCatalog(simbadService) - val types = ArrayList(4) if (stars) { @@ -188,22 +191,42 @@ class ImageService( if (dsos) { types.add(SkyObjectType.CLUSTER_OF_STARS) - types.add(SkyObjectType.INTERSTELLAR_MEDIUM_OBJECT) + types.add(SkyObjectType.ASSOCIATION_OF_STARS) types.add(SkyObjectType.GALAXY) + types.add(SkyObjectType.INTERSTELLAR_MEDIUM_OBJECT) types.add(SkyObjectType.CLUSTER_OF_GALAXIES) types.add(SkyObjectType.INTERACTING_GALAXIES) + types.add(SkyObjectType.GROUP_OF_GALAXIES) + types.add(SkyObjectType.SUPERCLUSTER_OF_GALAXIES) + types.add(SkyObjectType.PAIR_OF_GALAXIES) } - catalog.search(calibration.rightAscension, calibration.declination, calibration.radius, types) + var lastID = 0L + var count = 0 + + while (true) { + val search = SimbadSearch.Builder() + .region(calibration.rightAscension, calibration.declination, calibration.radius) + .types(types) + .limit(5000) + .lastID(lastID) + .build() - for (entry in catalog) { - val (x, y) = wcs.skyToPix(entry.rightAscensionJ2000, entry.declinationJ2000) - val annotation = if (entry.type.classification == ClassificationType.STAR) ImageAnnotation(x, y, star = entry) - else ImageAnnotation(x, y, dso = entry) - annotations.add(annotation) + val catalog = simbadService.search(search) + + if (catalog.isEmpty()) break + + for (entry in catalog) { + val (x, y) = wcs.skyToPix(entry.rightAscensionJ2000, entry.declinationJ2000) + val annotation = if (entry.type.classification == ClassificationType.STAR) ImageAnnotation(x, y, star = entry) + else ImageAnnotation(x, y, dso = entry) + annotations.add(annotation) + lastID = entry.id + count++ + } } - LOG.info("Found {} stars/DSOs", catalog.size) + LOG.info("Found {} stars/DSOs", count) }, systemExecutorService).whenComplete { _, e -> e?.printStackTrace() }.also(tasks::add) } @@ -214,31 +237,6 @@ class ImageService( return annotations } - fun solveImage( - path: Path, type: PlateSolverType, - blind: Boolean, - centerRA: Angle, centerDEC: Angle, radius: Angle, - downsampleFactor: Int, - pathOrUrl: String, apiKey: String, - ): ImageCalibrated { - val solver = when (type) { - PlateSolverType.ASTROMETRY_NET_LOCAL -> LocalAstrometryNetPlateSolver(pathOrUrl) - PlateSolverType.ASTROMETRY_NET_ONLINE -> NovaAstrometryNetPlateSolver(NovaAstrometryNetService(pathOrUrl), apiKey) - PlateSolverType.WATNEY -> WatneyPlateSolver(pathOrUrl) - PlateSolverType.ASTAP -> AstapPlateSolver(pathOrUrl) - } - - val calibration = solver.solve( - path, blind, - centerRA, centerDEC, radius, - downsampleFactor, Duration.ofMinutes(2L), - ) - - imageBucket.put(path, calibration) - - return ImageCalibrated(calibration) - } - fun saveImageAs(inputPath: Path, outputPath: Path) { if (inputPath != outputPath) { if (inputPath.extension == outputPath.extension) { @@ -260,10 +258,8 @@ class ImageService( width: Int, height: Int, fov: Angle, rotation: Angle = 0.0, hipsSurveyType: HipsSurveyType = HipsSurveyType.CDS_P_DSS2_COLOR, ): Path { - val (image, calibration) = framingService + val (image, calibration, path) = framingService .frame(rightAscension, declination, width, height, fov, rotation, hipsSurveyType)!! - - val path = Path.of("@framing") imageBucket.put(path, image, calibration) return path } @@ -271,7 +267,7 @@ class ImageService( fun coordinateInterpolation(path: Path): CoordinateInterpolation? { val (image, calibration) = imageBucket[path] ?: return null - if (calibration == null || calibration.isEmpty || !calibration.solved) { + if (calibration.isNullOrEmpty() || !calibration.solved) { return null } @@ -304,9 +300,15 @@ class ImageService( return CoordinateInterpolation(ma, md, 0, 0, width, height, delta, image.header.observationDate) } + fun detectStars(path: Path): List { + val (image) = imageBucket[path] ?: return emptyList() + return WATNEY_STAR_DETECTOR.detect(image) + } + companion object { @JvmStatic private val LOG = loggerFor() + @JvmStatic private val WATNEY_STAR_DETECTOR = WatneyStarDetector(computeHFD = true) private const val COORDINATE_INTERPOLATION_DELTA = 24 } diff --git a/api/src/main/kotlin/nebulosa/api/image/PlateSolverType.kt b/api/src/main/kotlin/nebulosa/api/image/PlateSolverType.kt deleted file mode 100644 index 6a8aa052e..000000000 --- a/api/src/main/kotlin/nebulosa/api/image/PlateSolverType.kt +++ /dev/null @@ -1,8 +0,0 @@ -package nebulosa.api.image - -enum class PlateSolverType { - ASTROMETRY_NET_LOCAL, - ASTROMETRY_NET_ONLINE, - ASTAP, - WATNEY, -} diff --git a/api/src/main/kotlin/nebulosa/api/indi/INDIEventHandler.kt b/api/src/main/kotlin/nebulosa/api/indi/INDIEventHandler.kt index 8b48eef6b..60c285424 100644 --- a/api/src/main/kotlin/nebulosa/api/indi/INDIEventHandler.kt +++ b/api/src/main/kotlin/nebulosa/api/indi/INDIEventHandler.kt @@ -34,19 +34,19 @@ class INDIEventHandler( fun sendINDIPropertyChanged(event: DevicePropertyEvent) { if (canSendEvents) { - messageService.sendMessage(DEVICE_PROPERTY_CHANGED, event.property) + messageService.sendMessage(INDIMessageEvent(DEVICE_PROPERTY_CHANGED, event)) } } fun sendINDIPropertyDeleted(event: DevicePropertyEvent) { if (canSendEvents) { - messageService.sendMessage(DEVICE_PROPERTY_DELETED, event.property) + messageService.sendMessage(INDIMessageEvent(DEVICE_PROPERTY_DELETED, event)) } } fun sendINDIMessageReceived(event: DeviceMessageReceived) { if (canSendEvents) { - messageService.sendMessage(DEVICE_MESSAGE_RECEIVED, event) + messageService.sendMessage(INDIMessageEvent(DEVICE_MESSAGE_RECEIVED, event)) } } diff --git a/api/src/main/kotlin/nebulosa/api/indi/INDIMessageEvent.kt b/api/src/main/kotlin/nebulosa/api/indi/INDIMessageEvent.kt new file mode 100644 index 000000000..767fd7850 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/indi/INDIMessageEvent.kt @@ -0,0 +1,19 @@ +package nebulosa.api.indi + +import nebulosa.api.services.DeviceMessageEvent +import nebulosa.indi.device.Device +import nebulosa.indi.device.DeviceMessageReceived +import nebulosa.indi.device.DevicePropertyEvent +import nebulosa.indi.device.PropertyVector + +data class INDIMessageEvent( + override val eventName: String, + override val device: Device? = null, + val property: PropertyVector<*, *>? = null, + val message: String? = null, +) : DeviceMessageEvent { + + constructor(eventName: String, event: DevicePropertyEvent) : this(eventName, event.device, property = event.property) + + constructor(eventName: String, event: DeviceMessageReceived) : this(eventName, event.device, message = event.message) +} diff --git a/api/src/main/kotlin/nebulosa/api/locations/LocationController.kt b/api/src/main/kotlin/nebulosa/api/locations/LocationController.kt index 4af0721d2..d1112aab9 100644 --- a/api/src/main/kotlin/nebulosa/api/locations/LocationController.kt +++ b/api/src/main/kotlin/nebulosa/api/locations/LocationController.kt @@ -6,26 +6,31 @@ import org.springframework.web.bind.annotation.* @RestController @RequestMapping("locations") class LocationController( - private val locationRepository: LocationRepository, + private val locationService: LocationService, ) { @GetMapping - fun location(): List { - return locationRepository.findAll() + fun locations(): List { + return locationService.locations() + } + + @GetMapping("{id}") + fun location(@PathVariable id: Long): LocationEntity { + return locationService.location(id) + } + + @GetMapping("selected") + fun selected(): LocationEntity? { + return locationService.selected() } @PutMapping - @Synchronized fun saveLocation(@RequestBody @Valid body: LocationEntity): LocationEntity { - val id = if (body.id <= 0L) System.currentTimeMillis() else body.id - return locationRepository.save(body.copy(id = id)) + return locationService.save(body) } - @Synchronized @DeleteMapping("{id}") fun deleteLocation(@PathVariable id: Long) { - if (id > 0L && locationRepository.count() > 1) { - locationRepository.deleteById(id) - } + locationService.delete(id) } } diff --git a/api/src/main/kotlin/nebulosa/api/locations/LocationEntity.kt b/api/src/main/kotlin/nebulosa/api/locations/LocationEntity.kt index 43ef0b34f..29bbeb10f 100644 --- a/api/src/main/kotlin/nebulosa/api/locations/LocationEntity.kt +++ b/api/src/main/kotlin/nebulosa/api/locations/LocationEntity.kt @@ -20,6 +20,7 @@ data class LocationEntity( @field:Range(min = -180, max = 180) @Column(name = "longitude", columnDefinition = "REAL") var longitude: Double = 0.0, // deg. @field:Range(min = -1000, max = 10000) @Column(name = "elevation", columnDefinition = "REAL") var elevation: Double = 0.0, // m. @field:Range(min = -720, max = 720) @Column(name = "offset_in_minutes", columnDefinition = "INT2") var offsetInMinutes: Int = 0, + @Column(name = "selected", columnDefinition = "INT1") var selected: Boolean = false, ) { fun geographicPosition(): GeographicPosition { diff --git a/api/src/main/kotlin/nebulosa/api/locations/LocationInitializerTask.kt b/api/src/main/kotlin/nebulosa/api/locations/LocationInitializer.kt similarity index 58% rename from api/src/main/kotlin/nebulosa/api/locations/LocationInitializerTask.kt rename to api/src/main/kotlin/nebulosa/api/locations/LocationInitializer.kt index bea5ec9b3..9b8ad74ef 100644 --- a/api/src/main/kotlin/nebulosa/api/locations/LocationInitializerTask.kt +++ b/api/src/main/kotlin/nebulosa/api/locations/LocationInitializer.kt @@ -5,11 +5,11 @@ import org.springframework.stereotype.Component @Component @ThreadedTask -class LocationInitializerTask(private val locationRepository: LocationRepository) : Runnable { +class LocationInitializer(private val locationRepository: LocationRepository) : Runnable { override fun run() { if (locationRepository.count() <= 0) { - val location = LocationEntity(System.currentTimeMillis(), "Saint Helena", -15.9755300, -5.6987929, 819.0) + val location = LocationEntity(1, "Null Island", 0.0, 0.0, 0.0, selected = true) locationRepository.saveAndFlush(location) } } diff --git a/api/src/main/kotlin/nebulosa/api/locations/LocationRepository.kt b/api/src/main/kotlin/nebulosa/api/locations/LocationRepository.kt index 7da40fcac..ebffc80aa 100644 --- a/api/src/main/kotlin/nebulosa/api/locations/LocationRepository.kt +++ b/api/src/main/kotlin/nebulosa/api/locations/LocationRepository.kt @@ -1,7 +1,21 @@ package nebulosa.api.locations import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Modifying +import org.springframework.data.jpa.repository.Query import org.springframework.stereotype.Repository +import org.springframework.transaction.annotation.Isolation +import org.springframework.transaction.annotation.Transactional @Repository -interface LocationRepository : JpaRepository +@Transactional(readOnly = false, isolation = Isolation.SERIALIZABLE) +interface LocationRepository : JpaRepository { + + fun findFirstByOrderById(): LocationEntity? + + fun findFirstBySelectedTrueOrderById(): LocationEntity? + + @Modifying + @Query("UPDATE LocationEntity l SET l.selected = false") + fun unselectedAll() +} diff --git a/api/src/main/kotlin/nebulosa/api/locations/LocationService.kt b/api/src/main/kotlin/nebulosa/api/locations/LocationService.kt new file mode 100644 index 000000000..83314a4d1 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/locations/LocationService.kt @@ -0,0 +1,43 @@ +package nebulosa.api.locations + +import org.springframework.stereotype.Service + +@Service +class LocationService( + private val locationRepository: LocationRepository, +) { + + fun locations(): List { + return locationRepository.findAll() + } + + fun location(id: Long): LocationEntity { + return locationRepository.findById(id).get() + } + + fun selected(): LocationEntity? { + return locationRepository.findFirstBySelectedTrueOrderById() + } + + @Synchronized + fun save(location: LocationEntity): LocationEntity { + location.id = if (location.id <= 0L) System.currentTimeMillis() else location.id + if (location.selected) locationRepository.unselectedAll() + return locationRepository.save(location) + } + + @Synchronized + fun delete(id: Long) { + if (id > 0L && locationRepository.count() > 1) { + var location = location(id) + + locationRepository.delete(location) + + if (location.selected) { + location = locationRepository.findFirstByOrderById()!! + location.selected = true + save(location) + } + } + } +} diff --git a/api/src/main/kotlin/nebulosa/api/mounts/MountEventHandler.kt b/api/src/main/kotlin/nebulosa/api/mounts/MountEventHandler.kt index cfe5abe78..4a276b5eb 100644 --- a/api/src/main/kotlin/nebulosa/api/mounts/MountEventHandler.kt +++ b/api/src/main/kotlin/nebulosa/api/mounts/MountEventHandler.kt @@ -36,16 +36,16 @@ class MountEventHandler( throttler.onNext(event) } is MountAttached -> { - messageService.sendMessage(MOUNT_ATTACHED, event.device) + messageService.sendMessage(MountMessageEvent(MOUNT_ATTACHED, event.device)) } is MountDetached -> { - messageService.sendMessage(MOUNT_DETACHED, event.device) + messageService.sendMessage(MountMessageEvent(MOUNT_DETACHED, event.device)) } } } fun sendUpdate(device: Mount) { - messageService.sendMessage(MOUNT_UPDATED, device) + messageService.sendMessage(MountMessageEvent(MOUNT_UPDATED, device)) } companion object { diff --git a/api/src/main/kotlin/nebulosa/api/mounts/MountMessageEvent.kt b/api/src/main/kotlin/nebulosa/api/mounts/MountMessageEvent.kt new file mode 100644 index 000000000..f72989ea4 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/mounts/MountMessageEvent.kt @@ -0,0 +1,9 @@ +package nebulosa.api.mounts + +import nebulosa.api.services.DeviceMessageEvent +import nebulosa.indi.device.mount.Mount + +data class MountMessageEvent( + override val eventName: String, + override val device: Mount, +) : DeviceMessageEvent diff --git a/api/src/main/kotlin/nebulosa/api/mounts/MountConverter.kt b/api/src/main/kotlin/nebulosa/api/mounts/MountSerializer.kt similarity index 95% rename from api/src/main/kotlin/nebulosa/api/mounts/MountConverter.kt rename to api/src/main/kotlin/nebulosa/api/mounts/MountSerializer.kt index e62ae99ad..8fab6ee19 100644 --- a/api/src/main/kotlin/nebulosa/api/mounts/MountConverter.kt +++ b/api/src/main/kotlin/nebulosa/api/mounts/MountSerializer.kt @@ -2,8 +2,8 @@ package nebulosa.api.mounts import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.ser.std.StdSerializer import nebulosa.indi.device.mount.Mount -import nebulosa.json.ToJson import nebulosa.math.AngleFormatter import nebulosa.math.format import nebulosa.math.toDegrees @@ -12,9 +12,7 @@ import org.springframework.stereotype.Component import java.time.ZoneOffset @Component -class MountConverter : ToJson { - - override val type = Mount::class.java +class MountSerializer : StdSerializer(Mount::class.java) { override fun serialize(value: Mount, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() diff --git a/api/src/main/kotlin/nebulosa/api/mounts/MountService.kt b/api/src/main/kotlin/nebulosa/api/mounts/MountService.kt index b273424d1..bbeb45e6d 100644 --- a/api/src/main/kotlin/nebulosa/api/mounts/MountService.kt +++ b/api/src/main/kotlin/nebulosa/api/mounts/MountService.kt @@ -226,7 +226,7 @@ class MountService(private val imageBucket: ImageBucket) { fun pointMountHere(mount: Mount, path: Path, x: Double, y: Double, synchronized: Boolean) { val calibration = imageBucket[path]?.second ?: return - if (!calibration.isEmpty && calibration.solved) { + if (calibration.isNotEmpty() && calibration.solved) { val wcs = WCSTransform(calibration) val (rightAscension, declination) = wcs.use { it.pixToSky(x, y) } diff --git a/api/src/main/kotlin/nebulosa/api/notification/NotificationEvent.kt b/api/src/main/kotlin/nebulosa/api/notification/NotificationEvent.kt new file mode 100644 index 000000000..b63a79609 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/notification/NotificationEvent.kt @@ -0,0 +1,29 @@ +package nebulosa.api.notification + +import nebulosa.api.services.MessageEvent + +interface NotificationEvent : MessageEvent { + + enum class Severity { + INFO, + SUCCESS, + WARNING, + ERROR, + } + + val type: String + + val body: String + + val severity + get() = Severity.INFO + + val title: String? + get() = severity.name + + val silent + get() = false + + override val eventName + get() = "NOTIFICATION" +} diff --git a/api/src/main/kotlin/nebulosa/api/preferences/PreferenceController.kt b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceController.kt new file mode 100644 index 000000000..e0eb303c3 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceController.kt @@ -0,0 +1,85 @@ +package nebulosa.api.preferences + +import org.springframework.web.bind.annotation.* + +@RestController +@RequestMapping("preferences") +class PreferenceController( + private val preferenceService: PreferenceService, +) { + + @GetMapping("{key}/boolean") + fun getBoolean(@PathVariable key: String): Boolean? { + return preferenceService.getBoolean(key) + } + + @GetMapping("{key}/int") + fun getInt(@PathVariable key: String): Int? { + return preferenceService.getInt(key) + } + + @GetMapping("{key}/long") + fun getLong(@PathVariable key: String): Long? { + return preferenceService.getLong(key) + } + + @GetMapping("{key}/double") + fun getDouble(@PathVariable key: String): Double? { + return preferenceService.getDouble(key) + } + + @GetMapping("{key}/text") + fun getText(@PathVariable key: String): String? { + return preferenceService.getText(key) + } + + @GetMapping("{key}") + fun getJSON(@PathVariable key: String): Any? { + return preferenceService.getJSON(key) + } + + @PutMapping("{key}/boolean") + fun putBoolean(@PathVariable key: String, @RequestParam value: Boolean) { + return preferenceService.putBoolean(key, value) + } + + @PutMapping("{key}/int") + fun putInt(@PathVariable key: String, @RequestParam value: Int) { + return preferenceService.putInt(key, value) + } + + @PutMapping("{key}/long") + fun putLong(@PathVariable key: String, @RequestParam value: Long) { + return preferenceService.putLong(key, value) + } + + @PutMapping("{key}/double") + fun putDouble(@PathVariable key: String, @RequestParam value: Double) { + return preferenceService.putDouble(key, value) + } + + @PutMapping("{key}/text") + fun putText(@PathVariable key: String, @RequestParam value: String) { + return preferenceService.putText(key, value) + } + + @PutMapping("{key}") + fun putJSON(@PathVariable key: String, @RequestBody body: PreferenceRequestBody?) { + return preferenceService.putJSON(key, body?.data) + } + + @DeleteMapping("{key}") + fun delete(@PathVariable key: String) { + return preferenceService.delete(key) + } + + @PutMapping("{key}/exists") + fun exists(@PathVariable key: String): Boolean { + return key in preferenceService + } + + @PutMapping("clear") + fun clear() { + return preferenceService.clear() + } +} diff --git a/api/src/main/kotlin/nebulosa/api/configs/ConfigEntity.kt b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceEntity.kt similarity index 76% rename from api/src/main/kotlin/nebulosa/api/configs/ConfigEntity.kt rename to api/src/main/kotlin/nebulosa/api/preferences/PreferenceEntity.kt index 3187ba6df..4549c8714 100644 --- a/api/src/main/kotlin/nebulosa/api/configs/ConfigEntity.kt +++ b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceEntity.kt @@ -1,4 +1,4 @@ -package nebulosa.api.configs +package nebulosa.api.preferences import jakarta.persistence.Column import jakarta.persistence.Entity @@ -6,8 +6,8 @@ import jakarta.persistence.Id import jakarta.persistence.Table @Entity -@Table(name = "configs") -data class ConfigEntity( +@Table(name = "preferences") +data class PreferenceEntity( @Id @Column(name = "key", columnDefinition = "TEXT") var key: String = "", @Column(name = "value", columnDefinition = "TEXT") var value: String? = null, ) diff --git a/api/src/main/kotlin/nebulosa/api/preferences/PreferenceRepository.kt b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceRepository.kt new file mode 100644 index 000000000..d9641be71 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceRepository.kt @@ -0,0 +1,16 @@ +package nebulosa.api.preferences + +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import org.springframework.stereotype.Repository +import org.springframework.transaction.annotation.Isolation +import org.springframework.transaction.annotation.Propagation +import org.springframework.transaction.annotation.Transactional + +@Repository +@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE) +interface PreferenceRepository : JpaRepository { + + @Query("SELECT p.key FROM PreferenceEntity p") + fun keys(): Set +} diff --git a/api/src/main/kotlin/nebulosa/api/preferences/PreferenceRequestBody.kt b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceRequestBody.kt new file mode 100644 index 000000000..a7e5f052d --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceRequestBody.kt @@ -0,0 +1,3 @@ +package nebulosa.api.preferences + +data class PreferenceRequestBody(val data: Any?) diff --git a/api/src/main/kotlin/nebulosa/api/preferences/PreferenceService.kt b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceService.kt new file mode 100644 index 000000000..418eb4965 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/preferences/PreferenceService.kt @@ -0,0 +1,71 @@ +package nebulosa.api.preferences + +import com.fasterxml.jackson.databind.ObjectMapper +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service + +@Service +class PreferenceService( + private val preferenceRepository: PreferenceRepository, + private val objectMapper: ObjectMapper, +) { + + fun keys() = preferenceRepository.keys() + + operator fun get(key: String) = preferenceRepository.findByIdOrNull(key) + + @Synchronized + fun put(entity: PreferenceEntity): Unit = run { preferenceRepository.saveAndFlush(entity) } + + operator fun contains(key: String) = preferenceRepository.existsById(key) + + fun getJSON(key: String, type: Class): T? = this[key]?.value?.let { objectMapper.readValue(it, type) } + + final inline fun getJSON(key: String) = getJSON(key, T::class.java) + + fun getBoolean(key: String) = getJSON(key, Boolean::class.java) + + fun getText(key: String) = getJSON(key, String::class.java) + + final inline fun > getEnum(key: String) = getText(key)?.takeIf { it.isNotBlank() }?.let { enumValueOf(it) } + + fun getInt(key: String) = getJSON(key, Int::class.java) + + fun getLong(key: String) = getJSON(key, Long::class.java) + + fun getDouble(key: String) = getJSON(key, Double::class.java) + + fun putJSON(key: String, value: Any?) = put(PreferenceEntity(key, if (value == null) null else objectMapper.writeValueAsString(value))) + + fun putBoolean(key: String, value: Boolean) = putJSON(key, value) + + fun putText(key: String, value: String?) = putJSON(key, value) + + fun putEnum(key: String, value: Enum<*>) = putText(key, value.name) + + fun putInt(key: String, value: Int) = putJSON(key, value) + + fun putLong(key: String, value: Long) = putJSON(key, value) + + fun putDouble(key: String, value: Double) = putJSON(key, value) + + @Synchronized + fun clear() = preferenceRepository.deleteAll() + + @Synchronized + fun delete(key: String) = preferenceRepository.deleteById(key) + + final inline var skyAtlasVersion + get() = getText(SKY_ATLAS_VERSION) + set(value) = putText(SKY_ATLAS_VERSION, value) + + final inline var satellitesUpdatedAt + get() = getLong(SATELLITES_UPDATED_AT) ?: 0L + set(value) = putLong(SATELLITES_UPDATED_AT, value) + + companion object { + + const val SKY_ATLAS_VERSION = "SKY_ATLAS_VERSION" + const val SATELLITES_UPDATED_AT = "SATELLITES_UPDATED_AT" + } +} diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/DelayEvent.kt b/api/src/main/kotlin/nebulosa/api/sequencer/DelayEvent.kt deleted file mode 100644 index 792fea382..000000000 --- a/api/src/main/kotlin/nebulosa/api/sequencer/DelayEvent.kt +++ /dev/null @@ -1,14 +0,0 @@ -package nebulosa.api.sequencer - -import kotlin.time.Duration - -interface DelayEvent { - - val remainingTime: Duration - - val delayTime: Duration - - val waitTime: Duration - - val progress: Double -} diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SubjectSequenceTasklet.kt b/api/src/main/kotlin/nebulosa/api/sequencer/PublishSequenceTasklet.kt similarity index 68% rename from api/src/main/kotlin/nebulosa/api/sequencer/SubjectSequenceTasklet.kt rename to api/src/main/kotlin/nebulosa/api/sequencer/PublishSequenceTasklet.kt index 1bc664f23..f6b963240 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SubjectSequenceTasklet.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/PublishSequenceTasklet.kt @@ -1,5 +1,6 @@ package nebulosa.api.sequencer +import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.core.Observer import io.reactivex.rxjava3.disposables.Disposable import io.reactivex.rxjava3.functions.Consumer @@ -9,16 +10,18 @@ import nebulosa.log.debug import nebulosa.log.loggerFor import java.io.Closeable -abstract class SubjectSequenceTasklet(@JvmField protected val subject: Subject) : SequenceTasklet, Closeable { +abstract class PublishSequenceTasklet(@JvmField protected val subject: Subject) : SequenceTasklet, Closeable { constructor() : this(PublishSubject.create()) - override fun subscribe(onNext: Consumer): Disposable { - return subject.subscribe(onNext) + protected open fun Observable.transform() = this + + final override fun subscribe(onNext: Consumer): Disposable { + return subject.transform().subscribe(onNext) } - override fun subscribe(observer: Observer) { - return subject.subscribe(observer) + final override fun subscribe(observer: Observer) { + return subject.transform().subscribe(observer) } final override fun onSubscribe(disposable: Disposable) { @@ -47,6 +50,6 @@ abstract class SubjectSequenceTasklet(@JvmField protec companion object { - @JvmStatic private val LOG = loggerFor>() + @JvmStatic private val LOG = loggerFor>() } } diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobEvent.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobEvent.kt index d3e139ec1..b54d8a40b 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobEvent.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobEvent.kt @@ -5,4 +5,6 @@ import org.springframework.batch.core.JobExecution interface SequenceJobEvent { val jobExecution: JobExecution + + val progress: Double } diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobFactory.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobFactory.kt index b388516ce..549a6d8fd 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobFactory.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobFactory.kt @@ -11,7 +11,6 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Scope -import kotlin.time.Duration.Companion.seconds @Configuration class SequenceJobFactory( @@ -48,7 +47,7 @@ class SequenceJobFactory( val cameraExposureTasklet = sequenceTaskletFactory.cameraExposure(request) cameraExposureTasklet.subscribe(cameraCaptureListener) - val cameraDelayTasklet = sequenceTaskletFactory.delay(request.exposureDelayInSeconds.seconds) + val cameraDelayTasklet = sequenceTaskletFactory.delay(request.exposureDelay) cameraDelayTasklet.subscribe(cameraExposureTasklet) val ditherTasklet = sequenceTaskletFactory.ditherAfterExposure(request.dither) diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobConverter.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobSerializer.kt similarity index 83% rename from api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobConverter.kt rename to api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobSerializer.kt index e6676fdf8..88a0ce260 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobConverter.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceJobSerializer.kt @@ -2,14 +2,12 @@ package nebulosa.api.sequencer import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.SerializerProvider -import nebulosa.json.ToJson +import com.fasterxml.jackson.databind.ser.std.StdSerializer import org.springframework.stereotype.Component import java.time.ZoneOffset @Component -class SequenceJobConverter : ToJson { - - override val type = SequenceJob::class.java +class SequenceJobSerializer : StdSerializer(SequenceJob::class.java) { override fun serialize(value: SequenceJob, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceStepEvent.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceStepEvent.kt index 9ef1d2048..eb81f7aea 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceStepEvent.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceStepEvent.kt @@ -1,5 +1,6 @@ package nebulosa.api.sequencer +import com.fasterxml.jackson.annotation.JsonIgnore import org.springframework.batch.core.StepExecution interface SequenceStepEvent : SequenceJobEvent { @@ -7,5 +8,5 @@ interface SequenceStepEvent : SequenceJobEvent { val stepExecution: StepExecution override val jobExecution - get() = stepExecution.jobExecution + @JsonIgnore get() = stepExecution.jobExecution } diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceTaskletEvent.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceTaskletEvent.kt index 919b377e6..3bba9015c 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceTaskletEvent.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceTaskletEvent.kt @@ -5,4 +5,6 @@ import org.springframework.batch.core.step.tasklet.Tasklet interface SequenceTaskletEvent { val tasklet: Tasklet + + val progress: Double } diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceTaskletFactory.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceTaskletFactory.kt index 0ba49fdf5..68c097233 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceTaskletFactory.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceTaskletFactory.kt @@ -9,7 +9,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Scope -import kotlin.time.Duration +import java.time.Duration @Configuration class SequenceTaskletFactory { diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayElapsed.kt b/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayElapsed.kt index 81a4c2ed7..af1aa9450 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayElapsed.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayElapsed.kt @@ -1,26 +1,17 @@ package nebulosa.api.sequencer.tasklets.delay import com.fasterxml.jackson.annotation.JsonIgnore -import nebulosa.api.sequencer.DelayEvent -import nebulosa.api.sequencer.SequenceStepEvent -import nebulosa.api.sequencer.SequenceTaskletEvent import org.springframework.batch.core.StepExecution -import kotlin.time.Duration +import java.time.Duration data class DelayElapsed( override val remainingTime: Duration, - override val delayTime: Duration, - override val waitTime: Duration, + override val waitDuration: Duration, @JsonIgnore override val stepExecution: StepExecution, @JsonIgnore override val tasklet: DelayTasklet, -) : SequenceStepEvent, SequenceTaskletEvent, DelayEvent { +) : DelayEvent { override val progress - get() = if (remainingTime > Duration.ZERO) 1.0 - delayTime / remainingTime else 1.0 - - inline val isStarted - get() = remainingTime == delayTime - - inline val isFinished - get() = remainingTime == Duration.ZERO + get() = if (remainingTime > Duration.ZERO) (tasklet.duration.toNanos() - remainingTime.toNanos()) / tasklet.duration.toNanos().toDouble() + else 1.0 } diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayEvent.kt b/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayEvent.kt new file mode 100644 index 000000000..cf0dad2c5 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayEvent.kt @@ -0,0 +1,21 @@ +package nebulosa.api.sequencer.tasklets.delay + +import com.fasterxml.jackson.annotation.JsonIgnore +import nebulosa.api.sequencer.SequenceStepEvent +import nebulosa.api.sequencer.SequenceTaskletEvent +import java.time.Duration + +sealed interface DelayEvent : SequenceStepEvent, SequenceTaskletEvent { + + override val tasklet: DelayTasklet + + val remainingTime: Duration + + val waitDuration: Duration + + val isStarted + @JsonIgnore get() = remainingTime == tasklet.duration + + val isFinished + @JsonIgnore get() = remainingTime == Duration.ZERO +} diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayTasklet.kt b/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayTasklet.kt index dfe95d9cf..3d35c60a3 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayTasklet.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/tasklets/delay/DelayTasklet.kt @@ -1,40 +1,36 @@ package nebulosa.api.sequencer.tasklets.delay -import nebulosa.api.sequencer.SubjectSequenceTasklet +import nebulosa.api.sequencer.PublishSequenceTasklet import org.springframework.batch.core.JobExecution import org.springframework.batch.core.JobExecutionListener import org.springframework.batch.core.StepContribution import org.springframework.batch.core.scope.context.ChunkContext import org.springframework.batch.repeat.RepeatStatus +import java.time.Duration import java.util.concurrent.atomic.AtomicBoolean -import kotlin.math.min -import kotlin.time.Duration -import kotlin.time.Duration.Companion.milliseconds -data class DelayTasklet(private val duration: Duration) : SubjectSequenceTasklet(), JobExecutionListener { +data class DelayTasklet(val duration: Duration) : PublishSequenceTasklet(), JobExecutionListener { private val aborted = AtomicBoolean() override fun execute(contribution: StepContribution, chunkContext: ChunkContext): RepeatStatus { val stepExecution = contribution.stepExecution - val delayTimeInMilliseconds = stepExecution.executionContext - .getLong(DELAY_TIME_NAME, duration.inWholeMilliseconds) - val delayTime = delayTimeInMilliseconds.milliseconds + var remainingTime = duration - var remainingTime = delayTimeInMilliseconds + if (remainingTime > Duration.ZERO) { + aborted.set(false) - if (remainingTime > 0L) { - while (!aborted.get() && remainingTime > 0L) { - val waitTime = min(remainingTime, DELAY_INTERVAL) + while (!aborted.get() && remainingTime > Duration.ZERO) { + val waitTime = minOf(remainingTime, DELAY_INTERVAL) - if (waitTime > 0) { - onNext(DelayElapsed(remainingTime.milliseconds, delayTime, waitTime.milliseconds, stepExecution, this)) - Thread.sleep(waitTime) + if (waitTime > Duration.ZERO) { + onNext(DelayElapsed(remainingTime, waitTime, stepExecution, this)) + Thread.sleep(waitTime.toMillis()) remainingTime -= waitTime } } - onNext(DelayElapsed(Duration.ZERO, delayTime, Duration.ZERO, stepExecution, this)) + onNext(DelayElapsed(Duration.ZERO, Duration.ZERO, stepExecution, this)) } return RepeatStatus.FINISHED @@ -54,7 +50,6 @@ data class DelayTasklet(private val duration: Duration) : SubjectSequenceTasklet companion object { - const val DELAY_INTERVAL = 500L - const val DELAY_TIME_NAME = "delayTime" + @JvmField val DELAY_INTERVAL = Duration.ofMillis(500)!! } } diff --git a/api/src/main/kotlin/nebulosa/api/services/DeviceMessageEvent.kt b/api/src/main/kotlin/nebulosa/api/services/DeviceMessageEvent.kt new file mode 100644 index 000000000..1968a78af --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/services/DeviceMessageEvent.kt @@ -0,0 +1,8 @@ +package nebulosa.api.services + +import nebulosa.indi.device.Device + +interface DeviceMessageEvent : MessageEvent { + + val device: T? +} diff --git a/api/src/main/kotlin/nebulosa/api/services/MessageService.kt b/api/src/main/kotlin/nebulosa/api/services/MessageService.kt index cc54974b1..7389be237 100644 --- a/api/src/main/kotlin/nebulosa/api/services/MessageService.kt +++ b/api/src/main/kotlin/nebulosa/api/services/MessageService.kt @@ -1,34 +1,52 @@ package nebulosa.api.services -import com.fasterxml.jackson.databind.ObjectMapper -import nebulosa.log.debug import nebulosa.log.loggerFor +import org.springframework.context.event.EventListener +import org.springframework.messaging.simp.SimpMessageHeaderAccessor import org.springframework.messaging.simp.SimpMessagingTemplate import org.springframework.stereotype.Service +import org.springframework.web.socket.messaging.SessionDisconnectEvent +import org.springframework.web.socket.messaging.SessionSubscribeEvent +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.atomic.AtomicBoolean @Service class MessageService( private val simpleMessageTemplate: SimpMessagingTemplate, - private val objectMapper: ObjectMapper, ) { - fun sendMessage(eventName: String, payload: Any) { - LOG.debug { "$eventName: $payload" } - simpleMessageTemplate.convertAndSend(eventName, payload) + private val connected = AtomicBoolean() + private val messageQueue = LinkedBlockingQueue() + + @EventListener + private fun handleSessionSubscribe(event: SessionSubscribeEvent) { + val destination = SimpMessageHeaderAccessor.wrap(event.message).destination ?: return + + if (destination == EVENT_NAME && connected.compareAndSet(false, true)) { + while (messageQueue.isNotEmpty()) { + sendMessage(messageQueue.take()) + } + } } - fun sendMessage(eventName: String, vararg attributes: Pair) { - val payload = objectMapper.createObjectNode() - attributes.forEach { payload.putPOJO(it.first, it.second) } - sendMessage(eventName, payload) + @EventListener + private fun handleSessionDisconnect(event: SessionDisconnectEvent) { + connected.set(false) } fun sendMessage(event: MessageEvent) { - sendMessage(event.eventName, event) + if (connected.get()) { + simpleMessageTemplate.convertAndSend(EVENT_NAME, event) + } else { + LOG.warn("queueing message. event={}", event) + messageQueue.offer(event) + } } companion object { + const val EVENT_NAME = "NEBULOSA_EVENT" + @JvmStatic private val LOG = loggerFor() } } diff --git a/api/src/main/kotlin/nebulosa/api/solver/PlateSolverController.kt b/api/src/main/kotlin/nebulosa/api/solver/PlateSolverController.kt new file mode 100644 index 000000000..e8dc46de1 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/solver/PlateSolverController.kt @@ -0,0 +1,33 @@ +package nebulosa.api.solver + +import jakarta.validation.Valid +import nebulosa.math.deg +import nebulosa.math.hours +import org.springframework.web.bind.annotation.* +import java.nio.file.Path + +@RestController +@RequestMapping("plate-solver") +class PlateSolverController( + private val plateSolverService: PlateSolverService, +) { + + @PutMapping + fun solveImage( + @RequestParam path: Path, + @RequestParam(required = false, defaultValue = "true") blind: Boolean, + @RequestParam(required = false, defaultValue = "0.0") centerRA: String, + @RequestParam(required = false, defaultValue = "0.0") centerDEC: String, + @RequestParam(required = false, defaultValue = "8.0") radius: String, + ) = plateSolverService.solveImage(path, centerRA.hours, centerDEC.deg, if (blind) 0.0 else radius.deg) + + @PutMapping("settings") + fun settings(@RequestBody @Valid body: PlateSolverOptions) { + plateSolverService.settings(body) + } + + @GetMapping("settings") + fun settings(): PlateSolverOptions { + return plateSolverService.settings() + } +} diff --git a/api/src/main/kotlin/nebulosa/api/solver/PlateSolverOptions.kt b/api/src/main/kotlin/nebulosa/api/solver/PlateSolverOptions.kt new file mode 100644 index 000000000..85f8041fb --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/solver/PlateSolverOptions.kt @@ -0,0 +1,15 @@ +package nebulosa.api.solver + +import java.nio.file.Path + +data class PlateSolverOptions( + val type: PlateSolverType = PlateSolverType.ASTAP, + val executablePath: Path? = null, + val downsampleFactor: Int = 2, +) { + + companion object { + + @JvmStatic val EMPTY = PlateSolverOptions() + } +} diff --git a/api/src/main/kotlin/nebulosa/api/solver/PlateSolverService.kt b/api/src/main/kotlin/nebulosa/api/solver/PlateSolverService.kt new file mode 100644 index 000000000..dabecc511 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/solver/PlateSolverService.kt @@ -0,0 +1,71 @@ +package nebulosa.api.solver + +import com.sun.jna.Platform +import jakarta.annotation.PostConstruct +import nebulosa.api.image.ImageBucket +import nebulosa.api.image.ImageCalibrated +import nebulosa.api.preferences.PreferenceService +import nebulosa.astap.plate.solving.AstapPlateSolver +import nebulosa.astrometrynet.plate.solving.LocalAstrometryNetPlateSolver +import nebulosa.math.Angle +import org.springframework.stereotype.Service +import java.nio.file.Path +import java.time.Duration + +@Service +class PlateSolverService( + private val preferenceService: PreferenceService, + private val imageBucket: ImageBucket, +) { + + @PostConstruct + private fun initialize() { + val settings = settings() + + if (settings.executablePath == null) { + val executablePath = when { + Platform.isLinux() -> "astap" + Platform.isWindows() -> "C:\\Program Files\\astap\\astap.exe" + else -> "astap" + } + + settings(settings.copy(executablePath = Path.of(executablePath))) + } + } + + fun solveImage( + path: Path, + centerRA: Angle, centerDEC: Angle, radius: Angle, + ): ImageCalibrated { + val calibration = solve(path, centerRA, centerDEC, radius) + imageBucket.put(path, calibration) + return ImageCalibrated(calibration) + } + + @Synchronized + fun solve( + path: Path, + centerRA: Angle = 0.0, centerDEC: Angle = 0.0, radius: Angle = 0.0, + ) = with(settings()) { + val plateSolver = when (type) { + PlateSolverType.ASTAP -> AstapPlateSolver(executablePath!!) + PlateSolverType.ASTROMETRY_NET -> LocalAstrometryNetPlateSolver(executablePath!!) + } + + plateSolver.solve(path, centerRA, centerDEC, radius, 2, DEFAULT_TIMEOUT) + } + + fun settings(options: PlateSolverOptions) { + preferenceService.putJSON("SETTINGS.PLATE_SOLVER", options) + } + + fun settings(): PlateSolverOptions { + return preferenceService.getJSON("SETTINGS.PLATE_SOLVER") + ?: PlateSolverOptions.EMPTY + } + + companion object { + + @JvmStatic private val DEFAULT_TIMEOUT = Duration.ofMinutes(5) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/solver/PlateSolverType.kt b/api/src/main/kotlin/nebulosa/api/solver/PlateSolverType.kt new file mode 100644 index 000000000..3542995db --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/solver/PlateSolverType.kt @@ -0,0 +1,6 @@ +package nebulosa.api.solver + +enum class PlateSolverType { + ASTAP, + ASTROMETRY_NET, +} diff --git a/api/src/main/kotlin/nebulosa/api/wheels/WheelEventHandler.kt b/api/src/main/kotlin/nebulosa/api/wheels/WheelEventHandler.kt index 20008417c..88a5bb660 100644 --- a/api/src/main/kotlin/nebulosa/api/wheels/WheelEventHandler.kt +++ b/api/src/main/kotlin/nebulosa/api/wheels/WheelEventHandler.kt @@ -36,16 +36,16 @@ class WheelEventHandler( throttler.onNext(event) } is FilterWheelAttached -> { - messageService.sendMessage(WHEEL_ATTACHED, event.device) + messageService.sendMessage(WheelMessageEvent(WHEEL_ATTACHED, event.device)) } is FilterWheelDetached -> { - messageService.sendMessage(WHEEL_DETACHED, event.device) + messageService.sendMessage(WheelMessageEvent(WHEEL_DETACHED, event.device)) } } } fun sendUpdate(device: FilterWheel) { - messageService.sendMessage(WHEEL_UPDATED, device) + messageService.sendMessage(WheelMessageEvent(WHEEL_UPDATED, device)) } companion object { diff --git a/api/src/main/kotlin/nebulosa/api/wheels/WheelMessageEvent.kt b/api/src/main/kotlin/nebulosa/api/wheels/WheelMessageEvent.kt new file mode 100644 index 000000000..9dce40456 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/wheels/WheelMessageEvent.kt @@ -0,0 +1,9 @@ +package nebulosa.api.wheels + +import nebulosa.api.services.DeviceMessageEvent +import nebulosa.indi.device.filterwheel.FilterWheel + +data class WheelMessageEvent( + override val eventName: String, + override val device: FilterWheel, +) : DeviceMessageEvent diff --git a/api/src/main/kotlin/nebulosa/api/wheels/WheelConverter.kt b/api/src/main/kotlin/nebulosa/api/wheels/WheelSerializer.kt similarity index 83% rename from api/src/main/kotlin/nebulosa/api/wheels/WheelConverter.kt rename to api/src/main/kotlin/nebulosa/api/wheels/WheelSerializer.kt index c0327b77c..35c8da51c 100644 --- a/api/src/main/kotlin/nebulosa/api/wheels/WheelConverter.kt +++ b/api/src/main/kotlin/nebulosa/api/wheels/WheelSerializer.kt @@ -2,14 +2,12 @@ package nebulosa.api.wheels import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.ser.std.StdSerializer import nebulosa.indi.device.filterwheel.FilterWheel -import nebulosa.json.ToJson import org.springframework.stereotype.Component @Component -class WheelConverter : ToJson { - - override val type = FilterWheel::class.java +class WheelSerializer : StdSerializer(FilterWheel::class.java) { override fun serialize(value: FilterWheel, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() diff --git a/api/src/main/resources/db/migration/V1.sql b/api/src/main/resources/db/migration/V1.sql index c09231621..e1d4a24d2 100644 --- a/api/src/main/resources/db/migration/V1.sql +++ b/api/src/main/resources/db/migration/V1.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS configs( +CREATE TABLE IF NOT EXISTS preferences( key TEXT PRIMARY KEY, value TEXT ); @@ -60,28 +60,6 @@ CREATE TABLE IF NOT EXISTS locations( longitude REAL, latitude REAL, elevation REAL, - offset_in_minutes INT2 + offset_in_minutes INT2, + selected INT1 ); - -CREATE TABLE IF NOT EXISTS guide_calibrations( - id INT8 PRIMARY KEY, - camera TEXT, - mount TEXT, - guide_output TEXT, - saved_at INT8, - x_rate REAL, - y_rate REAL, - x_angle REAL, - y_angle REAL, - declination REAL, - rotator_angle REAL, - binning INT1, - pier_side_at_east INT1, - ra_guide_parity INT1, - dec_guide_parity INT1 -); - -CREATE INDEX IF NOT EXISTS guide_calibrations_camera_idx ON guide_calibrations(camera); -CREATE INDEX IF NOT EXISTS guide_calibrations_mount_idx ON guide_calibrations(mount); -CREATE INDEX IF NOT EXISTS guide_calibrations_guide_output_idx ON guide_calibrations(guide_output); -CREATE INDEX IF NOT EXISTS guide_calibrations_saved_at_idx ON guide_calibrations(saved_at); diff --git a/api/src/main/resources/db/migration/V2.sql b/api/src/main/resources/db/migration/V2.sql new file mode 100644 index 000000000..6d3d3100a --- /dev/null +++ b/api/src/main/resources/db/migration/V2.sql @@ -0,0 +1,26 @@ +CREATE TABLE IF NOT EXISTS calibration_frames( + id INT8 PRIMARY KEY, + type INT1, + camera TEXT, + filter TEXT, + exposure_time INT8, + temperature REAL, + width INT4, + height INT4, + bin_x INT1, + bin_y INT1, + gain REAL, + path TEXT, + enabled INT1 +); + +CREATE INDEX IF NOT EXISTS calibration_frames_type_idx ON calibration_frames(type); +CREATE INDEX IF NOT EXISTS calibration_frames_camera_idx ON calibration_frames(camera); +CREATE INDEX IF NOT EXISTS calibration_frames_filter_idx ON calibration_frames(filter); +CREATE INDEX IF NOT EXISTS calibration_frames_width_idx ON calibration_frames(width); +CREATE INDEX IF NOT EXISTS calibration_frames_height_idx ON calibration_frames(height); +CREATE INDEX IF NOT EXISTS calibration_frames_exposure_time_idx ON calibration_frames(exposure_time); +CREATE INDEX IF NOT EXISTS calibration_frames_bin_x_idx ON calibration_frames(bin_x); +CREATE INDEX IF NOT EXISTS calibration_frames_bin_y_idx ON calibration_frames(bin_y); +CREATE INDEX IF NOT EXISTS calibration_frames_gain_idx ON calibration_frames(gain); +CREATE INDEX IF NOT EXISTS calibration_frames_enabled_idx ON calibration_frames(enabled); diff --git a/build.gradle.kts b/build.gradle.kts index d95ae7dfe..e0d5c7a60 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,8 +5,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile buildscript { dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.10") - classpath("org.jetbrains.kotlin:kotlin-allopen:1.9.10") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21") + classpath("org.jetbrains.kotlin:kotlin-allopen:1.9.21") classpath("com.adarshr:gradle-test-logger-plugin:4.0.0") } diff --git a/desktop/.gitignore b/desktop/.gitignore index 4dfb8602b..b5d571c54 100644 --- a/desktop/.gitignore +++ b/desktop/.gitignore @@ -6,6 +6,7 @@ main.js src/**/*.js app/**/*.js +!app/**/preload.js *.js.map node_modules diff --git a/desktop/README.md b/desktop/README.md index f0d9076cc..0774de28f 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -1,4 +1,4 @@ -# Nebulosa +# ![](src/assets/icons/nebulosa.png) Nebulosa The complete integrated solution for all of your astronomical imaging needs. @@ -28,14 +28,7 @@ The complete integrated solution for all of your astronomical imaging needs. ## Sky Atlas -![](atlas.1.png) -![](atlas.2.png) -![](atlas.3.png) -![](atlas.4.png) -![](atlas.5.png) -![](atlas.6.png) -![](atlas.7.png) -![](atlas.8.png) +![](sky-atlas.png) ## Image diff --git a/desktop/alignment.darv.png b/desktop/alignment.darv.png index a71936b40..33b0db6b1 100644 Binary files a/desktop/alignment.darv.png and b/desktop/alignment.darv.png differ diff --git a/desktop/angular.json b/desktop/angular.json index a928e7749..9a9c42ade 100644 --- a/desktop/angular.json +++ b/desktop/angular.json @@ -122,20 +122,20 @@ "serve": { "builder": "@angular-builders/custom-webpack:dev-server", "options": { - "browserTarget": "angular-electron:build" + "buildTarget": "angular-electron:build" }, "configurations": { "dev": { - "browserTarget": "angular-electron:build:dev" + "buildTarget": "angular-electron:build:dev" }, "production": { - "browserTarget": "angular-electron:build:production" + "buildTarget": "angular-electron:build:production" }, "web": { - "browserTarget": "angular-electron:build:web" + "buildTarget": "angular-electron:build:web" }, "web-production": { - "browserTarget": "angular-electron:build:web-production" + "buildTarget": "angular-electron:build:web-production" } } } diff --git a/desktop/app/main.ts b/desktop/app/main.ts index 012c13451..d6ae79640 100644 --- a/desktop/app/main.ts +++ b/desktop/app/main.ts @@ -1,25 +1,17 @@ import { Client } from '@stomp/stompjs' -import { app, BrowserWindow, dialog, ipcMain, Menu, screen, shell } from 'electron' +import { BrowserWindow, Menu, Notification, app, dialog, ipcMain, screen, shell } from 'electron' import { ChildProcessWithoutNullStreams, spawn } from 'node:child_process' import * as path from 'path' -import { Camera, FilterWheel, Focuser, INDI_EVENT_TYPES, INTERNAL_EVENT_TYPES, Mount, OpenWindow } from './types' +import { InternalEventType, MessageEvent, NotificationEvent, OpenDirectory, OpenWindow } from './types' -import { CronJob } from 'cron' import { WebSocket } from 'ws' -import { OpenDirectory } from '../src/shared/types' Object.assign(global, { WebSocket }) const browserWindows = new Map() -const cronedWindows = new Map[]>() let api: ChildProcessWithoutNullStreams | null = null let apiPort = 7000 let wsClient: Client -let selectedCamera: Camera -let selectedMount: Mount -let selectedFocuser: Focuser -let selectedWheel: FilterWheel - const args = process.argv.slice(1) const serve = args.some(e => e === '--serve') @@ -30,38 +22,48 @@ function createMainWindow() { splashWindow?.close() browserWindows.delete('splash') - createWindow({ id: 'home', path: 'home' }) + createWindow({ id: 'home', path: 'home', data: undefined }) wsClient = new Client({ brokerURL: `ws://localhost:${apiPort}/ws`, onConnect: () => { - for (const item of INDI_EVENT_TYPES) { - wsClient.subscribe(item, (message) => { - const data = JSON.parse(message.body) - - if (serve) { - console.info(item, message.body) + wsClient.subscribe('NEBULOSA_EVENT', message => { + const event = JSON.parse(message.body) as MessageEvent + + if (event.eventName) { + if (event.eventName === 'NOTIFICATION') { + showNotification(event as NotificationEvent) + } else { + sendToAllWindows(event.eventName, event) } + } else { + console.warn('invalid message event', event) + } + }) - sendToAllWindows(item, data) - }) - } + console.info('Web Socket connected') }, - onDisconnect() { + onDisconnect: () => { console.warn('Web Socket disconnected') }, + onWebSocketClose: () => { + console.warn('Web Socket closed') + }, + onWebSocketError: (e) => { + console.error('Web Socket error', e) + }, }) wsClient.activate() } -function createWindow(data: OpenWindow) { - let window = browserWindows.get(data.id) +function createWindow(options: OpenWindow) { + let window = browserWindows.get(options.id) if (window) { - if (data.params) { - console.info('params changed. id=%s, params=%s', data.id, data.params) - window.webContents.send('PARAMS_CHANGED', data.params) + if (options.data) { + console.info('window data changed. id=%s, data=%s', options.id, options.data) + window.webContents.send('DATA_CHANGED', options.data) } return window @@ -79,7 +81,7 @@ function createWindow(data: OpenWindow) { } } - const width = data.width ? Math.trunc(computeWidth(data.width)) : 320 + const width = options.width ? Math.trunc(computeWidth(options.width)) : 320 function computeHeight(value: number | string) { if (typeof value === 'number') { @@ -93,11 +95,11 @@ function createWindow(data: OpenWindow) { } } - const height = data.height ? Math.trunc(computeHeight(data.height)) : 420 + const height = options.height ? Math.trunc(computeHeight(options.height)) : 420 - const resizable = data.resizable ?? false - const icon = data.icon ?? 'nebulosa' - const params = encodeURIComponent(JSON.stringify(data.params || {})) + const resizable = options.resizable ?? false + const icon = options.icon ?? 'nebulosa' + const data = encodeURIComponent(JSON.stringify(options.data || {})) window = new BrowserWindow({ title: 'Nebulosa', @@ -123,9 +125,9 @@ function createWindow(data: OpenWindow) { debug({ showDevTools: false }) require('electron-reloader')(module) - window.loadURL(`http://localhost:4200/${data.path}?params=${params}&resizable=${resizable}`) + window.loadURL(`http://localhost:4200/${options.path}?data=${data}&resizable=${resizable}`) } else { - const url = new URL(path.join('file:', __dirname, `index.html`) + `#/${data.path}?params=${params}&resizable=${resizable}`) + const url = new URL(path.join('file:', __dirname, `index.html`) + `#/${options.path}?data=${data}&resizable=${resizable}`) window.loadURL(url.href) } @@ -157,7 +159,7 @@ function createWindow(data: OpenWindow) { } }) - browserWindows.set(data.id, window) + browserWindows.set(options.id, window) return window } @@ -185,6 +187,14 @@ function createSplashScreen() { } } +function showNotification(event: NotificationEvent) { + const icon = path.join(__dirname, serve ? `../src/assets/icons/nebulosa.png` : `assets/icons/nebulosa.png`) + + new Notification({ ...event, icon }) + .on('click', () => sendToAllWindows(event.type, event)) + .show() +} + function findWindowById(id: number) { for (const [_, window] of browserWindows) if (window.id === id) return window return undefined @@ -270,17 +280,18 @@ try { }) }) - ipcMain.on('OPEN_FITS', async (event) => { + ipcMain.handle('OPEN_FITS', async (event) => { const ownerWindow = findWindowById(event.sender.id) + const value = await dialog.showOpenDialog(ownerWindow!, { filters: [{ name: 'FITS files', extensions: ['fits', 'fit'] }], properties: ['openFile'], }) - event.returnValue = !value.canceled && value.filePaths[0] + return !value.canceled && value.filePaths[0] }) - ipcMain.on('SAVE_FITS_AS', async (event) => { + ipcMain.handle('SAVE_FITS_AS', async (event) => { const ownerWindow = findWindowById(event.sender.id) const value = await dialog.showSaveDialog(ownerWindow!, { filters: [ @@ -290,124 +301,69 @@ try { properties: ['createDirectory', 'showOverwriteConfirmation'], }) - event.returnValue = !value.canceled && value.filePath + return !value.canceled && value.filePath }) - ipcMain.on('OPEN_DIRECTORY', async (event, data?: OpenDirectory) => { + ipcMain.handle('OPEN_DIRECTORY', async (event, data?: OpenDirectory) => { const ownerWindow = findWindowById(event.sender.id) const value = await dialog.showOpenDialog(ownerWindow!, { properties: ['openDirectory'], defaultPath: data?.defaultPath, }) - event.returnValue = !value.canceled && value.filePaths[0] + return !value.canceled && value.filePaths[0] }) - ipcMain.on('PIN_WINDOW', (event) => { + ipcMain.handle('PIN_WINDOW', (event) => { const window = findWindowById(event.sender.id) window?.setAlwaysOnTop(true) - event.returnValue = !!window + return !!window }) - ipcMain.on('UNPIN_WINDOW', (event) => { + ipcMain.handle('UNPIN_WINDOW', (event) => { const window = findWindowById(event.sender.id) window?.setAlwaysOnTop(false) - event.returnValue = !!window + return !!window }) - ipcMain.on('MINIMIZE_WINDOW', (event) => { + ipcMain.handle('MINIMIZE_WINDOW', (event) => { const window = findWindowById(event.sender.id) window?.minimize() - event.returnValue = !!window + return !!window }) - ipcMain.on('MAXIMIZE_WINDOW', (event) => { + ipcMain.handle('MAXIMIZE_WINDOW', (event) => { const window = findWindowById(event.sender.id) if (window?.isMaximized()) window.unmaximize() else window?.maximize() - event.returnValue = window?.isMaximized() ?? false + return window?.isMaximized() ?? false }) - ipcMain.on('CLOSE_WINDOW', (event, id?: string) => { + ipcMain.handle('CLOSE_WINDOW', (event, id?: string) => { if (id) { for (const [key, value] of browserWindows) { if (key === id) { value.close() - event.returnValue = true - return + return true } } - event.returnValue = false + return false } else { const window = findWindowById(event.sender.id) window?.close() - event.returnValue = !!window + return !!window } }) - ipcMain.on('REGISTER_CRON', async (event, cronTime: string) => { - const window = findWindowById(event.sender.id) - - if (!window) return + const events: InternalEventType[] = ['WHEEL_RENAMED', 'LOCATION_CHANGED'] - const cronJobs = cronedWindows.get(window) ?? [] - cronJobs.forEach(e => e.stop()) - const cronJob = new CronJob(cronTime, () => window.webContents.send('CRON_TICKED', cronTime)) - cronJobs.push(cronJob) - cronedWindows.set(window, cronJobs) - - event.returnValue = true - }) - - ipcMain.on('UNREGISTER_CRON', async (event) => { - const window = findWindowById(event.sender.id) - - if (!window) return - - const cronJobs = cronedWindows.get(window) - cronJobs?.forEach(e => e.stop()) - cronedWindows.delete(window) - - event.returnValue = true - }) - - for (const item of INTERNAL_EVENT_TYPES) { - ipcMain.on(item, (event, data) => { - switch (item) { - case 'CAMERA_CHANGED': - selectedCamera = data - break - case 'MOUNT_CHANGED': - selectedMount = data - break - case 'FOCUSER_CHANGED': - selectedFocuser = data - break - case 'WHEEL_CHANGED': - selectedWheel = data - break - } - - switch (item) { - case 'SELECTED_CAMERA': - event.returnValue = selectedCamera - break - case 'SELECTED_MOUNT': - event.returnValue = selectedMount - break - case 'SELECTED_FOCUSER': - event.returnValue = selectedFocuser - break - case 'SELECTED_WHEEL': - event.returnValue = selectedWheel - break - default: - sendToAllWindows(item, data) - break - } + for (const item of events) { + ipcMain.handle(item, (_, data) => { + sendToAllWindows(item, data) + return true }) } } catch (e) { @@ -424,6 +380,6 @@ function sendToAllWindows(channel: string, data: any, home: boolean = true) { } if (serve) { - console.info(channel, data) + console.info(data) } } diff --git a/desktop/app/package-lock.json b/desktop/app/package-lock.json index 76d47019a..e2f4afadf 100644 --- a/desktop/app/package-lock.json +++ b/desktop/app/package-lock.json @@ -10,7 +10,6 @@ "license": "MIT", "dependencies": { "@stomp/stompjs": "7.0.0", - "cron": "3.1.3", "ws": "8.14.2" } }, @@ -19,28 +18,6 @@ "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.0.0.tgz", "integrity": "sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw==" }, - "node_modules/@types/luxon": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.2.tgz", - "integrity": "sha512-l5cpE57br4BIjK+9BSkFBOsWtwv6J9bJpC7gdXIzZyI0vuKvNTk0wZZrkQxMGsUAuGW9+WMNWF2IJMD7br2yeQ==" - }, - "node_modules/cron": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.3.tgz", - "integrity": "sha512-KVxeKTKYj2eNzN4ElnT6nRSbjbfhyxR92O/Jdp6SH3pc05CDJws59jBrZWEMQlxevCiE6QUTrXy+Im3vC3oD3A==", - "dependencies": { - "@types/luxon": "~3.3.0", - "luxon": "~3.4.0" - } - }, - "node_modules/luxon": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz", - "integrity": "sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==", - "engines": { - "node": ">=12" - } - }, "node_modules/ws": { "version": "8.14.2", "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", diff --git a/desktop/app/package.json b/desktop/app/package.json index 09e21751f..e350a816c 100644 --- a/desktop/app/package.json +++ b/desktop/app/package.json @@ -12,7 +12,6 @@ "private": true, "dependencies": { "@stomp/stompjs": "7.0.0", - "ws": "8.14.2", - "cron": "3.1.3" + "ws": "8.14.2" } } diff --git a/desktop/atlas.1.png b/desktop/atlas.1.png deleted file mode 100644 index feaa1e96c..000000000 Binary files a/desktop/atlas.1.png and /dev/null differ diff --git a/desktop/atlas.2.png b/desktop/atlas.2.png deleted file mode 100644 index 74f0c75d4..000000000 Binary files a/desktop/atlas.2.png and /dev/null differ diff --git a/desktop/atlas.3.png b/desktop/atlas.3.png deleted file mode 100644 index 29248b6e2..000000000 Binary files a/desktop/atlas.3.png and /dev/null differ diff --git a/desktop/atlas.4.png b/desktop/atlas.4.png deleted file mode 100644 index be73a183c..000000000 Binary files a/desktop/atlas.4.png and /dev/null differ diff --git a/desktop/atlas.5.png b/desktop/atlas.5.png deleted file mode 100644 index 1f78287a9..000000000 Binary files a/desktop/atlas.5.png and /dev/null differ diff --git a/desktop/atlas.6.png b/desktop/atlas.6.png deleted file mode 100644 index 24c1a0203..000000000 Binary files a/desktop/atlas.6.png and /dev/null differ diff --git a/desktop/atlas.7.png b/desktop/atlas.7.png deleted file mode 100644 index 57592a36a..000000000 Binary files a/desktop/atlas.7.png and /dev/null differ diff --git a/desktop/atlas.8.png b/desktop/atlas.8.png deleted file mode 100644 index 39e73f29b..000000000 Binary files a/desktop/atlas.8.png and /dev/null differ diff --git a/desktop/camera.png b/desktop/camera.png index 6b9ebe01d..9b0fa4147 100644 Binary files a/desktop/camera.png and b/desktop/camera.png differ diff --git a/desktop/electron-builder.json b/desktop/electron-builder.json index 9b9a73aa7..cb3567660 100644 --- a/desktop/electron-builder.json +++ b/desktop/electron-builder.json @@ -42,5 +42,10 @@ "deb", "rpm" ] + }, + "rpm": { + "depends": [ + "/usr/lib64/libuuid.so.1" + ] } } \ No newline at end of file diff --git a/desktop/filter-wheel.png b/desktop/filter-wheel.png index 083857457..ed299e4d0 100644 Binary files a/desktop/filter-wheel.png and b/desktop/filter-wheel.png differ diff --git a/desktop/focuser.png b/desktop/focuser.png index 78003ee07..825aa76a7 100644 Binary files a/desktop/focuser.png and b/desktop/focuser.png differ diff --git a/desktop/framing.png b/desktop/framing.png index c05318918..e0f96b053 100644 Binary files a/desktop/framing.png and b/desktop/framing.png differ diff --git a/desktop/guider.png b/desktop/guider.png index f20e2c348..294fa1e2e 100644 Binary files a/desktop/guider.png and b/desktop/guider.png differ diff --git a/desktop/home.png b/desktop/home.png index fed30a480..644d15cde 100644 Binary files a/desktop/home.png and b/desktop/home.png differ diff --git a/desktop/image.png b/desktop/image.png index 1937d2f8c..0818b6144 100644 Binary files a/desktop/image.png and b/desktop/image.png differ diff --git a/desktop/indi.png b/desktop/indi.png index 20508dbc3..c45891dc4 100644 Binary files a/desktop/indi.png and b/desktop/indi.png differ diff --git a/desktop/mount.png b/desktop/mount.png index 3bb14686f..33cb5c40f 100644 Binary files a/desktop/mount.png and b/desktop/mount.png differ diff --git a/desktop/package-lock.json b/desktop/package-lock.json index d4d3315c8..23895af40 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -10,51 +10,52 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@angular/animations": "16.2.10", - "@angular/cdk": "16.2.9", - "@angular/common": "16.2.10", - "@angular/compiler": "16.2.10", - "@angular/core": "16.2.10", - "@angular/forms": "16.2.10", - "@angular/language-service": "16.2.10", - "@angular/platform-browser": "16.2.10", - "@angular/platform-browser-dynamic": "16.2.10", - "@angular/router": "16.2.10", + "@angular/animations": "17.0.5", + "@angular/cdk": "17.0.1", + "@angular/common": "17.0.5", + "@angular/compiler": "17.0.5", + "@angular/core": "17.0.5", + "@angular/forms": "17.0.5", + "@angular/language-service": "17.0.5", + "@angular/platform-browser": "17.0.5", + "@angular/platform-browser-dynamic": "17.0.5", + "@angular/router": "17.0.5", + "@fontsource/roboto": "5.0.8", + "@mdi/font": "7.3.67", "chart.js": "4.4.0", "chartjs-plugin-zoom": "2.0.1", - "interactjs": "1.10.19", + "interactjs": "1.10.23", "leaflet": "1.9.4", "moment": "2.29.4", "panzoom": "9.4.3", "primeflex": "3.3.1", "primeicons": "6.0.1", - "primeng": "16.5.1", - "random": "4.1.0", + "primeng": "17.0.0-rc.1", "rxjs": "7.8.1", "tslib": "2.6.2", "uuid": "9.0.1", - "zone.js": "0.13.3" + "zone.js": "0.14.2" }, "devDependencies": { - "@angular-builders/custom-webpack": "16.0.1", - "@angular-devkit/build-angular": "16.2.7", - "@angular/cli": "16.2.7", - "@angular/compiler-cli": "16.2.10", - "@types/leaflet": "1.9.7", - "@types/node": "20.8.7", - "@types/uuid": "9.0.6", - "electron": "27.0.1", - "electron-builder": "24.6.4", + "@angular-builders/custom-webpack": "17.0.0", + "@angular-devkit/build-angular": "17.0.5", + "@angular/cli": "17.0.5", + "@angular/compiler-cli": "17.0.5", + "@types/leaflet": "1.9.8", + "@types/node": "20.10.1", + "@types/uuid": "9.0.7", + "electron": "27.1.3", + "electron-builder": "24.9.1", "electron-debug": "3.2.0", "electron-reloader": "1.2.3", "node-polyfill-webpack-plugin": "2.0.1", "npm-run-all": "4.1.5", "ts-node": "10.9.1", - "typescript": "5.1.6", - "wait-on": "7.0.1" + "typescript": "5.2.2", + "wait-on": "7.2.0" }, "engines": { - "node": ">= 16.14.0 || >= 18.10.0" + "node": ">= 20.9.0" } }, "node_modules/@ampproject/remapping": { @@ -71,14 +72,14 @@ } }, "node_modules/@angular-builders/custom-webpack": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-16.0.1.tgz", - "integrity": "sha512-C6INC8UOYDcp8LJwNhE0m66yp+nZX50JdgGI8oRn7fqw3gO58qhDgXrR/8BCrSeC8eOx8WxSuvBJ6u+9dozhyw==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-17.0.0.tgz", + "integrity": "sha512-gKZKRzCE4cbDYyQLu1G/2CkAFbMd0oF07jMxX+jOTADzDeOy9mPOeBaFO60oWgeknrhXf31rynho55LGrHStkg==", "dev": true, "dependencies": { - "@angular-devkit/architect": ">=0.1600.0 < 0.1700.0", - "@angular-devkit/build-angular": "^16.0.0", - "@angular-devkit/core": "^16.0.0", + "@angular-devkit/architect": ">=0.1700.0 < 0.1800.0", + "@angular-devkit/build-angular": "^17.0.0", + "@angular-devkit/core": "^17.0.0", "lodash": "^4.17.15", "ts-node": "^10.0.0", "tsconfig-paths": "^4.1.0", @@ -88,115 +89,116 @@ "node": "^14.20.0 || ^16.13.0 || >=18.10.0" }, "peerDependencies": { - "@angular/compiler-cli": "^16.0.0" + "@angular/compiler-cli": "^17.0.0" } }, "node_modules/@angular-devkit/architect": { - "version": "0.1602.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.7.tgz", - "integrity": "sha512-r6+z4jRE+e9VNeTmJCGz5VI5azRagOqE4SIDqaywz75eHOJ9UPSo9MHy8zFw1eLt1WcvCDqk+Pk9+krh2E+B8Q==", + "version": "0.1700.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.5.tgz", + "integrity": "sha512-kPGiPzystxyLDj79Wy+wCZs5vzx6iUy6fjZ9dKFNS3M9T9UXoo8CZLJS0dWrgO/97M25MSgufyIEDmi+HvwZ7w==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.2.7", + "@angular-devkit/core": "17.0.5", "rxjs": "7.8.1" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular-devkit/build-angular": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-16.2.7.tgz", - "integrity": "sha512-OTH4qzXmWXifhvH0iXwPUhElWEU9SUcIZyWYbv2NR5ImAw/GE07vDuBljGRJeSEC9MpFbThwEFbHD8oRWiLUag==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.0.5.tgz", + "integrity": "sha512-45+DTM3F8OFlMFRxQRgTBXnfndysgiZXiqItiKmFFau7wENZiTijUuFMFjOIHlLXFDI1qs130hYE4YkPNFffxg==", "dev": true, "dependencies": { "@ampproject/remapping": "2.2.1", - "@angular-devkit/architect": "0.1602.7", - "@angular-devkit/build-webpack": "0.1602.7", - "@angular-devkit/core": "16.2.7", - "@babel/core": "7.22.9", - "@babel/generator": "7.22.9", + "@angular-devkit/architect": "0.1700.5", + "@angular-devkit/build-webpack": "0.1700.5", + "@angular-devkit/core": "17.0.5", + "@babel/core": "7.23.2", + "@babel/generator": "7.23.0", "@babel/helper-annotate-as-pure": "7.22.5", "@babel/helper-split-export-declaration": "7.22.6", - "@babel/plugin-proposal-async-generator-functions": "7.20.7", + "@babel/plugin-transform-async-generator-functions": "7.23.2", "@babel/plugin-transform-async-to-generator": "7.22.5", - "@babel/plugin-transform-runtime": "7.22.9", - "@babel/preset-env": "7.22.9", - "@babel/runtime": "7.22.6", - "@babel/template": "7.22.5", + "@babel/plugin-transform-runtime": "7.23.2", + "@babel/preset-env": "7.23.2", + "@babel/runtime": "7.23.2", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "16.2.7", + "@ngtools/webpack": "17.0.5", "@vitejs/plugin-basic-ssl": "1.0.1", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.14", + "autoprefixer": "10.4.16", "babel-loader": "9.1.3", "babel-plugin-istanbul": "6.1.1", + "browser-sync": "2.29.3", "browserslist": "^4.21.5", "chokidar": "3.5.3", "copy-webpack-plugin": "11.0.0", "critters": "0.0.20", "css-loader": "6.8.1", - "esbuild-wasm": "0.18.17", + "esbuild-wasm": "0.19.5", "fast-glob": "3.3.1", - "guess-parser": "0.4.22", - "https-proxy-agent": "5.0.1", - "inquirer": "8.2.4", + "http-proxy-middleware": "2.0.6", + "https-proxy-agent": "7.0.2", + "inquirer": "9.2.11", "jsonc-parser": "3.2.0", "karma-source-map-support": "1.4.0", - "less": "4.1.3", + "less": "4.2.0", "less-loader": "11.1.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.2.1", - "magic-string": "0.30.1", + "magic-string": "0.30.5", "mini-css-extract-plugin": "2.7.6", "mrmime": "1.0.1", "open": "8.4.2", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", - "picomatch": "2.3.1", - "piscina": "4.0.0", + "picomatch": "3.0.1", + "piscina": "4.1.0", "postcss": "8.4.31", "postcss-loader": "7.3.3", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.64.1", + "sass": "1.69.5", "sass-loader": "13.3.2", "semver": "7.5.4", "source-map-loader": "4.0.1", "source-map-support": "0.5.21", - "terser": "5.19.2", + "terser": "5.24.0", "text-table": "0.2.0", "tree-kill": "1.2.2", - "tslib": "2.6.1", - "vite": "4.4.7", - "webpack": "5.88.2", + "tslib": "2.6.2", + "undici": "5.27.2", + "vite": "4.5.0", + "webpack": "5.89.0", "webpack-dev-middleware": "6.1.1", "webpack-dev-server": "4.15.1", - "webpack-merge": "5.9.0", + "webpack-merge": "5.10.0", "webpack-subresource-integrity": "5.1.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.18.17" + "esbuild": "0.19.5" }, "peerDependencies": { - "@angular/compiler-cli": "^16.0.0", - "@angular/localize": "^16.0.0", - "@angular/platform-server": "^16.0.0", - "@angular/service-worker": "^16.0.0", + "@angular/compiler-cli": "^17.0.0", + "@angular/localize": "^17.0.0", + "@angular/platform-server": "^17.0.0", + "@angular/service-worker": "^17.0.0", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "karma": "^6.3.0", - "ng-packagr": "^16.0.0", + "ng-packagr": "^17.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=4.9.3 <5.2" + "typescript": ">=5.2 <5.3" }, "peerDependenciesMeta": { "@angular/localize": { @@ -228,132 +230,17 @@ } } }, - "node_modules/@angular-devkit/build-angular/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/@angular-devkit/build-angular/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/@angular-devkit/build-angular/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/@angular-devkit/build-angular/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/tslib": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", - "dev": true - }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack": { - "version": "5.88.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", - "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack-merge": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", - "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1602.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1602.7.tgz", - "integrity": "sha512-3+MV9ehn65XUUMSBBgfg5K2zZs2jhif75ypI+BBUfZDUWeKR5MeGJy0aDHZ+2H94kPkmSD3PrkOuitWdnDjTgA==", + "version": "0.1700.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1700.5.tgz", + "integrity": "sha512-rLtDIK6je7JxhWG76aM8smfX13XHv+LlepwdK4lQqPEnz5BnkTfNFBnqwIWHA2eNUNTnVgeS356PxckZI3YL1g==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1602.7", + "@angular-devkit/architect": "0.1700.5", "rxjs": "7.8.1" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -363,20 +250,20 @@ } }, "node_modules/@angular-devkit/core": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.7.tgz", - "integrity": "sha512-XskObYrg7NRdEuHnSVZOM7OeinEL8HzugjmKnawAa+dAbFCCoGsVWjMliA/Q8sb1yfGkyL0WW7DZABZj7EGwWA==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.5.tgz", + "integrity": "sha512-e1evgRabAfOZBnmFCe8E0oufcu+FzBe5hBzS94Dm42GlxdX965/M4yVKQxIMpjivQTmjl+AWb6cF1ltBdSGZeQ==", "dev": true, "dependencies": { "ajv": "8.12.0", "ajv-formats": "2.1.1", "jsonc-parser": "3.2.0", - "picomatch": "2.3.1", + "picomatch": "3.0.1", "rxjs": "7.8.1", "source-map": "0.7.4" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -390,41 +277,41 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.7.tgz", - "integrity": "sha512-zu3xHwA4w+kXHkyyjGl3i7uSU2/kKLPKuyyixw0WLcKUQCYd7TWmu8OC0qCDa42XkxP9gGL091dJFu56exgneA==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.0.5.tgz", + "integrity": "sha512-KYPku0qTb8B+TtRbFqXGYpJOPg1k6d5bNHV6n8jTc35mlEUUghOd7HkovdfkQ3cgGNQM56a74D1CvSeruZEGsA==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.2.7", + "@angular-devkit/core": "17.0.5", "jsonc-parser": "3.2.0", - "magic-string": "0.30.1", + "magic-string": "0.30.5", "ora": "5.4.1", "rxjs": "7.8.1" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular/animations": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.2.10.tgz", - "integrity": "sha512-UudunZoyFWWNpuWkwiBxC3cleLCVJGHIfMgypFwC35YjtiIlRJ0r4nVkc96Rq1xd4mT71Dbk1kQHc8urB8A7aw==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.0.5.tgz", + "integrity": "sha512-NZ9Y3QWqrn0THypVNwsztMV9rnjxNMRIf6to8aZv+ehIUOvskqcA/lW5qAdcMr1uNoyloB9vahJrDniWWEKT5A==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "16.2.10" + "@angular/core": "17.0.5" } }, "node_modules/@angular/cdk": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.2.9.tgz", - "integrity": "sha512-TrLV68YpddUx3t2rs8W29CPk8YkgNGA8PKHwjB4Xvo1yaEH5XUnsw3MQCh42Ee7FKseaqzFgG85USZXAK0IB0A==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-17.0.1.tgz", + "integrity": "sha512-0hrXm2D0s0/vUtDoLFRWTs75k5WY/hQmfnsaJXHeqinbE3eKOxmQxL1i7ymnMSQthEWzgRAhzS3Nfs7Alw3dQA==", "dependencies": { "tslib": "^2.3.0" }, @@ -432,32 +319,32 @@ "parse5": "^7.1.2" }, "peerDependencies": { - "@angular/common": "^16.0.0 || ^17.0.0", - "@angular/core": "^16.0.0 || ^17.0.0", + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/cli": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.7.tgz", - "integrity": "sha512-30yBAYzbrj/WM4tLiX4IU5byw0b5Y5LEzcpjYZglv/RXPrnevGlRXmgCulpt8wIdkd668N7kXEQ23nipuJDXMg==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.0.5.tgz", + "integrity": "sha512-IWtepjO1yTVGblbpTI7vtdxX5EjOYSL4BGa+3g85XuY6U2H38Bc9ZVBAYteAvRX1ZA2yvwJw068YY52ITlnr4A==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1602.7", - "@angular-devkit/core": "16.2.7", - "@angular-devkit/schematics": "16.2.7", - "@schematics/angular": "16.2.7", + "@angular-devkit/architect": "0.1700.5", + "@angular-devkit/core": "17.0.5", + "@angular-devkit/schematics": "17.0.5", + "@schematics/angular": "17.0.5", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", "ini": "4.1.1", - "inquirer": "8.2.4", + "inquirer": "9.2.11", "jsonc-parser": "3.2.0", - "npm-package-arg": "10.1.0", - "npm-pick-manifest": "8.0.1", + "npm-package-arg": "11.0.1", + "npm-pick-manifest": "9.0.0", "open": "8.4.2", "ora": "5.4.1", - "pacote": "15.2.0", - "resolve": "1.22.2", + "pacote": "17.0.4", + "resolve": "1.22.8", "semver": "7.5.4", "symbol-observable": "4.0.0", "yargs": "17.7.2" @@ -466,38 +353,38 @@ "ng": "bin/ng.js" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular/common": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.2.10.tgz", - "integrity": "sha512-cLth66aboInNcWFjDBRmK30jC5KN10nKDDcv4U/r3TDTBpKOtnmTjNFFr7dmjfUmVhHFy/66piBMfpjZI93Rxg==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.0.5.tgz", + "integrity": "sha512-1vFZ7nd8xyAYh/DwFtRuSieP8Dy/6QuOxl914/TOUr26F1a4e+7ywCyMLVjmYjx+WkZe7uu/Hgpr2raBaVTnQw==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "16.2.10", + "@angular/core": "17.0.5", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-16.2.10.tgz", - "integrity": "sha512-ty6SfqkZlV2bLU/SSi3wmxrEFgPrK+WVslCNIr3FlTnCBdqpIbadHN2QB3A1d9XaNc7c4Tq5DQKh34cwMwNbuw==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.0.5.tgz", + "integrity": "sha512-V6LnX/B2YXpzXeNWavtX/XPNUnWrVUFpiOniKqHYhAxXnibhyXL9DRsyVs8QbKgIcPPcQeJMHdAjklCWJsePvg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/core": "16.2.10" + "@angular/core": "17.0.5" }, "peerDependenciesMeta": { "@angular/core": { @@ -506,9 +393,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-16.2.10.tgz", - "integrity": "sha512-swgmtm4R23vQV9nJTXdDEFpOyIw3kz80mdT9qo3VId/2rqenOK253JsFypoqEj/fKzjV9gwXtTbmrMlhVyuyxw==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.0.5.tgz", + "integrity": "sha512-Nb99iKz8LMoc5HC9iu5rbWblXb68sHHI6bcN8sdqvc2g+PohkGNbtRjVZFhP+WKMaNFYDSvLWcHFFYItLRkT4g==", "dev": true, "dependencies": { "@babel/core": "7.23.2", @@ -526,141 +413,67 @@ "ngcc": "bundles/ngcc/index.js" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/compiler": "16.2.10", - "typescript": ">=4.9.3 <5.2" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" + "@angular/compiler": "17.0.5", + "typescript": ">=5.2 <5.3" } }, "node_modules/@angular/core": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.10.tgz", - "integrity": "sha512-0XTsPjNflFhOl2CfNEdGeDOklG2t+m/D3g10Y7hg9dBjC1dURUEqTmM4d6J7JNbBURrP+/iP7uLsn3WRSipGUw==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.0.5.tgz", + "integrity": "sha512-siWUrdBWgTAqMnRF+qxGZznj5AdR/x3+8l0/bj4CkSZzwZGL/CHy40ec71bbgiPkYob1v4v40voXu2aSSeCLPg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.13.0" + "zone.js": "~0.14.0" } }, "node_modules/@angular/forms": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-16.2.10.tgz", - "integrity": "sha512-TZliEtSWIL1UzY8kjed4QcMawWS8gk/H60KVgzCh83NGE0wd1OGv20Z5OR7O8j07dxB9vaxY7CQz/8eCz5KaNQ==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.0.5.tgz", + "integrity": "sha512-d91Rre/NK+SgamF1OJmDJUx+Zs8M7qFmrKu7c+hNsXPe8J/fkMNoWFikne/WSsegwY929E1xpeqvu/KXQt90ug==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "16.2.10", - "@angular/core": "16.2.10", - "@angular/platform-browser": "16.2.10", + "@angular/common": "17.0.5", + "@angular/core": "17.0.5", + "@angular/platform-browser": "17.0.5", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-16.2.10.tgz", - "integrity": "sha512-r3KNXizhZDtj5/L68xnrtgHp5iSYf4NPyWHovoyAWClabsZ64cK38fOzMNCT/otrwqJWlz9ELnW/b/pxR+M9sw==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-17.0.5.tgz", + "integrity": "sha512-tVXYamdjkAYYv4YCiMKxCYqLgvI/g2y2Ny6fUUVPti9xFqiF88q8V7j3N8FeLdSNvgok1LSdfFjJAgQonJ4Sxw==", "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" } }, "node_modules/@angular/platform-browser": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.10.tgz", - "integrity": "sha512-TOZiK7ji550F8G39Ri255NnK1+2Xlr74RiElJdQct4TzfN0lqNf2KRDFFNwDohkP/78FUzcP4qBxs+Nf8M7OuQ==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.0.5.tgz", + "integrity": "sha512-VJQ6bVS40xJLNGNcX59/QFPrZesIm2zETOqAc6K04onuWF1EnJqvcDog9eYJsm0sLWhQeCdWVmAFRenTkDoqng==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/animations": "16.2.10", - "@angular/common": "16.2.10", - "@angular/core": "16.2.10" + "@angular/animations": "17.0.5", + "@angular/common": "17.0.5", + "@angular/core": "17.0.5" }, "peerDependenciesMeta": { "@angular/animations": { @@ -669,36 +482,36 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-16.2.10.tgz", - "integrity": "sha512-YVmhAjOmsp2SWRonv6Mr/qXuKroCiew9asd1IlAZ//wqcml9ZrNAcX3WlDa8ZqdmOplQb0LuvvirfNB/6Is/jg==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.0.5.tgz", + "integrity": "sha512-Ki+0B3/S+Rv3O4jf+tbDBPs0m+VUMoS6VVCCLviaurYGPLPtGblhCzRv49Zoyo5gEVoEOgnxS6CI91Tv6My9ug==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "16.2.10", - "@angular/compiler": "16.2.10", - "@angular/core": "16.2.10", - "@angular/platform-browser": "16.2.10" + "@angular/common": "17.0.5", + "@angular/compiler": "17.0.5", + "@angular/core": "17.0.5", + "@angular/platform-browser": "17.0.5" } }, "node_modules/@angular/router": { - "version": "16.2.10", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-16.2.10.tgz", - "integrity": "sha512-ndiq2NkGZ8hTsyL/KK8qsiR3UA0NjOFIn1jtGXOKtHryXZ6vSTtkhtkE4h4+G6/QNTL1IKtocFhOQt/xsc7DUA==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.0.5.tgz", + "integrity": "sha512-9e5MQJzDdfhXKSYrduIDmDf73GBRcjx6qE+k5CliGY4sFza10wdbrM4LkiuA3Z2Ja+2AKkotrGG3ZMCtAsFY1g==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0" + "node": "^18.13.0 || >=20.9.0" }, "peerDependencies": { - "@angular/common": "16.2.10", - "@angular/core": "16.2.10", - "@angular/platform-browser": "16.2.10", + "@angular/common": "17.0.5", + "@angular/core": "17.0.5", + "@angular/platform-browser": "17.0.5", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -709,12 +522,12 @@ "dev": true }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -722,34 +535,34 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", + "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", + "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { @@ -760,6 +573,12 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -770,12 +589,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", - "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -834,17 +653,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", - "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.5.tgz", + "integrity": "sha512-QELlRWxSpgdwdJzSJn4WAhKC+hvw/AtHbbrIoncKHkhKKR/luAlKkgBDcri1EzWAo8f8VvYVryEHN4tax/V67A==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" @@ -929,20 +748,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-function-name/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-hoist-variables": { "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", @@ -980,9 +785,9 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", @@ -1090,9 +895,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -1108,9 +913,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, "engines": { "node": ">=6.9.0" @@ -1130,52 +935,24 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", + "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", "dev": true, "dependencies": { "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.23.5", + "@babel/types": "^7.23.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -1187,9 +964,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", + "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1199,9 +976,9 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.15.tgz", - "integrity": "sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", + "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1214,14 +991,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.15.tgz", - "integrity": "sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", + "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.15" + "@babel/plugin-transform-optional-chaining": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -1230,29 +1007,10 @@ "@babel/core": "^7.13.0" } }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -1261,23 +1019,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", @@ -1342,9 +1083,9 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", + "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1357,9 +1098,9 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", + "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1514,9 +1255,9 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", + "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1564,9 +1305,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", + "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1579,9 +1320,9 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz", - "integrity": "sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", + "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1594,12 +1335,12 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", + "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1610,12 +1351,12 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", - "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", + "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, @@ -1627,18 +1368,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.15.tgz", - "integrity": "sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.5.tgz", + "integrity": "sha512-jvOTR4nicqYC9yzOHIhXG5emiFEOpappSJAl73SDSEDcybD+Puuze8Tnpb9p9qEyYup24tq891gkaygIFvWDqg==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-replace-supers": "^7.22.20", "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, @@ -1650,13 +1391,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", + "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" + "@babel/template": "^7.22.15" }, "engines": { "node": ">=6.9.0" @@ -1666,9 +1407,9 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz", - "integrity": "sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", + "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1681,12 +1422,12 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", + "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1697,9 +1438,9 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", + "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1712,9 +1453,9 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", - "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", + "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1728,12 +1469,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", + "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1744,9 +1485,9 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", - "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", + "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1760,9 +1501,9 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.15.tgz", - "integrity": "sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.3.tgz", + "integrity": "sha512-X8jSm8X1CMwxmK878qsUGJRmbysKNbdpTv/O1/v0LuY/ZkZrng5WYiekYSdg9m09OTmDDUWeEDsTE+17WYbAZw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1775,13 +1516,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", + "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1792,9 +1533,9 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", - "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", + "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1808,9 +1549,9 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", + "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1823,9 +1564,9 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", - "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", + "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1839,9 +1580,9 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", + "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1854,12 +1595,12 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz", - "integrity": "sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", + "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1870,12 +1611,12 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz", - "integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, @@ -1887,13 +1628,13 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz", - "integrity": "sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", + "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", "dev": true, "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.0", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20" }, @@ -1905,12 +1646,12 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", + "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -1937,9 +1678,9 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", + "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1952,9 +1693,9 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", - "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", + "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1968,9 +1709,9 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", - "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", + "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -1984,16 +1725,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.15.tgz", - "integrity": "sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz", + "integrity": "sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", + "@babel/compat-data": "^7.23.3", "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.15" + "@babel/plugin-transform-parameters": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -2003,13 +1744,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", + "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" + "@babel/helper-replace-supers": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -2019,9 +1760,9 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", - "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", + "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2035,9 +1776,9 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz", - "integrity": "sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", + "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2052,9 +1793,9 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.15.tgz", - "integrity": "sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", + "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -2067,12 +1808,12 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", + "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -2083,13 +1824,13 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", - "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", + "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, @@ -2101,9 +1842,9 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", + "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -2116,9 +1857,9 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", - "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", + "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2132,9 +1873,9 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", + "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -2147,16 +1888,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz", - "integrity": "sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz", + "integrity": "sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", "semver": "^6.3.1" }, "engines": { @@ -2176,9 +1917,9 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", - "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", + "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -2191,9 +1932,9 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", + "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", @@ -2207,9 +1948,9 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", + "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -2222,9 +1963,9 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", + "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -2237,9 +1978,9 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", + "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -2252,9 +1993,9 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", - "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", + "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -2267,12 +2008,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", + "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -2283,12 +2024,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", + "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -2299,12 +2040,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", + "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-create-regexp-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5" }, "engines": { @@ -2315,17 +2056,17 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.9.tgz", - "integrity": "sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.2.tgz", + "integrity": "sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", + "@babel/compat-data": "^7.23.2", + "@babel/helper-compilation-targets": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.15", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.15", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -2346,58 +2087,58 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.22.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.7", + "@babel/plugin-transform-async-generator-functions": "^7.23.2", "@babel/plugin-transform-async-to-generator": "^7.22.5", "@babel/plugin-transform-block-scoped-functions": "^7.22.5", - "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.23.0", "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-class-static-block": "^7.22.5", - "@babel/plugin-transform-classes": "^7.22.6", + "@babel/plugin-transform-class-static-block": "^7.22.11", + "@babel/plugin-transform-classes": "^7.22.15", "@babel/plugin-transform-computed-properties": "^7.22.5", - "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.23.0", "@babel/plugin-transform-dotall-regex": "^7.22.5", "@babel/plugin-transform-duplicate-keys": "^7.22.5", - "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.11", "@babel/plugin-transform-exponentiation-operator": "^7.22.5", - "@babel/plugin-transform-export-namespace-from": "^7.22.5", - "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.11", + "@babel/plugin-transform-for-of": "^7.22.15", "@babel/plugin-transform-function-name": "^7.22.5", - "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.11", "@babel/plugin-transform-literals": "^7.22.5", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.11", "@babel/plugin-transform-member-expression-literals": "^7.22.5", - "@babel/plugin-transform-modules-amd": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.23.0", + "@babel/plugin-transform-modules-commonjs": "^7.23.0", + "@babel/plugin-transform-modules-systemjs": "^7.23.0", "@babel/plugin-transform-modules-umd": "^7.22.5", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", "@babel/plugin-transform-new-target": "^7.22.5", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", - "@babel/plugin-transform-numeric-separator": "^7.22.5", - "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", + "@babel/plugin-transform-numeric-separator": "^7.22.11", + "@babel/plugin-transform-object-rest-spread": "^7.22.15", "@babel/plugin-transform-object-super": "^7.22.5", - "@babel/plugin-transform-optional-catch-binding": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.6", - "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.11", + "@babel/plugin-transform-optional-chaining": "^7.23.0", + "@babel/plugin-transform-parameters": "^7.22.15", "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.11", "@babel/plugin-transform-property-literals": "^7.22.5", - "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.10", "@babel/plugin-transform-reserved-words": "^7.22.5", "@babel/plugin-transform-shorthand-properties": "^7.22.5", "@babel/plugin-transform-spread": "^7.22.5", "@babel/plugin-transform-sticky-regex": "^7.22.5", "@babel/plugin-transform-template-literals": "^7.22.5", "@babel/plugin-transform-typeof-symbol": "^7.22.5", - "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.10", "@babel/plugin-transform-unicode-property-regex": "^7.22.5", "@babel/plugin-transform-unicode-regex": "^7.22.5", "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.4", - "babel-plugin-polyfill-corejs3": "^0.8.2", - "babel-plugin-polyfill-regenerator": "^0.5.1", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "@babel/types": "^7.23.0", + "babel-plugin-polyfill-corejs2": "^0.4.6", + "babel-plugin-polyfill-corejs3": "^0.8.5", + "babel-plugin-polyfill-regenerator": "^0.5.3", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -2418,14 +2159,12 @@ } }, "node_modules/@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, @@ -2440,45 +2179,45 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", - "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", + "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.5", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/parser": "^7.23.5", + "@babel/types": "^7.23.5", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -2487,12 +2226,12 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", + "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.5", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -2502,12 +2241,12 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", + "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, @@ -2595,9 +2334,9 @@ } }, "node_modules/@electron/asar": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.7.tgz", - "integrity": "sha512-8FaSCAIiZGYFWyjeevPQt+0e9xCK9YmJ2Rjg5SXgdsXon6cRnU0Yxnbe6CvJbQn26baifur2Y2G5EBayRIsjyg==", + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.8.tgz", + "integrity": "sha512-cmskk5M06ewHMZAplSiF4AlME3IrnnZhKnWbtwKVLRkdJkKyUVjMLhDIiPIx/+6zQWVlKX/LtmK9xDme7540Sg==", "dev": true, "dependencies": { "commander": "^5.0.0", @@ -2654,6 +2393,29 @@ "global-agent": "^3.0.0" } }, + "node_modules/@electron/get/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/@electron/get/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/@electron/get/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2705,9 +2467,9 @@ } }, "node_modules/@electron/notarize/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -2773,9 +2535,9 @@ } }, "node_modules/@electron/osx-sign/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -2849,18 +2611,18 @@ } }, "node_modules/@electron/universal/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, "node_modules/@esbuild/android-arm": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.17.tgz", - "integrity": "sha512-wHsmJG/dnL3OkpAcwbgoBTTMHVi4Uyou3F5mf58ZtmUyIKfcdA7TROav/6tCzET4A3QW2Q2FC+eFneMU+iyOxg==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.5.tgz", + "integrity": "sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==", "cpu": [ "arm" ], @@ -2874,9 +2636,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.17.tgz", - "integrity": "sha512-9np+YYdNDed5+Jgr1TdWBsozZ85U1Oa3xW0c7TWqH0y2aGghXtZsuT8nYRbzOMcl0bXZXjOGbksoTtVOlWrRZg==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz", + "integrity": "sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==", "cpu": [ "arm64" ], @@ -2890,9 +2652,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.17.tgz", - "integrity": "sha512-O+FeWB/+xya0aLg23hHEM2E3hbfwZzjqumKMSIqcHbNvDa+dza2D0yLuymRBQQnC34CWrsJUXyH2MG5VnLd6uw==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.5.tgz", + "integrity": "sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==", "cpu": [ "x64" ], @@ -2906,9 +2668,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.17.tgz", - "integrity": "sha512-M9uJ9VSB1oli2BE/dJs3zVr9kcCBBsE883prage1NWz6pBS++1oNn/7soPNS3+1DGj0FrkSvnED4Bmlu1VAE9g==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz", + "integrity": "sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==", "cpu": [ "arm64" ], @@ -2922,9 +2684,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.17.tgz", - "integrity": "sha512-XDre+J5YeIJDMfp3n0279DFNrGCXlxOuGsWIkRb1NThMZ0BsrWXoTg23Jer7fEXQ9Ye5QjrvXpxnhzl3bHtk0g==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz", + "integrity": "sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==", "cpu": [ "x64" ], @@ -2938,9 +2700,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.17.tgz", - "integrity": "sha512-cjTzGa3QlNfERa0+ptykyxs5A6FEUQQF0MuilYXYBGdBxD3vxJcKnzDlhDCa1VAJCmAxed6mYhA2KaJIbtiNuQ==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz", + "integrity": "sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==", "cpu": [ "arm64" ], @@ -2954,9 +2716,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.17.tgz", - "integrity": "sha512-sOxEvR8d7V7Kw8QqzxWc7bFfnWnGdaFBut1dRUYtu+EIRXefBc/eIsiUiShnW0hM3FmQ5Zf27suDuHsKgZ5QrA==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz", + "integrity": "sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==", "cpu": [ "x64" ], @@ -2970,9 +2732,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.17.tgz", - "integrity": "sha512-2d3Lw6wkwgSLC2fIvXKoMNGVaeY8qdN0IC3rfuVxJp89CRfA3e3VqWifGDfuakPmp90+ZirmTfye1n4ncjv2lg==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz", + "integrity": "sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==", "cpu": [ "arm" ], @@ -2986,9 +2748,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.17.tgz", - "integrity": "sha512-c9w3tE7qA3CYWjT+M3BMbwMt+0JYOp3vCMKgVBrCl1nwjAlOMYzEo+gG7QaZ9AtqZFj5MbUc885wuBBmu6aADQ==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz", + "integrity": "sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==", "cpu": [ "arm64" ], @@ -3002,9 +2764,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.17.tgz", - "integrity": "sha512-1DS9F966pn5pPnqXYz16dQqWIB0dmDfAQZd6jSSpiT9eX1NzKh07J6VKR3AoXXXEk6CqZMojiVDSZi1SlmKVdg==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz", + "integrity": "sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==", "cpu": [ "ia32" ], @@ -3018,9 +2780,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.17.tgz", - "integrity": "sha512-EvLsxCk6ZF0fpCB6w6eOI2Fc8KW5N6sHlIovNe8uOFObL2O+Mr0bflPHyHwLT6rwMg9r77WOAWb2FqCQrVnwFg==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz", + "integrity": "sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==", "cpu": [ "loong64" ], @@ -3034,9 +2796,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.17.tgz", - "integrity": "sha512-e0bIdHA5p6l+lwqTE36NAW5hHtw2tNRmHlGBygZC14QObsA3bD4C6sXLJjvnDIjSKhW1/0S3eDy+QmX/uZWEYQ==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz", + "integrity": "sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==", "cpu": [ "mips64el" ], @@ -3050,9 +2812,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.17.tgz", - "integrity": "sha512-BAAilJ0M5O2uMxHYGjFKn4nJKF6fNCdP1E0o5t5fvMYYzeIqy2JdAP88Az5LHt9qBoUa4tDaRpfWt21ep5/WqQ==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz", + "integrity": "sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==", "cpu": [ "ppc64" ], @@ -3066,9 +2828,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.17.tgz", - "integrity": "sha512-Wh/HW2MPnC3b8BqRSIme/9Zhab36PPH+3zam5pqGRH4pE+4xTrVLx2+XdGp6fVS3L2x+DrsIcsbMleex8fbE6g==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz", + "integrity": "sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==", "cpu": [ "riscv64" ], @@ -3082,9 +2844,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.17.tgz", - "integrity": "sha512-j/34jAl3ul3PNcK3pfI0NSlBANduT2UO5kZ7FCaK33XFv3chDhICLY8wJJWIhiQ+YNdQ9dxqQctRg2bvrMlYgg==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz", + "integrity": "sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==", "cpu": [ "s390x" ], @@ -3098,9 +2860,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.17.tgz", - "integrity": "sha512-QM50vJ/y+8I60qEmFxMoxIx4de03pGo2HwxdBeFd4nMh364X6TIBZ6VQ5UQmPbQWUVWHWws5MmJXlHAXvJEmpQ==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz", + "integrity": "sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==", "cpu": [ "x64" ], @@ -3114,9 +2876,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.17.tgz", - "integrity": "sha512-/jGlhWR7Sj9JPZHzXyyMZ1RFMkNPjC6QIAan0sDOtIo2TYk3tZn5UDrkE0XgsTQCxWTTOcMPf9p6Rh2hXtl5TQ==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz", + "integrity": "sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==", "cpu": [ "x64" ], @@ -3130,9 +2892,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.17.tgz", - "integrity": "sha512-rSEeYaGgyGGf4qZM2NonMhMOP/5EHp4u9ehFiBrg7stH6BYEEjlkVREuDEcQ0LfIl53OXLxNbfuIj7mr5m29TA==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz", + "integrity": "sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==", "cpu": [ "x64" ], @@ -3146,9 +2908,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.17.tgz", - "integrity": "sha512-Y7ZBbkLqlSgn4+zot4KUNYst0bFoO68tRgI6mY2FIM+b7ZbyNVtNbDP5y8qlu4/knZZ73fgJDlXID+ohY5zt5g==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz", + "integrity": "sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==", "cpu": [ "x64" ], @@ -3162,9 +2924,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.17.tgz", - "integrity": "sha512-bwPmTJsEQcbZk26oYpc4c/8PvTY3J5/QK8jM19DVlEsAB41M39aWovWoHtNm78sd6ip6prilxeHosPADXtEJFw==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz", + "integrity": "sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==", "cpu": [ "arm64" ], @@ -3178,9 +2940,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.17.tgz", - "integrity": "sha512-H/XaPtPKli2MhW+3CQueo6Ni3Avggi6hP/YvgkEe1aSaxw+AeO8MFjq8DlgfTd9Iz4Yih3QCZI6YLMoyccnPRg==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz", + "integrity": "sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==", "cpu": [ "ia32" ], @@ -3194,9 +2956,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.17.tgz", - "integrity": "sha512-fGEb8f2BSA3CW7riJVurug65ACLuQAzKq0SSqkY2b2yHHH0MzDfbLyKIGzHwOI/gkHcxM/leuSW6D5w/LMNitA==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz", + "integrity": "sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==", "cpu": [ "x64" ], @@ -3209,6 +2971,20 @@ "node": ">=12" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@fontsource/roboto": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.8.tgz", + "integrity": "sha512-XxPltXs5R31D6UZeLIV1td3wTXU3jzd3f2DLsXI8tytMGBkIsGcc9sIyiupRtA8y73HAhuSCeweOoBqf6DbWCA==" + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -3225,9 +3001,9 @@ } }, "node_modules/@interactjs/types": { - "version": "1.10.19", - "resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.19.tgz", - "integrity": "sha512-oEqGmt9/Ob+jz0FUaBzpDXBmf+2dfdhPuEwQcMGH6nQTR2ETGtYIlAnQtADHvnCin+cVkrmqVohfHBysyQr4Lw==" + "version": "1.10.23", + "resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.23.tgz", + "integrity": "sha512-8/s1gFVNW60SqFLiFQDsvJuuzICthzyOu52bu8MhLFsxFnhVfng1xzjxi2+UokQULsp0WgBsctIS9bF7se9nJQ==" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -3419,6 +3195,18 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@ljharb/through": { + "version": "2.3.11", + "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.11.tgz", + "integrity": "sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/@malept/cross-spawn-promise": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", @@ -3484,27 +3272,32 @@ } }, "node_modules/@malept/flatpak-bundler/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, + "node_modules/@mdi/font": { + "version": "7.3.67", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.3.67.tgz", + "integrity": "sha512-SWxvzRbUQRfewlIV+OF4/YF4DkeTjMWoT8Hh9yeU/5UBVdJZj9Uf4a9+cXjknSIhIaMxZ/4N1O/s7ojApOOGjg==" + }, "node_modules/@ngtools/webpack": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.7.tgz", - "integrity": "sha512-QnVoYpMNMuV387VgmP/c/ylD9qUIZpN02LMg3rQqz7NDej0jboBZaxqLJ+7jQaCoEIFVGIgL/RR/X1kponxJZg==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.0.5.tgz", + "integrity": "sha512-r82k7mxErJHtd6dzq0PKHQNhOuEjUZn95f2adJpO5mP/R/ms8LUk1ILvP3EocxkisYU8ET2EeGj3wQZC2g3RcA==", "dev": true, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "peerDependencies": { - "@angular/compiler-cli": "^16.0.0", - "typescript": ">=4.9.3 <5.2", + "@angular/compiler-cli": "^17.0.0", + "typescript": ">=5.2 <5.3", "webpack": "^5.54.0" } }, @@ -3543,6 +3336,56 @@ "node": ">= 8" } }, + "node_modules/@npmcli/agent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.0.tgz", + "integrity": "sha512-2yThA1Es98orMkpSLVqlDZAMPK3jHJhifP2gnNUdk1754uZ8yI5c+ulCoVG+WlntQA6MzhrURMXjSd9Z7dJ2/Q==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, "node_modules/@npmcli/fs": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", @@ -3556,46 +3399,55 @@ } }, "node_modules/@npmcli/git": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", - "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.3.tgz", + "integrity": "sha512-UZp9NwK+AynTrKvHn5k3KviW/hA5eENmFsu3iAPe7sWRt0lFUdsY/wXIYjpDFe7cdSNwOIzbObfwgt6eL5/2zw==", "dev": true, "dependencies": { - "@npmcli/promise-spawn": "^6.0.0", - "lru-cache": "^7.4.4", - "npm-pick-manifest": "^8.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", "proc-log": "^3.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", - "which": "^3.0.0" + "which": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, "engines": { - "node": ">=12" + "node": "14 || >=16.14" } }, "node_modules/@npmcli/git/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/installed-package-contents": { @@ -3624,61 +3476,79 @@ } }, "node_modules/@npmcli/promise-spawn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", - "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.0.tgz", + "integrity": "sha512-wBqcGsMELZna0jDblGd7UXgOby45TQaMWmbFwWX+SEotk4HV6zG2t6rT9siyLhPk4P6YYqgfL1UO8nMWDBVJXQ==", "dev": true, "dependencies": { - "which": "^3.0.0" + "which": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" } }, "node_modules/@npmcli/promise-spawn/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/run-script": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz", - "integrity": "sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.2.tgz", + "integrity": "sha512-Omu0rpA8WXvcGeY6DDzyRoY1i5DkCBkzyJ+m2u7PD6quzb0TvSqdIPOkTn8ZBOj7LbbcbMfZ3c5skwSu6m8y2w==", "dev": true, "dependencies": { "@npmcli/node-gyp": "^3.0.0", - "@npmcli/promise-spawn": "^6.0.0", - "node-gyp": "^9.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", "read-package-json-fast": "^3.0.0", - "which": "^3.0.0" + "which": "^4.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" } }, "node_modules/@npmcli/run-script/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.13.0 || >=18.0.0" } }, "node_modules/@pkgjs/parseargs": { @@ -3692,17 +3562,17 @@ } }, "node_modules/@schematics/angular": { - "version": "16.2.7", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.7.tgz", - "integrity": "sha512-sL+7vmwYPdo29rp99XYlm8gibqcjjOL5LKEleVQlv63SRES3HLMt7DeYivUfizcMENu/1hDtX41ig4Mu1SpNzg==", + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.0.5.tgz", + "integrity": "sha512-sOc1UG4NiV+7cGwrbWPnyW71O+NgsKaFb2agSrVduRL7o4neMDeqF04ik4Kv1jKA7sZOQfPV+3cn6XI49Mumrw==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.2.7", - "@angular-devkit/schematics": "16.2.7", + "@angular-devkit/core": "17.0.5", + "@angular-devkit/schematics": "17.0.5", "jsonc-parser": "3.2.0" }, "engines": { - "node": "^16.14.0 || >=18.10.0", + "node": "^18.13.0 || >=20.9.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -3729,15 +3599,15 @@ "dev": true }, "node_modules/@sigstore/bundle": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-1.1.0.tgz", - "integrity": "sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.1.0.tgz", + "integrity": "sha512-89uOo6yh/oxaU8AeOUnVrTdVMcGk9Q1hJa7Hkvalc6G3Z3CupWk4Xe9djSgJm9fMkH69s0P0cVHUoKSOemLdng==", "dev": true, "dependencies": { - "@sigstore/protobuf-specs": "^0.2.0" + "@sigstore/protobuf-specs": "^0.2.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@sigstore/protobuf-specs": { @@ -3750,30 +3620,30 @@ } }, "node_modules/@sigstore/sign": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-1.0.0.tgz", - "integrity": "sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.2.0.tgz", + "integrity": "sha512-AAbmnEHDQv6CSfrWA5wXslGtzLPtAtHZleKOgxdQYvx/s76Fk6T6ZVt7w2IGV9j1UrFeBocTTQxaXG2oRrDhYA==", "dev": true, "dependencies": { - "@sigstore/bundle": "^1.1.0", - "@sigstore/protobuf-specs": "^0.2.0", - "make-fetch-happen": "^11.0.1" + "@sigstore/bundle": "^2.1.0", + "@sigstore/protobuf-specs": "^0.2.1", + "make-fetch-happen": "^13.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@sigstore/tuf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.3.tgz", - "integrity": "sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.2.0.tgz", + "integrity": "sha512-KKATZ5orWfqd9ZG6MN8PtCIx4eevWSuGRKQvofnWXRpyMyUEpmrzg5M5BrCpjM+NfZ0RbNGOh5tCz/P2uoRqOA==", "dev": true, "dependencies": { - "@sigstore/protobuf-specs": "^0.2.0", - "tuf-js": "^1.1.7" + "@sigstore/protobuf-specs": "^0.2.1", + "tuf-js": "^2.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@sindresorhus/is": { @@ -3788,6 +3658,12 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -3834,25 +3710,25 @@ "dev": true }, "node_modules/@tufjs/canonical-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", - "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@tufjs/models": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", - "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.0.tgz", + "integrity": "sha512-c8nj8BaOExmZKO2DXhDfegyhSGcG9E/mPN3U13L+/PsoWm1uaGiHHjxqSHQiasDBQwDA3aHuw9+9spYAP1qvvg==", "dev": true, "dependencies": { - "@tufjs/canonical-json": "1.0.0", - "minimatch": "^9.0.0" + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/@tufjs/models/node_modules/minimatch": { @@ -3871,9 +3747,9 @@ } }, "node_modules/@types/body-parser": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.4.tgz", - "integrity": "sha512-N7UDG0/xiPQa2D/XrVJXjkWbpqHCd2sBaB32ggRF2l83RhPfamgKGF8gwwqyksS95qUS5ZYF9aF+lLPRlwI2UA==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, "dependencies": { "@types/connect": "*", @@ -3881,9 +3757,9 @@ } }, "node_modules/@types/bonjour": { - "version": "3.5.12", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.12.tgz", - "integrity": "sha512-ky0kWSqXVxSqgqJvPIkgFkcn4C8MnRog308Ou8xBBIVo39OmUFy+jqNe0nPwLCDFxUpmT9EvT91YzOJgkDRcFg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -3902,37 +3778,52 @@ } }, "node_modules/@types/connect": { - "version": "3.4.37", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.37.tgz", - "integrity": "sha512-zBUSRqkfZ59OcwXon4HVxhx5oWCJmc0OtBTK05M+p0dYjgN6iTwIL2T/WbsQZrEsdnwaF9cWQ+azOnpPvIqY3Q==", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.2.tgz", - "integrity": "sha512-gX2j9x+NzSh4zOhnRPSdPPmTepS4DfxES0AvIFv3jGv5QyeAJf6u6dY5/BAoAJU9Qq1uTvwOku8SSC2GnCRl6Q==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/debug": { - "version": "4.1.10", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.10.tgz", - "integrity": "sha512-tOSCru6s732pofZ+sMv9o4o3Zc+Sa8l3bxd/tweTQudFn06vAzb13ZX46Zi6m6EJ+RUbRTHvgQJ1gBtSgkaUYA==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, "dependencies": { "@types/ms": "*" } }, "node_modules/@types/eslint": { - "version": "8.44.6", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.6.tgz", - "integrity": "sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw==", + "version": "8.44.8", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.8.tgz", + "integrity": "sha512-4K8GavROwhrYl2QXDXm0Rv9epkA8GBFu0EI+XrrnnuCl7u8CWBRusX7fXJfanhZTDWSAL24gDI/UqXyUM0Injw==", "dev": true, "dependencies": { "@types/estree": "*", @@ -3940,9 +3831,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.6", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.6.tgz", - "integrity": "sha512-zfM4ipmxVKWdxtDaJ3MP3pBurDXOCoyjvlpE3u6Qzrmw4BPbfm4/ambIeTk/r/J0iq/+2/xp0Fmt+gFvXJY2PQ==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -3950,15 +3841,15 @@ } }, "node_modules/@types/estree": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.3.tgz", - "integrity": "sha512-CS2rOaoQ/eAgAfcTfq6amKG7bsN+EMcgGY4FAFQdvSj2y1ixvOZTUA9mOtCai7E1SYu283XNw7urKK30nP3wkQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, "node_modules/@types/express": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.20.tgz", - "integrity": "sha512-rOaqlkgEvOW495xErXMsmyX3WKBInbhG5eqojXYi3cGUaLoRDlXa5d52fkfWZT963AZ3v2eZ4MbKE6WpDAGVsw==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, "dependencies": { "@types/body-parser": "*", @@ -3968,9 +3859,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.39", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.39.tgz", - "integrity": "sha512-BiEUfAiGCOllomsRAZOiMFP7LAnrifHpt56pc4Z7l9K6ACyN06Ns1JLMBxwkfLOjJRlSf06NwWsT7yzfpaVpyQ==", + "version": "4.17.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", "dev": true, "dependencies": { "@types/node": "*", @@ -3989,36 +3880,36 @@ } }, "node_modules/@types/geojson": { - "version": "7946.0.12", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.12.tgz", - "integrity": "sha512-uK2z1ZHJyC0nQRbuovXFt4mzXDwf27vQeUWNhfKGwRcWW429GOhP8HxUHlM6TLH4bzmlv/HlEjpvJh3JfmGsAA==", + "version": "7946.0.13", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz", + "integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==", "dev": true }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz", - "integrity": "sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, "node_modules/@types/http-errors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.3.tgz", - "integrity": "sha512-pP0P/9BnCj1OVvQR2lF41EkDG/lWWnDyA203b/4Fmi2eTyORnBtcDoKDwjWQthELrBvWkMOrvSOnZ8OVlW6tXA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "dev": true }, "node_modules/@types/http-proxy": { - "version": "1.17.13", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.13.tgz", - "integrity": "sha512-GkhdWcMNiR5QSQRYnJ+/oXzu0+7JJEPC8vkWXK351BkhjraZF+1W13CUYARUvX9+NqIU2n6YHA4iwywsc/M6Sw==", + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/json-schema": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", - "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/keyv": { @@ -4031,62 +3922,71 @@ } }, "node_modules/@types/leaflet": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.7.tgz", - "integrity": "sha512-FOfKB1ALYUDnXkH7LfTFreWiZr9R7GErqGP+8lYQGWr2GFq5+jy3Ih0M7e9j41cvRN65kLALJ4dc43yZwyl/6g==", + "version": "1.9.8", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.8.tgz", + "integrity": "sha512-EXdsL4EhoUtGm2GC2ZYtXn+Fzc6pluVgagvo2VC1RHWToLGlTRwVYoDpqS/7QXa01rmDyBjJk3Catpf60VMkwg==", "dev": true, "dependencies": { "@types/geojson": "*" } }, "node_modules/@types/mime": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz", - "integrity": "sha512-1Gjee59G25MrQGk8bsNvC6fxNiRgUlGn2wlhGf95a59DrprnnHk80FIMMFG9XHMdrfsuA119ht06QPDXA1Z7tw==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, "node_modules/@types/ms": { - "version": "0.7.33", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.33.tgz", - "integrity": "sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==", + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", "dev": true }, "node_modules/@types/node": { - "version": "20.8.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.7.tgz", - "integrity": "sha512-21TKHHh3eUHIi2MloeptJWALuCu5H7HQTdTrWIFReA8ad+aggoX+lRes3ex7/FtpC+sVUpFMQ+QTfYr74mruiQ==", + "version": "20.10.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.1.tgz", + "integrity": "sha512-T2qwhjWwGH81vUEx4EXmBKsTJRXFXNZTL4v0gi01+zyBmCwzE6TyHszqX01m+QHTEq+EZNo13NeJIdEqf+Myrg==", "dev": true, "dependencies": { - "undici-types": "~5.25.1" + "undici-types": "~5.26.4" } }, - "node_modules/@types/plist": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.4.tgz", - "integrity": "sha512-pTa9xUFQFM9WJGSWHajYNljD+DbVylE1q9IweK1LBhUYJdJ28YNU8j3KZ4Q1Qw+cSl4+QLLLOVmqNjhhvVO8fA==", + "node_modules/@types/node-forge": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.10.tgz", + "integrity": "sha512-y6PJDYN4xYBxwd22l+OVH35N+1fCYWiuC3aiP2SlXVE6Lo7SS+rSx9r89hLxrP4pn6n1lBGhHJ12pj3F3Mpttw==", "dev": true, - "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "optional": true, "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "node_modules/@types/qs": { - "version": "6.9.9", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", - "integrity": "sha512-wYLxw35euwqGvTDx6zfY1vokBFnsK0HNrzc6xNHchxfO2hpuRg74GbkEW7e3sSmPvj0TjCDT1VCa6OtHXnubsg==", + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", "dev": true }, "node_modules/@types/range-parser": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.6.tgz", - "integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, "node_modules/@types/responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha512-/4YQT5Kp6HxUDb4yhRkm0bJ7TbjvTddqX7PZ5hz6qV3pxSo72f/6YPRo+Mu2DU307tm9IioO69l7uAwn5XNcFA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", "dev": true, "dependencies": { "@types/node": "*" @@ -4099,9 +3999,9 @@ "dev": true }, "node_modules/@types/send": { - "version": "0.17.3", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.3.tgz", - "integrity": "sha512-/7fKxvKUoETxjFUsuFlPB9YndePpxxRAOfGC/yJdc9kTjTeP5kRCTzfnE8kPUKCeyiyIZu0YQ76s50hCedI1ug==", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, "dependencies": { "@types/mime": "^1", @@ -4109,18 +4009,18 @@ } }, "node_modules/@types/serve-index": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.3.tgz", - "integrity": "sha512-4KG+yMEuvDPRrYq5fyVm/I2uqAJSAwZK9VSa+Zf+zUq9/oxSSvy3kkIqyL+jjStv6UCVi8/Aho0NHtB1Fwosrg==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, "dependencies": { "@types/express": "*" } }, "node_modules/@types/serve-static": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.4.tgz", - "integrity": "sha512-aqqNfs1XTF0HDrFdlY//+SGUxmdSUbjeRXb5iaZc3x0/vMbYmdw9qvOgHWOyyLFxSSRnUuP5+724zBgfw8/WAw==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", "dev": true, "dependencies": { "@types/http-errors": "*", @@ -4129,40 +4029,40 @@ } }, "node_modules/@types/sockjs": { - "version": "0.3.35", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.35.tgz", - "integrity": "sha512-tIF57KB+ZvOBpAQwSaACfEu7htponHXaFzP7RfKYgsOS0NoYnn+9+jzp7bbq4fWerizI3dTB4NfAZoyeQKWJLw==", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/uuid": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.6.tgz", - "integrity": "sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.7.tgz", + "integrity": "sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==", "dev": true }, "node_modules/@types/verror": { - "version": "1.10.8", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.8.tgz", - "integrity": "sha512-YhUhnxRYs/NiVUbIs3F/EzviDP/NZCEAE2Mx5DUqLdldUmphOhFCVh7Kc+7zlYEExM0P8dzfbJi0yRlNb2Bw5g==", + "version": "1.10.9", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.9.tgz", + "integrity": "sha512-MLx9Z+9lGzwEuW16ubGeNkpBDE84RpB/NyGgg6z2BTpWzKkGU451cAY3UkUzZEp72RHF585oJ3V8JVNqIplcAQ==", "dev": true, "optional": true }, "node_modules/@types/ws": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.8.tgz", - "integrity": "sha512-flUksGIQCnJd6sZ1l5dqCEG/ksaoAg/eUwiLAGTJQcfgvZJKF++Ta4bJA6A5aPSJmsr+xlseHn4KLgVlNnvPTg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/yauzl": { - "version": "2.10.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.2.tgz", - "integrity": "sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==", + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, "optional": true, "dependencies": { @@ -4327,99 +4227,6 @@ "@xtuc/long": "4.2.2" } }, - "node_modules/@wessberg/ts-evaluator": { - "version": "0.0.27", - "resolved": "https://registry.npmjs.org/@wessberg/ts-evaluator/-/ts-evaluator-0.0.27.tgz", - "integrity": "sha512-7gOpVm3yYojUp/Yn7F4ZybJRxyqfMNf0LXK5KJiawbPfL0XTsJV+0mgrEDjOIR6Bi0OYk2Cyg4tjFu1r8MCZaA==", - "deprecated": "this package has been renamed to ts-evaluator. Please install ts-evaluator instead", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "jsdom": "^16.4.0", - "object-path": "^0.11.5", - "tslib": "^2.0.3" - }, - "engines": { - "node": ">=10.1.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/wessberg/ts-evaluator?sponsor=1" - }, - "peerDependencies": { - "typescript": ">=3.2.x || >= 4.x" - } - }, - "node_modules/@wessberg/ts-evaluator/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/@wessberg/ts-evaluator/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/@wessberg/ts-evaluator/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/@wessberg/ts-evaluator/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/@wessberg/ts-evaluator/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/@wessberg/ts-evaluator/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/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -4448,22 +4255,26 @@ "dev": true }, "node_modules/7zip-bin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", - "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", "dev": true }, "node_modules/abab": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", "dev": true }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/abort-controller": { "version": "3.0.0", @@ -4491,31 +4302,9 @@ } }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "node_modules/acorn-globals/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -4534,9 +4323,9 @@ } }, "node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", + "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", "dev": true, "engines": { "node": ">=0.4.0" @@ -4581,18 +4370,6 @@ "node": ">= 6.0.0" } }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "dev": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -4729,6 +4506,18 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/app-builder-bin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", @@ -4736,9 +4525,9 @@ "dev": true }, "node_modules/app-builder-lib": { - "version": "24.6.4", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.6.4.tgz", - "integrity": "sha512-m9931WXb83teb32N0rKg+ulbn6+Hl8NV5SUpVDOVz9MWOXfhV6AQtTdftf51zJJvCQnQugGtSqoLvgw6mdF/Rg==", + "version": "24.9.1", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.9.1.tgz", + "integrity": "sha512-Q1nYxZcio4r+W72cnIRVYofEAyjBd3mG47o+zms8HlD51zWtA/YxJb01Jei5F+jkWhge/PTQK+uldsPh6d0/4g==", "dev": true, "dependencies": { "@develar/schema-utils": "~2.6.5", @@ -4747,15 +4536,15 @@ "@electron/universal": "1.4.1", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", - "7zip-bin": "~5.1.1", + "7zip-bin": "~5.2.0", "async-exit-hook": "^2.0.1", "bluebird-lst": "^1.0.9", - "builder-util": "24.5.0", - "builder-util-runtime": "9.2.1", + "builder-util": "24.8.1", + "builder-util-runtime": "9.2.3", "chromium-pickle-js": "^0.2.0", "debug": "^4.3.4", "ejs": "^3.1.8", - "electron-publish": "24.5.0", + "electron-publish": "24.8.1", "form-data": "^4.0.0", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", @@ -4819,47 +4608,14 @@ } }, "node_modules/app-builder-lib/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "dev": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -4967,11 +4723,20 @@ } }, "node_modules/async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, + "node_modules/async-each-series": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", + "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/async-exit-hook": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", @@ -4997,9 +4762,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.16", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", "dev": true, "funding": [ { @@ -5009,12 +4774,16 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -5042,13 +4811,12 @@ } }, "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==", + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", "dev": true, "dependencies": { - "follow-redirects": "^1.14.9", - "form-data": "^4.0.0" + "follow-redirects": "^1.14.0" } }, "node_modules/babel-loader": { @@ -5108,13 +4876,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.5.tgz", - "integrity": "sha512-Q6CdATeAvbScWPNLB8lzSO7fgUVBkQt6zLgNlfyeCr/EQaEQR+bWiBYYPYAFyE528BMjRhL+1QBMOI4jc/c5TA==", + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.6.tgz", + "integrity": "sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==", "dev": true, "dependencies": { "@babel/helper-define-polyfill-provider": "^0.4.3", - "core-js-compat": "^3.32.2" + "core-js-compat": "^3.33.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -5158,6 +4926,15 @@ } ] }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -5257,15 +5034,6 @@ "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5275,6 +5043,16 @@ "ms": "2.0.0" } }, + "node_modules/body-parser/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, "node_modules/body-parser/node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -5293,6 +5071,18 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/body-parser/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/body-parser/node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -5308,6 +5098,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/bonjour-service": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", @@ -5360,49 +5165,255 @@ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "dev": true }, - "node_modules/browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true + "node_modules/browser-sync": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.29.3.tgz", + "integrity": "sha512-NiM38O6XU84+MN+gzspVmXV2fTOoe+jBqIBx3IBdhZrdeURr6ZgznJr/p+hQ+KzkKEiGH/GcC4SQFSL0jV49bg==", + "dev": true, + "dependencies": { + "browser-sync-client": "^2.29.3", + "browser-sync-ui": "^2.29.3", + "bs-recipes": "1.3.4", + "chalk": "4.1.2", + "chokidar": "^3.5.1", + "connect": "3.6.6", + "connect-history-api-fallback": "^1", + "dev-ip": "^1.0.1", + "easy-extender": "^2.3.4", + "eazy-logger": "^4.0.1", + "etag": "^1.8.1", + "fresh": "^0.5.2", + "fs-extra": "3.0.1", + "http-proxy": "^1.18.1", + "immutable": "^3", + "localtunnel": "^2.0.1", + "micromatch": "^4.0.2", + "opn": "5.3.0", + "portscanner": "2.2.0", + "raw-body": "^2.3.2", + "resp-modifier": "6.0.2", + "rx": "4.1.0", + "send": "0.16.2", + "serve-index": "1.9.1", + "serve-static": "1.13.2", + "server-destroy": "1.0.1", + "socket.io": "^4.4.1", + "ua-parser-js": "^1.0.33", + "yargs": "^17.3.1" + }, + "bin": { + "browser-sync": "dist/bin.js" + }, + "engines": { + "node": ">= 8.0.0" + } }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "node_modules/browser-sync-client": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.29.3.tgz", + "integrity": "sha512-4tK5JKCl7v/3aLbmCBMzpufiYLsB1+UI+7tUXCCp5qF0AllHy/jAqYu6k7hUF3hYtlClKpxExWaR+rH+ny07wQ==", "dev": true, "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "etag": "1.8.1", + "fresh": "0.5.2", + "mitt": "^1.1.3" + }, + "engines": { + "node": ">=8.0.0" } }, - "node_modules/browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "node_modules/browser-sync-ui": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.29.3.tgz", + "integrity": "sha512-kBYOIQjU/D/3kYtUIJtj82e797Egk1FB2broqItkr3i4eF1qiHbFCG6srksu9gWhfmuM/TNG76jMfzAdxEPakg==", "dev": true, "dependencies": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "async-each-series": "0.1.1", + "chalk": "4.1.2", + "connect-history-api-fallback": "^1", + "immutable": "^3", + "server-destroy": "1.0.1", + "socket.io-client": "^4.4.1", + "stream-throttle": "^0.1.3" } }, - "node_modules/browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "node_modules/browser-sync-ui/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": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/browser-sync-ui/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/browser-sync-ui/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/browser-sync-ui/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/browser-sync-ui/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/browser-sync-ui/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/browser-sync/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/browser-sync/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/browser-sync/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/browser-sync/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/browser-sync/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/browser-sync/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/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "node_modules/browserify-rsa": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", @@ -5488,6 +5499,12 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-recipes": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", + "integrity": "sha512-BXvDkqhDNxXEjeGM8LFkSbR+jzmP/CYpCiVKYn+soB1dDldeU15EBNDkwVXndKuX35wnNUaPd0qSoQEAkmQtMw==", + "dev": true + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -5546,16 +5563,16 @@ "dev": true }, "node_modules/builder-util": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.5.0.tgz", - "integrity": "sha512-STnBmZN/M5vGcv01u/K8l+H+kplTaq4PAIn3yeuufUKSpcdro0DhJWxPI81k5XcNfC//bjM3+n9nr8F9uV4uAQ==", + "version": "24.8.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.8.1.tgz", + "integrity": "sha512-ibmQ4BnnqCnJTNrdmdNlnhF48kfqhNzSeqFMXHLIl+o9/yhn6QfOaVrloZ9YUu3m0k3rexvlT5wcki6LWpjTZw==", "dev": true, "dependencies": { "@types/debug": "^4.1.6", - "7zip-bin": "~5.1.1", + "7zip-bin": "~5.2.0", "app-builder-bin": "4.0.0", "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.2.1", + "builder-util-runtime": "9.2.3", "chalk": "^4.1.2", "cross-spawn": "^7.0.3", "debug": "^4.3.4", @@ -5570,9 +5587,9 @@ } }, "node_modules/builder-util-runtime": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.1.tgz", - "integrity": "sha512-2rLv/uQD2x+dJ0J3xtsmI12AlRyk7p45TEbE/6o/fbb633e/S3pPgm+ct+JHsoY7r39dKHnGEFk/AASRFdnXmA==", + "version": "9.2.3", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.3.tgz", + "integrity": "sha512-FGhkqXdFFZ5dNC4C+yuQB9ak311rpGAw+/ASz8ZdxwODCv1GGMWgLDeofRkdi0F3VCHQEWy/aXcJQozx2nOPiw==", "dev": true, "dependencies": { "debug": "^4.3.4", @@ -5660,6 +5677,19 @@ "node": ">=8" } }, + "node_modules/builder-util/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/builder-util/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -5697,9 +5727,9 @@ } }, "node_modules/builder-util/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -5721,26 +5751,26 @@ } }, "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "engines": { "node": ">= 0.8" } }, "node_modules/cacache": { - "version": "17.1.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz", - "integrity": "sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.1.tgz", + "integrity": "sha512-g4Uf2CFZPaxtJKre6qr4zqLDOOPU7bNVhWjlNhvzc51xaTOx2noMOLhfFkTAqwtrAZAKQUuDfyjitzilpA8WsQ==", "dev": true, "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", - "lru-cache": "^7.7.1", + "lru-cache": "^10.0.1", "minipass": "^7.0.3", - "minipass-collect": "^1.0.2", + "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^4.0.0", @@ -5749,7 +5779,7 @@ "unique-filename": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/cacache/node_modules/glob": { @@ -5775,12 +5805,12 @@ } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, "engines": { - "node": ">=12" + "node": "14 || >=16.14" } }, "node_modules/cacache/node_modules/minimatch": { @@ -5798,15 +5828,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/cacache/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -5867,9 +5888,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001551", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz", - "integrity": "sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==", + "version": "1.0.30001565", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001565.tgz", + "integrity": "sha512-xrE//a3O7TP0vaJ8ikzkD2c2NgcVUvsEe2IvFTntV4Yd1Z9FVzh+gW+enX96L0psrbaFMcVcH2l90xNuGDWc8w==", "dev": true, "funding": [ { @@ -6026,9 +6047,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", - "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, "engines": { "node": ">=6" @@ -6055,12 +6076,12 @@ } }, "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, "engines": { - "node": ">= 10" + "node": ">= 12" } }, "node_modules/cliui": { @@ -6077,6 +6098,56 @@ "node": ">=12" } }, + "node_modules/cliui/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/cliui/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/cliui/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/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", @@ -6127,15 +6198,6 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" - } - }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -6208,12 +6270,21 @@ "node": ">= 0.8.0" } }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { "ms": "2.0.0" } }, @@ -6258,27 +6329,51 @@ "node": ">=4.2.0" } }, + "node_modules/connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha512-OO7axMmPpu/2XuX1+2Yrg0ddju31B6xLZMWkJ5rYBu4YRmRVlOjvlY6kw2FJKiAzyxGwnrDUAG4s1Pf0sbBMCQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "dev": true, "engines": { "node": ">=0.8" } }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", "dev": true }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true - }, "node_modules/constants-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", @@ -6313,9 +6408,9 @@ "dev": true }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "dev": true, "engines": { "node": ">= 0.6" @@ -6376,9 +6471,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.33.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.0.tgz", - "integrity": "sha512-0w4LcLXsVEuNkIqwjjf9rjCoPhK8uqA4tMRh4Ge26vfLtUutshn+aRJU21I9LCJlh2QQHfisNToLjw1XEJLTWw==", + "version": "3.33.3", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.33.3.tgz", + "integrity": "sha512-cNzGqFsh3Ot+529GIXacjTJ7kegdt5fPXxCBVS1G0iaZpuo/tBz399ymceLJveQhFFZ8qThHiP3fzuoQjKN2ow==", "dev": true, "dependencies": { "browserslist": "^4.22.1" @@ -6394,6 +6489,19 @@ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", "dev": true }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cosmiconfig": { "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", @@ -6684,44 +6792,6 @@ "node": ">=4" } }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/date-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", @@ -6751,12 +6821,6 @@ } } }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -6866,12 +6930,6 @@ "node": ">=0.4.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6892,14 +6950,10 @@ } }, "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true }, "node_modules/detect-node": { "version": "2.1.0", @@ -6907,6 +6961,18 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, + "node_modules/dev-ip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", + "integrity": "sha512-LmVkry/oDShEgSZPNgqCIp2/TlqtExeGmymru3uCELnfyjY11IzpAproLYs+1X88fXO6DBoYP3ul2Xo2yz2j6A==", + "dev": true, + "bin": { + "dev-ip": "lib/dev-ip.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -6978,14 +7044,14 @@ } }, "node_modules/dmg-builder": { - "version": "24.6.4", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.6.4.tgz", - "integrity": "sha512-BNcHRc9CWEuI9qt0E655bUBU/j/3wUCYBVKGu1kVpbN5lcUdEJJJeiO0NHK3dgKmra6LUUZlo+mWqc+OCbi0zw==", + "version": "24.9.1", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.9.1.tgz", + "integrity": "sha512-huC+O6hvHd24Ubj3cy2GMiGLe2xGFKN3klqVMLAdcbB6SWMd1yPSdZvV8W1O01ICzCCRlZDHiv4VrNUgnPUfbQ==", "dev": true, "dependencies": { - "app-builder-lib": "24.6.4", - "builder-util": "24.5.0", - "builder-util-runtime": "9.2.1", + "app-builder-lib": "24.9.1", + "builder-util": "24.8.1", + "builder-util-runtime": "9.2.3", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" @@ -7039,9 +7105,9 @@ } }, "node_modules/dmg-builder/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -7130,9 +7196,9 @@ } }, "node_modules/domain-browser": { - "version": "4.22.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz", - "integrity": "sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-4.23.0.tgz", + "integrity": "sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==", "dev": true, "engines": { "node": ">=10" @@ -7153,27 +7219,6 @@ } ] }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "dependencies": { - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", @@ -7224,6 +7269,100 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/easy-extender": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz", + "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==", + "dev": true, + "dependencies": { + "lodash": "^4.17.10" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/eazy-logger": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-4.0.1.tgz", + "integrity": "sha512-2GSFtnnC6U4IEKhEI7+PvdxrmjJ04mdsj3wHZTFiw0tUtG4HCWzTr13ZYTk8XOGnA1xQMaDljoBOYlk3D/MMSw==", + "dev": true, + "dependencies": { + "chalk": "4.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eazy-logger/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/eazy-logger/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/eazy-logger/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/eazy-logger/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/eazy-logger/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/eazy-logger/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/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -7246,9 +7385,9 @@ } }, "node_modules/electron": { - "version": "27.0.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-27.0.1.tgz", - "integrity": "sha512-AjDGgpf2thNxVXoNqEG+0GCUK4upAEa2B+IoM5Yk9YrOLd6uUOEMfGI9rhPtj+jC14iKOvBdefY2uAzcDC0qng==", + "version": "27.1.3", + "resolved": "https://registry.npmjs.org/electron/-/electron-27.1.3.tgz", + "integrity": "sha512-7eD8VMhhlL5J531OOawn00eMthUkX1e3qN5Nqd7eMK8bg5HxQBrn8bdPlvUEnCano9KhrVwaDnGeuzWoDOGpjQ==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -7264,16 +7403,16 @@ } }, "node_modules/electron-builder": { - "version": "24.6.4", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.6.4.tgz", - "integrity": "sha512-uNWQoU7pE7qOaIQ6CJHpBi44RJFVG8OHRBIadUxrsDJVwLLo8Nma3K/EEtx5/UyWAQYdcK4nVPYKoRqBb20hbA==", + "version": "24.9.1", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.9.1.tgz", + "integrity": "sha512-v7BuakDuY6sKMUYM8mfQGrwyjBpZ/ObaqnenU0H+igEL10nc6ht049rsCw2HghRBdEwJxGIBuzs3jbEhNaMDmg==", "dev": true, "dependencies": { - "app-builder-lib": "24.6.4", - "builder-util": "24.5.0", - "builder-util-runtime": "9.2.1", + "app-builder-lib": "24.9.1", + "builder-util": "24.8.1", + "builder-util-runtime": "9.2.3", "chalk": "^4.1.2", - "dmg-builder": "24.6.4", + "dmg-builder": "24.9.1", "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", @@ -7386,9 +7525,9 @@ } }, "node_modules/electron-builder/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -7432,14 +7571,14 @@ } }, "node_modules/electron-publish": { - "version": "24.5.0", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.5.0.tgz", - "integrity": "sha512-zwo70suH15L15B4ZWNDoEg27HIYoPsGJUF7xevLJLSI7JUPC8l2yLBdLGwqueJ5XkDL7ucYyRZzxJVR8ElV9BA==", + "version": "24.8.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.8.1.tgz", + "integrity": "sha512-IFNXkdxMVzUdweoLJNXSupXkqnvgbrn3J4vognuOY06LaS/m0xvfFYIf+o1CM8if6DuWYWoQFKPcWZt/FUjZPw==", "dev": true, "dependencies": { "@types/fs-extra": "^9.0.11", - "builder-util": "24.5.0", - "builder-util-runtime": "9.2.1", + "builder-util": "24.8.1", + "builder-util-runtime": "9.2.3", "chalk": "^4.1.2", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", @@ -7543,9 +7682,9 @@ } }, "node_modules/electron-publish/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" @@ -7699,16 +7838,19 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.561", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.561.tgz", - "integrity": "sha512-eS5t4ulWOBfVHdq9SW2dxEaFarj1lPjvJ8PaYMOjY0DecBaj/t4ARziL2IPpDr4atyWwjLFGQ2vo/VCgQFezVQ==", + "version": "1.4.601", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.601.tgz", + "integrity": "sha512-SpwUMDWe9tQu8JX5QCO1+p/hChAi9AE9UpoC3rcHVc+gdCGlbT3SGb5I1klgb952HRIyvt9wZhSz9bNBYz9swA==", "dev": true }, "node_modules/electron/node_modules/@types/node": { - "version": "18.18.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.6.tgz", - "integrity": "sha512-wf3Vz+jCmOQ2HV1YUJuCWdL64adYxumkrxtc+H1VUQlnQI04+5HtH+qZCOE21lBE7gIrt+CwX2Wv8Acrw5Ak6w==", - "dev": true + "version": "18.19.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.0.tgz", + "integrity": "sha512-667KNhaD7U29mT5wf+TZUnrzPrlL2GNQ5N0BMjO2oNULhBxX0/FKCkm6JMu0Jh7Z+1LwUlR21ekd7KhIboNFNw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/elliptic": { "version": "6.5.4", @@ -7774,6 +7916,49 @@ "once": "^1.4.0" } }, + "node_modules/engine.io": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", + "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", + "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", @@ -7837,26 +8022,26 @@ } }, "node_modules/es-abstract": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", - "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "arraybuffer.prototype.slice": "^1.0.2", "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-set-tostringtag": "^2.0.1", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.1", + "get-intrinsic": "^1.2.2", "get-symbol-description": "^1.0.0", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", "has-property-descriptors": "^1.0.0", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", + "hasown": "^2.0.0", "internal-slot": "^1.0.5", "is-array-buffer": "^3.0.2", "is-callable": "^1.2.7", @@ -7866,7 +8051,7 @@ "is-string": "^1.0.7", "is-typed-array": "^1.1.12", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", "object.assign": "^4.1.4", "regexp.prototype.flags": "^1.5.1", @@ -7880,7 +8065,7 @@ "typed-array-byte-offset": "^1.0.0", "typed-array-length": "^1.0.4", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -7890,20 +8075,20 @@ } }, "node_modules/es-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.1.tgz", - "integrity": "sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", "dev": true }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" }, "engines": { "node": ">= 0.4" @@ -7934,11 +8119,12 @@ "optional": true }, "node_modules/esbuild": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.17.tgz", - "integrity": "sha512-1GJtYnUxsJreHYA0Y+iQz2UEykonY66HNWOb0yXYZi9/kNrORUEHVg87eQsCtqh59PEJ5YVZJO98JHznMJSWjg==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.5.tgz", + "integrity": "sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==", "dev": true, "hasInstallScript": true, + "optional": true, "bin": { "esbuild": "bin/esbuild" }, @@ -7946,34 +8132,34 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.18.17", - "@esbuild/android-arm64": "0.18.17", - "@esbuild/android-x64": "0.18.17", - "@esbuild/darwin-arm64": "0.18.17", - "@esbuild/darwin-x64": "0.18.17", - "@esbuild/freebsd-arm64": "0.18.17", - "@esbuild/freebsd-x64": "0.18.17", - "@esbuild/linux-arm": "0.18.17", - "@esbuild/linux-arm64": "0.18.17", - "@esbuild/linux-ia32": "0.18.17", - "@esbuild/linux-loong64": "0.18.17", - "@esbuild/linux-mips64el": "0.18.17", - "@esbuild/linux-ppc64": "0.18.17", - "@esbuild/linux-riscv64": "0.18.17", - "@esbuild/linux-s390x": "0.18.17", - "@esbuild/linux-x64": "0.18.17", - "@esbuild/netbsd-x64": "0.18.17", - "@esbuild/openbsd-x64": "0.18.17", - "@esbuild/sunos-x64": "0.18.17", - "@esbuild/win32-arm64": "0.18.17", - "@esbuild/win32-ia32": "0.18.17", - "@esbuild/win32-x64": "0.18.17" + "@esbuild/android-arm": "0.19.5", + "@esbuild/android-arm64": "0.19.5", + "@esbuild/android-x64": "0.19.5", + "@esbuild/darwin-arm64": "0.19.5", + "@esbuild/darwin-x64": "0.19.5", + "@esbuild/freebsd-arm64": "0.19.5", + "@esbuild/freebsd-x64": "0.19.5", + "@esbuild/linux-arm": "0.19.5", + "@esbuild/linux-arm64": "0.19.5", + "@esbuild/linux-ia32": "0.19.5", + "@esbuild/linux-loong64": "0.19.5", + "@esbuild/linux-mips64el": "0.19.5", + "@esbuild/linux-ppc64": "0.19.5", + "@esbuild/linux-riscv64": "0.19.5", + "@esbuild/linux-s390x": "0.19.5", + "@esbuild/linux-x64": "0.19.5", + "@esbuild/netbsd-x64": "0.19.5", + "@esbuild/openbsd-x64": "0.19.5", + "@esbuild/sunos-x64": "0.19.5", + "@esbuild/win32-arm64": "0.19.5", + "@esbuild/win32-ia32": "0.19.5", + "@esbuild/win32-x64": "0.19.5" } }, "node_modules/esbuild-wasm": { - "version": "0.18.17", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.18.17.tgz", - "integrity": "sha512-9OHGcuRzy+I8ziF9FzjfKLWAPbvi0e/metACVg9k6bK+SI4FFxeV6PcZsz8RIVaMD4YNehw+qj6UMR3+qj/EuQ==", + "version": "0.19.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.19.5.tgz", + "integrity": "sha512-7zmLLn2QCj93XfMmHtzrDJ1UBuOHB2CZz1ghoCEZiRajxjUvHsF40PnbzFIY/pmesqPRaEtEWii0uzsTbnAgrA==", "dev": true, "bin": { "esbuild": "bin/esbuild" @@ -8006,37 +8192,6 @@ "node": ">=0.8.0" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/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==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -8050,15 +8205,6 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -8084,7 +8230,7 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { + "node_modules/esrecurse/node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", @@ -8093,6 +8239,15 @@ "node": ">=4.0" } }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -8240,6 +8395,15 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -8249,12 +8413,64 @@ "ms": "2.0.0" } }, + "node_modules/express/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/express/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/express/node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -8270,6 +8486,60 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/express/node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express/node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/express/node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -8385,15 +8655,28 @@ } }, "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-5.0.0.tgz", + "integrity": "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==", "dev": true, "dependencies": { - "escape-string-regexp": "^1.0.5" + "escape-string-regexp": "^5.0.0", + "is-unicode-supported": "^1.2.0" }, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "engines": { + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -8430,17 +8713,17 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha512-ejnvM9ZXYzp6PUPUyQBMBf0Co5VX2gr5H2VQe2Ui2jWXNlxv+PYZo8wpAymJNJdLsG1R4p+M4aynF8KuoUEwRw==", "dev": true, "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~1.0.1", "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", "unpipe": "~1.0.0" }, "engines": { @@ -8603,17 +8886,14 @@ } }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha512-V3Z3WZWVUYd8hoCL5xfXJCaHWYzmtwW5XWYSlLgERi8PWd8bx1kUHUk8L1BT57e49oKnDDD180mjfrHc1yA9rg==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" } }, "node_modules/fs-minipass": { @@ -8628,15 +8908,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/fs-monkey": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", @@ -8699,25 +8970,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "dev": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -8737,15 +8989,15 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", + "function-bind": "^1.1.2", "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8955,18 +9207,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/guess-parser": { - "version": "0.4.22", - "resolved": "https://registry.npmjs.org/guess-parser/-/guess-parser-0.4.22.tgz", - "integrity": "sha512-KcUWZ5ACGaBM69SbqwVIuWGoSAgD+9iJnchR9j/IarVI1jHVeXv+bUXBIMeqVMSKt3zrn0Dgf9UpcOEpPBLbSg==", - "dev": true, - "dependencies": { - "@wessberg/ts-evaluator": "0.0.27" - }, - "peerDependencies": { - "typescript": ">=3.7.5" - } - }, "node_modules/hammerjs": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", @@ -8981,15 +9221,6 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -9009,12 +9240,12 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "get-intrinsic": "^1.2.2" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9059,12 +9290,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true - }, "node_modules/hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -9103,6 +9328,18 @@ "minimalistic-assert": "^1.0.1" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hdr-histogram-js": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", @@ -9209,18 +9446,6 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/html-entities": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", @@ -9284,6 +9509,15 @@ "node": ">= 0.8" } }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", @@ -9362,16 +9596,28 @@ "dev": true }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "dev": true, "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, "node_modules/human-signals": { @@ -9383,15 +9629,6 @@ "node": ">=10.17.0" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dev": true, - "dependencies": { - "ms": "^2.0.0" - } - }, "node_modules/iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", @@ -9454,18 +9691,18 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "dev": true, "engines": { "node": ">= 4" } }, "node_modules/ignore-walk": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.3.tgz", - "integrity": "sha512-C7FfFoTA+bI10qfeydT8aZbvr91vAEU+2W5BZUlzPec47oNb07SsOfwYrtxuvOYdUApPP/Qlh4DtAO51Ekk2QA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.4.tgz", + "integrity": "sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==", "dev": true, "dependencies": { "minimatch": "^9.0.0" @@ -9503,10 +9740,13 @@ } }, "node_modules/immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", - "dev": true + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/import-fresh": { "version": "3.3.0", @@ -9577,117 +9817,59 @@ } }, "node_modules/inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "version": "9.2.11", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.11.tgz", + "integrity": "sha512-B2LafrnnhbRzCWfAdOXisUzL89Kg8cVJlYmhqoi3flSiV/TveO+nsXwgKr9h9PIo+J1hz7nBSk6gegRIMBBf7g==", "dev": true, "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", + "@ljharb/through": "^2.3.9", + "ansi-escapes": "^4.3.2", + "chalk": "^5.3.0", "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", + "cli-width": "^4.1.0", + "external-editor": "^3.1.0", + "figures": "^5.0.0", "lodash": "^4.17.21", - "mute-stream": "0.0.8", + "mute-stream": "1.0.0", "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "run-async": "^3.0.0", + "rxjs": "^7.8.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.18.0" } }, - "node_modules/inquirer/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==", + "node_modules/inquirer/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/inquirer/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/inquirer/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/inquirer/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/inquirer/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/inquirer/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" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/interactjs": { - "version": "1.10.19", - "resolved": "https://registry.npmjs.org/interactjs/-/interactjs-1.10.19.tgz", - "integrity": "sha512-5zWXBrfLnXAyhrxKlhRiud/JxWd3GvZkvdTf8bqjeHWDx9zgiu+qFNA3nnJMszadFCig2GU5zKx9PYrkT87OKA==", + "version": "1.10.23", + "resolved": "https://registry.npmjs.org/interactjs/-/interactjs-1.10.23.tgz", + "integrity": "sha512-ZnxfYh4QBnWnnCXVOVHEU4r2w01EQMTsLCd71n0mpsItFhV7S/jXycvzgsNvf5I99trBRRwP8RJXU8oy4hRFEw==", "dependencies": { - "@interactjs/types": "1.10.19" + "@interactjs/types": "1.10.23" } }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -9810,12 +9992,12 @@ } }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9948,6 +10130,15 @@ "node": ">=0.12.0" } }, + "node_modules/is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "dev": true, + "dependencies": { + "lodash.isfinite": "^3.3.2" + } + }, "node_modules/is-number-object": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", @@ -9987,12 +10178,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -10079,12 +10264,12 @@ } }, "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10154,9 +10339,9 @@ } }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" @@ -10354,9 +10539,9 @@ } }, "node_modules/jiti": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz", - "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", "dev": true, "bin": { "jiti": "bin/jiti.js" @@ -10394,95 +10579,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "dependencies": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsdom/node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsdom/node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/jsdom/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -10545,9 +10641,9 @@ "dev": true }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==", "dev": true, "optionalDependencies": { "graceful-fs": "^4.1.6" @@ -10632,9 +10728,9 @@ "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" }, "node_modules/less": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", - "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", "dev": true, "dependencies": { "copy-anything": "^2.0.1", @@ -10717,6 +10813,12 @@ } } }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", + "dev": true + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -10778,6 +10880,129 @@ "node": ">= 12.13.0" } }, + "node_modules/localtunnel": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.2.tgz", + "integrity": "sha512-n418Cn5ynvJd7m/N1d9WVJISLJF/ellZnfsLnx8WBWGzxv/ntNcFkJ1o6se5quUhCplfLGBNL5tYHiq5WF3Nug==", + "dev": true, + "dependencies": { + "axios": "0.21.4", + "debug": "4.3.2", + "openurl": "1.1.1", + "yargs": "17.1.1" + }, + "bin": { + "lt": "bin/lt.js" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/localtunnel/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/localtunnel/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/localtunnel/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/localtunnel/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/localtunnel/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/localtunnel/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/localtunnel/node_modules/yargs": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/localtunnel/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -10802,6 +11027,12 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -10876,6 +11107,18 @@ "node": ">=8" } }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/log-symbols/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10907,9 +11150,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.1", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", - "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", + "version": "0.30.5", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", + "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -10949,38 +11192,25 @@ "dev": true }, "node_modules/make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz", + "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==", "dev": true, "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", + "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", "ssri": "^10.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/matcher": { @@ -11093,6 +11323,18 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/miller-rabin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", @@ -11216,44 +11458,26 @@ } }, "node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "dev": true, "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/minipass-collect/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 - }, "node_modules/minipass-fetch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", @@ -11271,15 +11495,6 @@ "encoding": "^0.1.13" } }, - "node_modules/minipass-fetch/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/minipass-flush": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", @@ -11429,6 +11644,12 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/mitt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz", + "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==", + "dev": true + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -11478,15 +11699,18 @@ } }, "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, "funding": [ { @@ -11594,34 +11818,33 @@ } }, "node_modules/node-gyp": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", - "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.0.1.tgz", + "integrity": "sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==", "dev": true, "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", + "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^11.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", "semver": "^7.3.5", "tar": "^6.1.2", - "which": "^2.0.2" + "which": "^4.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^12.13 || ^14.13 || >=16" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/node-gyp-build": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", - "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", + "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==", "dev": true, "optional": true, "bin": { @@ -11630,6 +11853,67 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-gyp/node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, "node_modules/node-polyfill-webpack-plugin": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-2.0.1.tgz", @@ -11706,60 +11990,60 @@ } }, "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", "dev": true, "dependencies": { - "abbrev": "^1.0.0" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/normalize-package-data": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", - "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", + "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", "dev": true, "dependencies": { - "hosted-git-info": "^6.0.0", + "hosted-git-info": "^7.0.0", "is-core-module": "^2.8.1", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/normalize-package-data/node_modules/hosted-git-info": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", - "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dev": true, "dependencies": { - "lru-cache": "^7.5.1" + "lru-cache": "^10.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/normalize-package-data/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, "engines": { - "node": ">=12" + "node": "14 || >=16.14" } }, "node_modules/normalize-path": { @@ -11826,45 +12110,45 @@ } }, "node_modules/npm-package-arg": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", - "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", + "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", "dev": true, "dependencies": { - "hosted-git-info": "^6.0.0", + "hosted-git-info": "^7.0.0", "proc-log": "^3.0.0", "semver": "^7.3.5", "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm-package-arg/node_modules/hosted-git-info": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", - "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", + "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", "dev": true, "dependencies": { - "lru-cache": "^7.5.1" + "lru-cache": "^10.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm-package-arg/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, "engines": { - "node": ">=12" + "node": "14 || >=16.14" } }, "node_modules/npm-packlist": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", - "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.0.tgz", + "integrity": "sha512-ErAGFB5kJUciPy1mmx/C2YFbvxoJ0QJ9uwkCZOeR6CqLLISPZBOiFModAbSXnjjlwW5lOhuhXva+fURsSGJqyw==", "dev": true, "dependencies": { "ignore-walk": "^6.0.0" @@ -11874,36 +12158,36 @@ } }, "node_modules/npm-pick-manifest": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz", - "integrity": "sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.0.tgz", + "integrity": "sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==", "dev": true, "dependencies": { "npm-install-checks": "^6.0.0", "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^10.0.0", + "npm-package-arg": "^11.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm-registry-fetch": { - "version": "14.0.5", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", - "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.1.0.tgz", + "integrity": "sha512-PQCELXKt8Azvxnt5Y85GseQDJJlglTFM9L9U9gkv2y4e9s0k3GVDdOx3YoB6gm2Do0hlkzC39iCGXby+Wve1Bw==", "dev": true, "dependencies": { - "make-fetch-happen": "^11.0.0", - "minipass": "^5.0.0", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", "minipass-fetch": "^3.0.0", "minipass-json-stream": "^1.0.1", "minizlib": "^2.1.2", - "npm-package-arg": "^10.0.0", + "npm-package-arg": "^11.0.0", "proc-log": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/npm-run-all": { @@ -12032,21 +12316,6 @@ "node": ">=8" } }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "dev": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -12059,11 +12328,14 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", - "dev": true + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, "node_modules/object-inspect": { "version": "1.13.1", @@ -12099,23 +12371,14 @@ "node": ">= 0.4" } }, - "node_modules/object-path": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz", - "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==", - "dev": true, - "engines": { - "node": ">= 10.12.0" - } - }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -12133,9 +12396,9 @@ "dev": true }, "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "dev": true, "dependencies": { "ee-first": "1.1.1" @@ -12194,6 +12457,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openurl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz", + "integrity": "sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==", + "dev": true + }, + "node_modules/opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "dev": true, + "dependencies": { + "is-wsl": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/opn/node_modules/is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -12275,6 +12565,18 @@ "node": ">=8" } }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ora/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -12385,27 +12687,27 @@ } }, "node_modules/pacote": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", - "integrity": "sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.4.tgz", + "integrity": "sha512-eGdLHrV/g5b5MtD5cTPyss+JxOlaOloSMG3UwPMAvL8ywaLJ6beONPF40K4KKl/UI6q5hTKCJq5rCu8tkF+7Dg==", "dev": true, "dependencies": { - "@npmcli/git": "^4.0.0", + "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/promise-spawn": "^6.0.1", - "@npmcli/run-script": "^6.0.0", - "cacache": "^17.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^7.0.0", + "cacache": "^18.0.0", "fs-minipass": "^3.0.0", - "minipass": "^5.0.0", - "npm-package-arg": "^10.0.0", - "npm-packlist": "^7.0.0", - "npm-pick-manifest": "^8.0.0", - "npm-registry-fetch": "^14.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", "proc-log": "^3.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^6.0.0", + "read-package-json": "^7.0.0", "read-package-json-fast": "^3.0.0", - "sigstore": "^1.3.0", + "sigstore": "^2.0.0", "ssri": "^10.0.0", "tar": "^6.1.11" }, @@ -12413,7 +12715,7 @@ "pacote": "lib/bin.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/pako": { @@ -12587,9 +12889,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -12639,12 +12941,12 @@ "dev": true }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", "dev": true, "engines": { - "node": ">=8.6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -12673,9 +12975,9 @@ } }, "node_modules/piscina": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.0.0.tgz", - "integrity": "sha512-641nAmJS4k4iqpNUqfggqUBUMmlw0ZoM5VZKdQkV2e970Inn3Tk9kroCc1wpsYLD07vCwpys5iY0d3xI/9WkTg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.1.0.tgz", + "integrity": "sha512-sjbLMi3sokkie+qmtZpkfMCUJTpbxJm/wvaPzU28vmYSsTSW8xk9JcFUsbqGJdtPpIQ9tuj+iDcTtgZjwnOSig==", "dev": true, "dependencies": { "eventemitter-asyncresource": "^1.0.0", @@ -12797,6 +13099,29 @@ "node": ">=10.4.0" } }, + "node_modules/portscanner": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", + "dev": true, + "dependencies": { + "async": "^2.6.0", + "is-number-like": "^1.0.3" + }, + "engines": { + "node": ">=0.4", + "npm": ">=1.0.0" + } + }, + "node_modules/portscanner/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -12948,18 +13273,18 @@ "integrity": "sha512-KDeO94CbWI4pKsPnYpA1FPjo79EsY9I+M8ywoPBSf9XMXoe/0crjbUK7jcQEDHuc0ZMRIZsxH3TYLv4TUtHmAA==" }, "node_modules/primeng": { - "version": "16.5.1", - "resolved": "https://registry.npmjs.org/primeng/-/primeng-16.5.1.tgz", - "integrity": "sha512-D1Eu7HAm4DelAGXAncKw39bVRMzV9N2XOl9gbI5bEcfUhqbwDzSldbN57ULSdTaRcHugUMUolbYFJsJHMc/k1g==", + "version": "17.0.0-rc.1", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-17.0.0-rc.1.tgz", + "integrity": "sha512-S1z7DnP2p3Ia6KmBICHXsrxAlSVIiIyu6jSEKnuQTrUiTWMsaJnZX8IMHp+d21f8Q35HsS65etxyqxt8wn/MVQ==", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": "^16.2.0", - "@angular/core": "^16.2.0", - "@angular/forms": "^16.2.0", + "@angular/common": "^17.0.0", + "@angular/core": "^17.0.0", + "@angular/forms": "^17.0.0", "rxjs": "^6.0.0 || ^7.8.1", - "zone.js": "~0.13.0" + "zone.js": "~0.14.0" } }, "node_modules/proc-log": { @@ -13036,6 +13361,12 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -13043,12 +13374,6 @@ "dev": true, "optional": true }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -13080,9 +13405,9 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -13112,12 +13437,6 @@ "node": ">=0.4.x" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -13150,17 +13469,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/random": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/random/-/random-4.1.0.tgz", - "integrity": "sha512-6Ajb7XmMSE9EFAMGC3kg9mvE7fGlBip25mYYuSMzw/uUSrmGilvZo2qwX3RnTRjwXkwkS+4swse9otZ92VjAtQ==", - "dependencies": { - "seedrandom": "^3.0.5" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -13190,9 +13498,9 @@ } }, "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", "dev": true, "dependencies": { "bytes": "3.1.2", @@ -13204,15 +13512,6 @@ "node": ">= 0.8" } }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/raw-body/node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -13261,18 +13560,18 @@ } }, "node_modules/read-package-json": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", - "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.0.tgz", + "integrity": "sha512-uL4Z10OKV4p6vbdvIXB+OzhInYtIozl/VxUBPgNkBuUi2DeRonnuspmaVAMcrkmfjKGNmRndyQAbE7/AmzGwFg==", "dev": true, "dependencies": { "glob": "^10.2.2", "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", + "normalize-package-data": "^6.0.0", "npm-normalize-package-bin": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/read-package-json-fast": { @@ -13289,9 +13588,9 @@ } }, "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -13320,9 +13619,9 @@ } }, "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", - "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -13457,6 +13756,18 @@ "node": ">=8.10.0" } }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", @@ -13482,9 +13793,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", "dev": true }, "node_modules/regenerator-transform": { @@ -13582,12 +13893,12 @@ "dev": true }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -13652,23 +13963,73 @@ "node": ">=0.10.0" } }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "node_modules/resp-modifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", + "integrity": "sha512-U1+0kWC/+4ncRFYqQWTx/3qkfE6a4B/h3XXgmXypfa0SPZ3t7cbbaFk297PjQS/yov24R18h6OZe6iZwj3NSLw==", "dev": true, "dependencies": { - "lowercase-keys": "^2.0.0" + "debug": "^2.2.0", + "minimatch": "^3.0.2" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, + "node_modules/resp-modifier/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/resp-modifier/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/resp-modifier/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/resp-modifier/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -13763,9 +14124,9 @@ } }, "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", + "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, "engines": { "node": ">=0.12.0" @@ -13794,6 +14155,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", + "dev": true + }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -13870,9 +14237,9 @@ } }, "node_modules/sass": { - "version": "1.64.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz", - "integrity": "sha512-16rRACSOFEE8VN7SCgBu1MpYCyN7urj9At898tyzdXFhC+a+yOX5dXwAR7L8/IdPJ1NB8OYoXmD55DM30B2kEQ==", + "version": "1.69.5", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz", + "integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -13923,24 +14290,18 @@ } } }, + "node_modules/sass/node_modules/immutable": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", + "dev": true + }, "node_modules/sax": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", "dev": true }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -13960,11 +14321,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/seedrandom": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", - "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" - }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -13972,11 +14328,12 @@ "dev": true }, "node_modules/selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "dependencies": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" }, "engines": { @@ -14024,24 +14381,24 @@ "dev": true }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", "dev": true, "dependencies": { "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", + "depd": "~1.1.2", + "destroy": "~1.0.4", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "http-errors": "~1.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" }, "engines": { "node": ">= 0.8.0" @@ -14056,30 +14413,66 @@ "ms": "2.0.0" } }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "node_modules/send/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true }, "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", "dev": true, "bin": { "mime": "cli.js" - }, - "engines": { - "node": ">=4" } }, "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true }, + "node_modules/send/node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serialize-error": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", @@ -14197,24 +14590,24 @@ } }, "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "dev": true, "dependencies": { "encodeurl": "~1.0.2", "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" + "parseurl": "~1.3.2", + "send": "0.16.2" }, "engines": { "node": ">= 0.8.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "node_modules/server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", "dev": true }, "node_modules/set-function-length": { @@ -14334,22 +14727,18 @@ "dev": true }, "node_modules/sigstore": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.9.0.tgz", - "integrity": "sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.1.0.tgz", + "integrity": "sha512-kPIj+ZLkyI3QaM0qX8V/nSsweYND3W448pwkDgS6CQ74MfhEkIR8ToK5Iyx46KJYRjseVcD3Rp9zAmUAj6ZjPw==", "dev": true, "dependencies": { - "@sigstore/bundle": "^1.1.0", - "@sigstore/protobuf-specs": "^0.2.0", - "@sigstore/sign": "^1.0.0", - "@sigstore/tuf": "^1.0.3", - "make-fetch-happen": "^11.0.1" - }, - "bin": { - "sigstore": "bin/sigstore.js" + "@sigstore/bundle": "^2.1.0", + "@sigstore/protobuf-specs": "^0.2.1", + "@sigstore/sign": "^2.1.0", + "@sigstore/tuf": "^2.1.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/simple-update-notifier": { @@ -14437,6 +14826,61 @@ "npm": ">= 3.0.0" } }, + "node_modules/socket.io": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.2.tgz", + "integrity": "sha512-bvKVS29/I5fl2FGLNHuXlQaUH/BlzX1IN6S+NKLNZpBsPZIDH+90eQmCs2Railn4YUiww4SzUedJ6+uzwFnKLw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dev": true, + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-client": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz", + "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -14472,17 +14916,29 @@ } }, "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", + "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", "dev": true, "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" }, "engines": { - "node": ">= 10" + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, "node_modules/source-map": { @@ -14637,15 +15093,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/ssri/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/stat-mode": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", @@ -14656,12 +15103,12 @@ } }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==", "dev": true, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, "node_modules/stream-browserify": { @@ -14714,6 +15161,28 @@ "node": ">= 6" } }, + "node_modules/stream-throttle": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", + "integrity": "sha512-889+B9vN9dq7/vLbGyuHeZ6/ctf5sNuGWsDy89uNxkFTAgzy0eK7+w5fL3KLNRTkLle7EgZGvHUphZW0Q26MnQ==", + "dev": true, + "dependencies": { + "commander": "^2.2.0", + "limiter": "^1.0.5" + }, + "bin": { + "throttleproxy": "bin/throttleproxy.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-throttle/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -14902,12 +15371,6 @@ "node": ">=0.10" } }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -14958,6 +15421,15 @@ "node": ">=8" } }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -15001,18 +15473,18 @@ } }, "node_modules/temp-file/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, "node_modules/terser": { - "version": "5.19.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", - "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", + "integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", "dev": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -15158,12 +15630,6 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -15254,42 +15720,6 @@ "node": ">=0.6" } }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -15351,15 +15781,6 @@ } } }, - "node_modules/ts-node/node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -15386,17 +15807,17 @@ "dev": true }, "node_modules/tuf-js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", - "integrity": "sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.1.0.tgz", + "integrity": "sha512-eD7YPPjVlMzdggrOeE8zwoegUaG/rt6Bt3jwoQPunRiNVzgcCE009UDFJKJjG+Gk9wFu6W/Vi+P5d/5QpdD9jA==", "dev": true, "dependencies": { - "@tufjs/models": "1.0.4", + "@tufjs/models": "2.0.0", "debug": "^4.3.4", - "make-fetch-happen": "^11.1.1" + "make-fetch-happen": "^13.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/type-fest": { @@ -15496,9 +15917,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -15508,6 +15929,29 @@ "node": ">=14.17" } }, + "node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "engines": { + "node": "*" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -15523,10 +15967,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "5.27.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", + "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", + "dev": true, + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/undici-types": { - "version": "5.25.3", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", - "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "dev": true }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -15660,16 +16116,6 @@ "qs": "^6.11.2" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "node_modules/url/node_modules/punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -15775,14 +16221,14 @@ } }, "node_modules/vite": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.7.tgz", - "integrity": "sha512-6pYf9QJ1mHylfVh39HpuSfMPojPSKVxZvnclX1K1FyZ1PXDOcLBibdq5t1qxJSnL63ca8Wf4zts6mD8u8oc9Fw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", + "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", "dev": true, "dependencies": { "esbuild": "^0.18.10", - "postcss": "^8.4.26", - "rollup": "^3.25.2" + "postcss": "^8.4.27", + "rollup": "^3.27.1" }, "bin": { "vite": "bin/vite.js" @@ -15829,45 +16275,412 @@ } } }, - "node_modules/vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "browser-process-hrtime": "^1.0.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" } }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "xml-name-validator": "^3.0.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=10" + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" } }, + "node_modules/vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, "node_modules/wait-on": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.0.1.tgz", - "integrity": "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", + "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==", "dev": true, "dependencies": { - "axios": "^0.27.2", - "joi": "^17.7.0", + "axios": "^1.6.1", + "joi": "^17.11.0", "lodash": "^4.17.21", - "minimist": "^1.2.7", - "rxjs": "^7.8.0" + "minimist": "^1.2.8", + "rxjs": "^7.8.1" }, "bin": { "wait-on": "bin/wait-on" @@ -15876,6 +16689,17 @@ "node": ">=12.0.0" } }, + "node_modules/wait-on/node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -15907,21 +16731,11 @@ "defaults": "^1.0.3" } }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true, - "engines": { - "node": ">=10.4" - } - }, "node_modules/webpack": { "version": "5.89.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", "dev": true, - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", @@ -16051,6 +16865,15 @@ } } }, + "node_modules/webpack-dev-server/node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", @@ -16144,7 +16967,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -16161,7 +16983,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "peer": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -16170,15 +16991,13 @@ "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, - "peer": true + "dev": true }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, - "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -16215,47 +17034,6 @@ "node": ">=0.8.0" } }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "dependencies": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/wheel": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wheel/-/wheel-1.0.0.tgz", @@ -16311,15 +17089,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", @@ -16327,9 +17096,9 @@ "dev": true }, "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", @@ -16337,10 +17106,7 @@ "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=8" } }, "node_modules/wrap-ansi-cjs": { @@ -16434,12 +17200,12 @@ "dev": true }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "dev": true, "engines": { - "node": ">=8.3.0" + "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", @@ -16454,12 +17220,6 @@ } } }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", @@ -16469,11 +17229,14 @@ "node": ">=8.0" } }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } }, "node_modules/xtend": { "version": "4.0.2", @@ -16558,9 +17321,9 @@ } }, "node_modules/zone.js": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.3.tgz", - "integrity": "sha512-MKPbmZie6fASC/ps4dkmIhaT5eonHkEt6eAy80K42tAm0G2W+AahLJjbfi6X9NPdciOE9GRFTTM8u2IiF6O3ww==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.2.tgz", + "integrity": "sha512-X4U7J1isDhoOmHmFWiLhloWc2lzMkdnumtfQ1LXzf/IOZp5NQYuMUTaviVzG/q1ugMBIXzin2AqeVJUoSEkNyQ==", "dependencies": { "tslib": "^2.3.0" } diff --git a/desktop/package.json b/desktop/package.json index 3aadef640..98712052e 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -15,7 +15,7 @@ "ng": "ng", "copy:files": "node copyFiles.js", "start": "npm run copy:files && npm-run-all -p electron:serve ng:serve", - "ng:serve": "ng serve -c web --hmr", + "ng:serve": "ng serve -c web", "build": "npm run copy:files && npm run electron:serve-tsc && ng build --base-href ./", "build:dev": "npm run build -- -c dev", "build:prod": "npm run build -- -c production", @@ -30,51 +30,52 @@ "lint": "ng lint" }, "dependencies": { - "@angular/animations": "16.2.10", - "@angular/cdk": "16.2.9", - "@angular/common": "16.2.10", - "@angular/compiler": "16.2.10", - "@angular/core": "16.2.10", - "@angular/forms": "16.2.10", - "@angular/language-service": "16.2.10", - "@angular/platform-browser": "16.2.10", - "@angular/platform-browser-dynamic": "16.2.10", - "@angular/router": "16.2.10", + "@angular/animations": "17.0.5", + "@angular/cdk": "17.0.1", + "@angular/common": "17.0.5", + "@angular/compiler": "17.0.5", + "@angular/core": "17.0.5", + "@angular/forms": "17.0.5", + "@angular/language-service": "17.0.5", + "@angular/platform-browser": "17.0.5", + "@angular/platform-browser-dynamic": "17.0.5", + "@angular/router": "17.0.5", + "@fontsource/roboto": "5.0.8", + "@mdi/font": "7.3.67", "chart.js": "4.4.0", "chartjs-plugin-zoom": "2.0.1", - "interactjs": "1.10.19", + "interactjs": "1.10.23", "leaflet": "1.9.4", "moment": "2.29.4", "panzoom": "9.4.3", "primeflex": "3.3.1", "primeicons": "6.0.1", - "primeng": "16.5.1", - "random": "4.1.0", + "primeng": "17.0.0-rc.1", "rxjs": "7.8.1", "tslib": "2.6.2", "uuid": "9.0.1", - "zone.js": "0.13.3" + "zone.js": "0.14.2" }, "devDependencies": { - "@angular-builders/custom-webpack": "16.0.1", - "@angular-devkit/build-angular": "16.2.7", - "@angular/cli": "16.2.7", - "@angular/compiler-cli": "16.2.10", - "@types/leaflet": "1.9.7", - "@types/node": "20.8.7", - "@types/uuid": "9.0.6", - "electron": "27.0.1", - "electron-builder": "24.6.4", + "@angular-builders/custom-webpack": "17.0.0", + "@angular-devkit/build-angular": "17.0.5", + "@angular/cli": "17.0.5", + "@angular/compiler-cli": "17.0.5", + "@types/leaflet": "1.9.8", + "@types/node": "20.10.1", + "@types/uuid": "9.0.7", + "electron": "27.1.3", + "electron-builder": "24.9.1", "electron-debug": "3.2.0", "electron-reloader": "1.2.3", "node-polyfill-webpack-plugin": "2.0.1", "npm-run-all": "4.1.5", "ts-node": "10.9.1", - "typescript": "5.1.6", - "wait-on": "7.0.1" + "typescript": "5.2.2", + "wait-on": "7.2.0" }, "engines": { - "node": ">= 16.14.0 || >= 18.10.0" + "node": ">= 20.9.0" }, "browserslist": [ "chrome 114" diff --git a/desktop/sky-atlas.png b/desktop/sky-atlas.png new file mode 100644 index 000000000..cd5b2b732 Binary files /dev/null and b/desktop/sky-atlas.png differ diff --git a/desktop/splash.xcf b/desktop/splash.xcf index 60093ca37..6d566aabd 100644 Binary files a/desktop/splash.xcf and b/desktop/splash.xcf differ diff --git a/desktop/src/app/about/about.component.html b/desktop/src/app/about/about.component.html index c12b6cfe2..98b2c5860 100644 --- a/desktop/src/app/about/about.component.html +++ b/desktop/src/app/about/about.component.html @@ -1,10 +1,11 @@
- +
-

Nebulosa v0.1.0 (Ceres)

-

© 2023 Tiago Melo 🇧🇷

+

Nebulosa v0.1.0

+

Ceres

+

© 2023 Tiago Melo 🇧🇷

This program comes with absolutely no warranty and is provided on an 'as is' basis.

diff --git a/desktop/src/app/alignment/alignment.component.html b/desktop/src/app/alignment/alignment.component.html index d9611b818..a3dc17a88 100644 --- a/desktop/src/app/alignment/alignment.component.html +++ b/desktop/src/app/alignment/alignment.component.html @@ -37,16 +37,16 @@
- {{ darvStatus }} - + {{ darvStatus | enum | lowercase }} + {{ darvDirection }} - {{ darvCaptureEvent.captureRemainingTime | exposureTime }} + {{ darvCapture.remainingTime | exposureTime }} - {{ darvCaptureEvent.captureProgress | percent:'1.1-1' }} + {{ darvCapture.progress | percent:'1.1-1' }}
diff --git a/desktop/src/app/alignment/alignment.component.ts b/desktop/src/app/alignment/alignment.component.ts index b550a9735..9714c7eba 100644 --- a/desktop/src/app/alignment/alignment.component.ts +++ b/desktop/src/app/alignment/alignment.component.ts @@ -2,8 +2,7 @@ import { AfterViewInit, Component, HostListener, NgZone, OnDestroy } from '@angu import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' -import { Camera, CameraCaptureEvent, DARVPolarAlignmentEvent, DARVPolarAlignmentGuidePulseElapsed, DARVPolarAlignmentInitialPauseElapsed, GuideDirection, GuideOutput, Hemisphere } from '../../shared/types' +import { Camera, DARVPolarAlignmentState, GuideDirection, GuideOutput, Hemisphere, Union } from '../../shared/types' import { AppComponent } from '../app.component' @Component({ @@ -26,52 +25,55 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy { darvInProgress = false readonly darvHemispheres: Hemisphere[] = ['NORTHERN', 'SOUTHERN'] darvHemisphere: Hemisphere = 'NORTHERN' - darvCaptureEvent?: CameraCaptureEvent darvDirection?: GuideDirection - darvStatus = 'idle' + darvStatus: Union = 'IDLE' + + readonly darvCapture = { + remainingTime: 0, + progress: 0, + } constructor( app: AppComponent, private api: ApiService, private browserWindow: BrowserWindowService, - private electron: ElectronService, - private preference: PreferenceService, + electron: ElectronService, ngZone: NgZone, ) { app.title = 'Alignment' - electron.on('CAMERA_UPDATED', (_, event: Camera) => { - if (event.name === this.camera?.name) { + electron.on('CAMERA_UPDATED', event => { + if (event.device.name === this.camera?.name) { ngZone.run(() => { - Object.assign(this.camera!, event) + Object.assign(this.camera!, event.device) this.updateCamera() }) } }) - electron.on('GUIDE_OUTPUT_ATTACHED', (_, event: GuideOutput) => { + electron.on('GUIDE_OUTPUT_ATTACHED', event => { ngZone.run(() => { - this.guideOutputs.push(event) + this.guideOutputs.push(event.device) }) }) - electron.on('GUIDE_OUTPUT_DETACHED', (_, event: GuideOutput) => { + electron.on('GUIDE_OUTPUT_DETACHED', event => { ngZone.run(() => { - const index = this.guideOutputs.findIndex(e => e.name === event.name) + const index = this.guideOutputs.findIndex(e => e.name === event.device.name) if (index >= 0) this.guideOutputs.splice(index, 1) }) }) - electron.on('GUIDE_OUTPUT_UPDATED', (_, event: GuideOutput) => { - if (event.name === this.guideOutput?.name) { + electron.on('GUIDE_OUTPUT_UPDATED', event => { + if (event.device.name === this.guideOutput?.name) { ngZone.run(() => { - Object.assign(this.guideOutput!, event) + Object.assign(this.guideOutput!, event.device) this.updateGuideOutput() }) } }) - electron.on('DARV_POLAR_ALIGNMENT_STARTED', (_, event: DARVPolarAlignmentEvent) => { + electron.on('DARV_POLAR_ALIGNMENT_STARTED', event => { if (event.camera.name === this.camera?.name && event.guideOutput.name === this.guideOutput?.name) { ngZone.run(() => { @@ -80,40 +82,35 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy { } }) - electron.on('DARV_POLAR_ALIGNMENT_FINISHED', (_, event: DARVPolarAlignmentEvent) => { + electron.on('DARV_POLAR_ALIGNMENT_FINISHED', event => { if (event.camera.name === this.camera?.name && event.guideOutput.name === this.guideOutput?.name) { ngZone.run(() => { this.darvInProgress = false - this.darvStatus = 'idle' + this.darvStatus = 'IDLE' this.darvDirection = undefined }) } }) - electron.on('DARV_POLAR_ALIGNMENT_INITIAL_PAUSE_ELAPSED', (_, event: DARVPolarAlignmentInitialPauseElapsed) => { + electron.on('DARV_POLAR_ALIGNMENT_UPDATED', event => { if (event.camera.name === this.camera?.name && event.guideOutput.name === this.guideOutput?.name) { ngZone.run(() => { - this.darvStatus = 'initial pause' - }) - } - }) + this.darvStatus = event.state - electron.on('DARV_POLAR_ALIGNMENT_GUIDE_PULSE_ELAPSED', (_, event: DARVPolarAlignmentGuidePulseElapsed) => { - if (event.camera.name === this.camera?.name && - event.guideOutput.name === this.guideOutput?.name) { - ngZone.run(() => { - this.darvDirection = event.direction - this.darvStatus = event.forward ? 'forwarding' : 'backwarding' + if (event.state !== 'INITIAL_PAUSE') { + this.darvDirection = event.direction + } }) } }) - electron.on('CAMERA_EXPOSURE_UPDATED', (_, event: CameraCaptureEvent) => { + electron.on('CAMERA_EXPOSURE_ELAPSED', event => { if (event.camera.name === this.camera?.name) { ngZone.run(() => { - this.darvCaptureEvent = event + this.darvCapture.remainingTime = event.remainingTime + this.darvCapture.progress = event.progress }) } }) @@ -167,7 +164,7 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy { // TODO: Horizonte leste e oeste tem um impacto no "reversed"? const reversed = this.darvHemisphere === 'SOUTHERN' await this.openCameraImage() - await this.api.darvStart(this.camera!, this.guideOutput!, this.darvDrift, this.darvInitialPause, direction, reversed) + await this.api.darvStart(this.camera!, this.guideOutput!, this.darvDrift * 1000000, this.darvInitialPause * 1000000, direction, reversed) } darvAzimuth() { diff --git a/desktop/src/app/app-routing.module.ts b/desktop/src/app/app-routing.module.ts index dc63f7892..6dcf848d8 100644 --- a/desktop/src/app/app-routing.module.ts +++ b/desktop/src/app/app-routing.module.ts @@ -4,6 +4,7 @@ import { APP_CONFIG } from '../environments/environment' import { AboutComponent } from './about/about.component' import { AlignmentComponent } from './alignment/alignment.component' import { AtlasComponent } from './atlas/atlas.component' +import { CalibrationComponent } from './calibration/calibration.component' import { CameraComponent } from './camera/camera.component' import { FilterWheelComponent } from './filterwheel/filterwheel.component' import { FocuserComponent } from './focuser/focuser.component' @@ -13,6 +14,7 @@ import { HomeComponent } from './home/home.component' import { ImageComponent } from './image/image.component' import { INDIComponent } from './indi/indi.component' import { MountComponent } from './mount/mount.component' +import { SettingsComponent } from './settings/settings.component' const routes: Routes = [ { @@ -64,6 +66,14 @@ const routes: Routes = [ path: 'alignment', component: AlignmentComponent, }, + { + path: 'calibration', + component: CalibrationComponent, + }, + { + path: 'settings', + component: SettingsComponent, + }, { path: 'about', component: AboutComponent, diff --git a/desktop/src/app/app.component.html b/desktop/src/app/app.component.html index e7a9005d3..aaf3509f6 100644 --- a/desktop/src/app/app.component.html +++ b/desktop/src/app/app.component.html @@ -1,9 +1,15 @@ -
{{ title }}
{{ subTitle }}
+ + + + +
- \ No newline at end of file + + \ No newline at end of file diff --git a/desktop/src/app/app.component.scss b/desktop/src/app/app.component.scss index 3231c27e5..0916c4a10 100644 --- a/desktop/src/app/app.component.scss +++ b/desktop/src/app/app.component.scss @@ -1,7 +1,17 @@ :host { #titlebar { - ::ng-deep .mdi:before { - color: #000000 + ::ng-deep { + .mdi:before { + color: #FFF + } + + .p-disabled { + opacity: 0.3; + } + } + + .separator { + width: 16px; } } } \ No newline at end of file diff --git a/desktop/src/app/app.component.ts b/desktop/src/app/app.component.ts index d30659cc9..6f97c42db 100644 --- a/desktop/src/app/app.component.ts +++ b/desktop/src/app/app.component.ts @@ -1,10 +1,9 @@ import { AfterViewInit, Component } from '@angular/core' import { Title } from '@angular/platform-browser' import { ActivatedRoute } from '@angular/router' -import random from 'random' +import { MenuItem } from 'primeng/api' import { APP_CONFIG } from '../environments/environment' import { ElectronService } from '../shared/services/electron.service' -import { PreferenceService } from '../shared/services/preference.service' @Component({ selector: 'app-root', @@ -13,18 +12,11 @@ import { PreferenceService } from '../shared/services/preference.service' }) export class AppComponent implements AfterViewInit { - static readonly BACKGROUND_COLORS = [ - '#880E4F', '#4A148C', '#311B92', '#1A237E', - '#0D47A1', '#01579B', '#006064', '#004D40', - '#1B5E20', '#33691E', '#B71C1C', - ] - pinned = false maximizable = false - backgroundColor = AppComponent.BACKGROUND_COLORS[random.int(0, AppComponent.BACKGROUND_COLORS.length - 1)] subTitle = '' - - private night!: Element + backgroundColor = '#212121' + extra: MenuItem[] = [] get title() { return this.windowTitle.getTitle() @@ -37,59 +29,38 @@ export class AppComponent implements AfterViewInit { constructor( private windowTitle: Title, private route: ActivatedRoute, - private electronService: ElectronService, - private preference: PreferenceService, + private electron: ElectronService, ) { console.info('APP_CONFIG', APP_CONFIG) - if (electronService.isElectron) { + if (electron.isElectron) { console.info('Run in electron') } else { console.info('Run in browser') } } - ngAfterViewInit() { + async ngAfterViewInit() { this.route.queryParams.subscribe(e => { this.maximizable = e.resizable === 'true' }) - - this.night = document.getElementsByTagName('night')[0] - - const nightMode = this.preference.get('settings.nightMode', false) - this.nightMode(nightMode) - } - - nightMode(enabled: boolean) { - if (enabled) { - this.night.classList.remove('hidden') - this.night.classList.add('block') - } else { - this.night.classList.remove('block') - this.night.classList.add('hidden') - } - - this.preference.set('settings.nightMode', enabled) - - // TODO: MAKE TITLEBAR RED IF NIGHT MODE IS ON - // TODO: NOTIFY ALL WINDOWS PREFERENCE_UPDATED(name, oldValue, newValue) } pin() { this.pinned = !this.pinned - if (this.pinned) this.electronService.sendSync('PIN_WINDOW') - else this.electronService.sendSync('UNPIN_WINDOW') + if (this.pinned) this.electron.send('PIN_WINDOW') + else this.electron.send('UNPIN_WINDOW') } minimize() { - this.electronService.sendSync('MINIMIZE_WINDOW') + this.electron.send('MINIMIZE_WINDOW') } maximize() { - this.electronService.sendSync('MAXIMIZE_WINDOW') + this.electron.send('MAXIMIZE_WINDOW') } close() { - this.electronService.sendSync('CLOSE_WINDOW') + this.electron.send('CLOSE_WINDOW') } } diff --git a/desktop/src/app/app.module.ts b/desktop/src/app/app.module.ts index c5904e96f..1bf5c14b8 100644 --- a/desktop/src/app/app.module.ts +++ b/desktop/src/app/app.module.ts @@ -4,11 +4,12 @@ import { LOCALE_ID, NgModule } from '@angular/core' import { FormsModule } from '@angular/forms' import { BrowserModule } from '@angular/platform-browser' import { BrowserAnimationsModule } from '@angular/platform-browser/animations' -import { MessageService } from 'primeng/api' +import { ConfirmationService, MessageService } from 'primeng/api' import { ButtonModule } from 'primeng/button' import { CalendarModule } from 'primeng/calendar' import { ChartModule } from 'primeng/chart' import { CheckboxModule } from 'primeng/checkbox' +import { ConfirmDialogModule } from 'primeng/confirmdialog' import { ContextMenuModule } from 'primeng/contextmenu' import { DialogModule } from 'primeng/dialog' import { DropdownModule } from 'primeng/dropdown' @@ -19,6 +20,8 @@ import { InputSwitchModule } from 'primeng/inputswitch' import { InputTextModule } from 'primeng/inputtext' import { ListboxModule } from 'primeng/listbox' import { MenuModule } from 'primeng/menu' +import { OverlayPanelModule } from 'primeng/overlaypanel' +import { ScrollPanelModule } from 'primeng/scrollpanel' import { SelectButtonModule } from 'primeng/selectbutton' import { SlideMenuModule } from 'primeng/slidemenu' import { SliderModule } from 'primeng/slider' @@ -29,7 +32,9 @@ import { TagModule } from 'primeng/tag' import { TieredMenuModule } from 'primeng/tieredmenu' import { ToastModule } from 'primeng/toast' import { TooltipModule } from 'primeng/tooltip' +import { DeviceMenuComponent } from '../shared/components/devicemenu/devicemenu.component' import { DialogMenuComponent } from '../shared/components/dialogmenu/dialogmenu.component' +import { MenuItemComponent } from '../shared/components/menuitem/menuitem.component' import { MoonComponent } from '../shared/components/moon/moon.component' import { OpenStreetMapComponent } from '../shared/components/openstreetmap/openstreetmap.component' import { LocationDialog } from '../shared/dialogs/location/location.dialog' @@ -39,12 +44,14 @@ import { AnglePipe } from '../shared/pipes/angle.pipe' import { EnumPipe } from '../shared/pipes/enum.pipe' import { EnvPipe } from '../shared/pipes/env.pipe' import { ExposureTimePipe } from '../shared/pipes/exposureTime.pipe' +import { SkyObjectPipe } from '../shared/pipes/skyObject.pipe' import { WinPipe } from '../shared/pipes/win.pipe' import { AboutComponent } from './about/about.component' import { AlignmentComponent } from './alignment/alignment.component' import { AppRoutingModule } from './app-routing.module' import { AppComponent } from './app.component' import { AtlasComponent } from './atlas/atlas.component' +import { CalibrationComponent } from './calibration/calibration.component' import { CameraComponent } from './camera/camera.component' import { FilterWheelComponent } from './filterwheel/filterwheel.component' import { FocuserComponent } from './focuser/focuser.component' @@ -55,6 +62,7 @@ import { ImageComponent } from './image/image.component' import { INDIComponent } from './indi/indi.component' import { INDIPropertyComponent } from './indi/property/indi-property.component' import { MountComponent } from './mount/mount.component' +import { SettingsComponent } from './settings/settings.component' @NgModule({ declarations: [ @@ -75,12 +83,17 @@ import { MountComponent } from './mount/mount.component' GuiderComponent, DialogMenuComponent, AlignmentComponent, + SettingsComponent, LocationDialog, + MenuItemComponent, + DeviceMenuComponent, + CalibrationComponent, EnvPipe, WinPipe, EnumPipe, ExposureTimePipe, AnglePipe, + SkyObjectPipe, StopPropagationDirective, NoDropdownDirective, ], @@ -115,13 +128,20 @@ import { MountComponent } from './mount/mount.component' InplaceModule, SlideMenuModule, DynamicDialogModule, + ScrollPanelModule, + ConfirmDialogModule, + OverlayPanelModule, ], providers: [ MessageService, DialogService, + ConfirmationService, EnvPipe, WinPipe, EnumPipe, + ExposureTimePipe, + AnglePipe, + SkyObjectPipe, { provide: LOCALE_ID, useValue: 'en-US', diff --git a/desktop/src/app/atlas/atlas.component.html b/desktop/src/app/atlas/atlas.component.html index edffe381a..878590527 100644 --- a/desktop/src/app/atlas/atlas.component.html +++ b/desktop/src/app/atlas/atlas.component.html @@ -1,26 +1,32 @@
- + - +
+ +
-
+ - +
+ +
-
- +
+
- +
+ +
- +
+ +
@@ -80,7 +88,9 @@ - +
+ +
@@ -91,7 +101,7 @@
- +
@@ -108,7 +118,7 @@ - {{ item.name.replaceAll('|', ' · ') }} + {{ item | skyObject:'name' }} {{ item.type | enum }} {{ item.magnitude < 99 ? item.magnitude.toFixed(1) : '-' }} {{ item.constellation }} @@ -119,7 +129,9 @@ - +
+ +
@@ -130,7 +142,7 @@
- +
@@ -147,7 +159,48 @@ - {{ item.name.replaceAll('|', ' · ') }} + {{ item | skyObject:'name' }} + {{ item.type | enum }} + {{ item.magnitude < 99 ? item.magnitude.toFixed(1) : '-' }} + {{ item.constellation }} + + + +
+
+ + +
+ +
+
+
+
+ + + + +
+
+ + +
+
+
+ + + + Name + Type + Mag. + Const. + + + + + {{ item | skyObject:'name' }} {{ item.type | enum }} {{ item.magnitude < 99 ? item.magnitude.toFixed(1) : '-' }} {{ item.constellation }} @@ -158,7 +211,9 @@ - +
+ +
@@ -198,35 +253,10 @@
- - - - -
-
- - - - -
-
- - - -
-
-
-
- {{ name }} - -
@@ -300,17 +330,19 @@
- +
- - + - - +
@@ -318,20 +350,9 @@ -
-
- - - - -
+
+ {{ name ?? '' }} +
@@ -342,30 +363,30 @@
- -
+ +
- +
- +
+ [(ngModel)]="skyObjectFilter.radius" />
-
@@ -383,55 +404,7 @@
- - -
- {{ item | enum }} -
-
- -
- {{ item | enum }} -
-
-
- -
-
-
-

Magnitude min: {{ starFilter.magnitude[0] }} max: {{ starFilter.magnitude[1] }}

- -
-
- - - - - - -
-
- - - - -
-
- - - - -
-
- - - - -
-
- -
@@ -444,33 +417,16 @@
- -
-
-
- - - -
- {{ item | enum }} -
-
- -
- {{ item | enum }} -
-
-
-

Magnitude min: {{ dsoFilter.magnitude[0] }} max: {{ dsoFilter.magnitude[1] }}

- +

Magnitude min: {{ skyObjectFilter.magnitude[0] }} max: {{ skyObjectFilter.magnitude[1] }}

+
- +
@@ -489,4 +445,28 @@ - \ No newline at end of file + + +
+
+ + +
+ + +
+
+
+ +
+
+
+
+ + + \ No newline at end of file diff --git a/desktop/src/app/atlas/atlas.component.scss b/desktop/src/app/atlas/atlas.component.scss index ea3850aaa..b3054b2a5 100644 --- a/desktop/src/app/atlas/atlas.component.scss +++ b/desktop/src/app/atlas/atlas.component.scss @@ -2,31 +2,56 @@ display: flex; flex-direction: column; height: 100vh; -} -p-table ::ng-deep .p-datatable-wrapper { - max-height: 217px; - min-width: 100vw; -} + p-table ::ng-deep .p-datatable-wrapper { + max-height: 206px; + min-width: 100vw; + } -p-table.planet ::ng-deep .p-datatable-wrapper { - max-height: 217px; -} + p-table.planet ::ng-deep .p-datatable-wrapper { + max-height: 206px; + } -p-table.minorPlanet ::ng-deep .p-datatable-wrapper { - max-height: 169px; -} + p-table.minorPlanet ::ng-deep .p-datatable-wrapper { + max-height: 161px; + } -p-table.star ::ng-deep .p-datatable-wrapper { - max-height: 123px; -} + p-table.star ::ng-deep .p-datatable-wrapper { + max-height: 114px; + } -p-table.dso ::ng-deep .p-datatable-wrapper { - max-height: 123px; -} + p-table.dso ::ng-deep .p-datatable-wrapper { + max-height: 114px; + } + + p-table.simbad ::ng-deep .p-datatable-wrapper { + max-height: 114px; + } -p-table.satellite ::ng-deep .p-datatable-wrapper { - max-height: 123px; + p-table.satellite ::ng-deep .p-datatable-wrapper { + max-height: 114px; + } + + ::ng-deep { + .main-tabs .p-tabview .p-tabview-nav li { + width: 12.5% !important; + + .p-tabview-nav-link { + display: flex; + justify-content: center; + padding: 0px; + height: 100%; + + img { + height: 20px; + } + } + } + + .p-tabview .p-tabview-left-icon { + margin-right: 0px; + } + } } .p-input-icon-left span.pi:first-of-type { @@ -40,3 +65,11 @@ p-table.satellite ::ng-deep .p-datatable-wrapper { .p-input-icon-right p-checkbox { margin-top: -0.745rem; } + +::ng-deep { + + .p-overlaypanel:after, + .p-overlaypanel:before { + left: calc(var(--overlayArrowLeft, 0) + 2.1rem) !important; + } +} \ No newline at end of file diff --git a/desktop/src/app/atlas/atlas.component.ts b/desktop/src/app/atlas/atlas.component.ts index fd12caa62..346b0ff14 100644 --- a/desktop/src/app/atlas/atlas.component.ts +++ b/desktop/src/app/atlas/atlas.component.ts @@ -1,19 +1,23 @@ -import { AfterContentInit, Component, ElementRef, HostListener, OnDestroy, OnInit, ViewChild } from '@angular/core' +import { AfterContentInit, AfterViewInit, Component, ElementRef, HostListener, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' import { Chart, ChartData, ChartOptions } from 'chart.js' import zoomPlugin from 'chartjs-plugin-zoom' +import moment from 'moment' import { MenuItem } from 'primeng/api' import { UIChart } from 'primeng/chart' -import { DialogService } from 'primeng/dynamicdialog' import { ListboxChangeEvent } from 'primeng/listbox' -import { EVERY_MINUTE_CRON_TIME, ONE_DECIMAL_PLACE_FORMATTER, TWO_DIGITS_FORMATTER } from '../../shared/constants' -import { LocationDialog } from '../../shared/dialogs/location/location.dialog' +import { OverlayPanel } from 'primeng/overlaypanel' +import { Subscription, timer } from 'rxjs' +import { DeviceMenuComponent } from '../../shared/components/devicemenu/devicemenu.component' +import { ONE_DECIMAL_PLACE_FORMATTER, TWO_DIGITS_FORMATTER } from '../../shared/constants' +import { SkyObjectPipe } from '../../shared/pipes/skyObject.pipe' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' +import { LocalStorageService } from '../../shared/services/local-storage.service' +import { PrimeService } from '../../shared/services/prime.service' import { - Angle, CONSTELLATIONS, Constellation, DeepSkyObject, EMPTY_BODY_POSITION, EMPTY_LOCATION, Location, - MinorPlanet, SATELLITE_GROUPS, Satellite, SatelliteGroupType, SkyObjectType, Star, Union + Angle, CONSTELLATIONS, Constellation, DeepSkyObject, EMPTY_BODY_POSITION, + Location, MinorPlanet, Mount, SATELLITE_GROUPS, Satellite, SatelliteGroupType, SkyObjectType, Star, Union } from '../../shared/types' import { AppComponent } from '../app.component' @@ -33,6 +37,11 @@ export interface SearchFilter { constellation: Union magnitude: [number, number] type: Union + types: Union[] +} + +export interface SkyAtlasPreference { + satellites?: { group: SatelliteGroupType, enabled: boolean }[] } @Component({ @@ -40,29 +49,24 @@ export interface SearchFilter { templateUrl: './atlas.component.html', styleUrls: ['./atlas.component.scss'], }) -export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { +export class AtlasComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy { refreshing = false private activeTab = 0 - private settingsTabActivated = false get tab() { - return this.settingsTabActivated ? 7 : this.activeTab + return this.activeTab } set tab(value: number) { - this.settingsTabActivated = false - if (value === 7) this.settingsTabActivated = true - else this.activeTab = value + this.activeTab = value } readonly bodyPosition = Object.assign({}, EMPTY_BODY_POSITION) moonIlluminated = 1 moonWaning = false - locations: Location[] = [] - location = Object.assign({}, EMPTY_LOCATION) useManualDateTime = false dateTime = new Date() dateTimeHour = this.dateTime.getHours() @@ -115,9 +119,8 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { star?: Star starItems: Star[] = [] starSearchText = '' - showStarFilterDialog = false - readonly starFilter: SearchFilter = { + private readonly starFilter: SearchFilter = { text: '', rightAscension: '00h00m00s', declination: `+000°00'00"`, @@ -125,22 +128,14 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { constellation: 'ALL', magnitude: [-30, 30], type: 'ALL', + types: ['ALL'], } - readonly starTypeOptions: Union[] = ['ALL'] - dso?: DeepSkyObject dsoItems: DeepSkyObject[] = [] dsoSearchText = '' - showDSOFilterDialog = false - satellite?: Satellite - satelliteItems: Satellite[] = [] - satelliteSearchText = '' - showSatelliteFilterDialog = false - readonly satelliteSearchGroup = new Map() - - readonly dsoFilter: SearchFilter = { + private readonly dsoFilter: SearchFilter = { text: '', rightAscension: '00h00m00s', declination: `+000°00'00"`, @@ -148,18 +143,46 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { constellation: 'ALL', magnitude: [-30, 30], type: 'ALL', + types: ['ALL'], } - readonly dsoTypeOptions: Union[] = ['ALL'] + simbad?: DeepSkyObject + simbadItems: DeepSkyObject[] = [] + simbadSearchText = '' + + private readonly simbadFilter: SearchFilter = { + text: '', + rightAscension: '00h00m00s', + declination: `+000°00'00"`, + radius: 0, + constellation: 'ALL', + magnitude: [-30, 30], + type: 'ALL', + types: ['ALL'], + } + showSkyObjectFilter = false + skyObjectFilter?: SearchFilter readonly constellationOptions: Union[] = ['ALL', ...CONSTELLATIONS] - name = 'Sun' + satellite?: Satellite + satelliteItems: Satellite[] = [] + satelliteSearchText = '' + showSatelliteFilterDialog = false + readonly satelliteSearchGroup = new Map() + + name?= 'Sun' tags: { title: string, severity: string }[] = [] @ViewChild('imageOfSun') private readonly imageOfSun!: ElementRef + @ViewChild('deviceMenu') + private readonly deviceMenu!: DeviceMenuComponent + + @ViewChild('calendarPanel') + private readonly calendarPanel!: OverlayPanel + @ViewChild('chart') private readonly chart!: UIChart @@ -402,7 +425,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { 'ONEWEB', 'SCIENCE', 'STARLINK', 'STATIONS', 'VISUAL' ] - readonly ephemerisMenuItems: MenuItem[] = [ + readonly ephemerisModel: MenuItem[] = [ { icon: 'mdi mdi-magnify', label: 'Find stars around this object', @@ -410,9 +433,10 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { this.starFilter.rightAscension = this.bodyPosition.rightAscensionJ2000 this.starFilter.declination = this.bodyPosition.declinationJ2000 if (this.starFilter.radius <= 0) this.starFilter.radius = 1 + this.skyObjectFilter = this.starFilter this.tab = 4 - // this.showStarFilterDialog = true - this.filterStar() + this.tabChanged() + this.filterSkyObject() }, }, { @@ -422,57 +446,72 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { this.dsoFilter.rightAscension = this.bodyPosition.rightAscensionJ2000 this.dsoFilter.declination = this.bodyPosition.declinationJ2000 if (this.dsoFilter.radius <= 0) this.dsoFilter.radius = 1 + this.skyObjectFilter = this.dsoFilter this.tab = 5 - // this.showDSOFilterDialog = true - this.filterDSO() + this.tabChanged() + this.filterSkyObject() + }, + }, + { + icon: 'mdi mdi-magnify', + label: 'Find around this object on Simbad', + command: () => { + this.simbadFilter.rightAscension = this.bodyPosition.rightAscensionJ2000 + this.simbadFilter.declination = this.bodyPosition.declinationJ2000 + if (this.simbadFilter.radius <= 0) this.simbadFilter.radius = 1 + this.skyObjectFilter = this.simbadFilter + this.tab = 6 + this.tabChanged() + this.filterSkyObject() }, }, ] + private refreshTimer?: Subscription + private refreshTabCount = 0 + constructor( private app: AppComponent, private api: ApiService, private browserWindow: BrowserWindowService, - private electron: ElectronService, - private preference: PreferenceService, - private dialog: DialogService, + electron: ElectronService, + private storage: LocalStorageService, + private skyObjectPipe: SkyObjectPipe, + private prime: PrimeService, + ngZone: NgZone, ) { app.title = 'Sky Atlas' - for (const item of SATELLITE_GROUPS) { - const enabled = preference.get(`atlas.satellite.filter.${item}`, AtlasComponent.DEFAULT_SATELLITE_FILTERS.includes(item)) - this.satelliteSearchGroup.set(item, enabled) - } + app.extra.push({ + icon: 'mdi mdi-calendar', + tooltip: 'Date & time', + command: (e) => { + this.calendarPanel.toggle(e.originalEvent) + }, + }) - electron.on('CRON_TICKED', () => { - if (!this.useManualDateTime) { - this.refreshTab() - } + electron.on('LOCATION_CHANGED', (event) => { + ngZone.run(() => this.refreshTab(true, true, event)) }) // TODO: Refresh graph and twilight if hours past 12 (noon) } async ngOnInit() { - this.electron.registerCron(EVERY_MINUTE_CRON_TIME) - - this.starTypeOptions.push(... await this.api.starTypes()) - this.dsoTypeOptions.push(... await this.api.dsoTypes()) - } + const preference = this.storage.get('atlas', {}) - async ngAfterContentInit() { - const locations = await this.api.locations() - const location = this.preference.get('atlas.location', EMPTY_LOCATION) - const index = locations.findIndex(e => e.id === location.id) - - if (index >= 1) { - const temp = locations[0] - locations[0] = location - locations[index] = temp + for (const group of SATELLITE_GROUPS) { + const satellite = preference.satellites?.find(e => e.group === group) + const enabled = satellite?.enabled ?? AtlasComponent.DEFAULT_SATELLITE_FILTERS.includes(group) + this.satelliteSearchGroup.set(group, enabled) } - this.locations = locations + this.starFilter.types.push(... await this.api.starTypes()) + this.dsoFilter.types.push(... await this.api.dsoTypes()) + this.simbadFilter.types.push(... await this.api.simbadTypes()) + } + async ngAfterContentInit() { // const canvas = this.chart.getCanvas() as HTMLCanvasElement // const chart = this.chart.chart as Chart @@ -480,19 +519,38 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { // const x = chart.scales['x'].getValueForPixel(event.offsetX) // const y = chart.scales['y'].getValueForPixel(event.offsetY) // } + + const now = new Date() + const initialDelay = 60 * 1000 - (now.getSeconds() * 1000 + now.getMilliseconds()) + this.refreshTimer = timer(initialDelay, 60 * 1000) + .subscribe(() => { + if (!this.useManualDateTime) { + this.refreshTab() + } + }) + + if (initialDelay > 2500) { + await this.refreshTab() + } + } + + ngAfterViewInit() { + this.calendarPanel.onOverlayClick = (e) => { + e.stopImmediatePropagation() + } } @HostListener('window:unload') ngOnDestroy() { - this.electron.unregisterCron() + this.refreshTimer?.unsubscribe() } - tabChanged() { - this.refreshTab(false, true) + async tabChanged() { + await this.refreshTab(false, true) } - planetChanged() { - this.refreshTab(false, true) + async planetChanged() { + await this.refreshTab(false, true) } async searchMinorPlanet() { @@ -503,7 +561,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { if (minorPlanet.found) { this.minorPlanet = minorPlanet - this.refreshTab(false, true) + await this.refreshTab(false, true) } else { this.minorPlanetChoiceItems = minorPlanet.searchItems this.showMinorPlanetChoiceDialog = true @@ -519,16 +577,25 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { this.showMinorPlanetChoiceDialog = false } - starChanged() { - this.refreshTab(false, true) + async starChanged() { + await this.refreshTab(false, true) + } + + async dsoChanged() { + await this.refreshTab(false, true) + } + + async simbadChanged() { + await this.refreshTab(false, true) } - dsoChanged() { - this.refreshTab(false, true) + async satelliteChanged() { + await this.refreshTab(false, true) } - satelliteChanged() { - this.refreshTab(false, true) + showStarFilterDialog() { + this.skyObjectFilter = this.starFilter + this.showSkyObjectFilter = true } async searchStar() { @@ -547,9 +614,9 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { } } - async filterStar() { - await this.searchStar() - this.showStarFilterDialog = false + showDSOFilterDialog() { + this.skyObjectFilter = this.dsoFilter + this.showSkyObjectFilter = true } async searchDSO() { @@ -568,110 +635,116 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { } } - async filterDSO() { - await this.searchDSO() - this.showDSOFilterDialog = false + showSimbadFilterDialog() { + this.skyObjectFilter = this.simbadFilter + this.showSkyObjectFilter = true } - async searchSatellite() { + async searchSimbad() { + const constellation = this.simbadFilter.constellation === 'ALL' ? undefined : this.simbadFilter.constellation + const type = this.simbadFilter.type === 'ALL' ? undefined : this.simbadFilter.type + this.refreshing = true try { - for (const item of SATELLITE_GROUPS) { - this.preference.set(`atlas.satellite.filter.${item}`, this.satelliteSearchGroup.get(item)) - } - - const groups = SATELLITE_GROUPS.filter(e => this.satelliteSearchGroup.get(e)) - this.satelliteItems = await this.api.searchSatellites(this.satelliteSearchText, groups) + this.simbadItems = await this.api.searchSimbad(this.simbadSearchText, + this.simbadFilter.rightAscension, this.simbadFilter.declination, this.simbadFilter.radius, + constellation, this.simbadFilter.magnitude[0], this.simbadFilter.magnitude[1], type, + ) } finally { this.refreshing = false } } - resetSatelliteFilter() { - for (const item of SATELLITE_GROUPS) { - const enabled = AtlasComponent.DEFAULT_SATELLITE_FILTERS.includes(item) - this.preference.set(`atlas.satellite.filter.${item}`, enabled) - this.satelliteSearchGroup.set(item, enabled) - } - } + async filterSkyObject() { + if (this.skyObjectFilter === this.starFilter) await this.searchStar() + else if (this.skyObjectFilter === this.dsoFilter) await this.searchDSO() + else if (this.skyObjectFilter === this.simbadFilter) await this.searchSimbad() - async filterSatellite() { - await this.searchSatellite() - this.showSatelliteFilterDialog = false + this.showSkyObjectFilter = false } - addLocation() { - this.showLocation(Object.assign({}, EMPTY_LOCATION)) - } + async searchSatellite() { + this.refreshing = true - editLocation() { - this.showLocation(Object.assign({}, this.location)) - } + try { + const preference = this.storage.get('atlas', {}) - private showLocation(location: Location) { - const dialog = LocationDialog.show(this.dialog, location) + preference.satellites = SATELLITE_GROUPS.map(group => { + return { group, enabled: this.satelliteSearchGroup.get(group) ?? false } + }) - dialog.onClose.subscribe((result?: Location) => { - result && this.saveLocation(result) - }) - } + this.storage.set('atlas', preference) - private async saveLocation(location: Location) { - this.location = await this.api.saveLocation(location) - this.locations = await this.api.locations() - this.refreshTab(true, true) + const groups = SATELLITE_GROUPS.filter(e => this.satelliteSearchGroup.get(e)) + this.satelliteItems = await this.api.searchSatellites(this.satelliteSearchText, groups) + } finally { + this.refreshing = false + } } - async deleteLocation() { - await this.api.deleteLocation(this.location) - this.locations = await this.api.locations() - this.location = this.locations[0] ?? Object.assign({}, EMPTY_LOCATION) - this.refreshTab(true, true) + resetSatelliteFilter() { + const preference = this.storage.get('atlas', {}) + + preference.satellites = [] + + for (const group of SATELLITE_GROUPS) { + const enabled = AtlasComponent.DEFAULT_SATELLITE_FILTERS.includes(group) + preference.satellites!.push({ group, enabled }) + this.satelliteSearchGroup.set(group, enabled) + } + + this.storage.set('atlas', preference) } - locationChanged() { - this.preference.set(`atlas.location`, this.location) - this.refreshTab(true, true) + async filterSatellite() { + await this.searchSatellite() + this.showSatelliteFilterDialog = false } - dateTimeChanged(dateChanged: boolean) { - this.refreshTab(dateChanged, true) + async dateTimeChanged(dateChanged: boolean) { + await this.refreshTab(dateChanged, true) } - useManualDateTimeChanged() { + async useManualDateTimeChanged() { if (!this.useManualDateTime) { - this.refreshTab(true, true) + await this.refreshTab(true, true) } } async mountGoTo() { - const mount = await this.electron.selectedMount() - if (!mount?.connected) return - this.api.mountGoTo(mount, this.bodyPosition.rightAscension, this.bodyPosition.declination, false) + this.executeMount((mount) => { + this.api.mountGoTo(mount, this.bodyPosition.rightAscension, this.bodyPosition.declination, false) + }) } async mountSlew() { - const mount = await this.electron.selectedMount() - if (!mount?.connected) return - this.api.mountSlew(mount, this.bodyPosition.rightAscension, this.bodyPosition.declination, false) + this.executeMount((mount) => { + this.api.mountSlew(mount, this.bodyPosition.rightAscension, this.bodyPosition.declination, false) + }) } async mountSync() { - const mount = await this.electron.selectedMount() - if (!mount?.connected) return - this.api.mountSync(mount, this.bodyPosition.rightAscension, this.bodyPosition.declination, false) + this.executeMount((mount) => { + this.api.mountSync(mount, this.bodyPosition.rightAscension, this.bodyPosition.declination, false) + }) } frame() { - this.browserWindow.openFraming({ rightAscension: this.bodyPosition.rightAscensionJ2000, declination: this.bodyPosition.declinationJ2000 }) + this.browserWindow.openFraming({ data: { rightAscension: this.bodyPosition.rightAscensionJ2000, declination: this.bodyPosition.declinationJ2000 } }) } async refreshTab( refreshTwilight: boolean = false, refreshChart: boolean = false, + location?: Location, ) { + if (this.refreshing) return + + location ??= await this.api.selectedLocation() + this.refreshing = true + this.refreshTabCount++ if (!this.useManualDateTime) { this.dateTime = new Date() @@ -682,7 +755,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { this.dateTime.setMinutes(this.dateTimeMinute) } - this.app.subTitle = this.location.name + this.app.subTitle = `${location.name} · ${moment(this.dateTime).format('YYYY-MM-DD HH:mm')}` try { // Sun. @@ -690,14 +763,14 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { this.name = 'Sun' this.tags = [] this.imageOfSun.nativeElement.src = `${this.api.baseUrl}/sky-atlas/sun/image` - const bodyPosition = await this.api.positionOfSun(this.location!, this.dateTime) + const bodyPosition = await this.api.positionOfSun(location!, this.dateTime) Object.assign(this.bodyPosition, bodyPosition) } // Moon. else if (this.activeTab === 1) { this.name = 'Moon' this.tags = [] - const bodyPosition = await this.api.positionOfMoon(this.location!, this.dateTime) + const bodyPosition = await this.api.positionOfMoon(location!, this.dateTime) Object.assign(this.bodyPosition, bodyPosition) this.moonIlluminated = this.bodyPosition.illuminated / 100.0 this.moonWaning = this.bodyPosition.leading @@ -708,10 +781,10 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { if (this.planet) { this.name = this.planet.name - const bodyPosition = await this.api.positionOfPlanet(this.location!, this.planet.code, this.dateTime) + const bodyPosition = await this.api.positionOfPlanet(location!, this.planet.code, this.dateTime) Object.assign(this.bodyPosition, bodyPosition) } else { - this.name = '-' + this.name = undefined Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) } } @@ -726,10 +799,10 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { if (this.minorPlanet.pha) this.tags.push({ title: 'PHA', severity: 'danger' }) if (this.minorPlanet.neo) this.tags.push({ title: 'NEO', severity: 'warning' }) const code = `DES=${this.minorPlanet.spkId};` - const bodyPosition = await this.api.positionOfPlanet(this.location!, code, this.dateTime) + const bodyPosition = await this.api.positionOfPlanet(location!, code, this.dateTime) Object.assign(this.bodyPosition, bodyPosition) } else { - this.name = '-' + this.name = undefined Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) } } @@ -738,11 +811,11 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { this.tags = [] if (this.star) { - this.name = this.star.name.replaceAll('|', ' · ') - const bodyPosition = await this.api.positionOfStar(this.location!, this.star, this.dateTime) + this.name = this.skyObjectPipe.transform(this.star, 'name') + const bodyPosition = await this.api.positionOfStar(location!, this.star, this.dateTime) Object.assign(this.bodyPosition, bodyPosition) } else { - this.name = '-' + this.name = undefined Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) } } @@ -751,30 +824,43 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { this.tags = [] if (this.dso) { - this.name = this.dso.name.replaceAll('|', ' · ') - const bodyPosition = await this.api.positionOfDSO(this.location!, this.dso, this.dateTime) + this.name = this.skyObjectPipe.transform(this.dso, 'name') + const bodyPosition = await this.api.positionOfDSO(location!, this.dso, this.dateTime) Object.assign(this.bodyPosition, bodyPosition) } else { - this.name = '-' + this.name = undefined Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) } } - // Satellite. + // Simbad. else if (this.activeTab === 6) { this.tags = [] + if (this.simbad) { + this.name = this.skyObjectPipe.transform(this.simbad, 'name') + const bodyPosition = await this.api.positionOfSimbad(location!, this.simbad, this.dateTime) + Object.assign(this.bodyPosition, bodyPosition) + } else { + this.name = undefined + Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) + } + } + // Satellite. + else if (this.activeTab === 7) { + this.tags = [] + if (this.satellite) { this.name = this.satellite.name - const bodyPosition = await this.api.positionOfSatellite(this.location!, this.satellite, this.dateTime) + const bodyPosition = await this.api.positionOfSatellite(location!, this.satellite, this.dateTime) Object.assign(this.bodyPosition, bodyPosition) } else { - this.name = '-' + this.name = undefined Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) } } - if (refreshTwilight) { - const twilight = await this.api.twilight(this.location!, this.dateTime) + if (this.refreshTabCount === 1 || refreshTwilight) { + const twilight = await this.api.twilight(location!, this.dateTime) this.altitudeData.datasets[0].data = [[0.0, 90], [twilight.civilDusk[0], 90]] this.altitudeData.datasets[1].data = [[twilight.civilDusk[0], 90], [twilight.civilDusk[1], 90]] this.altitudeData.datasets[2].data = [[twilight.nauticalDusk[0], 90], [twilight.nauticalDusk[1], 90]] @@ -787,30 +873,30 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { this.chart?.refresh() } - if (refreshChart) { - await this.refreshChart() + if (this.refreshTabCount === 1 || refreshChart) { + await this.refreshChart(location) } } finally { this.refreshing = false } } - private async refreshChart() { + private async refreshChart(location: Location) { // Sun. if (this.activeTab === 0) { - const points = await this.api.altitudePointsOfSun(this.location!, this.dateTime) + const points = await this.api.altitudePointsOfSun(location!, this.dateTime) AtlasComponent.belowZeroPoints(points) this.altitudeData.datasets[9].data = points } // Moon. else if (this.activeTab === 1) { - const points = await this.api.altitudePointsOfMoon(this.location!, this.dateTime) + const points = await this.api.altitudePointsOfMoon(location!, this.dateTime) AtlasComponent.belowZeroPoints(points) this.altitudeData.datasets[9].data = points } // Planet. else if (this.activeTab === 2 && this.planet) { - const points = await this.api.altitudePointsOfPlanet(this.location!, this.planet.code, this.dateTime) + const points = await this.api.altitudePointsOfPlanet(location!, this.planet.code, this.dateTime) AtlasComponent.belowZeroPoints(points) this.altitudeData.datasets[9].data = points } @@ -818,7 +904,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { else if (this.activeTab === 3) { if (this.minorPlanet) { const code = `DES=${this.minorPlanet.spkId};` - const points = await this.api.altitudePointsOfPlanet(this.location!, code, this.dateTime) + const points = await this.api.altitudePointsOfPlanet(location!, code, this.dateTime) AtlasComponent.belowZeroPoints(points) this.altitudeData.datasets[9].data = points } else { @@ -828,7 +914,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { // Star. else if (this.activeTab === 4) { if (this.star) { - const points = await this.api.altitudePointsOfStar(this.location!, this.star, this.dateTime) + const points = await this.api.altitudePointsOfStar(location!, this.star, this.dateTime) AtlasComponent.belowZeroPoints(points) this.altitudeData.datasets[9].data = points } else { @@ -838,17 +924,27 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { // DSO. else if (this.activeTab === 5) { if (this.dso) { - const points = await this.api.altitudePointsOfDSO(this.location!, this.dso, this.dateTime) + const points = await this.api.altitudePointsOfDSO(location!, this.dso, this.dateTime) AtlasComponent.belowZeroPoints(points) this.altitudeData.datasets[9].data = points } else { this.altitudeData.datasets[9].data = [] } } - // Satellite. + // Simbad. else if (this.activeTab === 6) { + if (this.simbad) { + const points = await this.api.altitudePointsOfSimbad(location!, this.simbad, this.dateTime) + AtlasComponent.belowZeroPoints(points) + this.altitudeData.datasets[9].data = points + } else { + this.altitudeData.datasets[9].data = [] + } + } + // Satellite. + else if (this.activeTab === 7) { if (this.satellite) { - const points = await this.api.altitudePointsOfSatellite(this.location!, this.satellite, this.dateTime) + const points = await this.api.altitudePointsOfSatellite(location!, this.satellite, this.dateTime) AtlasComponent.belowZeroPoints(points) this.altitudeData.datasets[9].data = points } else { @@ -868,4 +964,26 @@ export class AtlasComponent implements OnInit, AfterContentInit, OnDestroy { } } } + + private async executeMount(action: (mount: Mount) => void) { + if (await this.prime.confirm('Are you sure that you want to proceed?')) { + return + } + + const mounts = await this.api.mounts() + + if (mounts.length === 1) { + action(mounts[0]) + return true + } else { + const mount = await this.deviceMenu.show(mounts) + + if (mount && mount.connected) { + action(mount) + return true + } + } + + return false + } } diff --git a/desktop/src/app/calibration/calibration.component.html b/desktop/src/app/calibration/calibration.component.html new file mode 100644 index 000000000..cffb48474 --- /dev/null +++ b/desktop/src/app/calibration/calibration.component.html @@ -0,0 +1,85 @@ +
+
+
+ + + + Type + Filter + Duration + Size + Bin + T. (°C) + Gain + + + + + {{ item.key.type }} + {{ item.key.filter ?? '' }} + {{ item.key.exposureTime | exposureTime }} + {{ item.key.width }}x{{ item.key.height }} + {{ item.key.binX }}x{{ item.key.binY }} + {{ item.key.temperature }} + {{ item.key.gain }} + + + +
+
+
+
+ + + + + + + Type + Filter + Duration + Size + Bin + T. (°C) + Gain + + + + + + + + {{ item.type }} + {{ item.filter ?? '' }} + {{ item.exposureTime | exposureTime }} + {{ item.width }}x{{ item.height }} + {{ item.binX }}x{{ item.binY }} + {{ item.temperature }} + {{ item.gain }} + + + +
+
+ + + + +
+
+ + +
+
+
+
+
\ No newline at end of file diff --git a/desktop/src/app/calibration/calibration.component.scss b/desktop/src/app/calibration/calibration.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/desktop/src/app/calibration/calibration.component.ts b/desktop/src/app/calibration/calibration.component.ts new file mode 100644 index 000000000..b1da8bbf2 --- /dev/null +++ b/desktop/src/app/calibration/calibration.component.ts @@ -0,0 +1,120 @@ +import { AfterViewInit, Component, HostListener, OnDestroy } from '@angular/core' +import { ActivatedRoute } from '@angular/router' +import { CheckboxChangeEvent } from 'primeng/checkbox' +import { ApiService } from '../../shared/services/api.service' +import { ElectronService } from '../../shared/services/electron.service' +import { CalibrationFrame, CalibrationFrameGroup, Camera } from '../../shared/types' +import { AppComponent } from '../app.component' + +@Component({ + selector: 'app-calibration', + templateUrl: './calibration.component.html', + styleUrls: ['./calibration.component.scss'], +}) +export class CalibrationComponent implements AfterViewInit, OnDestroy { + + camera!: Camera + + groups: CalibrationFrameGroup[] = [] + group?: CalibrationFrameGroup + frame?: CalibrationFrame + + get groupIsEnabled() { + return !!this.group && !this.group.frames.find(e => !e.enabled) + } + + constructor( + private app: AppComponent, + private api: ApiService, + electron: ElectronService, + private route: ActivatedRoute, + ) { + app.title = 'Calibration' + + app.extra.push({ + icon: 'mdi mdi-image-plus', + tooltip: 'Add file', + command: async () => { + const path = await electron.openFITS() + + if (path) { + this.upload(path) + } + }, + }) + + app.extra.push({ + icon: 'mdi mdi-folder-plus', + tooltip: 'Add folder', + command: async () => { + const path = await electron.openDirectory() + + if (path) { + this.upload(path) + } + }, + }) + } + + async ngAfterViewInit() { + this.route.queryParams.subscribe(async e => { + this.camera = JSON.parse(decodeURIComponent(e.data)) as Camera + this.app.subTitle = this.camera.name + this.load() + }) + } + + @HostListener('window:unload') + ngOnDestroy() { } + + private async upload(path: string) { + const frames = await this.api.uploadCalibrationFrame(this.camera!, path) + + if (frames.length > 0) { + this.load() + } + } + + private async load() { + this.groups = await this.api.calibrationFrames(this.camera) + } + + groupSelected() { + this.frame = undefined + } + + groupChecked(event: CheckboxChangeEvent) { + this.group?.frames?.forEach(e => e.enabled = event.checked) + } + + async frameChecked(frame: CalibrationFrame, event: CheckboxChangeEvent) { + await this.api.editCalibrationFrame(frame) + } + + replaceFrame(frame: CalibrationFrame) { + console.info(frame) + } + + async deleteFrame(frame: CalibrationFrame) { + await this.api.deleteCalibrationFrame(frame) + + if (this.frame === frame) { + this.frame = undefined + } + + let index = this.group?.frames?.findIndex(e => e.id === frame.id) ?? -1 + + if (index >= 0) { + this.group!.frames.splice(index, 1) + + if (!this.group!.frames.length) { + index = this.groups.indexOf(this.group!) + + if (index >= 0) { + this.groups.splice(index, 1) + this.group = undefined + } + } + } + } +} \ No newline at end of file diff --git a/desktop/src/app/camera/camera.component.html b/desktop/src/app/camera/camera.component.html index 1fd6f26a7..eedad2166 100644 --- a/desktop/src/app/camera/camera.component.html +++ b/desktop/src/app/camera/camera.component.html @@ -1,9 +1,8 @@
-
+
- +
@@ -13,9 +12,13 @@
-
- +
+ + +
@@ -25,49 +28,54 @@ tooltipPosition="bottom"> - {{ savePath || capturesPath }} - +
{{ waiting ? 'waiting' : capturing ? 'capturing' : 'idle' }} - - + + - {{ event.exposureCount }} of {{ event.exposureAmount }} + {{ exposure.count }} of {{ capture.amount }} - + - {{ event.exposureCount }} + {{ exposure.count }} - {{ event.exposureRemainingTime | exposureTime }} + {{ exposure.remainingTime | exposureTime }} - {{ event.exposureProgress | percent:'1.1-1' }} + {{ exposure.progress | percent:'1.1-1' }} - + + + - {{ event.captureRemainingTime | exposureTime }} + {{ capture.remainingTime | exposureTime }} - {{ event.captureProgress | percent:'1.1-1' }} + {{ capture.progress | percent:'1.1-1' }} - + - {{ event.captureElapsedTime | exposureTime }} + {{ capture.elapsedTime | exposureTime }} - + - {{ event.waitRemainingTime | exposureTime }} + {{ wait.remainingTime | exposureTime }} + + + {{ wait.progress | percent:'1.1-1' }}
@@ -87,11 +95,11 @@ + [allowEmpty]="false" [minFractionDigits]="1" /> + styleClass="p-button-sm" severity="success" pTooltip="Apply" tooltipPosition="bottom" />
@@ -104,7 +112,7 @@ [allowEmpty]="false" (ngModelChange)="savePreference()" /> - +
@@ -233,11 +241,41 @@
+ severity="success" />
- \ No newline at end of file + + + +
+
+ Enabled + +
+
+ + + + +
+
+ Dither in RA only + +
+
+ + + + +
+
+
\ No newline at end of file diff --git a/desktop/src/app/camera/camera.component.ts b/desktop/src/app/camera/camera.component.ts index d3711af0a..1632366d9 100644 --- a/desktop/src/app/camera/camera.component.ts +++ b/desktop/src/app/camera/camera.component.ts @@ -1,15 +1,37 @@ import { AfterContentInit, Component, HostListener, NgZone, OnDestroy } from '@angular/core' -import { MegaMenuItem, MenuItem } from 'primeng/api' +import { ActivatedRoute } from '@angular/router' +import { MenuItem } from 'primeng/api' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' -import { - AutoSubFolderMode, Camera, CameraCaptureEvent, - CameraStartCapture, ExposureMode, ExposureTimeUnit, FilterWheel, FrameType -} from '../../shared/types' +import { LocalStorageService } from '../../shared/services/local-storage.service' +import { AutoSubFolderMode, Camera, CameraStartCapture, Dither, ExposureMode, ExposureTimeUnit, FilterWheel, FrameType } from '../../shared/types' import { AppComponent } from '../app.component' +export interface CameraPreference { + autoSave?: boolean + savePath?: string + autoSubFolderMode?: AutoSubFolderMode + setpointTemperature?: number + exposureTime?: number + exposureTimeUnit?: ExposureTimeUnit + exposureMode?: ExposureMode + exposureDelay?: number + exposureCount?: number + x?: number + y?: number + width?: number + height?: number + subFrame?: boolean + binX?: number + binY?: number + frameType?: FrameType + gain?: number + offset?: number + frameFormat?: string + dithering?: Dither +} + @Component({ selector: 'app-camera', templateUrl: './camera.component.html', @@ -17,7 +39,6 @@ import { AppComponent } from '../app.component' }) export class CameraComponent implements AfterContentInit, OnDestroy { - cameras: Camera[] = [] camera?: Camera connected = false @@ -28,23 +49,29 @@ export class CameraComponent implements AfterContentInit, OnDestroy { wheel?: FilterWheel - readonly cameraMenuItems: MenuItem[] = [ + showDitheringDialog = false + readonly dithering: Dither = { + enabled: false, + afterExposures: 1, + amount: 1.5, + raOnly: false, + } + + readonly cameraModel: MenuItem[] = [ { icon: 'mdi mdi-content-save', label: 'Auto save all exposures', - command: (e) => { + command: () => { this.autoSave = !this.autoSave this.savePreference() - - this.checkMenuItem(e.item, this.autoSave) }, }, { icon: 'mdi mdi-folder', label: 'Save path...', - command: () => { + command: async () => { const defaultPath = this.savePath || this.capturesPath - const path = this.electron.openDirectory({ defaultPath }) + const path = await this.electron.openDirectory({ defaultPath }) if (path) { this.savePath = path @@ -59,35 +86,39 @@ export class CameraComponent implements AfterContentInit, OnDestroy { { icon: 'mdi mdi-folder-off', label: 'None', - command: (e) => { + command: () => { this.autoSubFolderMode = 'OFF' this.savePreference() - - this.checkMenu(this.cameraMenuItems[2].items!, e.item) }, }, { icon: 'mdi mdi-weather-sunny', label: 'Noon', - command: (e) => { + command: () => { this.autoSubFolderMode = 'NOON' this.savePreference() - - this.checkMenu(this.cameraMenuItems[2].items!, e.item) }, }, { icon: 'mdi mdi-weather-night', label: 'Midnight', - command: (e) => { + command: () => { this.autoSubFolderMode = 'MIDNIGHT' this.savePreference() - - this.checkMenu(this.cameraMenuItems[2].items!, e.item) }, }, ], }, + { + separator: true, + }, + { + icon: 'icomoon random-dither', + label: 'Dithering', + command: () => { + this.showDitheringDialog = true + }, + }, ] cooler = false @@ -129,14 +160,34 @@ export class CameraComponent implements AfterContentInit, OnDestroy { offset = 0 offsetMin = 0 offsetMax = 0 - event?: CameraCaptureEvent + capturing = false waiting = false + readonly exposure = { + count: 0, + remainingTime: 0, + progress: 0, + } + + readonly capture = { + looping: false, + amount: 0, + remainingTime: 0, + elapsedTime: 0, + progress: 0, + } + + readonly wait = { + duration: 0, + remainingTime: 0, + progress: 0, + } + readonly exposureModeOptions: ExposureMode[] = ['SINGLE', 'FIXED', 'LOOP'] readonly frameTypeOptions: FrameType[] = ['LIGHT', 'DARK', 'FLAT', 'BIAS'] - readonly exposureTimeUnitOptions: MenuItem[] = [ + readonly exposureTimeUnitModel: MenuItem[] = [ { label: 'Minute (m)', command: () => { @@ -172,42 +223,60 @@ export class CameraComponent implements AfterContentInit, OnDestroy { private api: ApiService, private browserWindow: BrowserWindowService, private electron: ElectronService, - private preference: PreferenceService, + private storage: LocalStorageService, + private route: ActivatedRoute, ngZone: NgZone, ) { app.title = 'Camera' api.startListening('CAMERA') - electron.on('CAMERA_UPDATED', (_, event: Camera) => { - if (event.name === this.camera?.name) { + electron.on('CAMERA_UPDATED', event => { + if (event.device.name === this.camera?.name) { ngZone.run(() => { - Object.assign(this.camera!, event) + Object.assign(this.camera!, event.device) this.update() }) } }) - electron.on('CAMERA_EXPOSURE_STARTED', (_, event: CameraCaptureEvent) => { + electron.on('CAMERA_CAPTURE_STARTED', event => { if (event.camera.name === this.camera?.name) { ngZone.run(() => { - this.event = event + this.capture.looping = event.looping + this.capture.amount = event.exposureAmount + this.capture.elapsedTime = 0 + this.capture.remainingTime = event.estimatedTime + this.capture.progress = event.progress this.capturing = true this.waiting = false }) } }) - electron.on('CAMERA_EXPOSURE_UPDATED', (_, event: CameraCaptureEvent) => { + electron.on('CAMERA_CAPTURE_ELAPSED', event => { + if (event.camera.name === this.camera?.name) { + ngZone.run(() => { + this.capture.elapsedTime = event.elapsedTime + this.capture.remainingTime = event.remainingTime + this.capture.progress = event.progress + }) + } + }) + + electron.on('CAMERA_CAPTURE_WAITING', event => { if (event.camera.name === this.camera?.name) { ngZone.run(() => { - this.event = event - this.waiting = event.captureIsWaiting + this.wait.duration = event.waitDuration + this.wait.remainingTime = event.remainingTime + this.wait.progress = event.progress + this.capturing = false + this.waiting = true }) } }) - electron.on('CAMERA_CAPTURE_FINISHED', (_, event: CameraCaptureEvent) => { + electron.on('CAMERA_CAPTURE_FINISHED', event => { if (event.camera.name === this.camera?.name) { ngZone.run(() => { this.capturing = false @@ -216,15 +285,43 @@ export class CameraComponent implements AfterContentInit, OnDestroy { } }) - electron.on('WHEEL_CHANGED', (_, event?: FilterWheel) => { - ngZone.run(() => { - this.wheel = event - }) + electron.on('CAMERA_EXPOSURE_STARTED', event => { + if (event.camera.name === this.camera?.name) { + ngZone.run(() => { + this.exposure.remainingTime = event.remainingTime + this.exposure.progress = event.progress + this.exposure.count = event.exposureCount + this.capturing = true + this.waiting = false + }) + } + }) + + electron.on('CAMERA_EXPOSURE_ELAPSED', event => { + if (event.camera.name === this.camera?.name) { + ngZone.run(() => { + this.exposure.remainingTime = event.remainingTime + this.exposure.progress = event.progress + this.exposure.count = event.exposureCount + }) + } + }) + + electron.on('CAMERA_EXPOSURE_FINISHED', event => { + if (event.camera.name === this.camera?.name) { + ngZone.run(() => { + this.exposure.remainingTime = event.remainingTime + this.exposure.progress = event.progress + }) + } }) } async ngAfterContentInit() { - this.cameras = await this.api.cameras() + this.route.queryParams.subscribe(e => { + const camera = JSON.parse(decodeURIComponent(e.data)) as Camera + this.cameraChanged(camera) + }) } @HostListener('window:unload') @@ -233,21 +330,20 @@ export class CameraComponent implements AfterContentInit, OnDestroy { this.abortCapture() } - async cameraChanged() { + async cameraChanged(camera?: Camera) { + this.camera = camera + if (this.camera) { this.app.subTitle = this.camera.name const camera = await this.api.camera(this.camera.name) Object.assign(this.camera, camera) - this.loadPreference() + await this.loadPreference() this.update() - this.savePreference() } else { this.app.subTitle = '' } - - this.electron.send('CAMERA_CHANGED', this.camera) } connect() { @@ -277,18 +373,26 @@ export class CameraComponent implements AfterContentInit, OnDestroy { } } + openCameraImage() { + return this.browserWindow.openCameraImage(this.camera!) + } + + openCameraCalibration() { + return this.browserWindow.openCalibration({ data: this.camera! }) + } + async startCapture() { const x = this.subFrame ? this.x : this.camera!.minX const y = this.subFrame ? this.y : this.camera!.minY const width = this.subFrame ? this.width : this.camera!.maxWidth const height = this.subFrame ? this.height : this.camera!.maxHeight const exposureFactor = CameraComponent.exposureUnitFactor(this.exposureTimeUnit) - const exposureInMicroseconds = Math.trunc(this.exposureTime * 60000000 / exposureFactor) + const exposureTime = Math.trunc(this.exposureTime * 60000000 / exposureFactor) const exposureAmount = this.exposureMode === 'LOOP' ? 0 : (this.exposureMode === 'FIXED' ? this.exposureCount : 1) const data: CameraStartCapture = { - exposureInMicroseconds, exposureAmount, - exposureDelayInSeconds: this.exposureDelay, + exposureTime, exposureAmount, + exposureDelay: this.exposureDelay * 1000000, x, y, width, height, frameFormat: this.frameFormat, frameType: this.frameType, @@ -299,9 +403,10 @@ export class CameraComponent implements AfterContentInit, OnDestroy { autoSave: this.autoSave, savePath: this.savePath, autoSubFolderMode: this.autoSubFolderMode, + dither: this.dithering, } - await this.browserWindow.openCameraImage(this.camera!) + await this.openCameraImage() this.api.cameraStartCapture(this.camera!, data) } @@ -360,79 +465,77 @@ export class CameraComponent implements AfterContentInit, OnDestroy { this.gainMax = this.camera.gainMax this.offsetMin = this.camera.offsetMin this.offsetMax = this.camera.offsetMax - this.capturesPath = this.camera.capturesPath this.updateExposureUnit(this.exposureTimeUnit) } + + this.capturesPath = this.camera.capturesPath } } - resetSavePath() { + clearSavePath() { this.savePath = '' - this.preference.set(`camera.${this.camera!.name}.savePath`, this.savePath) + this.savePreference() } private loadPreference() { if (this.camera) { - this.autoSave = this.preference.get(`camera.${this.camera.name}.autoSave`, false) - this.savePath = this.preference.get(`camera.${this.camera.name}.savePath`, '') - this.autoSubFolderMode = this.preference.get(`camera.${this.camera.name}.autoSubFolderMode`, 'OFF') - - this.checkMenuItem(this.cameraMenuItems[0], this.autoSave) - const menuIndex = this.autoSubFolderMode === 'OFF' ? 0 : (this.autoSubFolderMode === 'NOON' ? 1 : 2) - this.checkMenu(this.cameraMenuItems[2].items!, this.cameraMenuItems[2].items![menuIndex], true) - - this.setpointTemperature = this.preference.get(`camera.${this.camera.name}.setpointTemperature`, 0) - this.exposureTime = this.preference.get(`camera.${this.camera.name}.exposureTime`, this.camera.exposureMin) - this.exposureTimeUnit = this.preference.get(`camera.${this.camera.name}.exposureTimeUnit`, ExposureTimeUnit.MICROSECOND) - this.exposureMode = this.preference.get(`camera.${this.camera.name}.exposureMode`, 'SINGLE') - this.exposureDelay = this.preference.get(`camera.${this.camera.name}.exposureDelay`, 0) - this.exposureCount = this.preference.get(`camera.${this.camera.name}.exposureCount`, 1) - this.x = this.preference.get(`camera.${this.camera.name}.x`, this.camera.minX) - this.y = this.preference.get(`camera.${this.camera.name}.y`, this.camera.minY) - this.width = this.preference.get(`camera.${this.camera.name}.width`, this.camera.maxWidth) - this.height = this.preference.get(`camera.${this.camera.name}.height`, this.camera.maxHeight) - this.subFrame = this.preference.get(`camera.${this.camera.name}.subFrame`, false) - this.binX = this.preference.get(`camera.${this.camera.name}.binX`, 1) - this.binY = this.preference.get(`camera.${this.camera.name}.binY`, 1) - this.frameType = this.preference.get(`camera.${this.camera.name}.frameType`, 'LIGHT') - this.gain = this.preference.get(`camera.${this.camera.name}.gain`, 0) - this.offset = this.preference.get(`camera.${this.camera.name}.offset`, 0) - this.frameFormat = this.preference.get(`camera.${this.camera.name}.frameFormat`, this.camera.frameFormats[0] || '') + const preference = this.storage.get(`camera.${this.camera.name}`, {}) + this.autoSave = preference.autoSave ?? false + this.savePath = preference.savePath ?? '' + this.autoSubFolderMode = preference.autoSubFolderMode ?? 'OFF' + this.setpointTemperature = preference.setpointTemperature ?? 0 + this.exposureTime = preference.exposureTime ?? this.camera.exposureMin + this.exposureTimeUnit = preference.exposureTimeUnit ?? ExposureTimeUnit.MICROSECOND + this.exposureMode = preference.exposureMode ?? 'SINGLE' + this.exposureDelay = preference.exposureDelay ?? 0 + this.exposureCount = preference.exposureCount ?? 1 + this.x = preference.x ?? this.camera.minX + this.y = preference.y ?? this.camera.minY + this.width = preference.width ?? this.camera.maxWidth + this.height = preference.height ?? this.camera.maxHeight + this.subFrame = preference.subFrame ?? false + this.binX = preference.binX ?? 1 + this.binY = preference.binY ?? 1 + this.frameType = preference.frameType ?? 'LIGHT' + this.gain = preference.gain ?? 0 + this.offset = preference.offset ?? 0 + this.frameFormat = preference.frameFormat ?? (this.camera.frameFormats[0] || '') + + this.dithering.enabled = preference.dithering?.enabled ?? false + this.dithering.raOnly = preference.dithering?.raOnly ?? false + this.dithering.amount = preference.dithering?.amount ?? 1.5 + this.dithering.afterExposures = preference.dithering?.afterExposures ?? 1 } } savePreference() { if (this.camera && this.camera.connected) { - this.preference.set(`camera.${this.camera.name}.autoSave`, this.autoSave) - this.preference.set(`camera.${this.camera.name}.savePath`, this.savePath) - this.preference.set(`camera.${this.camera.name}.autoSubFolderMode`, this.autoSubFolderMode) - this.preference.set(`camera.${this.camera.name}.setpointTemperature`, this.setpointTemperature) - this.preference.set(`camera.${this.camera.name}.exposureTime`, this.exposureTime) - this.preference.set(`camera.${this.camera.name}.exposureTimeUnit`, this.exposureTimeUnit) - this.preference.set(`camera.${this.camera.name}.exposureMode`, this.exposureMode) - this.preference.set(`camera.${this.camera.name}.exposureDelay`, this.exposureDelay) - this.preference.set(`camera.${this.camera.name}.exposureCount`, this.exposureCount) - this.preference.set(`camera.${this.camera.name}.x`, this.x) - this.preference.set(`camera.${this.camera.name}.y`, this.y) - this.preference.set(`camera.${this.camera.name}.width`, this.width) - this.preference.set(`camera.${this.camera.name}.height`, this.height) - this.preference.set(`camera.${this.camera.name}.subFrame`, this.subFrame) - this.preference.set(`camera.${this.camera.name}.binX`, this.binX) - this.preference.set(`camera.${this.camera.name}.binY`, this.binY) - this.preference.set(`camera.${this.camera.name}.frameType`, this.frameType) - this.preference.set(`camera.${this.camera.name}.gain`, this.gain) - this.preference.set(`camera.${this.camera.name}.offset`, this.offset) - this.preference.set(`camera.${this.camera.name}.frameFormat`, this.frameFormat) - } - } - - private checkMenuItem(item?: MenuItem | MegaMenuItem, checked: boolean = true) { - item && (item.styleClass = checked ? 'p-menuitem-checked' : '') - } + const preference: CameraPreference = { + autoSave: this.autoSave, + savePath: this.savePath, + autoSubFolderMode: this.autoSubFolderMode, + setpointTemperature: this.setpointTemperature, + exposureTime: this.exposureTime, + exposureTimeUnit: this.exposureTimeUnit, + exposureMode: this.exposureMode, + exposureDelay: this.exposureDelay, + exposureCount: this.exposureCount, + x: this.x, + y: this.y, + width: this.width, + height: this.height, + subFrame: this.subFrame, + binX: this.binX, + binY: this.binY, + frameType: this.frameType, + gain: this.gain, + offset: this.offset, + frameFormat: this.frameFormat, + dithering: this.dithering, + } - private checkMenu(menu: MenuItem[], item?: MenuItem | MegaMenuItem, checked: boolean = true) { - menu.forEach((e) => e !== item && this.checkMenuItem(e, false)) - this.checkMenuItem(item, checked) + this.storage.set(`camera.${this.camera.name}`, preference) + } } } diff --git a/desktop/src/app/filterwheel/filterwheel.component.html b/desktop/src/app/filterwheel/filterwheel.component.html index 9c02bd622..e7ac0fe78 100644 --- a/desktop/src/app/filterwheel/filterwheel.component.html +++ b/desktop/src/app/filterwheel/filterwheel.component.html @@ -1,10 +1,8 @@
-
+
- +
@@ -16,51 +14,62 @@
- {{ moving ? 'moving' : 'idle' }} + {{ moving ? 'moving' : 'idle' }} +
-
-
- - - - -
-
+
+
- + -
- {{ item.name }} - +
+ + + + + + {{ filter?.name }}
-
- - - {{ item.name }} - - - - - - - - - +
+ + + + + {{ item.name }}
+
+ +
+
+
+
+ + + + +
+
+ + + + +
+
+ Shutter + +
\ No newline at end of file diff --git a/desktop/src/app/filterwheel/filterwheel.component.scss b/desktop/src/app/filterwheel/filterwheel.component.scss index e69de29bb..fa0ed14c8 100644 --- a/desktop/src/app/filterwheel/filterwheel.component.scss +++ b/desktop/src/app/filterwheel/filterwheel.component.scss @@ -0,0 +1,10 @@ +.filter { + &.current { + border-left: 5px solid #00ffe0; + background-color: #38363f; + } + + &:not(.current):hover { + background-color: #1a1a1a; + } +} \ No newline at end of file diff --git a/desktop/src/app/filterwheel/filterwheel.component.ts b/desktop/src/app/filterwheel/filterwheel.component.ts index 37139cc0e..1ec686822 100644 --- a/desktop/src/app/filterwheel/filterwheel.component.ts +++ b/desktop/src/app/filterwheel/filterwheel.component.ts @@ -1,17 +1,25 @@ import { AfterContentInit, Component, HostListener, NgZone, OnDestroy } from '@angular/core' -import { CheckboxChangeEvent } from 'primeng/checkbox' +import { ActivatedRoute } from '@angular/router' +import { InputSwitchChangeEvent } from 'primeng/inputswitch' +import { Subject, Subscription, throttleTime } from 'rxjs' import { ApiService } from '../../shared/services/api.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' +import { LocalStorageService } from '../../shared/services/local-storage.service' +import { RemoteStorageService } from '../../shared/services/preference.service' import { FilterWheel } from '../../shared/types' import { AppComponent } from '../app.component' -export interface FilterSlot { +export interface WheelPreference { + shutterPosition?: number + names?: string[] + offsets?: number[] +} + +export interface Filter { position: number name: string - editing: boolean - newName: string dark: boolean + offset: number } @Component({ @@ -21,50 +29,64 @@ export interface FilterSlot { }) export class FilterWheelComponent implements AfterContentInit, OnDestroy { - wheels: FilterWheel[] = [] wheel?: FilterWheel connected = false moving = false position = 0 + filters: Filter[] = [] + filter?: Filter - filters: FilterSlot[] = [] - - get selectedFilter() { + get selectedFilter(): Filter | undefined { return this.filters[this.position - 1] } - set selectedFilter(value: FilterSlot) { - this.moveTo(value) - } + private readonly filterChangedPublisher = new Subject() + private subscription?: Subscription constructor( private app: AppComponent, private api: ApiService, private electron: ElectronService, - private preference: PreferenceService, + private storage: LocalStorageService, + private remoteStorage: RemoteStorageService, + private route: ActivatedRoute, ngZone: NgZone, ) { app.title = 'Filter Wheel' - electron.on('WHEEL_UPDATED', (_, event: FilterWheel) => { - if (event.name === this.wheel?.name) { + electron.on('WHEEL_UPDATED', event => { + if (event.device.name === this.wheel?.name) { ngZone.run(() => { - Object.assign(this.wheel!, event) + Object.assign(this.wheel!, event.device) this.update() }) } }) + + this.subscription = this.filterChangedPublisher + .pipe(throttleTime(1500)) + .subscribe((filter) => { + this.savePreference() + this.electron.send('WHEEL_RENAMED', { wheel: this.wheel!, filter }) + }) } async ngAfterContentInit() { - this.wheels = await this.api.wheels() + this.route.queryParams.subscribe(e => { + const wheel = JSON.parse(decodeURIComponent(e.data)) as FilterWheel + this.wheelChanged(wheel) + }) } @HostListener('window:unload') - ngOnDestroy() { } + ngOnDestroy() { + this.subscription?.unsubscribe() + } + + async wheelChanged(wheel?: FilterWheel) { + this.wheel = wheel - async wheelChanged() { if (this.wheel) { this.app.subTitle = this.wheel.name @@ -73,12 +95,9 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy { this.loadPreference() this.update() - this.savePreference() } else { this.app.subTitle = '' } - - this.electron.send('WHEEL_CHANGED', this.wheel) } connect() { @@ -89,30 +108,23 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy { } } - showFilterEdit(filter: FilterSlot, event: Event) { - filter.editing = true - event.stopImmediatePropagation() + moveTo(filter: Filter) { + this.api.wheelMoveTo(this.wheel!, filter.position) } - shutterToggled(filter: FilterSlot, event: CheckboxChangeEvent) { - this.filters.forEach(e => e.dark = e === filter ? e.dark : false) - this.savePreference() - event.originalEvent?.stopImmediatePropagation() + shutterToggled(filter: Filter, event: InputSwitchChangeEvent) { + this.filters.forEach(e => e.dark = event.checked && e === filter) + this.filterChangedPublisher.next(filter) } - moveTo(filter: FilterSlot) { - this.api.wheelMoveTo(this.wheel!, filter.position) + filterNameChanged(filter: Filter) { + if (filter.name) { + this.filterChangedPublisher.next(filter) + } } - applyFilterName(filter: FilterSlot, event: Event) { - filter.name = filter.newName - - this.preference.set(`wheel.${this.wheel!.name}.filterName.${filter.position}`, filter.name) - this.api.wheelSync(this.wheel!, this.filters.map(e => e.name)) - this.electron.send('WHEEL_RENAMED', this.wheel) - - filter.editing = false - event.stopImmediatePropagation() + focusOffsetChanged(filter: Filter) { + this.filterChangedPublisher.next(filter) } private async update() { @@ -124,32 +136,51 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy { this.moving = this.wheel.moving this.position = this.wheel.position + let filters: Filter[] = [] + if (this.wheel.count <= 0) { this.filters = [] + return } else if (this.wheel.count !== this.filters.length) { - this.filters = new Array(this.wheel.count) + filters = new Array(this.wheel.count) + } else { + filters = this.filters } - const darkFilter = this.preference.get(`wheel.${this.wheel.name}.shutterPosition`, 0) + const preference = this.storage.get(`wheel.${this.wheel.name}`, {}) - for (let i = 1; i <= this.filters.length; i++) { - const name = this.preference.get(`wheel.${this.wheel.name}.filterName.${i}`, `Filter #${i}`) - const filter = { position: i, name, editing: false, newName: name, dark: i === darkFilter } - this.filters[i - 1] = filter + for (let position = 1; position <= filters.length; position++) { + const name = preference.names?.[position - 1] ?? `Filter #${position}` + const offset = preference.offsets?.[position - 1] ?? 0 + const dark = position === preference.shutterPosition + const filter = { position, name, dark, offset } + filters[position - 1] = filter } + + this.filters = filters + this.filter = filters[this.position - 1] ?? filters[0] } private loadPreference() { if (this.wheel) { - const darkFilter = this.preference.get(`wheel.${this.wheel.name}.shutterPosition`, 0) - this.filters.forEach(e => e.dark = e.position === darkFilter) + const preference = this.storage.get(`wheel.${this.wheel.name}`, {}) + const shutterPosition = preference.shutterPosition ?? 0 + this.filters.forEach(e => e.dark = e.position === shutterPosition) } } private savePreference() { if (this.wheel && this.wheel.connected) { - const darkFilter = this.filters.find(e => e.dark) - this.preference.set(`wheel.${this.wheel.name}.shutterPosition`, darkFilter?.position || 0) + const dark = this.filters.find(e => e.dark) + + const preference: WheelPreference = { + shutterPosition: dark?.position ?? 0, + names: this.filters.map(e => e.name) + } + + this.storage.set(`wheel.${this.wheel.name}`, preference) + this.remoteStorage.set(`wheel.${this.wheel.name}.shutterPosition`, preference.shutterPosition) + this.api.wheelSync(this.wheel, preference.names!) } } } \ No newline at end of file diff --git a/desktop/src/app/focuser/focuser.component.html b/desktop/src/app/focuser/focuser.component.html index c42d97b56..f3bc285cc 100644 --- a/desktop/src/app/focuser/focuser.component.html +++ b/desktop/src/app/focuser/focuser.component.html @@ -1,9 +1,8 @@
-
+
- +
diff --git a/desktop/src/app/focuser/focuser.component.ts b/desktop/src/app/focuser/focuser.component.ts index dc52ed55d..666b8fd6a 100644 --- a/desktop/src/app/focuser/focuser.component.ts +++ b/desktop/src/app/focuser/focuser.component.ts @@ -1,10 +1,16 @@ import { AfterViewInit, Component, HostListener, NgZone, OnDestroy } from '@angular/core' +import { ActivatedRoute } from '@angular/router' import { ApiService } from '../../shared/services/api.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' +import { LocalStorageService } from '../../shared/services/local-storage.service' import { Focuser } from '../../shared/types' import { AppComponent } from '../app.component' +export interface FocuserPreference { + stepsRelative?: number + stepsAbsolute?: number +} + @Component({ selector: 'app-focuser', templateUrl: './focuser.component.html', @@ -12,7 +18,6 @@ import { AppComponent } from '../app.component' }) export class FocuserComponent implements AfterViewInit, OnDestroy { - focusers: Focuser[] = [] focuser?: Focuser connected = false @@ -36,15 +41,16 @@ export class FocuserComponent implements AfterViewInit, OnDestroy { private app: AppComponent, private api: ApiService, private electron: ElectronService, - private preference: PreferenceService, + private storage: LocalStorageService, + private route: ActivatedRoute, ngZone: NgZone, ) { app.title = 'Focuser' - electron.on('FOCUSER_UPDATED', (_, event: Focuser) => { - if (event.name === this.focuser?.name) { + electron.on('FOCUSER_UPDATED', event => { + if (event.device.name === this.focuser?.name) { ngZone.run(() => { - Object.assign(this.focuser!, event) + Object.assign(this.focuser!, event.device) this.update() }) } @@ -52,13 +58,20 @@ export class FocuserComponent implements AfterViewInit, OnDestroy { } async ngAfterViewInit() { - this.focusers = await this.api.focusers() + this.route.queryParams.subscribe(e => { + const focuser = JSON.parse(decodeURIComponent(e.data)) as Focuser + this.focuserChanged(focuser) + }) } @HostListener('window:unload') - ngOnDestroy() { } + ngOnDestroy() { + this.abort() + } + + async focuserChanged(focuser?: Focuser) { + this.focuser = focuser - async focuserChanged() { if (this.focuser) { this.app.subTitle = this.focuser.name @@ -67,12 +80,9 @@ export class FocuserComponent implements AfterViewInit, OnDestroy { this.loadPreference() this.update() - this.savePreference() } else { this.app.subTitle = '' } - - this.electron.send('FOCUSER_CHANGED', this.focuser) } connect() { @@ -132,15 +142,20 @@ export class FocuserComponent implements AfterViewInit, OnDestroy { private loadPreference() { if (this.focuser) { - this.stepsRelative = this.preference.get(`focuser.${this.focuser.name}.stepsRelative`, 0) - this.stepsAbsolute = this.preference.get(`focuser.${this.focuser.name}.stepsAbsolute`, 0) + const preference = this.storage.get(`focuser.${this.focuser.name}`, {}) + this.stepsRelative = preference.stepsRelative ?? 100 + this.stepsAbsolute = preference.stepsAbsolute ?? this.focuser.position } } private savePreference() { if (this.focuser && this.focuser.connected) { - this.preference.set(`focuser.${this.focuser.name}.stepsRelative`, this.stepsRelative) - this.preference.set(`focuser.${this.focuser.name}.stepsAbsolute`, this.stepsAbsolute) + const preference: FocuserPreference = { + stepsRelative: this.stepsRelative, + stepsAbsolute: this.stepsAbsolute, + } + + this.storage.set(`focuser.${this.focuser.name}`, preference) } } } \ No newline at end of file diff --git a/desktop/src/app/framing/framing.component.html b/desktop/src/app/framing/framing.component.html index 5f5242f5e..c3dc665b3 100644 --- a/desktop/src/app/framing/framing.component.html +++ b/desktop/src/app/framing/framing.component.html @@ -1,4 +1,4 @@ -
+
@@ -27,15 +27,15 @@
- +
+ [(ngModel)]="rotation" locale="en" [minFractionDigits]="1" />
diff --git a/desktop/src/app/framing/framing.component.ts b/desktop/src/app/framing/framing.component.ts index 5b03e6b2a..329230bef 100644 --- a/desktop/src/app/framing/framing.component.ts +++ b/desktop/src/app/framing/framing.component.ts @@ -5,11 +5,21 @@ import hipsSurveys from '../../assets/data/hipsSurveys.json' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' +import { LocalStorageService } from '../../shared/services/local-storage.service' import { Angle, HipsSurvey } from '../../shared/types' import { AppComponent } from '../app.component' -export interface FramingParams { +export interface FramingPreference { + rightAscension?: Angle + declination?: Angle + width?: number + height?: number + fov?: number + rotation?: number + hipsSurvey?: HipsSurvey +} + +export interface FramingData { rightAscension: Angle declination: Angle width?: number @@ -45,41 +55,41 @@ export class FramingComponent implements AfterViewInit, OnDestroy { private api: ApiService, private browserWindow: BrowserWindowService, private electron: ElectronService, - private preference: PreferenceService, + private storage: LocalStorageService, private message: MessageService, ngZone: NgZone, ) { app.title = 'Framing' - this.loadPreference() - - electron.on('PARAMS_CHANGED', (_, event: FramingParams) => { - ngZone.run(() => this.frameFromParams(event)) + electron.on('DATA_CHANGED', (event: FramingData) => { + ngZone.run(() => this.frameFromData(event)) }) } ngAfterViewInit() { + this.loadPreference() + this.route.queryParams.subscribe(e => { - const params = JSON.parse(decodeURIComponent(e.params)) as FramingParams - this.frameFromParams(params) + const data = JSON.parse(decodeURIComponent(e.data)) as FramingData + this.frameFromData(data) }) } @HostListener('window:unload') ngOnDestroy() { this.closeFrameImage() - this.electron.sendSync('CLOSE_WINDOW', this.frameId) + this.electron.send('CLOSE_WINDOW', this.frameId) } - private frameFromParams(params: FramingParams) { - this.rightAscension = params.rightAscension ?? this.rightAscension - this.declination = params.declination ?? this.declination - this.width = params.width ?? this.width - this.height = params.height ?? this.height - this.fov = params.fov ?? this.fov - if (params.rotation === 0 || params.rotation) this.rotation = params.rotation + private frameFromData(data: FramingData) { + this.rightAscension = data.rightAscension ?? this.rightAscension + this.declination = data.declination ?? this.declination + this.width = data.width ?? this.width + this.height = data.height ?? this.height + this.fov = data.fov ?? this.fov + if (data.rotation === 0 || data.rotation) this.rotation = data.rotation - if (params.rightAscension && params.declination) { + if (data.rightAscension && data.declination) { this.frame() } } @@ -96,7 +106,7 @@ export class FramingComponent implements AfterViewInit, OnDestroy { const title = `Framing ・ ${this.rightAscension} ・ ${this.declination}` this.framePath = path - this.frameId = await this.browserWindow.openImage(path, 'framing', 'FRAMING', title) + this.frameId = await this.browserWindow.openImage({ id: 'framing', source: 'FRAMING', path, title }) this.savePreference() } catch (e: any) { @@ -109,23 +119,29 @@ export class FramingComponent implements AfterViewInit, OnDestroy { } private loadPreference() { - this.rightAscension = this.preference.get('framing.rightAscension', '00h00m00s') - this.declination = this.preference.get('framing.declination', `+000°00'00"`) - this.width = this.preference.get('framing.width', 1280) - this.height = this.preference.get('framing.height', 720) - this.fov = this.preference.get('framing.fov', 1) - this.rotation = this.preference.get('framing.rotation', 0) - this.hipsSurvey = this.preference.get('framing.hipsSurvey', this.hipsSurvey) + const preference = this.storage.get('framing', {}) + + this.rightAscension = preference.rightAscension ?? '00h00m00s' + this.declination = preference.declination ?? `+00°00'00"` + this.width = preference.width ?? 1280 + this.height = preference.height ?? 720 + this.fov = preference.fov ?? 1 + this.rotation = preference.rotation ?? 0 + this.hipsSurvey ??= preference.hipsSurvey ?? this.hipsSurvey } private savePreference() { - this.preference.set('framing.rightAscension', this.rightAscension) - this.preference.set('framing.declination', this.declination) - this.preference.set('framing.width', this.width) - this.preference.set('framing.height', this.height) - this.preference.set('framing.fov', this.fov) - this.preference.set('framing.rotation', this.rotation) - this.preference.set('framing.hipsSurvey', this.hipsSurvey) + const preference: FramingPreference = { + rightAscension: this.rightAscension, + declination: this.declination, + width: this.width, + height: this.height, + fov: this.fov, + rotation: this.rotation, + hipsSurvey: this.hipsSurvey, + } + + this.storage.set('framing', preference) } private async closeFrameImage() { diff --git a/desktop/src/app/guider/guider.component.html b/desktop/src/app/guider/guider.component.html index ccd0f094b..a5767177b 100644 --- a/desktop/src/app/guider/guider.component.html +++ b/desktop/src/app/guider/guider.component.html @@ -5,110 +5,70 @@
- +
- +
- - +
- {{ phdState | enum | lowercase }} - {{ phdMessage }} + {{ guideState | enum | lowercase }} + {{ message }}
- -
-
- - - - -
-
- Dither in RA only - -
-
- - - - -
-
- - - - -
-
- - - - -
-
-
+ value="{{ rmsRA.toFixed(2) + ' (' + (rmsRA * pixelScale).toFixed(2) + '\" )' }}" />
+ value="{{ rmsDEC.toFixed(2) + ' (' + (rmsDEC * pixelScale).toFixed(2) + '\" )' }}" />
+ value="{{ rmsTotal.toFixed(2) + ' (' + (rmsTotal * pixelScale).toFixed(2) + '\" )' }}" />
- +
- +
- +
@@ -129,20 +89,49 @@
- +
North East
+ +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+
- - -
diff --git a/desktop/src/app/guider/guider.component.ts b/desktop/src/app/guider/guider.component.ts index 929b4425b..e0d3b88a6 100644 --- a/desktop/src/app/guider/guider.component.ts +++ b/desktop/src/app/guider/guider.component.ts @@ -3,14 +3,14 @@ import { Title } from '@angular/platform-browser' import { ChartData, ChartOptions } from 'chart.js' import { UIChart } from 'primeng/chart' import { ApiService } from '../../shared/services/api.service' -import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' -import { GuideDirection, GuideOutput, GuideStar, GuideState, GuideStep, GuiderStatus, HistoryStep } from '../../shared/types' +import { GuideDirection, GuideOutput, GuideState, GuideStep, Guider, GuiderPlotMode, GuiderYAxisUnit, HistoryStep } from '../../shared/types' -export type PlotMode = 'RA/DEC' | 'DX/DY' - -export type YAxisUnit = 'ARCSEC' | 'PIXEL' +export interface GuiderPreference { + settleAmount?: number + settleTime?: number + settleTimeout?: number +} @Component({ selector: 'app-guider', @@ -29,47 +29,46 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { guideWestDuration = 1000 guideEastDuration = 1000 - phdConnected = false - phdHost = 'localhost' - phdPort = 4400 - phdState: GuideState = 'STOPPED' - phdGuideStep?: GuideStep - phdMessage = '' - - phdDitherPixels = 5 - phdDitherRAOnly = false - phdSettleAmount = 1.5 - phdSettleTime = 10 - phdSettleTimeout = 30 + connected = false + host = 'localhost' + port = 4400 + guideState: GuideState = 'STOPPED' + guideStep?: GuideStep + message = '' + + settleAmount = 1.5 + settleTime = 10 + settleTimeout = 30 readonly phdGuideHistory: HistoryStep[] = [] private phdDurationScale = 1.0 - phdPixelScale = 1.0 - phdRmsRA = 0.0 - phdRmsDEC = 0.0 - phdRmsTotal = 0.0 + pixelScale = 1.0 + rmsRA = 0.0 + rmsDEC = 0.0 + rmsTotal = 0.0 - readonly plotModes: PlotMode[] = ['RA/DEC', 'DX/DY'] - plotMode: PlotMode = 'RA/DEC' - readonly yAxisUnits: YAxisUnit[] = ['ARCSEC', 'PIXEL'] - yAxisUnit: YAxisUnit = 'ARCSEC' + readonly plotModes: GuiderPlotMode[] = ['RA/DEC', 'DX/DY'] + plotMode = this.plotModes[0] - @ViewChild('phdChart') - private readonly phdChart!: UIChart + readonly yAxisUnits: GuiderYAxisUnit[] = ['ARCSEC', 'PIXEL'] + yAxisUnit = this.yAxisUnits[0] + + @ViewChild('chart') + private readonly chart!: UIChart get stopped() { - return this.phdState === 'STOPPED' + return this.guideState === 'STOPPED' } get looping() { - return this.phdState === 'LOOPING' + return this.guideState === 'LOOPING' } get guiding() { - return this.phdState === 'GUIDING' + return this.guideState === 'GUIDING' } - readonly phdChartData: ChartData = { + readonly chartData: ChartData = { labels: Array.from({ length: 100 }, (_, i) => `${i}`), datasets: [ // RA. @@ -107,7 +106,7 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { ] } - readonly phdChartOptions: ChartOptions = { + readonly chartOptions: ChartOptions = { responsive: true, plugins: { legend: { @@ -216,83 +215,83 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { constructor( title: Title, private api: ApiService, - private browserWindow: BrowserWindowService, - private electron: ElectronService, - private preference: PreferenceService, + electron: ElectronService, ngZone: NgZone, ) { title.setTitle('Guider') api.startListening('GUIDING') - electron.on('GUIDE_OUTPUT_UPDATED', (_, event: GuideOutput) => { - if (event.name === this.guideOutput?.name) { + electron.on('GUIDE_OUTPUT_UPDATED', event => { + if (event.device.name === this.guideOutput?.name) { ngZone.run(() => { - Object.assign(this.guideOutput!, event) + Object.assign(this.guideOutput!, event.device) this.update() }) } }) - electron.on('GUIDE_OUTPUT_ATTACHED', (_, event: GuideOutput) => { + electron.on('GUIDE_OUTPUT_ATTACHED', event => { ngZone.run(() => { - this.guideOutputs.push(event) + this.guideOutputs.push(event.device) }) }) - electron.on('GUIDE_OUTPUT_DETACHED', (_, event: GuideOutput) => { + electron.on('GUIDE_OUTPUT_DETACHED', event => { ngZone.run(() => { - const index = this.guideOutputs.findIndex(e => e.name === event.name) + const index = this.guideOutputs.findIndex(e => e.name === event.device.name) if (index >= 0) this.guideOutputs.splice(index, 1) }) }) electron.on('GUIDER_CONNECTED', () => { ngZone.run(() => { - this.phdConnected = true + this.connected = true }) }) electron.on('GUIDER_DISCONNECTED', () => { ngZone.run(() => { - this.phdConnected = false + this.connected = false }) }) - electron.on('GUIDER_UPDATED', (_, event: GuiderStatus) => { + electron.on('GUIDER_UPDATED', event => { ngZone.run(() => { - this.processGuiderStatus(event) + this.processGuiderStatus(event.data) }) }) - electron.on('GUIDER_STEPPED', (_, event: HistoryStep | GuideStar) => { + electron.on('GUIDER_STEPPED', event => { ngZone.run(() => { - if ("id" in event) { - if (this.phdGuideHistory.length >= 100) { - this.phdGuideHistory.splice(0, 1) - } - - this.phdGuideHistory.push(event) - this.updateGuideHistoryChart() + if (this.phdGuideHistory.length >= 100) { + this.phdGuideHistory.splice(0, this.phdGuideHistory.length - 99) } - if ("guideStep" in event && event.guideStep) { - this.phdGuideStep = event.guideStep + this.phdGuideHistory.push(event.data) + this.updateGuideHistoryChart() + + if (event.data.guideStep) { + this.guideStep = event.data.guideStep + } else { + // Dithering. } }) }) - electron.on('GUIDER_MESSAGE_RECEIVED', (_, event: { message: string }) => { + electron.on('GUIDER_MESSAGE_RECEIVED', event => { ngZone.run(() => { - this.phdMessage = event.message + this.message = event.data }) }) } async ngAfterViewInit() { - this.phdSettleAmount = this.preference.get('guiding.settleAmount', 1.5) - this.phdSettleTime = this.preference.get('guiding.settleTime', 10) - this.phdSettleTimeout = this.preference.get('guiding.settleTimeout', 30) + const settle = await this.api.getGuidingSettle() + + this.settleAmount = settle.amount ?? 1.5 + this.settleTime = settle.time ?? 10 + this.settleTimeout = settle.timeout ?? 30 this.guideOutputs = await this.api.guideOutputs() @@ -309,10 +308,10 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { this.api.stopListening('GUIDING') } - private processGuiderStatus(event: GuiderStatus) { - this.phdConnected = event.connected - this.phdState = event.state - this.phdPixelScale = event.pixelScale + private processGuiderStatus(event: Guider) { + this.connected = event.connected + this.guideState = event.state + this.pixelScale = event.pixelScale } plotModeChanged() { @@ -326,16 +325,16 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { private updateGuideHistoryChart() { if (this.phdGuideHistory.length > 0) { const history = this.phdGuideHistory[this.phdGuideHistory.length - 1] - this.phdRmsTotal = history.rmsTotal - this.phdRmsDEC = history.rmsDEC - this.phdRmsRA = history.rmsRA + this.rmsTotal = history.rmsTotal + this.rmsDEC = history.rmsDEC + this.rmsRA = history.rmsRA } else { return } const startId = this.phdGuideHistory[0].id const guideSteps = this.phdGuideHistory.filter(e => e.guideStep) - const scale = this.yAxisUnit === 'ARCSEC' ? this.phdPixelScale : 1.0 + const scale = this.yAxisUnit === 'ARCSEC' ? this.pixelScale : 1.0 let maxDuration = 0 @@ -347,14 +346,14 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { this.phdDurationScale = maxDuration / 16.0 if (this.plotMode === 'RA/DEC') { - this.phdChartData.datasets[0].data = guideSteps + this.chartData.datasets[0].data = guideSteps .map(e => [e.id - startId, -e.guideStep!.raDistance * scale]) - this.phdChartData.datasets[1].data = guideSteps + this.chartData.datasets[1].data = guideSteps .map(e => [e.id - startId, e.guideStep!.decDistance * scale]) } else { - this.phdChartData.datasets[0].data = guideSteps + this.chartData.datasets[0].data = guideSteps .map(e => [e.id - startId, -e.guideStep!.dx * scale]) - this.phdChartData.datasets[1].data = guideSteps + this.chartData.datasets[1].data = guideSteps .map(e => [e.id - startId, e.guideStep!.dy * scale]) } @@ -362,12 +361,12 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { return !direction || direction === 'NORTH' || direction === 'WEST' ? this.phdDurationScale : -this.phdDurationScale } - this.phdChartData.datasets[2].data = this.phdGuideHistory + this.chartData.datasets[2].data = this.phdGuideHistory .map(e => (e.guideStep?.raDuration ?? 0) / durationScale(e.guideStep?.raDirection)) - this.phdChartData.datasets[3].data = this.phdGuideHistory + this.chartData.datasets[3].data = this.phdGuideHistory .map(e => (e.guideStep?.decDuration ?? 0) / durationScale(e.guideStep?.decDirection)) - this.phdChart?.refresh() + this.chart?.refresh() } async guideOutputChanged() { @@ -377,8 +376,6 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { this.update() } - - this.electron.send('GUIDE_OUTPUT_CHANGED', this.guideOutput) } connectGuideOutput() { @@ -393,16 +390,16 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { for (const direction of directions) { switch (direction) { case 'NORTH': - this.api.guideOutputPulse(this.guideOutput!, direction, this.guideNorthDuration) + this.api.guideOutputPulse(this.guideOutput!, direction, this.guideNorthDuration * 1000) break case 'SOUTH': - this.api.guideOutputPulse(this.guideOutput!, direction, this.guideSouthDuration) + this.api.guideOutputPulse(this.guideOutput!, direction, this.guideSouthDuration * 1000) break case 'WEST': - this.api.guideOutputPulse(this.guideOutput!, direction, this.guideWestDuration) + this.api.guideOutputPulse(this.guideOutput!, direction, this.guideWestDuration * 1000) break case 'EAST': - this.api.guideOutputPulse(this.guideOutput!, direction, this.guideEastDuration) + this.api.guideOutputPulse(this.guideOutput!, direction, this.guideEastDuration * 1000) break } } @@ -416,10 +413,10 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { } guidingConnect() { - if (this.phdConnected) { + if (this.connected) { this.api.guidingDisconnect() } else { - this.api.guidingConnect(this.phdHost, this.phdPort) + this.api.guidingConnect(this.host, this.port) } } @@ -428,11 +425,8 @@ export class GuiderComponent implements AfterViewInit, OnDestroy { await this.api.guidingStart(event.shiftKey) } - async guidingSettleChanged() { - await this.api.guidingSettle(this.phdSettleAmount, this.phdSettleTime, this.phdSettleTimeout) - this.preference.set('guiding.settleAmount', this.phdSettleAmount) - this.preference.set('guiding.settleTime', this.phdSettleTime) - this.preference.set('guiding.settleTimeout', this.phdSettleTimeout) + async settleChanged() { + await this.api.setGuidingSettle({ amount: this.settleAmount, time: this.settleTime, timeout: this.settleTimeout }) } guidingClearHistory() { diff --git a/desktop/src/app/home/home.component.html b/desktop/src/app/home/home.component.html index a6c96703d..34efe5ad5 100644 --- a/desktop/src/app/home/home.component.html +++ b/desktop/src/app/home/home.component.html @@ -19,101 +19,112 @@
-
+
- +
Camera
- +
Mount
- +
Guider
- +
Filter Wheel
- +
Focuser
- +
Dome
- +
Rotator
- +
Switch
- +
Sky Atlas
- +
Alignment
- +
Sequencer
- +
Image Viewer
- +
Framing
- +
INDI
- + + +
Settings
+
+
+
+
About
-
\ No newline at end of file +
+ + + \ No newline at end of file diff --git a/desktop/src/app/home/home.component.scss b/desktop/src/app/home/home.component.scss index 7d9117070..3952ec4ba 100644 --- a/desktop/src/app/home/home.component.scss +++ b/desktop/src/app/home/home.component.scss @@ -3,9 +3,19 @@ p-button ::ng-deep { min-height: 56px; max-height: 56px; display: flex; + + img { + height: 32px; + } + } + + &.p-disabled { + img { + filter: grayscale(1); + } } } .grid::-webkit-scrollbar { display: none; -} +} \ No newline at end of file diff --git a/desktop/src/app/home/home.component.ts b/desktop/src/app/home/home.component.ts index 75f547a74..41f75c781 100644 --- a/desktop/src/app/home/home.component.ts +++ b/desktop/src/app/home/home.component.ts @@ -1,12 +1,26 @@ -import { AfterContentInit, Component, HostListener, NgZone, OnDestroy } from '@angular/core' -import { MessageService } from 'primeng/api' +import { AfterContentInit, Component, HostListener, NgZone, OnDestroy, ViewChild } from '@angular/core' +import { MenuItem, MessageService } from 'primeng/api' +import { DeviceMenuComponent } from '../../shared/components/devicemenu/devicemenu.component' +import { DialogMenuComponent } from '../../shared/components/dialogmenu/dialogmenu.component' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' +import { LocalStorageService } from '../../shared/services/local-storage.service' import { Camera, Device, FilterWheel, Focuser, HomeWindowType, Mount } from '../../shared/types' import { AppComponent } from '../app.component' +type MappedDevice = { + 'CAMERA': Camera + 'MOUNT': Mount + 'FOCUSER': Focuser + 'WHEEL': FilterWheel +} + +export interface HomePreference { + host?: string + port?: number +} + @Component({ selector: 'app-home', templateUrl: './home.component.html', @@ -14,6 +28,12 @@ import { AppComponent } from '../app.component' }) export class HomeComponent implements AfterContentInit, OnDestroy { + @ViewChild('deviceMenu') + private readonly deviceMenu!: DialogMenuComponent + + @ViewChild('imageMenu') + private readonly imageMenu!: DeviceMenuComponent + host = '' port = 7624 connected = false @@ -71,24 +91,32 @@ export class HomeComponent implements AfterContentInit, OnDestroy { || this.hasWheel || this.hasDome || this.hasRotator || this.hasSwitch } - private startListening( - type: 'CAMERA' | 'MOUNT' | 'FOCUSER' | 'WHEEL', - onAdd: (device: T) => number, - onRemove: (device: T) => number, + readonly deviceModel: MenuItem[] = [] + + readonly imageModel: MenuItem[] = [ + { + icon: 'mdi mdi-image-plus', + label: 'Open new image', + command: () => { + this.openImage(true) + } + } + ] + + private startListening( + type: K, + onAdd: (device: MappedDevice[K]) => number, + onRemove: (device: MappedDevice[K]) => number, ) { - this.electron.on(`${type}_ATTACHED`, (_, device: T) => { + this.electron.on(`${type}_ATTACHED`, event => { this.ngZone.run(() => { - if (onAdd(device) === 1) { - this.electron.send(`${type}_CHANGED`, device) - } + onAdd(event.device as any) }) }) - this.electron.on(`${type}_DETACHED`, (_, device: T) => { + this.electron.on(`${type}_DETACHED`, event => { this.ngZone.run(() => { - if (onRemove(device) === 0) { - this.electron.send(`${type}_CHANGED`, undefined) - } + onRemove(event.device as any) }) }) } @@ -99,12 +127,12 @@ export class HomeComponent implements AfterContentInit, OnDestroy { private browserWindow: BrowserWindowService, private api: ApiService, private message: MessageService, - private preference: PreferenceService, + private storage: LocalStorageService, private ngZone: NgZone, ) { app.title = 'Nebulosa' - this.startListening('CAMERA', + this.startListening('CAMERA', (device) => { return this.cameras.push(device) }, @@ -114,7 +142,7 @@ export class HomeComponent implements AfterContentInit, OnDestroy { }, ) - this.startListening('MOUNT', + this.startListening('MOUNT', (device) => { return this.mounts.push(device) }, @@ -124,7 +152,7 @@ export class HomeComponent implements AfterContentInit, OnDestroy { }, ) - this.startListening('FOCUSER', + this.startListening('FOCUSER', (device) => { return this.focusers.push(device) }, @@ -134,7 +162,7 @@ export class HomeComponent implements AfterContentInit, OnDestroy { }, ) - this.startListening('WHEEL', + this.startListening('WHEEL', (device) => { return this.wheels.push(device) }, @@ -143,34 +171,22 @@ export class HomeComponent implements AfterContentInit, OnDestroy { return this.wheels.length }, ) + + electron.on('SKY_ATLAS_UPDATE_FINISHED', () => this.open('SKY_ATLAS')) } async ngAfterContentInit() { this.updateConnection() - this.host = this.preference.get('home.host', 'localhost') - this.port = this.preference.get('home.port', 7624) + const preference = this.storage.get('home', {}) + + this.host = preference.host ?? 'localhost' + this.port = preference.port ?? 7624 this.cameras = await this.api.cameras() this.mounts = await this.api.mounts() this.focusers = await this.api.focusers() this.wheels = await this.api.wheels() - - if (this.cameras.length > 0) { - this.electron.send('CAMERA_CHANGED', this.cameras[0]) - } - - if (this.mounts.length > 0) { - this.electron.send('MOUNT_CHANGED', this.mounts[0]) - } - - if (this.focusers.length > 0) { - this.electron.send('FOCUSER_CHANGED', this.focusers[0]) - } - - if (this.wheels.length > 0) { - this.electron.send('WHEEL_CHANGED', this.wheels[0]) - } } @HostListener('window:unload') @@ -183,8 +199,12 @@ export class HomeComponent implements AfterContentInit, OnDestroy { } else { await this.api.connect(this.host || 'localhost', this.port) - this.preference.set('home.host', this.host) - this.preference.set('home.port', this.port) + const preference: HomePreference = { + host: this.host, + port: this.port, + } + + this.storage.set('home', preference) } } catch (e) { console.error(e) @@ -195,38 +215,92 @@ export class HomeComponent implements AfterContentInit, OnDestroy { } } - async open(type: HomeWindowType) { + private openDevice(type: K) { + this.deviceModel.length = 0 + + const devices: Device[] = type === 'CAMERA' ? this.cameras + : type === 'MOUNT' ? this.mounts + : type === 'FOCUSER' ? this.focusers + : type === 'WHEEL' ? this.wheels + : [] + + if (devices.length === 0) return + if (devices.length === 1) return this.openDeviceWindow(type, devices[0] as any) + + for (const device of devices) { + this.deviceModel.push({ + icon: 'mdi mdi-connection', + label: device.name, + command: () => { + this.openDeviceWindow(type, device as any) + } + }) + } + + this.deviceMenu.show() + } + + private openDeviceWindow(type: K, device: MappedDevice[K]) { switch (type) { case 'MOUNT': - this.browserWindow.openMount({ bringToFront: true }) + this.browserWindow.openMount({ bringToFront: true, data: device as Mount }) break case 'CAMERA': - this.browserWindow.openCamera({ bringToFront: true }) + this.browserWindow.openCamera({ bringToFront: true, data: device as Camera }) break case 'FOCUSER': - this.browserWindow.openFocuser({ bringToFront: true }) + this.browserWindow.openFocuser({ bringToFront: true, data: device as Focuser }) break case 'WHEEL': - this.browserWindow.openWheel({ bringToFront: true }) + this.browserWindow.openWheel({ bringToFront: true, data: device as FilterWheel }) + break + } + } + + private async openImage(force: boolean = false) { + if (force || this.cameras.length === 0) { + const path = await this.electron.openFITS() + + if (path) { + this.browserWindow.openImage({ path, source: 'PATH' }) + } + } else { + const camera = await this.imageMenu.show(this.cameras) + + if (camera) { + this.browserWindow.openCameraImage(camera) + } + } + } + + open(type: HomeWindowType) { + switch (type) { + case 'MOUNT': + case 'CAMERA': + case 'FOCUSER': + case 'WHEEL': + this.openDevice(type) break case 'GUIDER': this.browserWindow.openGuider({ bringToFront: true }) break - case 'ATLAS': + case 'SKY_ATLAS': this.browserWindow.openSkyAtlas({ bringToFront: true }) break case 'FRAMING': - this.browserWindow.openFraming(undefined, { bringToFront: true }) + this.browserWindow.openFraming({ bringToFront: true, data: undefined }) break case 'ALIGNMENT': this.browserWindow.openAlignment({ bringToFront: true }) break case 'INDI': - this.browserWindow.openINDI(undefined, { bringToFront: true }) + this.browserWindow.openINDI({ data: undefined, bringToFront: true }) break case 'IMAGE': - const path = await this.electron.sendSync('OPEN_FITS') - if (path) this.browserWindow.openImage(path, undefined, 'PATH') + this.openImage() + break + case 'SETTINGS': + this.browserWindow.openSettings() break case 'ABOUT': this.browserWindow.openAbout() diff --git a/desktop/src/app/image/image.component.html b/desktop/src/app/image/image.component.html index 4c92fec08..adb915555 100644 --- a/desktop/src/app/image/image.component.html +++ b/desktop/src/app/image/image.component.html @@ -1,6 +1,6 @@
+ class="select-none pixelated" (mousemove)="imageMouseMoved($event)" [class.cursor-crosshair]="isMouseCoordinateVisible" /> @@ -10,22 +10,36 @@ - + - {{ a.star?.name ?? - a.dso?.name ?? - a.minorPlanet?.name }} + + {{ (a.star ?? a.dso ?? a.minorPlanet) | skyObject:'name' }} + -
+ + + + + {{ s.hfd.toFixed(1) }} + + + + +
X: {{ roiX }} Y: {{ roiY }} W: {{ roiWidth }} H: {{ roiHeight }}
- + + + + +
@@ -38,7 +52,8 @@ + inputStyleClass="border-0 w-full" [step]="0.1" [(ngModel)]="annotateWithMinorPlanetsMagLimit" + [minFractionDigits]="1" locale="en" />
@@ -62,7 +77,7 @@
- +
@@ -108,7 +123,7 @@
@@ -120,54 +135,32 @@ -
-
- - - - -
-
- Blind -
- - - - -
-
- - - - -
-
- - - - -
-
- - - - +
+
+ + + + +
+
+ + + + +
+
-
+
- + -
-
- - - - +
@@ -195,7 +188,7 @@
- +
@@ -215,61 +208,63 @@
+ [text]="true" severity="info" /> + [text]="true" severity="success" /> + [text]="true" severity="success" />
- + -
-
+
- -
- - -
+ +
+
+
-
- +
+ -
- +
@@ -291,8 +286,8 @@
- @@ -323,11 +318,13 @@
-
+
X: {{ mouseCoordinate.x }}
Y: {{ mouseCoordinate.y }}
α: {{ mouseCoordinate.alpha }}
δ: {{ mouseCoordinate.delta }}
l: {{ mouseCoordinate.l }}
b: {{ mouseCoordinate.b }}
-
\ No newline at end of file +
+ + \ No newline at end of file diff --git a/desktop/src/app/image/image.component.ts b/desktop/src/app/image/image.component.ts index 8e6a93885..95649657b 100644 --- a/desktop/src/app/image/image.component.ts +++ b/desktop/src/app/image/image.component.ts @@ -4,21 +4,28 @@ import { Interactable } from '@interactjs/types/index' import interact from 'interactjs' import createPanZoom, { PanZoom } from 'panzoom' import * as path from 'path' -import { MegaMenuItem, MenuItem } from 'primeng/api' +import { MenuItem } from 'primeng/api' import { ContextMenu } from 'primeng/contextmenu' +import { DeviceMenuComponent } from '../../shared/components/devicemenu/devicemenu.component' +import { SEPARATOR_MENU_ITEM } from '../../shared/constants' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' +import { LocalStorageService } from '../../shared/services/local-storage.service' +import { PrimeService } from '../../shared/services/prime.service' import { - Angle, AstronomicalObject, Camera, CameraCaptureEvent, DeepSkyObject, EquatorialCoordinateJ2000, FITSHeaderItem, + Angle, AstronomicalObject, Camera, CheckableMenuItem, DeepSkyObject, DetectedStar, EquatorialCoordinateJ2000, FITSHeaderItem, ImageAnnotation, ImageCalibrated, ImageChannel, ImageInfo, ImageSource, - PlateSolverType, SCNRProtectionMethod, SCNR_PROTECTION_METHODS, Star + Mount, SCNRProtectionMethod, SCNR_PROTECTION_METHODS, Star, ToggleableMenuItem } from '../../shared/types' import { CoordinateInterpolator, InterpolatedCoordinate } from '../../shared/utils/coordinate-interpolation' import { AppComponent } from '../app.component' -export interface ImageParams { +export interface ImagePreference { + solverRadius?: number +} + +export interface ImageData { camera?: Camera path?: string source?: ImageSource @@ -41,7 +48,11 @@ export class ImageComponent implements AfterViewInit, OnDestroy { @ViewChild('menu') private readonly menu!: ContextMenu + @ViewChild('deviceMenu') + private readonly deviceMenu!: DeviceMenuComponent + debayer = true + calibrate = true mirrorHorizontal = false mirrorVertical = false invert = false @@ -60,24 +71,18 @@ export class ImageComponent implements AfterViewInit, OnDestroy { annotateWithMinorPlanets = false annotateWithMinorPlanetsMagLimit = 12.0 - autoStretch = true + autoStretched = true showStretchingDialog = false stretchShadowhHighlight = [0, 65536] stretchMidtone = 32768 - readonly solverTypeOptions: PlateSolverType[] = ['ASTAP'] - showSolverDialog = false solving = false solved = false - solverType: PlateSolverType = 'ASTAP' solverBlind = true solverCenterRA = '' solverCenterDEC = '' solverRadius = 4 - solverDownsampleFactor = 1 - solverPathOrUrl = '' - solverApiKey = '' solverCalibration?: ImageCalibrated crossHair = false @@ -85,6 +90,10 @@ export class ImageComponent implements AfterViewInit, OnDestroy { annotating = false showAnnotationInfoDialog = false annotationInfo?: AstronomicalObject & Partial + annotationIsVisible = false + + detectedStars: DetectedStar[] = [] + detectedStarsIsVisible = false showFITSHeadersDialog = false fitsHeaders: FITSHeaderItem[] = [] @@ -94,7 +103,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { private imageInfo?: ImageInfo private imageMouseX = 0 private imageMouseY = 0 - private imageParams: ImageParams = {} + private imageData: ImageData = {} roiX = 0 roiY = 0 @@ -102,6 +111,48 @@ export class ImageComponent implements AfterViewInit, OnDestroy { roiHeight = 128 roiInteractable?: Interactable + private readonly saveAsMenuItem: MenuItem = { + label: 'Save as...', + icon: 'mdi mdi-content-save', + command: async () => { + const path = await this.electron.send('SAVE_FITS_AS') + if (path) this.api.saveImageAs(this.imageData.path!, path) + }, + } + + private readonly plateSolveMenuItem: MenuItem = { + label: 'Plate Solve', + icon: 'mdi mdi-sigma', + command: () => { + this.showSolverDialog = true + }, + } + + private readonly stretchMenuItem: MenuItem = { + label: 'Stretch', + icon: 'mdi mdi-chart-histogram', + command: () => { + this.showStretchingDialog = true + }, + } + + private readonly autoStretchMenuItem: CheckableMenuItem = { + id: 'auto-stretch-menuitem', + label: 'Auto stretch', + icon: 'mdi mdi-auto-fix', + checked: true, + command: () => { + this.autoStretched = !this.autoStretched + this.autoStretchMenuItem.checked = this.autoStretched + + if (!this.autoStretched) { + this.resetStretch() + } else { + this.loadImage() + } + }, + } + private readonly scnrMenuItem: MenuItem = { label: 'SCNR', icon: 'mdi mdi-palette', @@ -111,209 +162,227 @@ export class ImageComponent implements AfterViewInit, OnDestroy { }, } - private readonly pointMountHereMenuItem: MenuItem = { - label: 'Point mount here', - icon: 'mdi mdi-telescope', - disabled: true, - command: async () => { - const mount = await this.electron.selectedMount() - if (!mount?.connected) return - this.api.pointMountHere(mount, this.imageParams.path!, this.imageMouseX, this.imageMouseY, !this.solved) + private readonly horizontalMirrorMenuItem: CheckableMenuItem = { + label: 'Horizontal mirror', + icon: 'mdi mdi-flip-horizontal', + checked: false, + command: () => { + this.mirrorHorizontal = !this.mirrorHorizontal + this.horizontalMirrorMenuItem.checked = this.mirrorHorizontal + this.loadImage() }, } - private readonly annotationMenuItem: MenuItem = { - label: 'Annotation', - icon: 'mdi mdi-format-color-text', - disabled: true, + private readonly verticalMirrorMenuItem: CheckableMenuItem = { + label: 'Vertical mirror', + icon: 'mdi mdi-flip-vertical', + checked: false, command: () => { - this.showAnnotationDialog = true + this.mirrorVertical = !this.mirrorVertical + this.verticalMirrorMenuItem.checked = this.mirrorVertical + this.loadImage() }, } - readonly menuItems: MenuItem[] = [ - { - label: 'Save as...', - icon: 'mdi mdi-content-save', - command: async () => { - const path = await this.electron.sendSync('SAVE_FITS_AS') - if (path) this.api.saveImageAs(this.imageParams.path!, path) - }, - }, - { - separator: true, - }, - { - label: 'Plate Solve', - icon: 'mdi mdi-sigma', - command: () => { - this.showSolverDialog = true - }, - }, - { - separator: true, + private readonly invertMenuItem: CheckableMenuItem = { + label: 'Invert', + icon: 'mdi mdi-invert-colors', + checked: false, + command: () => { + this.invert = !this.invert + this.invertMenuItem.checked = this.invert + this.loadImage() }, - { - label: 'Stretch', - icon: 'mdi mdi-chart-histogram', - command: () => { - this.showStretchingDialog = true - }, + } + + private readonly calibrateMenuItem: CheckableMenuItem = { + label: 'Calibrate', + icon: 'mdi mdi-tools', + checked: true, + command: () => { + this.calibrate = !this.calibrate + this.calibrateMenuItem.checked = this.calibrate + this.loadImage() }, - { - id: 'auto-stretch-menuitem', - label: 'Auto stretch', - icon: 'mdi mdi-chart-histogram', - styleClass: 'p-menuitem-checked', - command: (e) => { - this.autoStretch = !this.autoStretch - this.checkMenuItem(e.item, this.autoStretch) - - if (!this.autoStretch) { - this.resetStretch() - } else { - this.loadImage() - } - }, + } + + private readonly fitsHeaderMenuItem: MenuItem = { + icon: 'mdi mdi-list-box', + label: 'FITS Header', + command: () => { + this.showFITSHeadersDialog = true }, - this.scnrMenuItem, - { - label: 'Horizontal mirror', - icon: 'mdi mdi-flip-horizontal', - command: (e) => { - this.mirrorHorizontal = !this.mirrorHorizontal - this.checkMenuItem(e.item, this.mirrorHorizontal) - this.loadImage() - }, + } + + private readonly pointMountHereMenuItem: MenuItem = { + label: 'Point mount here', + icon: 'mdi mdi-telescope', + disabled: true, + command: () => { + this.executeMount((mount) => { + this.api.pointMountHere(mount, this.imageData.path!, this.imageMouseX, this.imageMouseY, !this.solved) + }) }, - { - label: 'Vertical mirror', - icon: 'mdi mdi-flip-vertical', - command: (e) => { - this.mirrorVertical = !this.mirrorVertical - this.checkMenuItem(e.item, this.mirrorVertical) - this.loadImage() - }, + } + + private readonly crosshairMenuItem: CheckableMenuItem = { + label: 'Crosshair', + icon: 'mdi mdi-bullseye', + checked: false, + command: () => { + this.crossHair = !this.crossHair + this.crosshairMenuItem.checked = this.crossHair }, - { - label: 'Invert', - icon: 'mdi mdi-invert-colors', - command: (e) => { - this.invert = !this.invert - this.checkMenuItem(e.item, this.invert) - this.loadImage() - }, + } + + private readonly annotationMenuItem: ToggleableMenuItem = { + label: 'Annotate', + icon: 'mdi mdi-marker', + disabled: true, + toggleable: true, + toggled: false, + command: () => { + this.showAnnotationDialog = true }, - { - separator: true, + toggle: (event) => { + event.originalEvent?.stopImmediatePropagation() + this.annotationIsVisible = event.checked }, - { - label: 'Overlay', - icon: 'mdi mdi-layer', - items: [ - { - label: 'Crosshair', - icon: 'mdi mdi-bullseye', - command: (e) => { - this.crossHair = !this.crossHair - this.checkMenuItem(e.item, this.crossHair) - }, - }, - this.annotationMenuItem, - { - label: 'ROI', - icon: 'mdi mdi-select', - command: (e) => { - if (this.roiInteractable) { - this.roiInteractable.unset() - this.roiInteractable = undefined - } else { - this.roiInteractable = interact(this.roi.nativeElement) - .origin({ x: 0, y: 0 }) - .resizable({ - edges: { left: true, right: true, bottom: true, top: true }, - inertia: true, - listeners: { move: (event: any) => this.roiResizableMove(event) }, - modifiers: [ - interact.modifiers.restrictEdges({ - outer: 'parent', - }), - interact.modifiers.restrictSize({ - min: { width: 8, height: 8 }, - }) - ], - }) - .draggable({ - listeners: { move: (event: any) => this.roiDraggableMove(event) }, - inertia: true, - modifiers: [ - interact.modifiers.restrictRect({ - restriction: 'parent', - endOnly: true, - }), - ] - }) - } - - this.checkMenuItem(e.item, !!this.roiInteractable) - }, - }, - ] + } + + private readonly detectStarsMenuItem: ToggleableMenuItem = { + label: 'Detect stars', + icon: 'mdi mdi-creation', + disabled: false, + toggleable: false, + toggled: false, + command: async () => { + this.detectedStars = await this.api.detectStars(this.imageData.path!) + this.detectedStarsIsVisible = this.detectedStars.length > 0 + this.detectStarsMenuItem.toggleable = this.detectedStarsIsVisible + this.detectStarsMenuItem.toggled = this.detectedStarsIsVisible }, - { - icon: 'mdi mdi-list-box', - label: 'FITS Header', - command: () => { - this.showFITSHeadersDialog = true - }, + toggle: (event) => { + event.originalEvent?.stopImmediatePropagation() + this.detectedStarsIsVisible = event.checked }, - { - separator: true, + } + + private readonly roiMenuItem: CheckableMenuItem = { + label: 'ROI', + icon: 'mdi mdi-select', + checked: false, + command: () => { + if (this.roiInteractable) { + this.roiInteractable.unset() + this.roiInteractable = undefined + } else { + this.roiInteractable = interact(this.roi.nativeElement) + .origin({ x: 0, y: 0 }) + .resizable({ + edges: { left: true, right: true, bottom: true, top: true }, + inertia: true, + listeners: { move: (event: any) => this.roiResizableMove(event) }, + modifiers: [ + interact.modifiers.restrictEdges({ + outer: 'parent', + }), + interact.modifiers.restrictSize({ + min: { width: 8, height: 8 }, + }) + ], + }) + .draggable({ + listeners: { move: (event: any) => this.roiDraggableMove(event) }, + inertia: true, + modifiers: [ + interact.modifiers.restrictRect({ + restriction: 'parent', + endOnly: true, + }), + ] + }) + } + + this.roiMenuItem.checked = !!this.roiInteractable }, + } + + private readonly overlayMenuItem: MenuItem = { + label: 'Overlay', + icon: 'mdi mdi-layers', + items: [ + this.crosshairMenuItem, + this.annotationMenuItem, + this.detectStarsMenuItem, + this.roiMenuItem, + ] + } + + readonly contextMenuItems = [ + this.saveAsMenuItem, + SEPARATOR_MENU_ITEM, + this.plateSolveMenuItem, + SEPARATOR_MENU_ITEM, + this.stretchMenuItem, + this.autoStretchMenuItem, + this.scnrMenuItem, + this.horizontalMirrorMenuItem, + this.verticalMirrorMenuItem, + this.invertMenuItem, + this.calibrateMenuItem, + SEPARATOR_MENU_ITEM, + this.overlayMenuItem, + this.fitsHeaderMenuItem, + SEPARATOR_MENU_ITEM, this.pointMountHereMenuItem, ] mouseCoordinate?: InterpolatedCoordinate & Partial<{ x: number, y: number }> private mouseCoordinateInterpolation?: CoordinateInterpolator + get isMouseCoordinateVisible() { + return !!this.mouseCoordinate && !this.mirrorHorizontal && !this.mirrorVertical + } + constructor( private app: AppComponent, private route: ActivatedRoute, private api: ApiService, private electron: ElectronService, private browserWindow: BrowserWindowService, - private preference: PreferenceService, + private storage: LocalStorageService, + private prime: PrimeService, private ngZone: NgZone, ) { app.title = 'Image' - electron.on('CAMERA_EXPOSURE_FINISHED', async (_, event: CameraCaptureEvent) => { - if (event.camera.name === this.imageParams.camera?.name) { + electron.on('CAMERA_EXPOSURE_FINISHED', async (event) => { + if (event.camera.name === this.imageData.camera?.name) { await this.closeImage() ngZone.run(() => { - this.annotations = [] - this.imageParams.path = event.savePath + this.imageData.path = event.savePath this.loadImage() }) } }) - electron.on('PARAMS_CHANGED', async (_, event: ImageParams) => { + electron.on('DATA_CHANGED', async (event: ImageData) => { await this.closeImage() - this.loadImageFromParams(event) + ngZone.run(() => { + this.loadImageFromData(event) + }) }) - - this.solverPathOrUrl = this.preference.get('image.solver.pathOrUrl', '') - this.solverRadius = this.preference.get('image.solver.radius', 4) - this.solverDownsampleFactor = this.preference.get('image.solver.downsampleFactor', 1) } ngAfterViewInit() { + this.loadPreference() + this.route.queryParams.subscribe(e => { - const params = JSON.parse(decodeURIComponent(e.params)) as ImageParams - this.loadImageFromParams(params) + const data = JSON.parse(decodeURIComponent(e.data)) as ImageData + this.loadImageFromData(data) }) } @@ -325,9 +394,9 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } private async closeImage(force: boolean = false) { - if (this.imageParams.path) { - if (force || !this.imageParams.path.startsWith('@')) { - await this.api.closeImage(this.imageParams.path) + if (this.imageData.path) { + if (force || !this.imageData.path.startsWith('@')) { + await this.api.closeImage(this.imageData.path) } } } @@ -337,8 +406,8 @@ export class ImageComponent implements AfterViewInit, OnDestroy { const { scale } = this.panZoom!.getTransform() - var x = parseFloat(target.getAttribute('data-x')) || 0 - var y = parseFloat(target.getAttribute('data-y')) || 0 + let x = parseFloat(target.getAttribute('data-x')) || 0 + let y = parseFloat(target.getAttribute('data-y')) || 0 target.style.width = event.rect.width / scale + 'px' target.style.height = event.rect.height / scale + 'px' @@ -378,32 +447,49 @@ export class ImageComponent implements AfterViewInit, OnDestroy { }) } - private loadImageFromParams(params: ImageParams) { - console.info('loading image from params: %s', params) + private loadImageFromData(data: ImageData) { + console.info('loading image from data: %s', data) - this.imageParams = params + this.imageData = data - if (params.source === 'FRAMING') { + if (data.source === 'FRAMING') { this.disableAutoStretch() } - if (this.imageParams.path) { - this.annotations = [] - this.loadImage() + this.calibrateMenuItem.disabled = !data.camera + + if (!data.camera) { + this.disableCalibrate() } + + this.loadImage() + } + + private clearOverlay() { + this.annotations = [] + this.annotationIsVisible = false + this.annotationMenuItem.toggleable = false + + this.detectedStars = [] + this.detectedStarsIsVisible = false + this.detectStarsMenuItem.toggleable = false } private async loadImage() { - if (this.imageParams.path) { - await this.loadImageFromPath(this.imageParams.path) + if (this.imageData.path) { + this.clearOverlay() + + await this.loadImageFromPath(this.imageData.path) } - if (this.imageParams.title) { - this.app.subTitle = this.imageParams.title - } else if (this.imageParams.camera) { - this.app.subTitle = this.imageParams.camera.name - } else if (this.imageParams.path) { - this.app.subTitle = path.basename(this.imageParams.path) + this.loadPreference(this.imageData.camera) + + if (this.imageData.title) { + this.app.subTitle = this.imageData.title + } else if (this.imageData.camera) { + this.app.subTitle = this.imageData.camera.name + } else if (this.imageData.path) { + this.app.subTitle = path.basename(this.imageData.path) } else { this.app.subTitle = '' } @@ -412,7 +498,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { private async loadImageFromPath(path: string) { const image = this.image.nativeElement const scnrEnabled = this.scnrChannel !== 'NONE' - const { info, blob } = await this.api.openImage(path, this.debayer, this.autoStretch, + const { info, blob } = await this.api.openImage(path, this.imageData.camera, this.calibrate, this.debayer, this.autoStretched, this.stretchShadowhHighlight[0] / 65536, this.stretchShadowhHighlight[1] / 65536, this.stretchMidtone / 65536, this.mirrorHorizontal, this.mirrorVertical, this.invert, scnrEnabled, scnrEnabled ? this.scnrChannel : 'GREEN', this.scnrAmount, this.scnrProtectionMethod) @@ -422,8 +508,9 @@ export class ImageComponent implements AfterViewInit, OnDestroy { if (info.rightAscension) this.solverCenterRA = info.rightAscension if (info.declination) this.solverCenterDEC = info.declination + this.solverBlind = !this.solverCenterRA || !this.solverCenterDEC - if (this.autoStretch) { + if (this.autoStretched) { this.stretchShadowhHighlight[0] = Math.trunc(info.stretchShadow * 65536) this.stretchShadowhHighlight[1] = Math.trunc(info.stretchHighlight * 65536) this.stretchMidtone = Math.trunc(info.stretchMidtone * 65536) @@ -450,18 +537,25 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } imageMouseMoved(event: MouseEvent) { + this.imageMouseMovedWithCoordinates(event.offsetX, event.offsetY) + } + + imageMouseMovedWithCoordinates(x: number, y: number) { if (!this.menu.visible()) { - this.mouseCoordinate = this.mouseCoordinateInterpolation?.interpolateAsText(event.offsetX, event.offsetY, true, true, false) - this.mouseCoordinate!.x = event.offsetX - this.mouseCoordinate!.y = event.offsetY + this.mouseCoordinate = this.mouseCoordinateInterpolation?.interpolateAsText(x, y, true, true, false) + this.mouseCoordinate!.x = x + this.mouseCoordinate!.y = y } } async annotateImage() { try { this.annotating = true - this.annotations = await this.api.annotationsOfImage(this.imageParams.path!, + this.annotations = await this.api.annotationsOfImage(this.imageData.path!, this.annotateWithStars, this.annotateWithDSOs, this.annotateWithMinorPlanets, this.annotateWithMinorPlanetsMagLimit) + this.annotationIsVisible = true + this.annotationMenuItem.toggleable = this.annotations.length > 0 + this.annotationMenuItem.toggled = this.annotationMenuItem.toggleable this.showAnnotationDialog = false } finally { this.annotating = false @@ -474,8 +568,20 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } private disableAutoStretch() { - this.autoStretch = false - this.checkMenuItem(this.menuItems[5], false) + this.autoStretched = false + this.autoStretchMenuItem.checked = false + } + + private disableCalibrate() { + this.calibrate = false + this.calibrateMenuItem.checked = false + } + + autoStretch() { + this.autoStretched = true + this.autoStretchMenuItem.checked = true + + this.loadImage() } resetStretch() { @@ -496,14 +602,14 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } private async retrieveCoordinateInterpolation() { - const coordinate = await this.api.coordinateInterpolation(this.imageParams.path!) + const coordinate = await this.api.coordinateInterpolation(this.imageData.path!) if (coordinate) { const { ma, md, x0, y0, x1, y1, delta } = coordinate + const x = Math.max(0, Math.min(this.mouseCoordinate?.x ?? 0, this.imageInfo!.width)) + const y = Math.max(0, Math.min(this.mouseCoordinate?.y ?? 0, this.imageInfo!.height)) this.mouseCoordinateInterpolation = new CoordinateInterpolator(ma, md, x0, y0, x1, y1, delta) - this.mouseCoordinate = this.mouseCoordinateInterpolation.interpolateAsText(0, 0) - this.mouseCoordinate.x = 0 - this.mouseCoordinate.y = 0 + this.imageMouseMovedWithCoordinates(x, y) } else { this.mouseCoordinateInterpolation = undefined this.mouseCoordinate = undefined @@ -514,13 +620,10 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.solving = true try { - this.solverCalibration = await this.api.solveImage(this.imageParams.path!, this.solverType, this.solverBlind, - this.solverCenterRA, this.solverCenterDEC, this.solverRadius, this.solverDownsampleFactor, - this.solverPathOrUrl, this.solverApiKey) + this.solverCalibration = await this.api.solveImage(this.imageData.path!, this.solverBlind, + this.solverCenterRA, this.solverCenterDEC, this.solverRadius) - this.preference.set('image.solver.pathOrUrl', this.solverPathOrUrl) - this.preference.set('image.solver.radius', this.solverRadius) - this.preference.set('image.solver.downsampleFactor', this.solverDownsampleFactor) + this.savePreference() this.solved = true this.annotationMenuItem.disabled = false @@ -536,26 +639,26 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } } - async mountSync(coordinate: EquatorialCoordinateJ2000) { - const mount = await this.electron.selectedMount() - if (!mount?.connected) return - this.api.mountSync(mount, coordinate.rightAscensionJ2000, coordinate.declinationJ2000, true) + mountSync(coordinate: EquatorialCoordinateJ2000) { + this.executeMount((mount) => { + this.api.mountSync(mount, coordinate.rightAscensionJ2000, coordinate.declinationJ2000, true) + }) } async mountGoTo(coordinate: EquatorialCoordinateJ2000) { - const mount = await this.electron.selectedMount() - if (!mount?.connected) return - this.api.mountGoTo(mount, coordinate.rightAscensionJ2000, coordinate.declinationJ2000, true) + this.executeMount((mount) => { + this.api.mountGoTo(mount, coordinate.rightAscensionJ2000, coordinate.declinationJ2000, true) + }) } async mountSlew(coordinate: EquatorialCoordinateJ2000) { - const mount = await this.electron.selectedMount() - if (!mount?.connected) return - this.api.mountSlew(mount, coordinate.rightAscensionJ2000, coordinate.declinationJ2000, true) + this.executeMount((mount) => { + this.api.mountSlew(mount, coordinate.rightAscensionJ2000, coordinate.declinationJ2000, true) + }) } frame(coordinate: EquatorialCoordinateJ2000) { - this.browserWindow.openFraming({ rightAscension: coordinate.rightAscensionJ2000, declination: coordinate.declinationJ2000 }) + this.browserWindow.openFraming({ data: { rightAscension: coordinate.rightAscensionJ2000, declination: coordinate.declinationJ2000 } }) } imageLoaded() { @@ -578,7 +681,49 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } } - private checkMenuItem(item?: MenuItem | MegaMenuItem, checked: boolean = true) { - item && (item.styleClass = checked ? 'p-menuitem-checked' : '') + private loadPreference(camera?: Camera) { + let preference: ImagePreference + + if (camera) { + preference = this.storage.get(`image.${camera.name}`, {}) + } else { + preference = this.storage.get('image', {}) + } + + this.solverRadius = preference.solverRadius ?? this.solverRadius + } + + private savePreference() { + const preference: ImagePreference = { + solverRadius: this.solverRadius + } + + if (this.imageData.camera) { + this.storage.set(`image.${this.imageData.camera.name}`, preference) + } else { + this.storage.set('image', preference) + } + } + + private async executeMount(action: (mount: Mount) => void) { + if (await this.prime.confirm('Are you sure that you want to proceed?')) { + return + } + + const mounts = await this.api.mounts() + + if (mounts.length === 1) { + action(mounts[0]) + return true + } else { + const mount = await this.deviceMenu.show(mounts) + + if (mount && mount.connected) { + action(mount) + return true + } + } + + return false } } diff --git a/desktop/src/app/indi/indi.component.ts b/desktop/src/app/indi/indi.component.ts index 6ee8abe63..057e361b1 100644 --- a/desktop/src/app/indi/indi.component.ts +++ b/desktop/src/app/indi/indi.component.ts @@ -3,13 +3,9 @@ import { ActivatedRoute } from '@angular/router' import { MenuItem } from 'primeng/api' import { ApiService } from '../../shared/services/api.service' import { ElectronService } from '../../shared/services/electron.service' -import { Device, INDIDeviceMessage, INDIProperty, INDIPropertyItem, INDISendProperty } from '../../shared/types' +import { Device, INDIProperty, INDIPropertyItem, INDISendProperty } from '../../shared/types' import { AppComponent } from '../app.component' -export interface INDIParams { - device?: Device -} - @Component({ selector: 'app-indi', templateUrl: './indi.component.html', @@ -37,15 +33,15 @@ export class INDIComponent implements AfterViewInit, OnDestroy { this.api.startListening('INDI') - electron.on('DEVICE_PROPERTY_CHANGED', (_, event: INDIProperty) => { + electron.on('DEVICE_PROPERTY_CHANGED', event => { ngZone.run(() => { - this.addOrUpdateProperty(event) + this.addOrUpdateProperty(event.property!) this.updateGroups() }) }) - electron.on('DEVICE_PROPERTY_DELETED', (_, event: INDIProperty) => { - const index = this.properties.findIndex((e) => e.name === event.name) + electron.on('DEVICE_PROPERTY_DELETED', event => { + const index = this.properties.findIndex((e) => e.name === event.property!.name) if (index >= 0) { ngZone.run(() => { @@ -55,10 +51,10 @@ export class INDIComponent implements AfterViewInit, OnDestroy { } }) - electron.on('DEVICE_MESSAGE_RECEIVED', (_, event: INDIDeviceMessage) => { + electron.on('DEVICE_MESSAGE_RECEIVED', event => { if (this.device && event.device?.name === this.device.name) { ngZone.run(() => { - this.messages.splice(0, 0, event.message) + this.messages.splice(0, 0, event.message!) }) } }) @@ -66,8 +62,7 @@ export class INDIComponent implements AfterViewInit, OnDestroy { async ngAfterViewInit() { this.route.queryParams.subscribe(e => { - const params = JSON.parse(decodeURIComponent(e.params)) as INDIParams - this.device = params.device + this.device = JSON.parse(decodeURIComponent(e.data)) as Device }) this.devices = [ diff --git a/desktop/src/app/indi/property/indi-property.component.ts b/desktop/src/app/indi/property/indi-property.component.ts index e2133f1b9..9c984adf1 100644 --- a/desktop/src/app/indi/property/indi-property.component.ts +++ b/desktop/src/app/indi/property/indi-property.component.ts @@ -8,7 +8,7 @@ import { INDIProperty, INDIPropertyItem, INDISendProperty, INDISendPropertyItem }) export class INDIPropertyComponent implements AfterContentInit, OnDestroy { - @Input() + @Input({ required: true }) property!: INDIProperty @Input() diff --git a/desktop/src/app/mount/mount.component.html b/desktop/src/app/mount/mount.component.html index de4850af3..4f43fe5b1 100644 --- a/desktop/src/app/mount/mount.component.html +++ b/desktop/src/app/mount/mount.component.html @@ -1,9 +1,8 @@
-
+
- +
@@ -69,7 +68,7 @@
-
+
@@ -113,7 +112,7 @@
+ (onDropdownClick)="targetMenu.show()" />
@@ -199,4 +198,4 @@
- \ No newline at end of file + \ No newline at end of file diff --git a/desktop/src/app/mount/mount.component.ts b/desktop/src/app/mount/mount.component.ts index 7b1dcd6e5..8b3f56339 100644 --- a/desktop/src/app/mount/mount.component.ts +++ b/desktop/src/app/mount/mount.component.ts @@ -1,13 +1,20 @@ import { AfterContentInit, Component, HostListener, NgZone, OnDestroy } from '@angular/core' +import { ActivatedRoute } from '@angular/router' import { MenuItem } from 'primeng/api' -import { Subject, Subscription, debounceTime, interval, throttleTime } from 'rxjs' +import { Subject, Subscription, interval, throttleTime } from 'rxjs' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' +import { LocalStorageService } from '../../shared/services/local-storage.service' import { Angle, ComputedLocation, Constellation, Mount, PierSide, SlewRate, TargetCoordinateType, TrackMode, Union } from '../../shared/types' import { AppComponent } from '../app.component' +export interface MountPreference { + targetCoordinateType?: TargetCoordinateType + targetRightAscension?: Angle + targetDeclination?: Angle +} + @Component({ selector: 'app-mount', templateUrl: './mount.component.html', @@ -15,9 +22,9 @@ import { AppComponent } from '../app.component' }) export class MountComponent implements AfterContentInit, OnDestroy { - mounts: Mount[] = [] mount?: Mount connected = false + slewing = false parking = false parked = false @@ -53,12 +60,12 @@ export class MountComponent implements AfterContentInit, OnDestroy { private computeCoordinateSubscriptions: Subscription[] = [] private readonly moveToDirection = [false, false] - readonly targetCoordinateOptions: MenuItem[] = [ + readonly targetCoordinateModel: MenuItem[] = [ { icon: 'mdi mdi-telescope', label: 'Go To', command: () => { - this.targetCoordinateOption = this.targetCoordinateOptions[0] + this.targetCoordinateOption = this.targetCoordinateModel[0] this.goTo() }, }, @@ -66,7 +73,7 @@ export class MountComponent implements AfterContentInit, OnDestroy { icon: 'mdi mdi-telescope', label: 'Slew', command: () => { - this.targetCoordinateOption = this.targetCoordinateOptions[1] + this.targetCoordinateOption = this.targetCoordinateModel[1] this.slewTo() }, }, @@ -74,7 +81,7 @@ export class MountComponent implements AfterContentInit, OnDestroy { icon: 'mdi mdi-sync', label: 'Sync', command: () => { - this.targetCoordinateOption = this.targetCoordinateOptions[2] + this.targetCoordinateOption = this.targetCoordinateModel[2] this.sync() }, }, @@ -136,24 +143,25 @@ export class MountComponent implements AfterContentInit, OnDestroy { }, ] - targetCoordinateOption = this.targetCoordinateOptions[0] + targetCoordinateOption = this.targetCoordinateModel[0] constructor( private app: AppComponent, private api: ApiService, private browserWindow: BrowserWindowService, private electron: ElectronService, - private preference: PreferenceService, + private storage: LocalStorageService, + private route: ActivatedRoute, ngZone: NgZone, ) { app.title = 'Mount' api.startListening('MOUNT') - electron.on('MOUNT_UPDATED', (_, event: Mount) => { - if (event.name === this.mount?.name) { + electron.on('MOUNT_UPDATED', event => { + if (event.device.name === this.mount?.name) { ngZone.run(() => { - Object.assign(this.mount!, event) + Object.assign(this.mount!, event.device) this.update() }) } @@ -170,23 +178,30 @@ export class MountComponent implements AfterContentInit, OnDestroy { }) this.computeCoordinateSubscriptions[2] = this.computeTargetCoordinatePublisher - .pipe(debounceTime(1000)) + .pipe(throttleTime(1000)) .subscribe(() => this.computeTargetCoordinates()) } async ngAfterContentInit() { - this.mounts = await this.api.mounts() + this.route.queryParams.subscribe(e => { + const mount = JSON.parse(decodeURIComponent(e.data)) as Mount + this.mountChanged(mount) + }) } @HostListener('window:unload') ngOnDestroy() { + this.abort() + this.computeCoordinateSubscriptions .forEach(e => e.unsubscribe()) this.api.stopListening('MOUNT') } - async mountChanged() { + async mountChanged(mount?: Mount) { + this.mount = mount + if (this.mount) { this.app.subTitle = this.mount!.name @@ -195,12 +210,9 @@ export class MountComponent implements AfterContentInit, OnDestroy { this.loadPreference() this.update() - this.savePreference() } else { this.app.subTitle = '' } - - this.electron.send('MOUNT_CHANGED', this.mount) } connect() { @@ -227,11 +239,11 @@ export class MountComponent implements AfterContentInit, OnDestroy { } targetCoordinateOptionClicked() { - if (this.targetCoordinateOption === this.targetCoordinateOptions[0]) { + if (this.targetCoordinateOption === this.targetCoordinateModel[0]) { this.goTo() - } else if (this.targetCoordinateOption === this.targetCoordinateOptions[1]) { + } else if (this.targetCoordinateOption === this.targetCoordinateModel[1]) { this.slewTo() - } else if (this.targetCoordinateOption === this.targetCoordinateOptions[2]) { + } else if (this.targetCoordinateOption === this.targetCoordinateModel[2]) { this.sync() } } @@ -374,18 +386,23 @@ export class MountComponent implements AfterContentInit, OnDestroy { private loadPreference() { if (this.mount) { - this.targetCoordinateType = this.preference.get(`mount.${this.mount.name}.targetCoordinateType`, 'JNOW') - this.targetRightAscension = this.preference.get(`mount.${this.mount.name}.targetRightAscension`, '00h00m00s') - this.targetDeclination = this.preference.get(`mount.${this.mount.name}.targetDeclination`, `00°00'00"`) + const preference = this.storage.get(`mount.${this.mount.name}`, {}) + this.targetCoordinateType = preference.targetCoordinateType ?? 'JNOW' + this.targetRightAscension = preference.targetRightAscension ?? '00h00m00s' + this.targetDeclination = preference.targetDeclination ?? `00°00'00"` this.computeTargetCoordinatePublisher.next() } } private savePreference() { if (this.mount && this.mount.connected) { - this.preference.set(`mount.${this.mount.name}.targetCoordinateType`, this.targetCoordinateType) - this.preference.set(`mount.${this.mount.name}.targetRightAscension`, this.targetRightAscension) - this.preference.set(`mount.${this.mount.name}.targetDeclination`, this.targetDeclination) + const preference: MountPreference = { + targetCoordinateType: this.targetCoordinateType, + targetRightAscension: this.targetRightAscension, + targetDeclination: this.targetDeclination, + } + + this.storage.set(`mount.${this.mount.name}`, preference) } } } diff --git a/desktop/src/app/settings/settings.component.html b/desktop/src/app/settings/settings.component.html new file mode 100644 index 000000000..16cf282ba --- /dev/null +++ b/desktop/src/app/settings/settings.component.html @@ -0,0 +1,44 @@ +
+
+ + +
+ + {{ item.label }} +
+
+
+
+
+
+
+ + + + +
+
+ + + +
+
+
+
+ + + + +
+
+ + + + +
+
+
+
\ No newline at end of file diff --git a/desktop/src/app/settings/settings.component.scss b/desktop/src/app/settings/settings.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/desktop/src/app/settings/settings.component.ts b/desktop/src/app/settings/settings.component.ts new file mode 100644 index 000000000..6ceb802ae --- /dev/null +++ b/desktop/src/app/settings/settings.component.ts @@ -0,0 +1,124 @@ +import { AfterViewInit, Component, HostListener, OnDestroy } from '@angular/core' +import { MenuItem } from 'primeng/api' +import { LocationDialog } from '../../shared/dialogs/location/location.dialog' +import { ApiService } from '../../shared/services/api.service' +import { ElectronService } from '../../shared/services/electron.service' +import { PrimeService } from '../../shared/services/prime.service' +import { EMPTY_LOCATION, Location, PlateSolverOptions, PlateSolverType } from '../../shared/types' +import { AppComponent } from '../app.component' + +@Component({ + selector: 'app-settings', + templateUrl: './settings.component.html', + styleUrls: ['./settings.component.scss'], +}) +export class SettingsComponent implements AfterViewInit, OnDestroy { + + activeTab = 0 + + locations: Location[] = [] + location = Object.assign({}, EMPTY_LOCATION) + private deletedLocations: Location[] = [] + + readonly plateSolvers: PlateSolverType[] = ['ASTAP', 'ASTROMETRY_NET'] + plateSolver!: PlateSolverOptions + + readonly items: MenuItem[] = [ + { + icon: 'mdi mdi-map-marker', + label: 'Location', + }, + { + icon: 'mdi mdi-sigma', + label: 'Plate Solver', + }, + ] + + item = this.items[0] + + constructor( + app: AppComponent, + private api: ApiService, + private electron: ElectronService, + private prime: PrimeService, + ) { + app.title = 'Settings' + + app.extra.push({ + icon: 'mdi mdi-content-save', + tooltip: 'Save changes', + command: () => { + this.save() + } + }) + } + + async ngAfterViewInit() { + this.plateSolver = await this.api.getPlateSolverSettings() + + this.loadLocation() + } + + @HostListener('window:unload') + ngOnDestroy() { } + + addLocation() { + this.showLocation(Object.assign({}, EMPTY_LOCATION)) + } + + editLocation() { + this.showLocation(this.location) + } + + private async showLocation(location: Location) { + const result = await this.prime.open(LocationDialog, { header: 'Location', data: location }) + + if (result && !this.locations.includes(result)) { + this.locations.push(result) + } + } + + private async loadLocation() { + this.locations = await this.api.locations() + this.location = this.locations.find(e => e.selected) ?? this.locations[0] ?? EMPTY_LOCATION + } + + async deleteLocation() { + if (this.location.id > 0) { + this.deletedLocations.push(this.location) + } + + const index = this.locations.indexOf(this.location) + + if (index >= 0) { + const deletedLocation = this.locations[index] + this.locations.splice(index, 1) + + if (this.location === deletedLocation) { + this.location = this.locations[0] + this.location.selected = true + } + } + } + + locationChanged() { + this.locations.forEach(e => e.selected = false) + this.location.selected = true + } + + async save() { + for (const location of this.locations) { + await this.api.saveLocation(location) + } + + for (const location of this.deletedLocations) { + await this.api.deleteLocation(location) + } + + this.deletedLocations = [] + await this.loadLocation() + this.electron.send('LOCATION_CHANGED', this.location) + + this.api.setPlateSolverSettings(this.plateSolver) + } +} \ No newline at end of file diff --git a/desktop/src/assets/fonts/icomoon.ttf b/desktop/src/assets/fonts/icomoon.ttf new file mode 100644 index 000000000..796fd516a Binary files /dev/null and b/desktop/src/assets/fonts/icomoon.ttf differ diff --git a/desktop/src/assets/icons/CREDITS.md b/desktop/src/assets/icons/CREDITS.md index d0c7b1449..9640a1559 100644 --- a/desktop/src/assets/icons/CREDITS.md +++ b/desktop/src/assets/icons/CREDITS.md @@ -2,12 +2,8 @@ * https://www.flaticon.com/free-icon/galaxy_1534067 * https://www.flaticon.com/free-icon/information_9195785 -* https://www.flaticon.com/free-icon/gps_856332 -* https://www.flaticon.com/free-icon/clock_3133110 -* https://www.flaticon.com/free-icon/map_854878 * https://www.flaticon.com/free-icon/remote-control_3567313 * https://www.flaticon.com/free-icon/sky_3982229 -* https://www.flaticon.com/free-icon/text-box_3815460 * https://www.flaticon.com/free-icon/histogram_2709742 * https://www.flaticon.com/free-icon/target_3207593 * https://www.flaticon.com/free-icon/camera-lens_5708327 @@ -19,12 +15,9 @@ * https://www.flaticon.com/free-icon/image-processing_6062419 * https://www.flaticon.com/free-icon/star_740882 * https://www.flaticon.com/free-icon/rotate_3303063 -* https://www.flaticon.com/free-icon/combination_8654893 -* https://www.flaticon.com/free-icon/rgb-print_4904473 * https://www.flaticon.com/free-icon/location_4631354 * https://www.flaticon.com/free-icon/rgb-print_7664547 -* https://www.flaticon.com/free-icon/setting_839374 -* https://www.flaticon.com/free-icon/filter_679991 +* https://www.flaticon.com/free-icon/cogwheel_3953226 * https://www.flaticon.com/free-icon/target_10542035 * https://www.flaticon.com/free-icon/contrast_439842 * https://www.flaticon.com/free-icon/full-moon_9689786 @@ -33,4 +26,7 @@ * https://www.flaticon.com/free-icon/stars_3266390 * https://www.flaticon.com/free-icon/milky-way_1086076 * https://www.flaticon.com/free-icon/satellite_1086093 -* https://www.flaticon.com/free-icon/gear_8753403 +* https://www.flaticon.com/free-icon/schedule_3652191 +* https://www.flaticon.com/free-icon/search_10770011 +* https://www.flaticon.com/free-icon/stack_3342239 +* https://thenounproject.com/icon/random-dither-4259782 diff --git a/desktop/src/assets/icons/about.png b/desktop/src/assets/icons/about.png index dcc3290ca..7a82378e1 100644 Binary files a/desktop/src/assets/icons/about.png and b/desktop/src/assets/icons/about.png differ diff --git a/desktop/src/assets/icons/asteroid.png b/desktop/src/assets/icons/asteroid.png index 3c4b7f4d8..c45e9308a 100644 Binary files a/desktop/src/assets/icons/asteroid.png and b/desktop/src/assets/icons/asteroid.png differ diff --git a/desktop/src/assets/icons/atlas.png b/desktop/src/assets/icons/atlas.png index dffc74958..5c4f59c2b 100644 Binary files a/desktop/src/assets/icons/atlas.png and b/desktop/src/assets/icons/atlas.png differ diff --git a/desktop/src/assets/icons/camera.png b/desktop/src/assets/icons/camera.png index cfcc105f9..2cdded1b5 100644 Binary files a/desktop/src/assets/icons/camera.png and b/desktop/src/assets/icons/camera.png differ diff --git a/desktop/src/assets/icons/filter-wheel.png b/desktop/src/assets/icons/filter-wheel.png index ed843f917..2daf28c9f 100644 Binary files a/desktop/src/assets/icons/filter-wheel.png and b/desktop/src/assets/icons/filter-wheel.png differ diff --git a/desktop/src/assets/icons/filter.png b/desktop/src/assets/icons/filter.png deleted file mode 100644 index b73b88f9b..000000000 Binary files a/desktop/src/assets/icons/filter.png and /dev/null differ diff --git a/desktop/src/assets/icons/focus.png b/desktop/src/assets/icons/focus.png index 6dfc87d46..155f13952 100644 Binary files a/desktop/src/assets/icons/focus.png and b/desktop/src/assets/icons/focus.png differ diff --git a/desktop/src/assets/icons/framing.png b/desktop/src/assets/icons/framing.png index e42b7720c..394a657b4 100644 Binary files a/desktop/src/assets/icons/framing.png and b/desktop/src/assets/icons/framing.png differ diff --git a/desktop/src/assets/icons/gear.png b/desktop/src/assets/icons/gear.png deleted file mode 100644 index a57fd0a7a..000000000 Binary files a/desktop/src/assets/icons/gear.png and /dev/null differ diff --git a/desktop/src/assets/icons/guider.png b/desktop/src/assets/icons/guider.png index 20897eb5f..70e48e71c 100644 Binary files a/desktop/src/assets/icons/guider.png and b/desktop/src/assets/icons/guider.png differ diff --git a/desktop/src/assets/icons/histogram.png b/desktop/src/assets/icons/histogram.png index 90c02cacf..ba4e5ec4d 100644 Binary files a/desktop/src/assets/icons/histogram.png and b/desktop/src/assets/icons/histogram.png differ diff --git a/desktop/src/assets/icons/image.png b/desktop/src/assets/icons/image.png index bd097621c..3b443000f 100644 Binary files a/desktop/src/assets/icons/image.png and b/desktop/src/assets/icons/image.png differ diff --git a/desktop/src/assets/icons/indi.png b/desktop/src/assets/icons/indi.png index 7d0bf1163..debf55518 100644 Binary files a/desktop/src/assets/icons/indi.png and b/desktop/src/assets/icons/indi.png differ diff --git a/desktop/src/assets/icons/jupiter.png b/desktop/src/assets/icons/jupiter.png index 6d2365460..880593dc7 100644 Binary files a/desktop/src/assets/icons/jupiter.png and b/desktop/src/assets/icons/jupiter.png differ diff --git a/desktop/src/assets/icons/location.png b/desktop/src/assets/icons/location.png index 1ce76af1c..9cdce86ae 100644 Binary files a/desktop/src/assets/icons/location.png and b/desktop/src/assets/icons/location.png differ diff --git a/desktop/src/assets/icons/map-marker.png b/desktop/src/assets/icons/map-marker.png index 37835cfbc..f63b28183 100644 Binary files a/desktop/src/assets/icons/map-marker.png and b/desktop/src/assets/icons/map-marker.png differ diff --git a/desktop/src/assets/icons/milky-way.png b/desktop/src/assets/icons/milky-way.png index 073c3817b..b6c1e3f78 100644 Binary files a/desktop/src/assets/icons/milky-way.png and b/desktop/src/assets/icons/milky-way.png differ diff --git a/desktop/src/assets/icons/moon.png b/desktop/src/assets/icons/moon.png index f64202ee2..0fe4e02a6 100644 Binary files a/desktop/src/assets/icons/moon.png and b/desktop/src/assets/icons/moon.png differ diff --git a/desktop/src/assets/icons/nebulosa-256.png b/desktop/src/assets/icons/nebulosa-256.png new file mode 100644 index 000000000..f4c5e6415 Binary files /dev/null and b/desktop/src/assets/icons/nebulosa-256.png differ diff --git a/desktop/src/assets/icons/nebulosa-512.png b/desktop/src/assets/icons/nebulosa-512.png deleted file mode 100644 index d0571cd7d..000000000 Binary files a/desktop/src/assets/icons/nebulosa-512.png and /dev/null differ diff --git a/desktop/src/assets/icons/nebulosa.png b/desktop/src/assets/icons/nebulosa.png index 38ab0d8d8..2d70654f2 100644 Binary files a/desktop/src/assets/icons/nebulosa.png and b/desktop/src/assets/icons/nebulosa.png differ diff --git a/desktop/src/assets/icons/observatory.png b/desktop/src/assets/icons/observatory.png index fe5814692..15420ca67 100644 Binary files a/desktop/src/assets/icons/observatory.png and b/desktop/src/assets/icons/observatory.png differ diff --git a/desktop/src/assets/icons/online-search.png b/desktop/src/assets/icons/online-search.png new file mode 100644 index 000000000..6c74d6dc1 Binary files /dev/null and b/desktop/src/assets/icons/online-search.png differ diff --git a/desktop/src/assets/icons/random-dither.svg b/desktop/src/assets/icons/random-dither.svg new file mode 100644 index 000000000..58f0c9c73 --- /dev/null +++ b/desktop/src/assets/icons/random-dither.svg @@ -0,0 +1 @@ + diff --git a/desktop/src/assets/icons/remote-control.png b/desktop/src/assets/icons/remote-control.png index 2fee819e9..3341d7c19 100644 Binary files a/desktop/src/assets/icons/remote-control.png and b/desktop/src/assets/icons/remote-control.png differ diff --git a/desktop/src/assets/icons/rgb.png b/desktop/src/assets/icons/rgb.png deleted file mode 100644 index 33edd1c04..000000000 Binary files a/desktop/src/assets/icons/rgb.png and /dev/null differ diff --git a/desktop/src/assets/icons/rotate.png b/desktop/src/assets/icons/rotate.png index 9073b2552..a1d936786 100644 Binary files a/desktop/src/assets/icons/rotate.png and b/desktop/src/assets/icons/rotate.png differ diff --git a/desktop/src/assets/icons/satellite.png b/desktop/src/assets/icons/satellite.png index 3a5a1291b..ef9f4660b 100644 Binary files a/desktop/src/assets/icons/satellite.png and b/desktop/src/assets/icons/satellite.png differ diff --git a/desktop/src/assets/icons/schedule.png b/desktop/src/assets/icons/schedule.png index b8a1d3809..5b879191a 100644 Binary files a/desktop/src/assets/icons/schedule.png and b/desktop/src/assets/icons/schedule.png differ diff --git a/desktop/src/assets/icons/settings.png b/desktop/src/assets/icons/settings.png index d6acd23dc..2c8a74b86 100644 Binary files a/desktop/src/assets/icons/settings.png and b/desktop/src/assets/icons/settings.png differ diff --git a/desktop/src/assets/icons/stack.png b/desktop/src/assets/icons/stack.png new file mode 100644 index 000000000..7cfd07d6b Binary files /dev/null and b/desktop/src/assets/icons/stack.png differ diff --git a/desktop/src/assets/icons/star.png b/desktop/src/assets/icons/star.png index 518d71ff7..b6d70ed1a 100644 Binary files a/desktop/src/assets/icons/star.png and b/desktop/src/assets/icons/star.png differ diff --git a/desktop/src/assets/icons/stars.png b/desktop/src/assets/icons/stars.png index 916705d97..eceb06ff8 100644 Binary files a/desktop/src/assets/icons/stars.png and b/desktop/src/assets/icons/stars.png differ diff --git a/desktop/src/assets/icons/sun.png b/desktop/src/assets/icons/sun.png index 545ac7b8e..326b05d5e 100644 Binary files a/desktop/src/assets/icons/sun.png and b/desktop/src/assets/icons/sun.png differ diff --git a/desktop/src/assets/icons/switch.png b/desktop/src/assets/icons/switch.png index 088c6cc8b..71f552c38 100644 Binary files a/desktop/src/assets/icons/switch.png and b/desktop/src/assets/icons/switch.png differ diff --git a/desktop/src/assets/icons/telescope.png b/desktop/src/assets/icons/telescope.png index dee85f31a..c1d0d039e 100644 Binary files a/desktop/src/assets/icons/telescope.png and b/desktop/src/assets/icons/telescope.png differ diff --git a/desktop/src/assets/icons/text-box.png b/desktop/src/assets/icons/text-box.png deleted file mode 100644 index 0a95948c8..000000000 Binary files a/desktop/src/assets/icons/text-box.png and /dev/null differ diff --git a/desktop/src/assets/images/splash.png b/desktop/src/assets/images/splash.png index a50b5bc99..aca9fba8b 100644 Binary files a/desktop/src/assets/images/splash.png and b/desktop/src/assets/images/splash.png differ diff --git a/desktop/src/index.html b/desktop/src/index.html index e75cf5e49..d4d0152a0 100644 --- a/desktop/src/index.html +++ b/desktop/src/index.html @@ -7,14 +7,10 @@ - - - - - + \ No newline at end of file diff --git a/desktop/src/shared/components/devicemenu/devicemenu.component.html b/desktop/src/shared/components/devicemenu/devicemenu.component.html new file mode 100644 index 000000000..d28537770 --- /dev/null +++ b/desktop/src/shared/components/devicemenu/devicemenu.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/desktop/src/shared/components/devicemenu/devicemenu.component.scss b/desktop/src/shared/components/devicemenu/devicemenu.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/desktop/src/shared/components/devicemenu/devicemenu.component.ts b/desktop/src/shared/components/devicemenu/devicemenu.component.ts new file mode 100644 index 000000000..542802201 --- /dev/null +++ b/desktop/src/shared/components/devicemenu/devicemenu.component.ts @@ -0,0 +1,74 @@ +import { Component, Input, ViewChild } from '@angular/core' +import { MenuItem, MessageService } from 'primeng/api' +import { SEPARATOR_MENU_ITEM } from '../../constants' +import { Device } from '../../types' +import { DialogMenuComponent } from '../dialogmenu/dialogmenu.component' + +@Component({ + selector: 'p-deviceMenu', + templateUrl: './devicemenu.component.html', + styleUrls: ['./devicemenu.component.scss'], +}) +export class DeviceMenuComponent { + + @Input() + readonly model: MenuItem[] = [] + + @Input() + readonly modelAtFirst: boolean = true + + @Input() + readonly disableIfNotConnected: boolean = true + + @ViewChild('menu') + private readonly menu!: DialogMenuComponent + + constructor(private message: MessageService) { } + + show(devices: T[]) { + const model: MenuItem[] = [] + + return new Promise((resolve) => { + if (devices.length <= 0 || !devices.find(e => e.connected)) { + resolve(undefined) + this.message.add({ severity: 'warn', detail: 'Please connect your equipment first!' }) + return + } + + const subscription = this.menu.visibleChange.subscribe((visible) => { + if (!visible) { + subscription.unsubscribe() + resolve(undefined) + } + }) + + if (this.model.length > 0 && this.modelAtFirst) { + model.push(...this.model) + model.push(SEPARATOR_MENU_ITEM) + } + + for (const device of devices) { + model.push({ + icon: 'mdi mdi-connection', + label: device.name, + disabled: this.disableIfNotConnected && !device.connected, + command: () => { + resolve(device) + }, + }) + } + + if (this.model.length > 0 && !this.modelAtFirst) { + model.push(SEPARATOR_MENU_ITEM) + model.push(...this.model) + } + + this.menu.model = model + this.menu.show() + }) + } + + hide() { + this.menu.hide() + } +} \ No newline at end of file diff --git a/desktop/src/shared/components/dialogmenu/dialogmenu.component.html b/desktop/src/shared/components/dialogmenu/dialogmenu.component.html index 54685f05b..05cb72c40 100644 --- a/desktop/src/shared/components/dialogmenu/dialogmenu.component.html +++ b/desktop/src/shared/components/dialogmenu/dialogmenu.component.html @@ -1,4 +1,4 @@ - + [dismissableMask]="true" [closable]="true" [draggable]="false" (onShow)="onShow()" (onHide)="hide()" [style]="{width: 'auto'}"> + \ No newline at end of file diff --git a/desktop/src/shared/components/dialogmenu/dialogmenu.component.scss b/desktop/src/shared/components/dialogmenu/dialogmenu.component.scss index e69de29bb..c73707ea5 100644 --- a/desktop/src/shared/components/dialogmenu/dialogmenu.component.scss +++ b/desktop/src/shared/components/dialogmenu/dialogmenu.component.scss @@ -0,0 +1,15 @@ +:host { + ::ng-deep { + .p-slidemenu { + background: #272727; + color: rgba(255, 255, 255, 0.87); + border: 0px; + border-radius: 0px; + padding: 12px; + } + + .p-menuitem-content { + border-radius: 4px; + } + } +} \ No newline at end of file diff --git a/desktop/src/shared/components/dialogmenu/dialogmenu.component.ts b/desktop/src/shared/components/dialogmenu/dialogmenu.component.ts index 483e1fbe2..1035ba13f 100644 --- a/desktop/src/shared/components/dialogmenu/dialogmenu.component.ts +++ b/desktop/src/shared/components/dialogmenu/dialogmenu.component.ts @@ -3,7 +3,7 @@ import { MenuItem } from 'primeng/api' import { SlideMenu } from 'primeng/slidemenu' @Component({ - selector: 'dialogMenu', + selector: 'p-dialogMenu', templateUrl: './dialogmenu.component.html', styleUrls: ['./dialogmenu.component.scss'], }) @@ -16,10 +16,10 @@ export class DialogMenuComponent { readonly visibleChange = new EventEmitter() @Input() - readonly model: MenuItem[] = [] + model: MenuItem[] = [] - @ViewChild('slideMenu') - private readonly slideMenu!: SlideMenu + @ViewChild('menu') + private readonly menu!: SlideMenu viewportHeight = 33.25 @@ -34,12 +34,12 @@ export class DialogMenuComponent { } protected onShow() { - const onItemClick = this.slideMenu.onItemClick + const onItemClick = this.menu.onItemClick - this.slideMenu.onItemClick = (e) => { + this.menu.onItemClick = (e) => { const size = e.processedItem.items.length if (size) this.viewportHeight = 33.25 * (size + 1) - onItemClick.call(this.slideMenu, e) + onItemClick.call(this.menu, e) if (size === 0) this.hide() } } diff --git a/desktop/src/shared/components/menuitem/menuitem.component.html b/desktop/src/shared/components/menuitem/menuitem.component.html new file mode 100644 index 000000000..78e6f5bd4 --- /dev/null +++ b/desktop/src/shared/components/menuitem/menuitem.component.html @@ -0,0 +1,14 @@ + +
+ {{ item.label }} +
+ @if (item.items?.length) { + + } + + @if (item.checked) { + + } @else if(item.toggleable) { + + } +
\ No newline at end of file diff --git a/desktop/src/shared/components/menuitem/menuitem.component.scss b/desktop/src/shared/components/menuitem/menuitem.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/desktop/src/shared/components/menuitem/menuitem.component.ts b/desktop/src/shared/components/menuitem/menuitem.component.ts new file mode 100644 index 000000000..1448b3d10 --- /dev/null +++ b/desktop/src/shared/components/menuitem/menuitem.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from '@angular/core' +import { MenuItem } from 'primeng/api' +import { CheckableMenuItem, ToggleableMenuItem } from '../../types' + +@Component({ + selector: 'p-menuItem', + templateUrl: './menuitem.component.html', + styleUrls: ['./menuitem.component.scss'], +}) +export class MenuItemComponent { + + @Input({ required: true }) + readonly item!: MenuItem | CheckableMenuItem | ToggleableMenuItem +} \ No newline at end of file diff --git a/desktop/src/shared/components/moon/moon.component.ts b/desktop/src/shared/components/moon/moon.component.ts index c18066b2d..70c6e6f55 100644 --- a/desktop/src/shared/components/moon/moon.component.ts +++ b/desktop/src/shared/components/moon/moon.component.ts @@ -1,7 +1,7 @@ import { AfterViewInit, Component, ElementRef, Input, OnChanges, SimpleChanges, ViewChild } from '@angular/core' @Component({ - selector: 'moon', + selector: 'p-moon', templateUrl: './moon.component.html', styleUrls: ['./moon.component.scss'], }) @@ -58,7 +58,7 @@ export class MoonComponent implements AfterViewInit, OnChanges { for (let a = 0; a < 180; a++) { const angle = (a - 90) * Math.PI / 180 let x1 = Math.ceil(Math.cos(angle) * cx) - let y1 = Math.ceil(Math.sin(angle) * cy) + const y1 = Math.ceil(Math.sin(angle) * cy) const moonWidth = x1 * 2 let x2 = Math.floor(moonWidth * this.illuminationRatio) diff --git a/desktop/src/shared/components/openstreetmap/openstreetmap.component.ts b/desktop/src/shared/components/openstreetmap/openstreetmap.component.ts index fcd1a98e0..66f77e46d 100644 --- a/desktop/src/shared/components/openstreetmap/openstreetmap.component.ts +++ b/desktop/src/shared/components/openstreetmap/openstreetmap.component.ts @@ -2,7 +2,7 @@ import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnChanges, O import * as L from 'leaflet' @Component({ - selector: 'openstreetmap', + selector: 'p-openStreetMap', templateUrl: './openstreetmap.component.html', styleUrls: ['./openstreetmap.component.scss'], }) diff --git a/desktop/src/shared/constants.ts b/desktop/src/shared/constants.ts index 73aaec7b9..0d6e1f0b3 100644 --- a/desktop/src/shared/constants.ts +++ b/desktop/src/shared/constants.ts @@ -1,5 +1,11 @@ +import { MenuItem } from 'primeng/api' + export const EVERY_MINUTE_CRON_TIME = '0 */1 * * * *' export const TWO_DIGITS_FORMATTER = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 2, minimumFractionDigits: 0, maximumFractionDigits: 0 }) export const THREE_DIGITS_FORMATTER = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 3, minimumFractionDigits: 0, maximumFractionDigits: 0 }) export const ONE_DECIMAL_PLACE_FORMATTER = new Intl.NumberFormat('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + +export const SEPARATOR_MENU_ITEM: MenuItem = { + separator: true, +} diff --git a/desktop/src/shared/dialogs/location/location.dialog.html b/desktop/src/shared/dialogs/location/location.dialog.html index f09e5689b..46c417b1f 100644 --- a/desktop/src/shared/dialogs/location/location.dialog.html +++ b/desktop/src/shared/dialogs/location/location.dialog.html @@ -1,40 +1,40 @@
- +
-
-
-
-
- +