diff --git a/.github/dependabot.yml b/.github/dependabot.yml index abfc3e194..bc49cdc12 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -36,6 +36,9 @@ updates: apache: patterns: - "org.apache*" + junit: + patterns: + - "org.junit*" - package-ecosystem: "npm" directory: "/desktop" diff --git a/.github/workflows/angular.yml b/.github/workflows/angular.yml index 1dfa7a11e..5b6d60d44 100644 --- a/.github/workflows/angular.yml +++ b/.github/workflows/angular.yml @@ -25,7 +25,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 20 - name: Install Dependencies run: npm i diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 05557ac63..613f9b1a7 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -2,8 +2,8 @@ import org.springframework.boot.gradle.tasks.bundling.BootJar plugins { kotlin("jvm") - id("org.springframework.boot") version "3.3.0" - id("io.spring.dependency-management") version "1.1.5" + id("org.springframework.boot") version "3.3.2" + id("io.spring.dependency-management") version "1.1.6" kotlin("plugin.spring") kotlin("kapt") id("io.objectbox") @@ -48,7 +48,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-undertow") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") - kapt("org.springframework:spring-context-indexer:6.1.8") + kapt("org.springframework:spring-context-indexer:6.1.11") testImplementation(project(":nebulosa-astrobin-api")) testImplementation(project(":nebulosa-skycatalog-stellarium")) testImplementation(project(":nebulosa-test")) diff --git a/api/schemas/objectbox.json b/api/schemas/objectbox.json index 811401385..0f7413c13 100644 --- a/api/schemas/objectbox.json +++ b/api/schemas/objectbox.json @@ -4,77 +4,77 @@ "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", "entities": [ { - "id": "1:4508028933515523414", - "lastPropertyId": "13:5569629325911720184", + "id": "1:3544801173480775772", + "lastPropertyId": "13:3755368355153819967", "name": "CalibrationFrameEntity", "properties": [ { - "id": "1:279471804400581871", + "id": "1:6440158350156700816", "name": "id", "type": 6, "flags": 1 }, { - "id": "2:9048727858630632737", + "id": "2:7830549305901803879", "name": "type", - "indexId": "1:3018423918314968566", + "indexId": "1:3705837194399110688", "type": 5, "flags": 8 }, { - "id": "3:5712791023807889534", - "name": "name", - "indexId": "2:8432810603549739468", + "id": "3:8490362500884478696", + "name": "group", + "indexId": "2:2460719507268221169", "type": 9, "flags": 2048 }, { - "id": "4:3434117744352502900", + "id": "4:169758157435742191", "name": "filter", "type": 9 }, { - "id": "5:1871034143652415809", + "id": "5:5772177826523179837", "name": "exposureTime", "type": 6 }, { - "id": "6:8846123268014704509", + "id": "6:979735190507089416", "name": "temperature", "type": 8 }, { - "id": "7:8561154143050278063", + "id": "7:1567591787936780727", "name": "width", "type": 5 }, { - "id": "8:6920579444153489022", + "id": "8:804894592407875320", "name": "height", "type": 5 }, { - "id": "9:4300769060778976734", + "id": "9:7150567366206966047", "name": "binX", "type": 5 }, { - "id": "10:4693474237106002327", + "id": "10:6904147472104067341", "name": "binY", "type": 5 }, { - "id": "11:8369728096653684761", + "id": "11:5805422636156073861", "name": "gain", "type": 8 }, { - "id": "12:617052828938607363", + "id": "12:3861144650886065321", "name": "path", "type": 9 }, { - "id": "13:5569629325911720184", + "id": "13:3755368355153819967", "name": "enabled", "type": 1 } @@ -82,25 +82,25 @@ "relations": [] }, { - "id": "2:4800249862026080527", - "lastPropertyId": "3:211299529025119304", + "id": "2:5695036645028998704", + "lastPropertyId": "3:5935807626551879093", "name": "PreferenceEntity", "properties": [ { - "id": "1:3593540058272630983", + "id": "1:1241938942467328378", "name": "id", "type": 6, "flags": 1 }, { - "id": "2:2699303611424729430", + "id": "2:5066364999797986961", "name": "key", - "indexId": "3:2030544424571300028", + "indexId": "3:361394127200064680", "type": 9, "flags": 34848 }, { - "id": "3:211299529025119304", + "id": "3:5935807626551879093", "name": "value", "type": 9 } @@ -108,28 +108,28 @@ "relations": [] }, { - "id": "3:9190695617085753667", - "lastPropertyId": "4:411434182698925224", + "id": "3:13725857459345728", + "lastPropertyId": "4:8575761112465612996", "name": "SatelliteEntity", "properties": [ { - "id": "1:7748265871438465999", + "id": "1:7008444193321057279", "name": "id", "type": 6, "flags": 129 }, { - "id": "2:2980713713220488130", + "id": "2:7254931361809919912", "name": "name", "type": 9 }, { - "id": "3:8036745814034214740", + "id": "3:7655077553453802998", "name": "tle", "type": 9 }, { - "id": "4:411434182698925224", + "id": "4:8575761112465612996", "name": "groups", "type": 30 } @@ -137,68 +137,68 @@ "relations": [] }, { - "id": "4:6299583728620001761", - "lastPropertyId": "12:4179508964623201115", + "id": "4:2355261488865870711", + "lastPropertyId": "12:8881688937650635468", "name": "SimbadEntity", "properties": [ { - "id": "1:7284883107181783588", + "id": "1:8754753767317947963", "name": "id", "type": 6, "flags": 129 }, { - "id": "2:1059978401562504177", + "id": "2:875189598014282513", "name": "name", "type": 9 }, { - "id": "3:2238737597611607433", + "id": "3:1840539013499888018", "name": "type", "type": 5 }, { - "id": "4:6034348124979703831", + "id": "4:8380920369067256416", "name": "rightAscensionJ2000", "type": 8 }, { - "id": "5:6603670815168137185", + "id": "5:4114744755808135895", "name": "declinationJ2000", "type": 8 }, { - "id": "6:4798847469480514750", + "id": "6:5877086147655445788", "name": "magnitude", "type": 8 }, { - "id": "7:4280564484498302769", + "id": "7:4614518058111040649", "name": "pmRA", "type": 8 }, { - "id": "8:1070997648386390650", + "id": "8:5619165542749552220", "name": "pmDEC", "type": 8 }, { - "id": "9:7408560810497672822", + "id": "9:8196290885692683478", "name": "parallax", "type": 8 }, { - "id": "10:7464931444484734827", + "id": "10:2681231197677728845", "name": "radialVelocity", "type": 8 }, { - "id": "11:531497562996887037", + "id": "11:2414643968839286765", "name": "redshift", "type": 8 }, { - "id": "12:4179508964623201115", + "id": "12:8881688937650635468", "name": "constellation", "type": 5 } @@ -206,8 +206,8 @@ "relations": [] } ], - "lastEntityId": "4:6299583728620001761", - "lastIndexId": "3:2030544424571300028", + "lastEntityId": "4:2355261488865870711", + "lastIndexId": "3:361394127200064680", "lastRelationId": "0:0", "lastSequenceId": "0:0", "modelVersion": 5, diff --git a/api/src/main/kotlin/nebulosa/api/Main.kt b/api/src/main/kotlin/nebulosa/api/Main.kt index b9eb21aaf..67929a568 100644 --- a/api/src/main/kotlin/nebulosa/api/Main.kt +++ b/api/src/main/kotlin/nebulosa/api/Main.kt @@ -1,6 +1,7 @@ package nebulosa.api import com.sun.jna.Platform +import nebulosa.time.SystemClock import org.springframework.boot.runApplication import java.nio.file.Path import java.time.LocalDate @@ -26,7 +27,7 @@ fun initAppDirectory(): Path { private fun Path.clearLogIfPastDays(days: Long = 7L) { if (exists()) { - val pastDays = LocalDate.now().minusDays(days) + val pastDays = LocalDate.now(SystemClock).minusDays(days) for (entry in listDirectoryEntries("nebulosa-*.log")) { val logDate = entry.fileName.toString() diff --git a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVStartRequest.kt b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVStartRequest.kt index 53b99114f..209792636 100644 --- a/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVStartRequest.kt +++ b/api/src/main/kotlin/nebulosa/api/alignment/polar/darv/DARVStartRequest.kt @@ -4,7 +4,7 @@ import nebulosa.api.cameras.CameraStartCaptureRequest import nebulosa.guiding.GuideDirection data class DARVStartRequest( - val capture: CameraStartCaptureRequest = CameraStartCaptureRequest.EMPTY, - val direction: GuideDirection = GuideDirection.NORTH, - val reversed: Boolean = false, + @JvmField val capture: CameraStartCaptureRequest = CameraStartCaptureRequest.EMPTY, + @JvmField val direction: GuideDirection = GuideDirection.NORTH, + @JvmField 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 64802dd6d..e455723ee 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/BodyPosition.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/BodyPosition.kt @@ -15,19 +15,19 @@ import nebulosa.nova.astrometry.Constellation import nebulosa.skycatalog.SkyObject data class BodyPosition( - @field:JsonSerialize(using = RightAscensionSerializer::class) val rightAscensionJ2000: Angle, - @field:JsonSerialize(using = DeclinationSerializer::class) val declinationJ2000: Angle, - @field:JsonSerialize(using = RightAscensionSerializer::class) val rightAscension: Angle, - @field:JsonSerialize(using = DeclinationSerializer::class) val declination: Angle, - @field:JsonSerialize(using = AzimuthSerializer::class) val azimuth: Angle, - @field:JsonSerialize(using = DeclinationSerializer::class) val altitude: Angle, - val magnitude: Double, - val constellation: Constellation, - val distance: Double, - val distanceUnit: String, - val illuminated: Double, - @field:JsonSerialize(using = DegreesSerializer::class) val elongation: Angle, - val leading: Boolean, // true = rises and sets BEFORE Sun. + @field:JsonSerialize(using = RightAscensionSerializer::class) @JvmField val rightAscensionJ2000: Angle, + @field:JsonSerialize(using = DeclinationSerializer::class) @JvmField val declinationJ2000: Angle, + @field:JsonSerialize(using = RightAscensionSerializer::class) @JvmField val rightAscension: Angle, + @field:JsonSerialize(using = DeclinationSerializer::class) @JvmField val declination: Angle, + @field:JsonSerialize(using = AzimuthSerializer::class) @JvmField val azimuth: Angle, + @field:JsonSerialize(using = DeclinationSerializer::class) @JvmField val altitude: Angle, + @JvmField val magnitude: Double, + @JvmField val constellation: Constellation, + @JvmField val distance: Double, + @JvmField val distanceUnit: String, + @JvmField val illuminated: Double, + @field:JsonSerialize(using = DegreesSerializer::class) @JvmField val elongation: Angle, + @JvmField val leading: Boolean, // true = rises and sets BEFORE Sun. ) { companion object { diff --git a/api/src/main/kotlin/nebulosa/api/atlas/CloseApproach.kt b/api/src/main/kotlin/nebulosa/api/atlas/CloseApproach.kt index 06914286a..c011e7c78 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/CloseApproach.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/CloseApproach.kt @@ -8,11 +8,11 @@ import java.time.format.DateTimeFormatter import java.util.* data class CloseApproach( - val name: String = "", - val designation: String = "", - val dateTime: Long = 0, - val distance: Double = 0.0, - val absoluteMagnitude: Double = 0.0, + @JvmField val name: String = "", + @JvmField val designation: String = "", + @JvmField val dateTime: Long = 0, + @JvmField val distance: Double = 0.0, + @JvmField val absoluteMagnitude: Double = 0.0, ) { companion object { diff --git a/api/src/main/kotlin/nebulosa/api/atlas/Location.kt b/api/src/main/kotlin/nebulosa/api/atlas/Location.kt index 7d92156e3..8bd3b0ca3 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/Location.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/Location.kt @@ -15,7 +15,7 @@ data class Location( @field:JsonSerialize(using = DegreesSerializer::class) @field:JsonDeserialize(using = DegreesDeserializer::class) override val latitude: Angle = 0.0, @field:JsonSerialize(using = DegreesSerializer::class) @field:JsonDeserialize(using = DegreesDeserializer::class) override val longitude: Angle = 0.0, @field:JsonSerialize(using = MetersSerializer::class) @field:JsonDeserialize(using = MetersDeserializer::class) override val elevation: Distance = 0.0, - val offsetInMinutes: Int = 0, + @JvmField val offsetInMinutes: Int = 0, ) : GeographicCoordinate, TimeZonedInSeconds { override val offsetInSeconds = offsetInMinutes * 60 diff --git a/api/src/main/kotlin/nebulosa/api/atlas/MinorPlanet.kt b/api/src/main/kotlin/nebulosa/api/atlas/MinorPlanet.kt index 423a774b0..1cdfcf562 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/MinorPlanet.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/MinorPlanet.kt @@ -3,20 +3,21 @@ package nebulosa.api.atlas import nebulosa.sbd.SmallBody data class MinorPlanet( - val found: Boolean = false, - val name: String = "", - val spkId: Int = -1, - val kind: SmallBody.BodyKind? = null, - val pha: Boolean = false, val neo: Boolean = false, - val orbitType: String = "", - val parameters: List = emptyList(), - val searchItems: List = emptyList(), + @JvmField val found: Boolean = false, + @JvmField val name: String = "", + @JvmField val spkId: Int = -1, + @JvmField val kind: SmallBody.BodyKind? = null, + @JvmField val pha: Boolean = false, + @JvmField val neo: Boolean = false, + @JvmField val orbitType: String = "", + @JvmField val parameters: List = emptyList(), + @JvmField val list: List = emptyList(), ) { data class OrbitalPhysicalParameter( - val name: String, - val description: String, - val value: String, + @JvmField val name: String, + @JvmField val description: String, + @JvmField val value: String, ) { constructor(param: SmallBody.OrbitElement) : this( @@ -31,8 +32,8 @@ data class MinorPlanet( } data class SearchItem( - val name: String, - val pdes: String, + @JvmField val name: String, + @JvmField val pdes: String, ) companion object { @@ -59,8 +60,8 @@ data class MinorPlanet( body.body!!.pha, body.body!!.neo, body.body?.type?.name ?: "", items, ) } else if (body.list != null) { - val searchItems = body.list!!.map { SearchItem(it.name, it.pdes) } - return MinorPlanet(searchItems = searchItems) + val list = body.list!!.map { SearchItem(it.name, it.pdes) } + return MinorPlanet(list = list) } else { return EMPTY } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteEntity.kt b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteEntity.kt index bdaaf93bf..bed2ebab2 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/SatelliteEntity.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SatelliteEntity.kt @@ -7,7 +7,7 @@ import nebulosa.api.database.BoxEntity @Entity data class SatelliteEntity( @Id(assignable = true) override var id: Long = 0L, - var name: String = "", - var tle: String = "", - var groups: MutableList = ArrayList(0), + @JvmField var name: String = "", + @JvmField var tle: String = "", + @JvmField var groups: MutableList = ArrayList(0), ) : BoxEntity diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasService.kt b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasService.kt index 23c12e405..5831048c4 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasService.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasService.kt @@ -19,6 +19,7 @@ import nebulosa.nova.position.Geoid import nebulosa.sbd.SmallBodyDatabaseService import nebulosa.skycatalog.SkyObject import nebulosa.skycatalog.SkyObjectType +import nebulosa.time.SystemClock import nebulosa.time.TimeZonedInSeconds import okhttp3.OkHttpClient import okhttp3.Request @@ -122,7 +123,7 @@ class SkyAtlasService( val nauticalDawn = doubleArrayOf(0.0, 0.0) val civilDawn = doubleArrayOf(0.0, 0.0) - val ephemeris = bodyEphemeris(if (fast) VSOP87E.SUN else SUN, location, LocalDateTime.of(date, LocalTime.now()), true) + val ephemeris = bodyEphemeris(if (fast) VSOP87E.SUN else SUN, location, LocalDateTime.of(date, LocalTime.now(SystemClock)), true) val (a) = findDiscrete(0.0, (ephemeris.size - 1).toDouble(), TwilightDiscreteFunction(ephemeris), 1.0) civilDusk[0] = a[0] / 60.0 @@ -147,12 +148,12 @@ class SkyAtlasService( } fun altitudePointsOfSun(location: GeographicCoordinate, date: LocalDate, stepSize: Int, fast: Boolean = false): List { - val ephemeris = bodyEphemeris(if (fast) VSOP87E.SUN else SUN, location, LocalDateTime.of(date, LocalTime.now()), true) + val ephemeris = bodyEphemeris(if (fast) VSOP87E.SUN else SUN, location, LocalDateTime.of(date, LocalTime.now(SystemClock)), true) return altitudePointsOfBody(ephemeris, stepSize) } fun altitudePointsOfMoon(location: GeographicCoordinate, date: LocalDate, stepSize: Int, fast: Boolean = false): List { - val ephemeris = bodyEphemeris(if (fast) FAST_MOON else MOON, location, LocalDateTime.of(date, LocalTime.now()), true) + val ephemeris = bodyEphemeris(if (fast) FAST_MOON else MOON, location, LocalDateTime.of(date, LocalTime.now(SystemClock)), true) return altitudePointsOfBody(ephemeris, stepSize) } @@ -161,7 +162,7 @@ class SkyAtlasService( stepSize: Int, fast: Boolean = false ): List { val target: Any = VSOP87E.entries.takeIf { fast }?.find { "${it.target}" == code } ?: code - val ephemeris = bodyEphemeris(target, location, LocalDateTime.of(date, LocalTime.now()), true) + val ephemeris = bodyEphemeris(target, location, LocalDateTime.of(date, LocalTime.now(SystemClock)), true) return altitudePointsOfBody(ephemeris, stepSize) } @@ -169,12 +170,12 @@ class SkyAtlasService( val target = cachedSimbadEntities[id] ?: simbadEntityRepository.find(id) ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Cannot found sky object: [$id]") cachedSimbadEntities[id] = target - val ephemeris = bodyEphemeris(target, location, LocalDateTime.of(date, LocalTime.now()), true) + val ephemeris = bodyEphemeris(target, location, LocalDateTime.of(date, LocalTime.now(SystemClock)), true) return altitudePointsOfBody(ephemeris, stepSize) } fun altitudePointsOfSatellite(location: GeographicCoordinate, satellite: SatelliteEntity, date: LocalDate, stepSize: Int): List { - val ephemeris = bodyEphemeris("TLE@${satellite.tle}", location, LocalDateTime.of(date, LocalTime.now()), true) + val ephemeris = bodyEphemeris("TLE@${satellite.tle}", location, LocalDateTime.of(date, LocalTime.now(SystemClock)), true) return altitudePointsOfBody(ephemeris, stepSize) } diff --git a/api/src/main/kotlin/nebulosa/api/atlas/SkyObjectInsideCoordinate.kt b/api/src/main/kotlin/nebulosa/api/atlas/SkyObjectInsideCoordinate.kt index 10b0857bb..ac2e89655 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/SkyObjectInsideCoordinate.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/SkyObjectInsideCoordinate.kt @@ -9,9 +9,9 @@ import kotlin.math.cos import kotlin.math.sin data class SkyObjectInsideCoordinate( - private val rightAscension: Angle, - private val declination: Angle, - private val radius: Angle, + @JvmField val rightAscension: Angle, + @JvmField val declination: Angle, + @JvmField val radius: Angle, ) : QueryFilter { private val sinDEC = declination.sin diff --git a/api/src/main/kotlin/nebulosa/api/atlas/Twilight.kt b/api/src/main/kotlin/nebulosa/api/atlas/Twilight.kt index 3414234d5..3ec8cab46 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/Twilight.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/Twilight.kt @@ -2,11 +2,11 @@ package nebulosa.api.atlas @Suppress("ArrayInDataClass") data class Twilight( - val civilDusk: DoubleArray, - val nauticalDusk: DoubleArray, - val astronomicalDusk: DoubleArray, - val night: DoubleArray, - val astronomicalDawn: DoubleArray, - val nauticalDawn: DoubleArray, - val civilDawn: DoubleArray, + @JvmField val civilDusk: DoubleArray, + @JvmField val nauticalDusk: DoubleArray, + @JvmField val astronomicalDusk: DoubleArray, + @JvmField val night: DoubleArray, + @JvmField val astronomicalDawn: DoubleArray, + @JvmField val nauticalDawn: DoubleArray, + @JvmField val civilDawn: DoubleArray, ) diff --git a/api/src/main/kotlin/nebulosa/api/beans/annotations/Subscriber.kt b/api/src/main/kotlin/nebulosa/api/beans/annotations/Subscriber.kt index f49f4c467..d9bfee611 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/annotations/Subscriber.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/annotations/Subscriber.kt @@ -2,7 +2,6 @@ package nebulosa.api.beans.annotations import org.springframework.context.annotation.Lazy -@Retention @Lazy(false) @Target(AnnotationTarget.CLASS) annotation class Subscriber 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 06571b436..698ad108a 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/configurations/BeanConfiguration.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/configurations/BeanConfiguration.kt @@ -13,12 +13,12 @@ import nebulosa.api.calibration.CalibrationFrameEntity import nebulosa.api.database.MyObjectBox import nebulosa.api.preference.PreferenceEntity import nebulosa.common.concurrency.DaemonThreadFactory -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.PathDeserializer +import nebulosa.json.PathSerializer import nebulosa.log.loggerFor import nebulosa.phd2.client.PHD2Client import nebulosa.sbd.SmallBodyDatabaseService @@ -77,8 +77,8 @@ class BeanConfiguration { ): SimpleModule = kotlinModule() .apply { serializers.forEach { addSerializer(it) } } .apply { deserializers.forEach { addDeserializer(it.handledType() as Class, it) } } - .addSerializer(PathSerializer) - .addDeserializer(Path::class.java, PathDeserializer) + .addSerializer(PathSerializer()) + .addDeserializer(Path::class.java, PathDeserializer()) @Bean fun jackson2ObjectMapperBuilderCustomizer() = Jackson2ObjectMapperBuilderCustomizer { @@ -217,7 +217,6 @@ class BeanConfiguration { private const val MAX_CACHE_SIZE = 1024L * 1024L * 32L // 32MB - @JvmStatic private val LOG = loggerFor() @JvmStatic private val OKHTTP_LOG = loggerFor() } } diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/angle/AngleParam.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/angle/AngleParam.kt index 147d173a6..f89ae63fd 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/converters/angle/AngleParam.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/angle/AngleParam.kt @@ -1,6 +1,5 @@ package nebulosa.api.beans.converters.angle -@Retention @Target(AnnotationTarget.VALUE_PARAMETER) annotation class AngleParam( val name: String = "", diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/device/DeviceOrEntityParam.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/device/DeviceOrEntityParam.kt index 0d7e2970b..e02e8eebf 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/converters/device/DeviceOrEntityParam.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/device/DeviceOrEntityParam.kt @@ -1,7 +1,6 @@ package nebulosa.api.beans.converters.device @Target(AnnotationTarget.VALUE_PARAMETER) -@Retention(AnnotationRetention.RUNTIME) annotation class DeviceOrEntityParam( val name: String = "", val defaultValue: String = "" diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/location/LocationParam.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/location/LocationParam.kt index e2c1a1bf5..311e3db3e 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/converters/location/LocationParam.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/location/LocationParam.kt @@ -1,5 +1,4 @@ package nebulosa.api.beans.converters.location -@Retention @Target(AnnotationTarget.VALUE_PARAMETER) annotation class LocationParam diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/time/DateAndTimeParam.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/time/DateAndTimeParam.kt index cdcc85c38..e41fad45d 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/converters/time/DateAndTimeParam.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/time/DateAndTimeParam.kt @@ -1,10 +1,9 @@ package nebulosa.api.beans.converters.time -@Retention @Target(AnnotationTarget.VALUE_PARAMETER) annotation class DateAndTimeParam( val datePattern: String = "yyyy-MM-dd", - val timePattern: String = "HH:mm", + val timePattern: String = "HH:mm:ss", val noSeconds: Boolean = true, val nullable: Boolean = false, ) diff --git a/api/src/main/kotlin/nebulosa/api/beans/converters/time/DateAndTimeParamMethodArgumentResolver.kt b/api/src/main/kotlin/nebulosa/api/beans/converters/time/DateAndTimeParamMethodArgumentResolver.kt index 539204254..9574c4e70 100644 --- a/api/src/main/kotlin/nebulosa/api/beans/converters/time/DateAndTimeParamMethodArgumentResolver.kt +++ b/api/src/main/kotlin/nebulosa/api/beans/converters/time/DateAndTimeParamMethodArgumentResolver.kt @@ -3,6 +3,7 @@ package nebulosa.api.beans.converters.time import nebulosa.api.beans.converters.annotation import nebulosa.api.beans.converters.hasAnnotation import nebulosa.api.beans.converters.parameter +import nebulosa.time.SystemClock import org.springframework.core.MethodParameter import org.springframework.stereotype.Component import org.springframework.web.bind.support.WebDataBinderFactory @@ -36,17 +37,17 @@ class DateAndTimeParamMethodArgumentResolver : HandlerMethodArgumentResolver { val date = dateValue ?.let { LocalDate.parse(it, DateTimeFormatter.ofPattern(dateAndTimeParam.datePattern)) } - ?: if (dateAndTimeParam.nullable) null else LocalDate.now() + ?: if (dateAndTimeParam.nullable) null else LocalDate.now(SystemClock) if (type === LocalDate::class.java) return date val time = timeValue ?.let { LocalTime.parse(it, DateTimeFormatter.ofPattern(dateAndTimeParam.timePattern)) } - ?: if (dateAndTimeParam.nullable) null else LocalTime.now() + ?: if (dateAndTimeParam.nullable) null else LocalTime.now(SystemClock) if (type === LocalTime::class.java) return time - return LocalDateTime.of(date ?: LocalDate.now(), time ?: LocalTime.now()) + return LocalDateTime.of(date ?: LocalDate.now(SystemClock), time ?: LocalTime.now(SystemClock)) .let { if (dateAndTimeParam.noSeconds) it.withSecond(0).withNano(0) else it } } } diff --git a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameController.kt b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameController.kt index 199bf8db7..d69ec5960 100644 --- a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameController.kt +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameController.kt @@ -1,7 +1,6 @@ package nebulosa.api.calibration import jakarta.validation.Valid -import jakarta.validation.constraints.NotBlank import org.springframework.validation.annotation.Validated import org.springframework.web.bind.annotation.* import java.nio.file.Path @@ -16,26 +15,24 @@ class CalibrationFrameController( @GetMapping fun groups() = calibrationFrameService.groups() - @GetMapping("{name}") - fun groupedCalibrationFrames(@PathVariable name: String): List { - var id = 0 - val groupedFrames = calibrationFrameService.groupedCalibrationFrames(name) - return groupedFrames.map { CalibrationFrameGroup(++id, name, it.key, it.value) } + @GetMapping("{group}") + fun frames(@PathVariable group: String): List { + return calibrationFrameService.frames(group).sorted() } - @PutMapping("{name}") - fun upload(@PathVariable name: String, @RequestParam path: Path): List { - return calibrationFrameService.upload(name, path) + @PutMapping("{group}") + fun upload(@PathVariable group: String, @RequestParam path: Path): List { + return calibrationFrameService.upload(group, path) } - @PatchMapping("{frame}") - fun edit( - frame: CalibrationFrameEntity, - @Valid @NotBlank @RequestParam name: String, @RequestParam enabled: Boolean, - ) = calibrationFrameService.edit(frame, name, enabled) + @PostMapping + fun update(@RequestBody @Valid body: CalibrationFrameEntity): CalibrationFrameEntity { + require(body.id > 0L) { "invalid frame id" } + return calibrationFrameService.edit(body) + } - @DeleteMapping("{frame}") - fun delete(frame: CalibrationFrameEntity) { - calibrationFrameService.delete(frame) + @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 index 631dc5b9e..4e684e1b4 100644 --- a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameEntity.kt +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameEntity.kt @@ -7,22 +7,47 @@ import io.objectbox.annotation.Index import nebulosa.api.beans.converters.database.FrameTypePropertyConverter import nebulosa.api.beans.converters.database.PathPropertyConverter import nebulosa.api.database.BoxEntity +import nebulosa.fits.INVALID_TEMPERATURE import nebulosa.indi.device.camera.FrameType import java.nio.file.Path @Entity data class CalibrationFrameEntity( @Id override var id: Long = 0L, - @Index @Convert(converter = FrameTypePropertyConverter::class, dbType = Int::class) var type: FrameType = FrameType.LIGHT, - @Index var name: String = "", - var filter: String? = null, - var exposureTime: Long = 0L, - var temperature: Double = 0.0, - var width: Int = 0, - var height: Int = 0, - var binX: Int = 0, - var binY: Int = 0, - var gain: Double = 0.0, - @Convert(converter = PathPropertyConverter::class, dbType = String::class) var path: Path? = null, - var enabled: Boolean = true, -) : BoxEntity + @JvmField @Index @Convert(converter = FrameTypePropertyConverter::class, dbType = Int::class) var type: FrameType = FrameType.LIGHT, + @JvmField @Index var group: String = "", + @JvmField var filter: String? = null, + @JvmField var exposureTime: Long = 0L, + @JvmField var temperature: Double = INVALID_TEMPERATURE, + @JvmField var width: Int = 0, + @JvmField var height: Int = 0, + @JvmField var binX: Int = 0, + @JvmField var binY: Int = 0, + @JvmField var gain: Double = 0.0, + @JvmField @Convert(converter = PathPropertyConverter::class, dbType = String::class) var path: Path? = null, + @JvmField var enabled: Boolean = true, +) : BoxEntity, Comparable { + + override fun compareTo(other: CalibrationFrameEntity): Int { + return if (type.ordinal > other.type.ordinal) 1 + else if (type.ordinal < other.type.ordinal) -1 + else if (exposureTime > other.exposureTime) 1 + else if (exposureTime < other.exposureTime) -1 + else if (width > other.width) 1 + else if (width < other.width) -1 + else if (height > other.height) 1 + else if (height < other.height) -1 + else if (binX > other.binX) 1 + else if (binX < other.binX) -1 + else if (binY > other.binY) 1 + else if (binY < other.binY) -1 + else if (gain > other.gain) 1 + else if (gain < other.gain) -1 + else if (temperature > other.temperature) 1 + else if (temperature < other.temperature) -1 + else if (filter != null && other.filter != null) filter!!.compareTo(other.filter!!) + else if (filter == null) -1 + else if (other.filter == null) 1 + else 0 + } +} diff --git a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameGroup.kt b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameGroup.kt deleted file mode 100644 index 7e4171cda..000000000 --- a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameGroup.kt +++ /dev/null @@ -1,8 +0,0 @@ -package nebulosa.api.calibration - -data class CalibrationFrameGroup( - val id: Int, - val name: String, - 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 index 056d8ac54..e91a4c454 100644 --- a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameRepository.kt +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameRepository.kt @@ -11,24 +11,24 @@ import org.springframework.stereotype.Component class CalibrationFrameRepository(@Qualifier("calibrationFrameBox") override val box: Box) : BoxRepository() { - fun groups() = box.all.map { it.name }.distinct() + fun groups() = box.all.map { it.group }.distinct() - fun findAll(name: String): List { - return box.query(CalibrationFrameEntity_.name equal name) + fun findAll(group: String): List { + return box.query(CalibrationFrameEntity_.group equal group) .build().use { it.find() } } @Synchronized - fun delete(name: String, path: String) { - val condition = and(CalibrationFrameEntity_.name equal name, CalibrationFrameEntity_.path equal path) + fun delete(group: String, path: String) { + val condition = and(CalibrationFrameEntity_.group equal group, CalibrationFrameEntity_.path equal path) return box.query(condition).build().use { it.remove() } } - fun darkFrames(name: String, width: Int, height: Int, bin: Int, exposureTime: Long, gain: Double): List { + fun darkFrames(group: String, width: Int, height: Int, bin: Int, exposureTime: Long, gain: Double): List { val condition = and( CalibrationFrameEntity_.type equal FrameType.DARK.ordinal, CalibrationFrameEntity_.enabled.isTrue, - CalibrationFrameEntity_.name equal name, + CalibrationFrameEntity_.group equal group, CalibrationFrameEntity_.width equal width, CalibrationFrameEntity_.height equal height, CalibrationFrameEntity_.binX equal bin, @@ -40,11 +40,11 @@ class CalibrationFrameRepository(@Qualifier("calibrationFrameBox") override val return box.query(condition).build().use { it.find() } } - fun biasFrames(name: String, width: Int, height: Int, bin: Int, gain: Double): List { + fun biasFrames(group: String, width: Int, height: Int, bin: Int, gain: Double): List { val condition = and( CalibrationFrameEntity_.type equal FrameType.BIAS.ordinal, CalibrationFrameEntity_.enabled.isTrue, - CalibrationFrameEntity_.name equal name, + CalibrationFrameEntity_.group equal group, CalibrationFrameEntity_.width equal width, CalibrationFrameEntity_.height equal height, CalibrationFrameEntity_.binX equal bin, @@ -55,11 +55,11 @@ class CalibrationFrameRepository(@Qualifier("calibrationFrameBox") override val return box.query(condition).build().use { it.find() } } - fun flatFrames(name: String, filter: String?, width: Int, height: Int, bin: Int): List { + fun flatFrames(group: String, filter: String?, width: Int, height: Int, bin: Int): List { val condition = and( CalibrationFrameEntity_.type equal FrameType.FLAT.ordinal, CalibrationFrameEntity_.enabled.isTrue, - CalibrationFrameEntity_.name equal name, + CalibrationFrameEntity_.group equal group, CalibrationFrameEntity_.width equal width, CalibrationFrameEntity_.height equal height, CalibrationFrameEntity_.binX equal bin, diff --git a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameService.kt b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameService.kt index 012a7dbd7..c5e25be84 100644 --- a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameService.kt +++ b/api/src/main/kotlin/nebulosa/api/calibration/CalibrationFrameService.kt @@ -6,8 +6,8 @@ import nebulosa.image.algorithms.transformation.correction.BiasSubtraction import nebulosa.image.algorithms.transformation.correction.DarkSubtraction import nebulosa.image.algorithms.transformation.correction.FlatCorrection import nebulosa.image.format.ImageHdu -import nebulosa.image.format.ReadableHeader import nebulosa.indi.device.camera.FrameType +import nebulosa.indi.device.camera.FrameType.Companion.frameType import nebulosa.log.loggerFor import nebulosa.xisf.isXisf import nebulosa.xisf.xisf @@ -28,11 +28,11 @@ class CalibrationFrameService( private val calibrationFrameRepository: CalibrationFrameRepository, ) : CalibrationFrameProvider { - fun calibrate(name: String, image: Image, createNew: Boolean = false): Image { + fun calibrate(group: String, image: Image, createNew: Boolean = false): Image { return synchronized(image) { - val darkFrame = findBestDarkFrames(name, image).firstOrNull() - val biasFrame = if (darkFrame == null) findBestBiasFrames(name, image).firstOrNull() else null - val flatFrame = findBestFlatFrames(name, image).firstOrNull() + val darkFrame = findBestDarkFrames(group, image).firstOrNull() + val biasFrame = if (darkFrame == null) findBestBiasFrames(group, image).firstOrNull() else null + val flatFrame = findBestFlatFrames(group, image).firstOrNull() val darkImage = darkFrame?.path?.fits()?.use(Image::open) val biasImage = biasFrame?.path?.fits()?.use(Image::open) @@ -92,27 +92,28 @@ class CalibrationFrameService( } } - fun groups() = calibrationFrameRepository.groups() + fun groups(): List { + return calibrationFrameRepository.groups() + } - fun groupedCalibrationFrames(name: String): Map> { - val frames = calibrationFrameRepository.findAll(name) - return frames.groupBy(CalibrationGroupKey::from) + fun frames(group: String): List { + return calibrationFrameRepository.findAll(group) } - fun upload(name: String, path: Path): List { - val files = if (path.isRegularFile() && path.isFits) listOf(path) + fun upload(group: String, path: Path): List { + val files = if (path.isRegularFile()) listOf(path) else if (path.isDirectory()) path.listDirectoryEntries("*.{fits,fit,xisf}").filter { it.isRegularFile() } else return emptyList() - return upload(name, files) + return upload(group, files) } @Synchronized - fun upload(name: String, files: List): List { + fun upload(group: String, files: List): List { val frames = ArrayList(files.size) for (file in files) { - calibrationFrameRepository.delete(name, "$file") + calibrationFrameRepository.delete(group, "$file") try { val image = if (file.isFits()) file.fits() @@ -125,12 +126,12 @@ class CalibrationFrameService( 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 temperature = if (frameType == FrameType.DARK) header.temperature else INVALID_TEMPERATURE val gain = if (frameType != FrameType.FLAT) header.gain else 0.0 val filter = if (frameType == FrameType.FLAT) header.filter else null val frame = CalibrationFrameEntity( - 0L, frameType, name, filter, + 0L, frameType, group, filter, exposureTime, temperature, header.width, header.height, header.binX, header.binY, gain, file, @@ -147,23 +148,21 @@ class CalibrationFrameService( return frames } - fun edit(frame: CalibrationFrameEntity, name: String, enabled: Boolean): CalibrationFrameEntity { - frame.name = name - frame.enabled = enabled + fun edit(frame: CalibrationFrameEntity): CalibrationFrameEntity { return calibrationFrameRepository.save(frame) } - fun delete(frame: CalibrationFrameEntity) { - calibrationFrameRepository.delete(frame) + fun delete(id: Long) { + calibrationFrameRepository.delete(id) } override fun findBestDarkFrames( - name: String, temperature: Double, width: Int, height: Int, + group: String, temperature: Double, width: Int, height: Int, binX: Int, binY: Int, exposureTimeInMicroseconds: Long, gain: Double, ): List { val frames = calibrationFrameRepository - .darkFrames(name, width, height, binX, exposureTimeInMicroseconds, gain) + .darkFrames(group, width, height, binX, exposureTimeInMicroseconds, gain) if (frames.isEmpty()) return emptyList() @@ -175,62 +174,49 @@ class CalibrationFrameService( return groupedFrames.firstEntry().value } - fun findBestDarkFrames(name: String, image: Image): List { + fun findBestDarkFrames(group: String, image: Image): List { val header = image.header val temperature = header.temperature val binX = header.binX val exposureTime = header.exposureTimeInMicroseconds - return findBestDarkFrames(name, temperature, image.width, image.height, binX, binX, exposureTime, header.gain) + return findBestDarkFrames(group, temperature, image.width, image.height, binX, binX, exposureTime, header.gain) } override fun findBestFlatFrames( - name: String, width: Int, height: Int, + group: String, width: Int, height: Int, binX: Int, binY: Int, filter: String? ): List { // TODO: Generate master from matched frames. (Subtract the master bias frame from each flat frame) return calibrationFrameRepository - .flatFrames(name, filter, width, height, binX) + .flatFrames(group, filter, width, height, binX) } - fun findBestFlatFrames(name: String, image: Image): List { + fun findBestFlatFrames(group: String, image: Image): List { val header = image.header val filter = header.filter val binX = header.binX - return findBestFlatFrames(name, image.width, image.height, binX, binX, filter) + return findBestFlatFrames(group, image.width, image.height, binX, binX, filter) } override fun findBestBiasFrames( - name: String, width: Int, height: Int, + group: String, width: Int, height: Int, binX: Int, binY: Int, gain: Double, ): List { // TODO: Generate master from matched frames. - return calibrationFrameRepository - .biasFrames(name, width, height, binX, gain) + return calibrationFrameRepository.biasFrames(group, width, height, binX, gain) } - fun findBestBiasFrames(name: String, image: Image): List { + fun findBestBiasFrames(group: String, image: Image): List { val header = image.header val binX = header.binX - return findBestBiasFrames(name, image.width, image.height, binX, binX, image.header.gain) + return findBestBiasFrames(group, image.width, image.height, binX, binX, image.header.gain) } companion object { @JvmStatic private val LOG = loggerFor() - - @JvmStatic val ReadableHeader.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 deleted file mode 100644 index dd659e464..000000000 --- a/api/src/main/kotlin/nebulosa/api/calibration/CalibrationGroupKey.kt +++ /dev/null @@ -1,24 +0,0 @@ -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/AutoSubFolderMode.kt b/api/src/main/kotlin/nebulosa/api/cameras/AutoSubFolderMode.kt index 3326d9570..aa0f46793 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/AutoSubFolderMode.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/AutoSubFolderMode.kt @@ -1,5 +1,6 @@ package nebulosa.api.cameras +import nebulosa.time.SystemClock import java.nio.file.Path import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -17,6 +18,7 @@ enum class AutoSubFolderMode { } fun pathFor(parentPath: Path, dateTime: LocalDateTime? = null): Path { - return if (this == OFF) parentPath else Path.of("$parentPath", directoryNameAt(dateTime ?: LocalDateTime.now())) + return if (this == OFF) parentPath + else Path.of("$parentPath", directoryNameAt(dateTime ?: LocalDateTime.now(SystemClock))) } } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureNamingFormat.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureNamingFormat.kt new file mode 100644 index 000000000..c30f8e47f --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureNamingFormat.kt @@ -0,0 +1,28 @@ +package nebulosa.api.cameras + +import nebulosa.api.cameras.CameraCaptureNamingFormatter.Companion.BIAS_FORMAT +import nebulosa.api.cameras.CameraCaptureNamingFormatter.Companion.DARK_FORMAT +import nebulosa.api.cameras.CameraCaptureNamingFormatter.Companion.FLAT_FORMAT +import nebulosa.api.cameras.CameraCaptureNamingFormatter.Companion.LIGHT_FORMAT +import nebulosa.indi.device.camera.FrameType + +data class CameraCaptureNamingFormat( + @JvmField val light: String? = null, + @JvmField val dark: String? = null, + @JvmField val flat: String? = null, + @JvmField val bias: String? = null, +) { + + fun formatFor(type: FrameType) = when (type) { + FrameType.LIGHT -> light?.ifBlank { null } ?: LIGHT_FORMAT + FrameType.DARK -> dark?.ifBlank { null } ?: DARK_FORMAT + FrameType.FLAT -> flat?.ifBlank { null } ?: FLAT_FORMAT + FrameType.BIAS -> bias?.ifBlank { null } ?: BIAS_FORMAT + } + + companion object { + + @JvmStatic val EMPTY = CameraCaptureNamingFormat() + @JvmStatic val DEFAULT = CameraCaptureNamingFormat(LIGHT_FORMAT, DARK_FORMAT, FLAT_FORMAT, BIAS_FORMAT) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureNamingFormatter.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureNamingFormatter.kt new file mode 100644 index 000000000..ab590b6f0 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureNamingFormatter.kt @@ -0,0 +1,100 @@ +package nebulosa.api.cameras + +import nebulosa.common.concurrency.atomic.Incrementer +import nebulosa.fits.* +import nebulosa.image.format.ReadableHeader +import nebulosa.indi.device.camera.Camera +import nebulosa.indi.device.camera.FrameType +import nebulosa.indi.device.camera.FrameType.Companion.frameType +import nebulosa.indi.device.filterwheel.FilterWheel +import nebulosa.indi.device.focuser.Focuser +import nebulosa.indi.device.mount.Mount +import nebulosa.indi.device.rotator.Rotator +import nebulosa.math.AngleFormatter +import nebulosa.math.format +import java.time.Clock +import java.time.LocalDate +import java.time.LocalTime +import kotlin.math.absoluteValue +import kotlin.math.roundToInt + +data class CameraCaptureNamingFormatter( + @JvmField internal val camera: Camera, + @JvmField internal val mount: Mount? = null, + @JvmField internal val wheel: FilterWheel? = null, + @JvmField internal val focuser: Focuser? = null, + @JvmField internal val rotator: Rotator? = null, + @JvmField internal val clock: Clock = Clock.systemDefaultZone(), + @JvmField internal val incrementer: Incrementer = Incrementer(), +) { + + fun format(text: String, header: ReadableHeader): String { + return REGEX.replace(text.trim()) { m -> + val value = m.groups[1]!!.value + val type = CameraCaptureNamingType.find(value) + + if (type == null) { + value + } else { + val groupValues = m.groups.mapNotNull { it?.value?.ifBlank { null } } + val arguments = if (groupValues.size > 2) groupValues.subList(2, groupValues.size) else emptyList() + type.replaceWith(header, arguments)?.ifBlank { null } ?: "" + } + }.replace(ILLEGAL_CHARS_REGEX, "") + } + + private fun CameraCaptureNamingType.replaceWith(header: ReadableHeader, args: List): String? { + return when (this) { + CameraCaptureNamingType.TYPE -> header.frameType?.name?.ifBlank { null } ?: FrameType.LIGHT.name + CameraCaptureNamingType.YEAR -> with(LocalDate.now(clock).year) { + when (args.firstOrNull()) { + "2" -> "%02d".format(this - 2000) + else -> "$this" + } + } + CameraCaptureNamingType.MONTH -> "%02d".format(LocalDate.now(clock).monthValue) + CameraCaptureNamingType.DAY -> "%02d".format(LocalDate.now(clock).dayOfMonth) + CameraCaptureNamingType.HOUR -> "%02d".format(LocalTime.now(clock).hour) + CameraCaptureNamingType.MIN -> "%02d".format(LocalTime.now(clock).minute) + CameraCaptureNamingType.SEC -> "%02d".format(LocalTime.now(clock).second) + CameraCaptureNamingType.MS -> "%03d".format(LocalTime.now(clock).nano / 1000000) + CameraCaptureNamingType.EXP -> with(header.exposureTime) { + when (args.firstOrNull()) { + "s" -> "%ds".format(toSeconds()) + "ms" -> "%dms".format(toMillis()) + else -> (toNanos() / 1000).toString() + } + } + CameraCaptureNamingType.FILTER -> header.filter?.ifBlank { null } + CameraCaptureNamingType.GAIN -> "${header.gain.roundToInt()}" + CameraCaptureNamingType.BIN -> "${header.binX}" + CameraCaptureNamingType.TEMP -> "${header.temperature.roundToInt()}" + CameraCaptureNamingType.RA -> header.rightAscension.takeIf { it.isFinite() }?.format(RA_FORMAT) + CameraCaptureNamingType.DEC -> header.declination.takeIf { it.isFinite() }?.format(DEC_FORMAT) + CameraCaptureNamingType.WIDTH -> "${header.width}" + CameraCaptureNamingType.HEIGHT -> "${header.height}" + CameraCaptureNamingType.CAMERA -> camera.name + CameraCaptureNamingType.MOUNT -> mount?.name ?: camera.snoopedDevices.firstOrNull { it is Mount }?.name + CameraCaptureNamingType.FOCUSER -> focuser?.name ?: camera.snoopedDevices.firstOrNull { it is Focuser }?.name + CameraCaptureNamingType.WHEEL -> wheel?.name ?: camera.snoopedDevices.firstOrNull { it is FilterWheel }?.name + CameraCaptureNamingType.ROTATOR -> rotator?.name ?: camera.snoopedDevices.firstOrNull { it is Rotator }?.name + CameraCaptureNamingType.N -> with(incrementer.increment()) { + val format = args.firstOrNull()?.toIntOrNull()?.absoluteValue ?: 4 + "%0${format}d".format(this) + } + } + } + + companion object { + + @JvmStatic private val REGEX = Regex("\\[(\\w+)(?::(\\w+))*]") + @JvmStatic private val ILLEGAL_CHARS_REGEX = Regex("[/\\\\:*?\"<>|]+") + @JvmStatic private val RA_FORMAT = AngleFormatter.HMS.newBuilder().secondsDecimalPlaces(0).build() + @JvmStatic private val DEC_FORMAT = AngleFormatter.SIGNED_DMS.newBuilder().secondsDecimalPlaces(0).separators("d", "m", "s").build() + + const val FLAT_FORMAT = "[camera]_[type]_[filter]_[width]_[height]_[bin]" + const val DARK_FORMAT = "[camera]_[type]_[width]_[height]_[exp]_[bin]_[gain]" + const val BIAS_FORMAT = "[camera]_[type]_[width]_[height]_[bin]_[gain]" + const val LIGHT_FORMAT = "[camera]_[type]_[year:2][month][day][hour][min][sec][ms]_[filter]_[width]_[height]_[exp]_[bin]_[gain]" + } +} diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureNamingType.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureNamingType.kt new file mode 100644 index 000000000..eb018cc9d --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureNamingType.kt @@ -0,0 +1,36 @@ +package nebulosa.api.cameras + +enum class CameraCaptureNamingType(private vararg val alias: String) { + TYPE, + YEAR, + MONTH, + DAY, + HOUR, + MIN("minute"), + SEC("second"), + MS, + EXP("exposure"), + FILTER, + GAIN, + BIN, + WIDTH("w"), + HEIGHT("h"), + TEMP("temperature"), + RA, + DEC, + CAMERA, + MOUNT, + FOCUSER, + WHEEL, + ROTATOR, + N; + + companion object { + + @JvmStatic + fun find(text: String): CameraCaptureNamingType? { + return entries.firstOrNull { it.name.equals(text, true) } + ?: entries.firstOrNull { e -> e.alias.isNotEmpty() && e.alias.any { it.equals(text, true) } } + } + } +} diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureTask.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureTask.kt index d8d3d4a98..6b2293693 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureTask.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureTask.kt @@ -32,6 +32,7 @@ import java.util.concurrent.Executor import java.util.concurrent.atomic.AtomicBoolean import kotlin.io.path.copyTo import kotlin.io.path.exists +import kotlin.io.path.extension data class CameraCaptureTask( @JvmField val camera: Camera, @@ -100,7 +101,7 @@ data class CameraCaptureTask( private fun LiveStackingRequest.processCalibrationGroup(): LiveStackingRequest { return if (calibrationFrameProvider != null && enabled && - !request.calibrationGroup.isNullOrBlank() && (dark == null || flat == null || bias == null) + !request.calibrationGroup.isNullOrBlank() && (darkPath == null || flatPath == null || biasPath == null) ) { val calibrationGroup = request.calibrationGroup val temperature = camera.temperature @@ -119,24 +120,27 @@ data class CameraCaptureTask( calibrationGroup, temperature, binX, binY, width, height, exposureTime, gain, filter ) - val newDark = dark?.takeIf { it.exists() } ?: calibrationFrameProvider + val newDarkPath = darkPath?.takeIf { it.exists() } ?: calibrationFrameProvider .findBestDarkFrames(calibrationGroup, temperature, width, height, binX, binY, exposureTime, gain) .firstOrNull() ?.path - val newFlat = flat?.takeIf { it.exists() } ?: calibrationFrameProvider + val newFlatPath = flatPath?.takeIf { it.exists() } ?: calibrationFrameProvider .findBestFlatFrames(calibrationGroup, width, height, binX, binY, filter) .firstOrNull() ?.path - val newBias = if (newDark != null) null else bias?.takeIf { it.exists() } ?: calibrationFrameProvider + val newBiasPath = if (newDarkPath != null) null else biasPath?.takeIf { it.exists() } ?: calibrationFrameProvider .findBestBiasFrames(calibrationGroup, width, height, binX, binY) .firstOrNull() ?.path - LOG.info("live stacking will use calibration frames. group={}, dark={}, flat={}, bias={}", calibrationGroup, newDark, newFlat, newBias) + LOG.info( + "live stacking will use calibration frames. group={}, dark={}, flat={}, bias={}", + calibrationGroup, newDarkPath, newFlatPath, newBiasPath + ) - copy(dark = newDark, flat = newFlat, bias = newBias) + copy(darkPath = newDarkPath, flatPath = newFlatPath, biasPath = newBiasPath) } else { this } @@ -255,10 +259,15 @@ data class CameraCaptureTask( // DITHER. if (!cancellationToken.isCancelled && !cameraExposureTask.isAborted && guider != null - && exposureCount >= 1 && exposureCount % request.dither.afterExposures == 0 + && exposureCount >= 1 && request.dither.afterExposures > 0 && exposureCount % request.dither.afterExposures == 0 ) { ditherAfterExposureTask.execute(cancellationToken) } + + if (cancellationToken.isPaused) { + pausing.set(false) + sendEvent(CameraCaptureState.PAUSED) + } } @Synchronized @@ -317,7 +326,8 @@ data class CameraCaptureTask( val isExposureFinished = state == CameraCaptureState.EXPOSURE_FINISHED val event = CameraCaptureEvent( - this, camera, if (pausing.get() && !isExposureFinished) CameraCaptureState.PAUSING else state, request.exposureAmount, exposureCount, + this, camera, if (pausing.get() && !isExposureFinished) CameraCaptureState.PAUSING else state, + request.exposureAmount, exposureCount, captureRemainingTime, captureElapsedTime, captureProgress, stepRemainingTime, stepElapsedTime, stepProgress, savedPath, liveStackedPath, @@ -334,7 +344,7 @@ data class CameraCaptureTask( sendEvent(CameraCaptureState.STACKING) liveStacker!!.add(path)?.let { - val stackedPath = Path.of("${path.parent}", "STACKED.fits") + val stackedPath = Path.of("${path.parent}", "STACKED.${it.extension}") it.copyTo(stackedPath, true) stackedPath } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureTask.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureTask.kt index 018417492..c592f3e53 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureTask.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraExposureTask.kt @@ -5,16 +5,20 @@ import nebulosa.common.concurrency.cancel.CancellationListener import nebulosa.common.concurrency.cancel.CancellationSource import nebulosa.common.concurrency.cancel.CancellationToken import nebulosa.common.concurrency.latch.CountUpDownLatch +import nebulosa.fits.fits +import nebulosa.image.format.ReadableHeader import nebulosa.indi.device.camera.* import nebulosa.io.transferAndClose import nebulosa.log.loggerFor import okio.sink +import java.nio.file.Files 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.deleteIfExists +import kotlin.io.path.moveTo import kotlin.io.path.outputStream data class CameraExposureTask( @@ -25,12 +29,14 @@ data class CameraExposureTask( private val latch = CountUpDownLatch() private val aborted = AtomicBoolean() - @Volatile private var state = CameraExposureState.IDLE @Volatile private var elapsedTime = Duration.ZERO @Volatile private var remainingTime = Duration.ZERO @Volatile private var progress = 0.0 @Volatile private var savedPath: Path? = null + private val outputPath = Files.createTempFile(camera.name, ".fits") + private val formatter = CameraCaptureNamingFormatter(camera) + val isAborted get() = aborted.get() @@ -52,8 +58,7 @@ data class CameraExposureTask( remainingTime = minOf(event.device.exposureTime, request.exposureTime) elapsedTime = exposureTime - remainingTime progress = elapsedTime.toNanos().toDouble() / exposureTime.toNanos() - state = CameraExposureState.ELAPSED - sendEvent() + sendEvent(CameraExposureState.ELAPSED) } } } @@ -67,8 +72,7 @@ data class CameraExposureTask( latch.countUp() - state = CameraExposureState.STARTED - sendEvent() + sendEvent(CameraExposureState.STARTED) with(camera) { enableBlob() @@ -109,30 +113,31 @@ data class CameraExposureTask( override fun close() { onCancel(CancellationSource.Close) + outputPath.deleteIfExists() super.close() } private fun save(event: CameraFrameCaptured) { try { - val savedPath = request.makeSavePath(event.device) - - LOG.info("saving FITS image at {}", savedPath) - - savedPath.createParentDirectories() - - if (event.stream != null) { - event.stream!!.transferAndClose(savedPath.outputStream()) + val header = if (event.stream != null) { + event.stream!!.transferAndClose(outputPath.outputStream()) + null } else if (event.image != null) { - savedPath.sink().use(event.image!!::write) + outputPath.sink().use(event.image!!::write) + event.image?.first()?.header } else { LOG.warn("invalid event. event={}", event) return } - this.savedPath = savedPath - state = CameraExposureState.FINISHED + with(request.makeSavePath(header = header)) { + LOG.info("saving FITS image at {}", this) + createParentDirectories() + outputPath.moveTo(this, true) + savedPath = this + } - sendEvent() + sendEvent(CameraExposureState.FINISHED) } catch (e: Throwable) { LOG.error("failed to save FITS image", e) aborted.set(true) @@ -141,30 +146,29 @@ data class CameraExposureTask( } } - private fun sendEvent() { + private fun sendEvent(state: CameraExposureState) { onNext(CameraExposureEvent(this, state, elapsedTime, remainingTime, progress, savedPath)) } + private fun CameraStartCaptureRequest.makeSavePath( + autoSave: Boolean = this.autoSave, + header: ReadableHeader? = null, + ): Path { + require(savePath != null) { "savePath is required" } + + return if (autoSave) { + val now = LocalDateTime.now(formatter.clock) + val savePath = autoSubFolderMode.pathFor(savePath, now) + val format = namingFormat.formatFor(frameType) + val fileName = formatter.format(format, header ?: outputPath.fits().use { it.first().header }) + Path.of("$savePath", "$fileName.fits") + } else { + Path.of("$savePath", "${formatter.camera.name}.fits") + } + } + companion object { @JvmStatic private val LOG = loggerFor() - @JvmStatic private val DATE_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd.HHmmssSSS") - - @JvmStatic - internal fun CameraStartCaptureRequest.makeSavePath( - camera: Camera, autoSave: Boolean = this.autoSave, - ): Path { - require(savePath != null) { "savePath is required" } - - return if (autoSave) { - val now = LocalDateTime.now() - val savePath = autoSubFolderMode.pathFor(savePath, now) - val fileName = "%s-%s.fits".format(now.format(DATE_TIME_FORMAT), frameType) - Path.of("$savePath", fileName) - } else { - val fileName = "%s.fits".format(camera.name) - Path.of("$savePath", fileName) - } - } } } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraSerializer.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraSerializer.kt index 97769f311..57384a107 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraSerializer.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraSerializer.kt @@ -13,6 +13,7 @@ class CameraSerializer(private val capturesPath: Path) : StdSerializer(C override fun serialize(value: Camera, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() + gen.writeStringField("type", value.type.name) gen.writeStringField("sender", value.sender.id) gen.writeStringField("id", value.id) gen.writeStringField("name", value.name) @@ -78,6 +79,7 @@ class CameraSerializer(private val capturesPath: Path) : StdSerializer(C private fun JsonGenerator.writeMainOrGuideHead(camera: Camera, fieldName: String) { writeObjectFieldStart(fieldName) + writeStringField("type", camera.type.name) writeStringField("id", camera.id) writeStringField("name", camera.name) writeStringField("sender", camera.sender.id) diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt index ac243e9d7..bb0ff0474 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt @@ -36,12 +36,14 @@ data class CameraStartCaptureRequest( @JvmField val autoSubFolderMode: AutoSubFolderMode = AutoSubFolderMode.OFF, @field:Valid @JvmField val dither: DitherAfterExposureRequest = DitherAfterExposureRequest.DISABLED, @JvmField val calibrationGroup: String? = null, - @JvmField val liveStacking: LiveStackingRequest = LiveStackingRequest.EMPTY, + @JvmField val liveStacking: LiveStackingRequest = LiveStackingRequest.DISABLED, // Filter Wheel. @JvmField val filterPosition: Int = 0, @JvmField val shutterPosition: Int = 0, // Focuser. @JvmField val focusOffset: Int = 0, + // Others. + @JvmField val namingFormat: CameraCaptureNamingFormat = CameraCaptureNamingFormat.DEFAULT, ) { inline val isLoop diff --git a/api/src/main/kotlin/nebulosa/api/connection/ConnectionStatus.kt b/api/src/main/kotlin/nebulosa/api/connection/ConnectionStatus.kt index 2eba3f9ae..f511fda92 100644 --- a/api/src/main/kotlin/nebulosa/api/connection/ConnectionStatus.kt +++ b/api/src/main/kotlin/nebulosa/api/connection/ConnectionStatus.kt @@ -1,8 +1,9 @@ package nebulosa.api.connection data class ConnectionStatus( - val id: String, - val type: ConnectionType, - val host: String, val port: Int, - val ip: String? = null, + @JvmField val id: String, + @JvmField val type: ConnectionType, + @JvmField val host: String, + @JvmField val port: Int, + @JvmField val ip: String? = null, ) diff --git a/api/src/main/kotlin/nebulosa/api/focusers/FocuserSerializer.kt b/api/src/main/kotlin/nebulosa/api/focusers/FocuserSerializer.kt index ab76e2024..9aeb365e3 100644 --- a/api/src/main/kotlin/nebulosa/api/focusers/FocuserSerializer.kt +++ b/api/src/main/kotlin/nebulosa/api/focusers/FocuserSerializer.kt @@ -11,6 +11,7 @@ class FocuserSerializer : StdSerializer(Focuser::class.java) { override fun serialize(value: Focuser, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() + gen.writeStringField("type", value.type.name) gen.writeStringField("sender", value.sender.id) gen.writeStringField("id", value.id) gen.writeStringField("name", value.name) diff --git a/api/src/main/kotlin/nebulosa/api/framing/FramingService.kt b/api/src/main/kotlin/nebulosa/api/framing/FramingService.kt index 5ed33f323..0c2b9eb10 100644 --- a/api/src/main/kotlin/nebulosa/api/framing/FramingService.kt +++ b/api/src/main/kotlin/nebulosa/api/framing/FramingService.kt @@ -1,22 +1,32 @@ package nebulosa.api.framing +import com.fasterxml.jackson.databind.ObjectMapper import nebulosa.fits.fits import nebulosa.hips2fits.FormatOutputType import nebulosa.hips2fits.Hips2FitsService +import nebulosa.hips2fits.HipsSurvey import nebulosa.image.Image import nebulosa.io.transferAndCloseOutput import nebulosa.log.loggerFor import nebulosa.math.Angle import nebulosa.platesolver.PlateSolution +import org.springframework.beans.factory.annotation.Value +import org.springframework.core.io.Resource import org.springframework.stereotype.Service import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.outputStream @Service -class FramingService(private val hips2FitsService: Hips2FitsService) { +class FramingService( + private val hips2FitsService: Hips2FitsService, + private val objectMapper: ObjectMapper, +) { - val availableHipsSurveys by lazy { hips2FitsService.availableSurveys().execute().body()!!.sorted() } + @Value("classpath:HIPS_SURVEYS.json") + private lateinit var hipsSurveysResource: Resource + + val availableHipsSurveys by lazy { hipsSurveysResource.inputStream.use { objectMapper.readValue(it, Array::class.java) }.sorted() } @Synchronized fun frame( diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputSerializer.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputSerializer.kt index 1d9c8d878..0e8232898 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputSerializer.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuideOutputSerializer.kt @@ -11,6 +11,7 @@ class GuideOutputSerializer : StdSerializer(GuideOutput::class.java override fun serialize(value: GuideOutput, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() + gen.writeStringField("type", value.type.name) gen.writeStringField("sender", value.sender.id) gen.writeStringField("id", value.id) gen.writeStringField("name", value.name) diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuiderInfo.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuiderInfo.kt index 3ce34f5b7..ec1d38a4f 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuiderInfo.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuiderInfo.kt @@ -3,10 +3,10 @@ package nebulosa.api.guiding import nebulosa.guiding.GuideState data class GuiderInfo( - val connected: Boolean = false, - val state: GuideState = GuideState.STOPPED, - val settling: Boolean = false, - val pixelScale: Double = 1.0, + @JvmField val connected: Boolean = false, + @JvmField val state: GuideState = GuideState.STOPPED, + @JvmField val settling: Boolean = false, + @JvmField val pixelScale: Double = 1.0, ) { companion object { diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuiderMessageEvent.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuiderMessageEvent.kt index 33ed3a7bc..8237710b5 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuiderMessageEvent.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuiderMessageEvent.kt @@ -2,4 +2,4 @@ package nebulosa.api.guiding import nebulosa.api.message.MessageEvent -data class GuiderMessageEvent(override val eventName: String, val data: Any? = null) : MessageEvent +data class GuiderMessageEvent(override val eventName: String, @JvmField val data: Any? = null) : MessageEvent diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidingController.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidingController.kt index ed2639a42..f0a54f1d4 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidingController.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidingController.kt @@ -56,11 +56,6 @@ class GuidingController(private val guidingService: GuidingService) { guidingService.settle(body) } - @GetMapping("settle") - fun settle(): SettleInfo { - return guidingService.settle() - } - @PutMapping("dither") fun dither( @RequestParam amount: Double, diff --git a/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt b/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt index 4b5c38ba7..422f31d64 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/GuidingService.kt @@ -88,10 +88,6 @@ class GuidingService( preferenceService.putJSON("GUIDER.SETTLE_INFO", settle) } - fun settle(): SettleInfo { - return SettleInfo.from(guider) - } - fun dither(amount: Double, raOnly: Boolean = false) { if (phd2Client.isOpen) { guider.dither(amount, raOnly) diff --git a/api/src/main/kotlin/nebulosa/api/guiding/HistoryStep.kt b/api/src/main/kotlin/nebulosa/api/guiding/HistoryStep.kt index a4f178d70..61f22abcd 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/HistoryStep.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/HistoryStep.kt @@ -3,11 +3,11 @@ package nebulosa.api.guiding import nebulosa.guiding.GuideStep data class HistoryStep( - val id: Long = 0L, - val rmsRA: Double = 0.0, - val rmsDEC: Double = 0.0, - val rmsTotal: Double = 0.0, - val guideStep: GuideStep? = null, - val ditherX: Double = 0.0, - val ditherY: Double = 0.0, + @JvmField val id: Long = 0L, + @JvmField val rmsRA: Double = 0.0, + @JvmField val rmsDEC: Double = 0.0, + @JvmField val rmsTotal: Double = 0.0, + @JvmField val guideStep: GuideStep? = null, + @JvmField val ditherX: Double = 0.0, + @JvmField val ditherY: Double = 0.0, ) diff --git a/api/src/main/kotlin/nebulosa/api/guiding/SettleInfo.kt b/api/src/main/kotlin/nebulosa/api/guiding/SettleInfo.kt index f46d6986d..e899eaf11 100644 --- a/api/src/main/kotlin/nebulosa/api/guiding/SettleInfo.kt +++ b/api/src/main/kotlin/nebulosa/api/guiding/SettleInfo.kt @@ -1,20 +1,15 @@ 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, + @Range(min = 1, max = 25) @JvmField val amount: Double = 1.5, + @Range(min = 1, max = 60) @JvmField val time: Long = 10, + @Range(min = 1, max = 60) @JvmField 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/AnnotateImageRequest.kt b/api/src/main/kotlin/nebulosa/api/image/AnnotateImageRequest.kt new file mode 100644 index 000000000..cc56de4e9 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/image/AnnotateImageRequest.kt @@ -0,0 +1,9 @@ +package nebulosa.api.image + +data class AnnotateImageRequest( + @JvmField val starsAndDSOs: Boolean = true, + @JvmField val minorPlanets: Boolean = false, + @JvmField val minorPlanetMagLimit: Double = 12.0, + @JvmField val includeMinorPlanetsWithoutMagnitude: Boolean = false, + @JvmField val useSimbad: Boolean = false, +) diff --git a/api/src/main/kotlin/nebulosa/api/image/CoordinateInterpolation.kt b/api/src/main/kotlin/nebulosa/api/image/CoordinateInterpolation.kt index 5fa45e25a..563c7723f 100644 --- a/api/src/main/kotlin/nebulosa/api/image/CoordinateInterpolation.kt +++ b/api/src/main/kotlin/nebulosa/api/image/CoordinateInterpolation.kt @@ -4,8 +4,12 @@ import java.time.LocalDateTime @Suppress("ArrayInDataClass") data class CoordinateInterpolation( - val ma: DoubleArray, - val md: DoubleArray, - val x0: Int, val y0: Int, val x1: Int, val y1: Int, - val delta: Int, val date: LocalDateTime?, + @JvmField val ma: DoubleArray, + @JvmField val md: DoubleArray, + @JvmField val x0: Int, + @JvmField val y0: Int, + @JvmField val x1: Int, + @JvmField val y1: Int, + @JvmField val delta: Int, + @JvmField val date: LocalDateTime?, ) diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageAnnotation.kt b/api/src/main/kotlin/nebulosa/api/image/ImageAnnotation.kt index 5253b0038..1db713fd5 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageAnnotation.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageAnnotation.kt @@ -15,9 +15,9 @@ import nebulosa.skycatalog.SkyObjectType data class ImageAnnotation( override val x: Double, override val y: Double, - val star: StarDSO? = null, - val dso: StarDSO? = null, - val minorPlanet: MinorPlanet? = null, + @JvmField val star: StarDSO? = null, + @JvmField val dso: StarDSO? = null, + @JvmField val minorPlanet: MinorPlanet? = null, ) : Point2D { data class StarDSO( @@ -48,6 +48,6 @@ data class ImageAnnotation( @field:JsonSerialize(using = RightAscensionSerializer::class) override val rightAscensionJ2000: Angle = 0.0, @field:JsonSerialize(using = DeclinationSerializer::class) override val declinationJ2000: Angle = 0.0, override val magnitude: Double = SkyObject.UNKNOWN_MAGNITUDE, - val constellation: Constellation = Constellation.find(ICRF.equatorial(rightAscensionJ2000, declinationJ2000)), + @JvmField val constellation: Constellation = Constellation.find(ICRF.equatorial(rightAscensionJ2000, declinationJ2000)), ) : SkyObject } diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageController.kt b/api/src/main/kotlin/nebulosa/api/image/ImageController.kt index 6c6d29312..33d886df0 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageController.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageController.kt @@ -38,17 +38,12 @@ class ImageController( imageService.saveImageAs(path, save, camera) } - @GetMapping("annotations") + @PutMapping("annotations") fun annotationsOfImage( @RequestParam path: Path, - @RequestParam(required = false, defaultValue = "true") starsAndDSOs: Boolean, - @RequestParam(required = false, defaultValue = "false") minorPlanets: Boolean, - @RequestParam(required = false, defaultValue = "12.0") minorPlanetMagLimit: Double, - @RequestParam(required = false, defaultValue = "false") includeMinorPlanetsWithoutMagnitude: Boolean, - @RequestParam(required = false, defaultValue = "false") useSimbad: Boolean, + @RequestBody request: AnnotateImageRequest, @LocationParam location: Location? = null, - ) = imageService - .annotations(path, starsAndDSOs, minorPlanets, minorPlanetMagLimit, includeMinorPlanetsWithoutMagnitude, useSimbad, location) + ) = imageService.annotations(path, request, location) @GetMapping("coordinate-interpolation") fun coordinateInterpolation(@RequestParam path: Path): CoordinateInterpolation? { diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageHeaderItem.kt b/api/src/main/kotlin/nebulosa/api/image/ImageHeaderItem.kt index faf357950..b02f3a15d 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageHeaderItem.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageHeaderItem.kt @@ -1,3 +1,3 @@ package nebulosa.api.image -data class ImageHeaderItem(val name: String, val value: String) +data class ImageHeaderItem(@JvmField val name: String, @JvmField val value: String) diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageInfo.kt b/api/src/main/kotlin/nebulosa/api/image/ImageInfo.kt index f9afc085d..757f7174d 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageInfo.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageInfo.kt @@ -10,14 +10,16 @@ import nebulosa.indi.device.camera.Camera import java.nio.file.Path data class ImageInfo( - val path: Path, - val width: Int, val height: Int, val mono: Boolean, - val stretchShadow: Float = 0.0f, val stretchHighlight: Float = 1.0f, val stretchMidtone: Float = 0.5f, - @field:JsonSerialize(using = RightAscensionSerializer::class) val rightAscension: Double? = null, - @field:JsonSerialize(using = DeclinationSerializer::class) val declination: Double? = null, - val solved: ImageSolved? = null, - val headers: List = emptyList(), - val bitpix: Bitpix = Bitpix.BYTE, - val camera: Camera? = null, - @JsonIgnoreProperties("histogram") val statistics: Statistics.Data? = null, + @JvmField val path: Path, + @JvmField val width: Int, + @JvmField val height: Int, + @JvmField val mono: Boolean, + @JvmField val stretch: ImageTransformation.Stretch, + @field:JsonSerialize(using = RightAscensionSerializer::class) @JvmField val rightAscension: Double? = null, + @field:JsonSerialize(using = DeclinationSerializer::class) @JvmField val declination: Double? = null, + @JvmField val solved: ImageSolved? = null, + @JvmField val headers: List = emptyList(), + @JvmField val bitpix: Bitpix = Bitpix.BYTE, + @JvmField val camera: Camera? = null, + @JsonIgnoreProperties("histogram") @JvmField val statistics: Statistics.Data? = null, ) diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt index 13ddb3b69..c7f638bb3 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt @@ -15,6 +15,7 @@ import nebulosa.image.algorithms.computation.Statistics import nebulosa.image.algorithms.transformation.* import nebulosa.image.format.ImageModifier import nebulosa.indi.device.camera.Camera +import nebulosa.log.debug import nebulosa.log.loggerFor import nebulosa.math.* import nebulosa.nova.astrometry.VSOP87E @@ -25,6 +26,7 @@ import nebulosa.simbad.SimbadService import nebulosa.skycatalog.ClassificationType import nebulosa.skycatalog.SkyObject import nebulosa.skycatalog.SkyObjectType +import nebulosa.time.SystemClock import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC import nebulosa.wcs.WCS @@ -42,6 +44,7 @@ import java.util.* import java.util.concurrent.CompletableFuture import javax.imageio.ImageIO import kotlin.io.path.outputStream +import kotlin.math.roundToInt @Service class ImageService( @@ -64,7 +67,7 @@ class ImageService( private data class TransformedImage( @JvmField val image: Image, @JvmField val statistics: Statistics.Data? = null, - @JvmField val strectchParams: ScreenTransformFunction.Parameters? = null, + @JvmField val stretchParameters: ScreenTransformFunction.Parameters? = null, @JvmField val instrument: Camera? = null, ) @@ -84,12 +87,16 @@ class ImageService( output: HttpServletResponse, ) { val (image, calibration) = imageBucket.open(path, transformation.debayer, force = transformation.force) - val (transformedImage, statistics, stretchParams, instrument) = image!!.transform(true, transformation, ImageOperation.OPEN, camera) + val (transformedImage, statistics, stretchParameters, instrument) = image!!.transform(true, transformation, ImageOperation.OPEN, camera) val info = ImageInfo( path, transformedImage.width, transformedImage.height, transformedImage.mono, - stretchParams!!.shadow, stretchParams.highlight, stretchParams.midtone, + transformation.stretch.copy( + shadow = (stretchParameters!!.shadow * 65536f).roundToInt(), + highlight = (stretchParameters.highlight * 65536f).roundToInt(), + midtone = (stretchParameters.midtone * 65536f).roundToInt(), + ), transformedImage.header.rightAscension.takeIf { it.isFinite() }, transformedImage.header.declination.takeIf { it.isFinite() }, calibration?.let(::ImageSolved), @@ -97,10 +104,14 @@ class ImageService( transformedImage.header.bitpix, instrument, statistics, ) + val format = if (transformation.useJPEG) "jpeg" else "png" + output.addHeader(IMAGE_INFO_HEADER, objectMapper.writeValueAsString(info)) - output.contentType = "image/png" + output.contentType = "image/$format" + + ImageIO.write(transformedImage, format, output.outputStream) - ImageIO.write(transformedImage, "PNG", output.outputStream) + LOG.debug { "image opened. path=$path" } } private fun Image.transform( @@ -111,7 +122,7 @@ class ImageService( val (autoStretch, shadow, highlight, midtone) = transformation.stretch val scnrEnabled = transformation.scnr.channel != null - val manualStretch = shadow != 0f || highlight != 1f || midtone != 0.5f + val manualStretch = shadow != 0 || highlight != 65536 || midtone != 32768 val shouldBeTransformed = enabled && (autoStretch || manualStretch || transformation.mirrorHorizontal || transformation.mirrorVertical || transformation.invert @@ -161,17 +172,11 @@ class ImageService( @Synchronized fun closeImage(path: Path) { imageBucket.remove(path) - LOG.info("image closed. path={}", path) + LOG.debug { "image closed. path=$path" } } @Synchronized - fun annotations( - path: Path, - starsAndDSOs: Boolean, minorPlanets: Boolean, - minorPlanetMagLimit: Double = 12.0, includeMinorPlanetsWithoutMagnitude: Boolean = false, - useSimbad: Boolean = false, - location: Location? = null, - ): List { + fun annotations(path: Path, request: AnnotateImageRequest, location: Location? = null): List { val (image, calibration) = imageBucket.open(path) if (image == null || calibration.isNullOrEmpty() || !calibration.solved) { @@ -181,16 +186,16 @@ class ImageService( val wcs = try { WCS(calibration) } catch (e: WCSException) { - LOG.error("unable to generate annotations for image. path={}", path) + LOG.error("unable to generate annotations for image. path={}", path, e) return emptyList() } val annotations = Vector(64) val tasks = ArrayList>(2) - val dateTime = image.header.observationDate ?: LocalDateTime.now() + val dateTime = image.header.observationDate ?: LocalDateTime.now(SystemClock) - if (minorPlanets) { + if (request.minorPlanets) { threadPoolTaskExecutor.submitCompletable { val latitude = image.header.latitude ?: location?.latitude?.deg ?: 0.0 val longitude = image.header.longitude ?: location?.longitude?.deg ?: 0.0 @@ -203,7 +208,7 @@ class ImageService( val identifiedBody = smallBodyDatabaseService.identify( dateTime, latitude, longitude, 0.0, calibration.rightAscension, calibration.declination, calibration.radius, - minorPlanetMagLimit, !includeMinorPlanetsWithoutMagnitude, + request.minorPlanetMagLimit, !request.includeMinorPlanetsWithoutMagnitude, ).execute().body() ?: return@submitCompletable val radiusInSeconds = calibration.radius.toArcsec @@ -229,15 +234,15 @@ class ImageService( .also(tasks::add) } - if (starsAndDSOs) { + if (request.starsAndDSOs) { threadPoolTaskExecutor.submitCompletable { - LOG.info("finding star/DSO annotations. dateTime={}, useSimbad={}, calibration={}", dateTime, useSimbad, calibration) + LOG.info("finding star/DSO annotations. dateTime={}, useSimbad={}, calibration={}", dateTime, request.useSimbad, calibration) val rightAscension = calibration.rightAscension val declination = calibration.declination val radius = calibration.radius - val catalog = if (useSimbad) { + val catalog = if (request.useSimbad) { simbadService.search(SimbadSearch.Builder().region(rightAscension, declination, radius).build()) } else { simbadEntityRepository.search(null, null, rightAscension, declination, radius) @@ -307,7 +312,7 @@ class ImageService( val wcs = try { WCS(calibration) } catch (e: WCSException) { - LOG.error("unable to generate annotations for image. path={}", path) + LOG.error("unable to generate annotations for image. path={}", path, e) return null } diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageSolved.kt b/api/src/main/kotlin/nebulosa/api/image/ImageSolved.kt index 5823fffc0..04202b45f 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageSolved.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageSolved.kt @@ -4,14 +4,14 @@ import nebulosa.math.* import nebulosa.platesolver.PlateSolution data class ImageSolved( - val solved: Boolean = false, - val orientation: Double = 0.0, - val scale: Double = 0.0, - val rightAscensionJ2000: String = "", - val declinationJ2000: String = "", - val width: Double = 0.0, - val height: Double = 0.0, - val radius: Double = 0.0, + @JvmField val solved: Boolean = false, + @JvmField val orientation: Double = 0.0, + @JvmField val scale: Double = 0.0, + @JvmField val rightAscensionJ2000: String = "", + @JvmField val declinationJ2000: String = "", + @JvmField val width: Double = 0.0, + @JvmField val height: Double = 0.0, + @JvmField val radius: Double = 0.0, ) { constructor(solution: PlateSolution) : this( diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageTransformation.kt b/api/src/main/kotlin/nebulosa/api/image/ImageTransformation.kt index 42455df07..ed20ba5c6 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageTransformation.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageTransformation.kt @@ -12,6 +12,7 @@ data class ImageTransformation( @JvmField val mirrorVertical: Boolean = false, @JvmField val invert: Boolean = false, @JvmField val scnr: SCNR = SCNR.EMPTY, + @JvmField val useJPEG: Boolean = false, ) { data class SCNR( @@ -28,9 +29,9 @@ data class ImageTransformation( data class Stretch( @JvmField val auto: Boolean = false, - @JvmField val shadow: Float = 0f, - @JvmField val highlight: Float = 0.5f, - @JvmField val midtone: Float = 1f, + @JvmField val shadow: Int = 0, + @JvmField val highlight: Int = 32768, + @JvmField val midtone: Int = 65536, ) { companion object { diff --git a/api/src/main/kotlin/nebulosa/api/image/SaveImage.kt b/api/src/main/kotlin/nebulosa/api/image/SaveImage.kt index 1bfa717f4..2c551086f 100644 --- a/api/src/main/kotlin/nebulosa/api/image/SaveImage.kt +++ b/api/src/main/kotlin/nebulosa/api/image/SaveImage.kt @@ -4,9 +4,9 @@ import nebulosa.fits.Bitpix import java.nio.file.Path data class SaveImage( - val format: ImageExtension = ImageExtension.FITS, - val bitpix: Bitpix = Bitpix.BYTE, - val shouldBeTransformed: Boolean = true, - val transformation: ImageTransformation = ImageTransformation.EMPTY, - val path: Path? = null, + @JvmField val format: ImageExtension = ImageExtension.FITS, + @JvmField val bitpix: Bitpix = Bitpix.BYTE, + @JvmField val shouldBeTransformed: Boolean = true, + @JvmField val transformation: ImageTransformation = ImageTransformation.EMPTY, + @JvmField val path: Path? = null, ) diff --git a/api/src/main/kotlin/nebulosa/api/indi/INDIMessageEvent.kt b/api/src/main/kotlin/nebulosa/api/indi/INDIMessageEvent.kt index 06c49459b..19b320255 100644 --- a/api/src/main/kotlin/nebulosa/api/indi/INDIMessageEvent.kt +++ b/api/src/main/kotlin/nebulosa/api/indi/INDIMessageEvent.kt @@ -9,8 +9,8 @@ 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, + @JvmField val property: PropertyVector<*, *>? = null, + @JvmField val message: String? = null, ) : DeviceMessageEvent { constructor(eventName: String, event: DevicePropertyEvent) : this(eventName, event.device, property = event.property) diff --git a/api/src/main/kotlin/nebulosa/api/indi/INDISendProperty.kt b/api/src/main/kotlin/nebulosa/api/indi/INDISendProperty.kt index 9e71146b8..3bd06d872 100644 --- a/api/src/main/kotlin/nebulosa/api/indi/INDISendProperty.kt +++ b/api/src/main/kotlin/nebulosa/api/indi/INDISendProperty.kt @@ -7,7 +7,7 @@ import jakarta.validation.constraints.NotNull import nebulosa.indi.protocol.PropertyType data class INDISendProperty( - @field:NotBlank val name: String = "", - @field:NotNull val type: PropertyType = PropertyType.SWITCH, - @field:NotEmpty @field:Valid val items: List = emptyList(), + @field:NotBlank @JvmField val name: String = "", + @field:NotNull @JvmField val type: PropertyType = PropertyType.SWITCH, + @field:NotEmpty @field:Valid @JvmField val items: List = emptyList(), ) diff --git a/api/src/main/kotlin/nebulosa/api/indi/INDISendPropertyItem.kt b/api/src/main/kotlin/nebulosa/api/indi/INDISendPropertyItem.kt index 8b506a378..c6d382497 100644 --- a/api/src/main/kotlin/nebulosa/api/indi/INDISendPropertyItem.kt +++ b/api/src/main/kotlin/nebulosa/api/indi/INDISendPropertyItem.kt @@ -4,6 +4,6 @@ import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull data class INDISendPropertyItem( - @field:NotBlank val name: String = "", - @field:NotNull val value: Any = "", + @field:NotBlank @JvmField val name: String = "", + @field:NotNull @JvmField val value: Any = "", ) diff --git a/api/src/main/kotlin/nebulosa/api/livestacker/LiveStackingRequest.kt b/api/src/main/kotlin/nebulosa/api/livestacker/LiveStackingRequest.kt index f44d606ff..1d9a3bb47 100644 --- a/api/src/main/kotlin/nebulosa/api/livestacker/LiveStackingRequest.kt +++ b/api/src/main/kotlin/nebulosa/api/livestacker/LiveStackingRequest.kt @@ -1,12 +1,10 @@ package nebulosa.api.livestacker +import jakarta.validation.constraints.NotNull import nebulosa.livestacker.LiveStacker import nebulosa.pixinsight.livestacker.PixInsightLiveStacker -import nebulosa.pixinsight.script.PixInsightIsRunning -import nebulosa.pixinsight.script.PixInsightScriptRunner -import nebulosa.pixinsight.script.PixInsightStartup +import nebulosa.pixinsight.script.startPixInsight import nebulosa.siril.livestacker.SirilLiveStacker -import org.jetbrains.annotations.NotNull import java.nio.file.Files import java.nio.file.Path import java.util.function.Supplier @@ -15,9 +13,9 @@ data class LiveStackingRequest( @JvmField val enabled: Boolean = false, @JvmField val type: LiveStackerType = LiveStackerType.SIRIL, @JvmField @field:NotNull val executablePath: Path? = null, - @JvmField val dark: Path? = null, - @JvmField val flat: Path? = null, - @JvmField val bias: Path? = null, + @JvmField val darkPath: Path? = null, + @JvmField val flatPath: Path? = null, + @JvmField val biasPath: Path? = null, @JvmField val use32Bits: Boolean = false, @JvmField val slot: Int = 1, ) : Supplier { @@ -26,23 +24,16 @@ data class LiveStackingRequest( val workingDirectory = Files.createTempDirectory("ls-") return when (type) { - LiveStackerType.SIRIL -> SirilLiveStacker(executablePath!!, workingDirectory, dark, flat, use32Bits) + LiveStackerType.SIRIL -> SirilLiveStacker(executablePath!!, workingDirectory, darkPath, flatPath, use32Bits) LiveStackerType.PIXINSIGHT -> { - val runner = PixInsightScriptRunner(executablePath!!) - - if (!PixInsightIsRunning(slot).use { it.runSync(runner) }) { - if (!PixInsightStartup(slot).use { it.runSync(runner) }) { - throw IllegalStateException("unable to start PixInsight") - } - } - - PixInsightLiveStacker(runner, workingDirectory, dark, flat, bias, use32Bits, slot) + val runner = startPixInsight(executablePath!!, slot) + PixInsightLiveStacker(runner, workingDirectory, darkPath, flatPath, biasPath, use32Bits, slot) } } } companion object { - @JvmStatic val EMPTY = LiveStackingRequest() + @JvmStatic val DISABLED = LiveStackingRequest() } } diff --git a/api/src/main/kotlin/nebulosa/api/mounts/ComputedLocation.kt b/api/src/main/kotlin/nebulosa/api/mounts/ComputedLocation.kt index f825170f9..497350194 100644 --- a/api/src/main/kotlin/nebulosa/api/mounts/ComputedLocation.kt +++ b/api/src/main/kotlin/nebulosa/api/mounts/ComputedLocation.kt @@ -12,15 +12,15 @@ import nebulosa.nova.astrometry.Constellation import java.time.LocalDateTime data class ComputedLocation( - @field:JsonSerialize(using = RightAscensionSerializer::class) var rightAscension: Angle = 0.0, - @field:JsonSerialize(using = DeclinationSerializer::class) var declination: Angle = 0.0, - @field:JsonSerialize(using = RightAscensionSerializer::class) var rightAscensionJ2000: Angle = 0.0, - @field:JsonSerialize(using = DeclinationSerializer::class) var declinationJ2000: Angle = 0.0, - @field:JsonSerialize(using = AzimuthSerializer::class) var azimuth: Angle = 0.0, - @field:JsonSerialize(using = DeclinationSerializer::class) var altitude: Angle = 0.0, - var constellation: Constellation = Constellation.AND, - @field:JsonSerialize(using = LSTSerializer::class) var lst: Angle = 0.0, - @field:JsonFormat(pattern = "HH:mm") var meridianAt: LocalDateTime = LocalDateTime.MIN, - @field:JsonSerialize(using = LSTSerializer::class) var timeLeftToMeridianFlip: Angle = 0.0, - var pierSide: PierSide = PierSide.NEITHER, + @field:JsonSerialize(using = RightAscensionSerializer::class) @JvmField var rightAscension: Angle = 0.0, + @field:JsonSerialize(using = DeclinationSerializer::class) @JvmField var declination: Angle = 0.0, + @field:JsonSerialize(using = RightAscensionSerializer::class) @JvmField var rightAscensionJ2000: Angle = 0.0, + @field:JsonSerialize(using = DeclinationSerializer::class) @JvmField var declinationJ2000: Angle = 0.0, + @field:JsonSerialize(using = AzimuthSerializer::class) @JvmField var azimuth: Angle = 0.0, + @field:JsonSerialize(using = DeclinationSerializer::class) @JvmField var altitude: Angle = 0.0, + @JvmField var constellation: Constellation = Constellation.AND, + @field:JsonSerialize(using = LSTSerializer::class) @JvmField var lst: Angle = 0.0, + @field:JsonFormat(pattern = "HH:mm") @JvmField var meridianAt: LocalDateTime = LocalDateTime.MIN, + @field:JsonSerialize(using = LSTSerializer::class) @JvmField var timeLeftToMeridianFlip: Angle = 0.0, + @JvmField var pierSide: PierSide = PierSide.NEITHER, ) diff --git a/api/src/main/kotlin/nebulosa/api/mounts/MountController.kt b/api/src/main/kotlin/nebulosa/api/mounts/MountController.kt index da3b115e4..0ee2d8a3c 100644 --- a/api/src/main/kotlin/nebulosa/api/mounts/MountController.kt +++ b/api/src/main/kotlin/nebulosa/api/mounts/MountController.kt @@ -189,15 +189,15 @@ class MountController( @PutMapping("{mount}/remote-control/start") fun remoteControlStart( mount: Mount, - @RequestParam type: MountRemoteControlType, + @RequestParam protocol: MountRemoteControlProtocol, @RequestParam(required = false, defaultValue = "0.0.0.0") host: String, @RequestParam(required = false, defaultValue = "10001") @Valid @Positive port: Int, ) { - mountService.remoteControlStart(mount, type, host, port) + mountService.remoteControlStart(mount, protocol, host, port) } @PutMapping("{mount}/remote-control/stop") - fun remoteControlStart(mount: Mount, @RequestParam type: MountRemoteControlType) { + fun remoteControlStart(mount: Mount, @RequestParam type: MountRemoteControlProtocol) { mountService.remoteControlStop(mount, type) } diff --git a/api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControl.kt b/api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControl.kt index 15118bcb1..377afc9e0 100644 --- a/api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControl.kt +++ b/api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControl.kt @@ -18,7 +18,7 @@ import java.io.Closeable import java.time.OffsetDateTime data class MountRemoteControl( - @JvmField val type: MountRemoteControlType, + @JvmField val protocol: MountRemoteControlProtocol, @field:JsonIgnore @JvmField val server: NettyServer, @JvmField val mount: Mount, ) : StellariumMountHandler, LX200MountHandler, DeviceEventHandler, Closeable { @@ -28,8 +28,8 @@ data class MountRemoteControl( @JsonIgnore private val deviceProvider = mount.sender as? INDIDeviceProvider init { - if (server is StellariumProtocolServer) { - deviceProvider?.registerDeviceEventHandler(this) + if (server is StellariumProtocolServer && deviceProvider != null) { + deviceProvider.registerDeviceEventHandler(this) server.attachMountHandler(this) } else if (server is LX200ProtocolServer) { server.attachMountHandler(this) diff --git a/api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControlType.kt b/api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControlProtocol.kt similarity index 59% rename from api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControlType.kt rename to api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControlProtocol.kt index 9641863eb..a49a51581 100644 --- a/api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControlType.kt +++ b/api/src/main/kotlin/nebulosa/api/mounts/MountRemoteControlProtocol.kt @@ -1,6 +1,6 @@ package nebulosa.api.mounts -enum class MountRemoteControlType { +enum class MountRemoteControlProtocol { STELLARIUM, LX200, } diff --git a/api/src/main/kotlin/nebulosa/api/mounts/MountSerializer.kt b/api/src/main/kotlin/nebulosa/api/mounts/MountSerializer.kt index 9fecfbc99..49a2b6dd1 100644 --- a/api/src/main/kotlin/nebulosa/api/mounts/MountSerializer.kt +++ b/api/src/main/kotlin/nebulosa/api/mounts/MountSerializer.kt @@ -16,6 +16,7 @@ class MountSerializer : StdSerializer(Mount::class.java) { override fun serialize(value: Mount, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() + gen.writeStringField("type", value.type.name) gen.writeStringField("sender", value.sender.id) gen.writeStringField("id", value.id) gen.writeStringField("name", value.name) diff --git a/api/src/main/kotlin/nebulosa/api/mounts/MountService.kt b/api/src/main/kotlin/nebulosa/api/mounts/MountService.kt index 36d8b6069..b9d1235aa 100644 --- a/api/src/main/kotlin/nebulosa/api/mounts/MountService.kt +++ b/api/src/main/kotlin/nebulosa/api/mounts/MountService.kt @@ -20,6 +20,7 @@ import nebulosa.nova.position.Geoid import nebulosa.nova.position.ICRF import nebulosa.stellarium.protocol.StellariumProtocolServer import nebulosa.time.CurrentTime +import nebulosa.time.SystemClock import nebulosa.wcs.WCS import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -92,7 +93,7 @@ class MountService( * @return true if mount can slew to [mountPosition] coordinates. */ private fun verifyMountWillPointToSun(idempotencyKey: String, location: GeographicPosition, mountPosition: ICRF): Boolean { - val sunPosition = skyAtlasService.positionOfSun(location, LocalDateTime.now(), true) + val sunPosition = skyAtlasService.positionOfSun(location, LocalDateTime.now(SystemClock), true) .let { ICRF.equatorial(it.rightAscensionJ2000, it.declinationJ2000) } return if (sunPosition.separationFrom(mountPosition).toDegrees <= 1.0) { @@ -262,7 +263,7 @@ class MountService( if (meridianAt) { computeTimeLeftToMeridianFlip(rightAscension, computeLST(mount).also { computedLocation.lst = it }) .also { computedLocation.timeLeftToMeridianFlip = it } - .also { computedLocation.meridianAt = LocalDateTime.now().plusSeconds((it.toHours * 3600.0).toLong()) } + .also { computedLocation.meridianAt = LocalDateTime.now(SystemClock).plusSeconds((it.toHours * 3600.0).toLong()) } } computedLocation.pierSide = PierSide.expectedPierSide(computedLocation.rightAscension, computedLocation.declination, computeLST(mount)) @@ -288,19 +289,19 @@ class MountService( } } - fun remoteControlStart(mount: Mount, type: MountRemoteControlType, host: String, port: Int) { - check(remoteControls.none { it.mount === mount && it.type == type }) { "$type ${mount.name} Remote Control is already running" } + fun remoteControlStart(mount: Mount, protocol: MountRemoteControlProtocol, host: String, port: Int) { + check(remoteControls.none { it.mount === mount && it.protocol == protocol }) { "$protocol ${mount.name} Remote Control is already running" } - val server = if (type == MountRemoteControlType.STELLARIUM) StellariumProtocolServer(host, port) + val server = if (protocol == MountRemoteControlProtocol.STELLARIUM) StellariumProtocolServer(host, port) else LX200ProtocolServer(host, port) server.run() - remoteControls.add(MountRemoteControl(type, server, mount)) + remoteControls.add(MountRemoteControl(protocol, server, mount)) } - fun remoteControlStop(mount: Mount, type: MountRemoteControlType) { - val remoteControl = remoteControls.find { it.mount === mount && it.type == type } ?: return + fun remoteControlStop(mount: Mount, type: MountRemoteControlProtocol) { + val remoteControl = remoteControls.find { it.mount === mount && it.protocol == type } ?: return remoteControl.use(remoteControls::remove) } diff --git a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverController.kt b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverController.kt index b21d1c08d..5e44131bf 100644 --- a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverController.kt +++ b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverController.kt @@ -1,8 +1,6 @@ package nebulosa.api.platesolver import jakarta.validation.Valid -import nebulosa.api.beans.converters.angle.AngleParam -import nebulosa.math.Angle import org.springframework.web.bind.annotation.* import java.nio.file.Path @@ -12,13 +10,14 @@ class PlateSolverController( private val plateSolverService: PlateSolverService, ) { - @PutMapping - fun solveImage( + @PutMapping("start") + fun start( @RequestParam path: Path, @RequestBody @Valid solver: PlateSolverRequest, - @RequestParam(required = false, defaultValue = "true") blind: Boolean, - @AngleParam(required = false, isHours = true, defaultValue = "0.0") centerRA: Angle, - @AngleParam(required = false, defaultValue = "0.0") centerDEC: Angle, - @AngleParam(required = false, defaultValue = "4.0") radius: Angle, - ) = plateSolverService.solveImage(solver, path, centerRA, centerDEC, if (blind) 0.0 else radius) + ) = plateSolverService.solveImage(solver, path) + + @PutMapping("stop") + fun stop() { + plateSolverService.stopSolver() + } } diff --git a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverRequest.kt b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverRequest.kt index dfd73f7d0..af3219921 100644 --- a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverRequest.kt +++ b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverRequest.kt @@ -1,9 +1,16 @@ package nebulosa.api.platesolver +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import nebulosa.api.beans.converters.angle.DeclinationDeserializer +import nebulosa.api.beans.converters.angle.DegreesDeserializer +import nebulosa.api.beans.converters.angle.RightAscensionDeserializer import nebulosa.astap.platesolver.AstapPlateSolver import nebulosa.astrometrynet.nova.NovaAstrometryNetService import nebulosa.astrometrynet.platesolver.LocalAstrometryNetPlateSolver import nebulosa.astrometrynet.platesolver.NovaAstrometryNetPlateSolver +import nebulosa.math.Angle +import nebulosa.pixinsight.platesolver.PixInsightPlateSolver +import nebulosa.pixinsight.script.startPixInsight import nebulosa.siril.platesolver.SirilPlateSolver import okhttp3.OkHttpClient import org.hibernate.validator.constraints.time.DurationMax @@ -23,6 +30,11 @@ data class PlateSolverRequest( @JvmField val apiKey: String = "", @field:DurationMin(seconds = 0) @field:DurationMax(minutes = 5) @field:DurationUnit(ChronoUnit.SECONDS) @JvmField val timeout: Duration = Duration.ZERO, + @JvmField val slot: Int = 1, + @JvmField val blind: Boolean = true, + @JsonDeserialize(using = RightAscensionDeserializer::class) @JvmField val centerRA: Angle = 0.0, + @JsonDeserialize(using = DeclinationDeserializer::class) @JvmField val centerDEC: Angle = 0.0, + @JsonDeserialize(using = DegreesDeserializer::class) @JvmField val radius: Angle = if (blind) 0.0 else 4.0, ) { fun get(httpClient: OkHttpClient? = null) = with(this) { @@ -30,11 +42,14 @@ data class PlateSolverRequest( PlateSolverType.ASTAP -> AstapPlateSolver(executablePath!!) PlateSolverType.ASTROMETRY_NET -> LocalAstrometryNetPlateSolver(executablePath!!) PlateSolverType.ASTROMETRY_NET_ONLINE -> { - val key = "$apiUrl@$apiKey" - val service = NOVA_ASTROMETRY_NET_CACHE.getOrPut(key) { NovaAstrometryNetService(apiUrl, httpClient) } + val service = NOVA_ASTROMETRY_NET_CACHE.getOrPut(apiUrl) { NovaAstrometryNetService(apiUrl, httpClient) } NovaAstrometryNetPlateSolver(service, apiKey) } PlateSolverType.SIRIL -> SirilPlateSolver(executablePath!!, focalLength, pixelSize) + PlateSolverType.PIXINSIGHT -> { + val runner = startPixInsight(executablePath!!, slot) + PixInsightPlateSolver(runner, pixelSize, 0.0, focalLength, slot) + } } } diff --git a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverService.kt b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverService.kt index aa7fbb45c..dfbe20d30 100644 --- a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverService.kt +++ b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverService.kt @@ -2,10 +2,11 @@ package nebulosa.api.platesolver import nebulosa.api.image.ImageBucket import nebulosa.api.image.ImageSolved -import nebulosa.math.Angle +import nebulosa.common.concurrency.cancel.CancellationToken import okhttp3.OkHttpClient import org.springframework.stereotype.Service import java.nio.file.Path +import java.util.concurrent.atomic.AtomicReference @Service class PlateSolverService( @@ -13,18 +14,23 @@ class PlateSolverService( private val httpClient: OkHttpClient, ) { - fun solveImage( - options: PlateSolverRequest, path: Path, - centerRA: Angle, centerDEC: Angle, radius: Angle, - ): ImageSolved { - val calibration = solve(options, path, centerRA, centerDEC, radius) + private val cancellationToken = AtomicReference() + + fun solveImage(request: PlateSolverRequest, path: Path): ImageSolved { + val calibration = solve(request, path) imageBucket.put(path, calibration) return ImageSolved(calibration) } @Synchronized - fun solve( - options: PlateSolverRequest, path: Path, - centerRA: Angle = 0.0, centerDEC: Angle = 0.0, radius: Angle = 0.0, - ) = options.get(httpClient).solve(path, null, centerRA, centerDEC, radius, options.downsampleFactor, options.timeout) + fun solve(request: PlateSolverRequest, path: Path) = CancellationToken().use { + cancellationToken.set(it) + val solver = request.get(httpClient) + val radius = if (request.blind) 0.0 else request.radius + solver.solve(path, null, request.centerRA, request.centerDEC, radius, request.downsampleFactor, request.timeout, it) + } + + fun stopSolver() { + cancellationToken.get()?.cancel() + } } diff --git a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverType.kt b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverType.kt index 3c85a9a86..13efe930d 100644 --- a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverType.kt +++ b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverType.kt @@ -5,4 +5,5 @@ enum class PlateSolverType { ASTROMETRY_NET, ASTROMETRY_NET_ONLINE, SIRIL, + PIXINSIGHT, } diff --git a/api/src/main/kotlin/nebulosa/api/preference/PreferenceEntity.kt b/api/src/main/kotlin/nebulosa/api/preference/PreferenceEntity.kt index b00b5d7f2..4364d1baa 100644 --- a/api/src/main/kotlin/nebulosa/api/preference/PreferenceEntity.kt +++ b/api/src/main/kotlin/nebulosa/api/preference/PreferenceEntity.kt @@ -9,6 +9,6 @@ import nebulosa.api.database.BoxEntity @Entity data class PreferenceEntity( @Id override var id: Long = 0L, - @Unique(onConflict = ConflictStrategy.REPLACE) var key: String = "", - var value: String? = null, + @Unique(onConflict = ConflictStrategy.REPLACE) @JvmField var key: String = "", + @JvmField var value: String? = null, ) : BoxEntity diff --git a/api/src/main/kotlin/nebulosa/api/preference/PreferenceRequestBody.kt b/api/src/main/kotlin/nebulosa/api/preference/PreferenceRequestBody.kt deleted file mode 100644 index 00e0448f2..000000000 --- a/api/src/main/kotlin/nebulosa/api/preference/PreferenceRequestBody.kt +++ /dev/null @@ -1,3 +0,0 @@ -package nebulosa.api.preference - -data class PreferenceRequestBody(val data: Any?) diff --git a/api/src/main/kotlin/nebulosa/api/rotators/RotatorSerializer.kt b/api/src/main/kotlin/nebulosa/api/rotators/RotatorSerializer.kt index 851b48561..fc654c7bb 100644 --- a/api/src/main/kotlin/nebulosa/api/rotators/RotatorSerializer.kt +++ b/api/src/main/kotlin/nebulosa/api/rotators/RotatorSerializer.kt @@ -11,6 +11,7 @@ class RotatorSerializer : StdSerializer(Rotator::class.java) { override fun serialize(value: Rotator, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() + gen.writeStringField("type", value.type.name) gen.writeStringField("sender", value.sender.id) gen.writeStringField("id", value.id) gen.writeStringField("name", value.name) diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceCaptureMode.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequenceCaptureMode.kt deleted file mode 100644 index d58b34657..000000000 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequenceCaptureMode.kt +++ /dev/null @@ -1,10 +0,0 @@ -package nebulosa.api.sequencer - -enum class SequenceCaptureMode { - INTERLEAVED, - - /** - * Processes each sequence entry in full before advancing to the next sequence entry. - */ - FULLY, -} diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerCaptureMode.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerCaptureMode.kt new file mode 100644 index 000000000..f865aae4a --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerCaptureMode.kt @@ -0,0 +1,10 @@ +package nebulosa.api.sequencer + +enum class SequencerCaptureMode { + INTERLEAVED, + + /** + * Processes each sequence in full before advancing to the next sequence. + */ + FULLY, +} diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerController.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerController.kt index 17f9d715a..d8d0478dd 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerController.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerController.kt @@ -18,7 +18,7 @@ class SequencerController( fun start( camera: Camera, mount: Mount?, wheel: FilterWheel?, focuser: Focuser?, rotator: Rotator?, - @RequestBody @Valid body: SequencePlanRequest, + @RequestBody @Valid body: SequencerPlanRequest, ) = sequencerService.start(camera, body, mount, wheel, focuser, rotator) @PutMapping("{camera}/stop") @@ -26,6 +26,16 @@ class SequencerController( sequencerService.stop(camera) } + @PutMapping("{camera}/pause") + fun pause(camera: Camera) { + sequencerService.pause(camera) + } + + @PutMapping("{camera}/unpause") + fun unpause(camera: Camera) { + sequencerService.unpause(camera) + } + @GetMapping("{camera}/status") fun status(camera: Camera): SequencerEvent? { return sequencerService.status(camera) diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerEvent.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerEvent.kt index 050ed94f2..f072ab4b8 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerEvent.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerEvent.kt @@ -10,6 +10,7 @@ data class SequencerEvent( @JvmField val remainingTime: Duration = Duration.ZERO, @JvmField val progress: Double = 0.0, @JvmField val capture: CameraCaptureEvent? = null, + @JvmField val state: SequencerState = SequencerState.IDLE, ) : MessageEvent { override val eventName = "SEQUENCER.ELAPSED" diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerExecutor.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerExecutor.kt index 7b0a4ecb9..efd19c02c 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerExecutor.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerExecutor.kt @@ -54,7 +54,7 @@ class SequencerExecutor( } fun execute( - camera: Camera, request: SequencePlanRequest, + camera: Camera, request: SequencerPlanRequest, mount: Mount? = null, wheel: FilterWheel? = null, focuser: Focuser? = null, rotator: Rotator? = null, ) { check(camera.connected) { "${camera.name} Camera is not connected" } @@ -82,6 +82,14 @@ class SequencerExecutor( } } + fun pause(camera: Camera) { + jobs.find { it.task.camera === camera }?.pause() + } + + fun unpause(camera: Camera) { + jobs.find { it.task.camera === camera }?.unpause() + } + fun stop(camera: Camera) { jobs.find { it.task.camera === camera }?.stop() } diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencePlanRequest.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerPlanRequest.kt similarity index 73% rename from api/src/main/kotlin/nebulosa/api/sequencer/SequencePlanRequest.kt rename to api/src/main/kotlin/nebulosa/api/sequencer/SequencerPlanRequest.kt index 110ab3942..3313353e8 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequencePlanRequest.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerPlanRequest.kt @@ -3,6 +3,7 @@ package nebulosa.api.sequencer import jakarta.validation.Valid import jakarta.validation.constraints.NotEmpty import nebulosa.api.cameras.AutoSubFolderMode +import nebulosa.api.cameras.CameraCaptureNamingFormat import nebulosa.api.cameras.CameraStartCaptureRequest import nebulosa.api.guiding.DitherAfterExposureRequest import org.hibernate.validator.constraints.time.DurationMax @@ -12,12 +13,13 @@ import java.nio.file.Path import java.time.Duration import java.time.temporal.ChronoUnit -data class SequencePlanRequest( +data class SequencerPlanRequest( @JvmField @field:DurationUnit(ChronoUnit.SECONDS) @field:DurationMin(seconds = 0) @field:DurationMax(minutes = 60) val initialDelay: Duration = Duration.ZERO, - @JvmField val captureMode: SequenceCaptureMode = SequenceCaptureMode.INTERLEAVED, + @JvmField val captureMode: SequencerCaptureMode = SequencerCaptureMode.INTERLEAVED, @JvmField val autoSubFolderMode: AutoSubFolderMode = AutoSubFolderMode.OFF, @JvmField val savePath: Path? = null, - @JvmField @field:NotEmpty val entries: List = emptyList(), + @JvmField @field:NotEmpty val sequences: List = emptyList(), @JvmField @field:Valid val dither: DitherAfterExposureRequest = DitherAfterExposureRequest.DISABLED, @JvmField @field:Valid val autoFocus: AutoFocusAfterConditions = AutoFocusAfterConditions.DISABLED, + @JvmField val namingFormat: CameraCaptureNamingFormat = CameraCaptureNamingFormat.DEFAULT, ) diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerService.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerService.kt index 871fcd1f3..1672a18d7 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerService.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerService.kt @@ -18,7 +18,7 @@ class SequencerService( @Synchronized fun start( - camera: Camera, request: SequencePlanRequest, + camera: Camera, request: SequencerPlanRequest, mount: Mount?, wheel: FilterWheel?, focuser: Focuser?, rotator: Rotator?, ) { val savePath = request.savePath @@ -28,6 +28,16 @@ class SequencerService( sequencerExecutor.execute(camera, request.copy(savePath = savePath), mount, wheel, focuser, rotator) } + @Synchronized + fun pause(camera: Camera) { + sequencerExecutor.pause(camera) + } + + @Synchronized + fun unpause(camera: Camera) { + sequencerExecutor.unpause(camera) + } + @Synchronized fun stop(camera: Camera) { sequencerExecutor.stop(camera) diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerState.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerState.kt new file mode 100644 index 000000000..4fc5ecca9 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerState.kt @@ -0,0 +1,8 @@ +package nebulosa.api.sequencer + +enum class SequencerState { + IDLE, + RUNNING, + PAUSING, + PAUSED, +} diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerTask.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerTask.kt index a37e1ec70..31f1d2c37 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerTask.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerTask.kt @@ -11,6 +11,7 @@ import nebulosa.api.tasks.delay.DelayTask import nebulosa.api.wheels.WheelEventAware import nebulosa.api.wheels.WheelMoveTask import nebulosa.common.concurrency.cancel.CancellationToken +import nebulosa.common.concurrency.latch.PauseListener import nebulosa.guiding.Guider import nebulosa.indi.device.camera.Camera import nebulosa.indi.device.camera.CameraEvent @@ -24,6 +25,7 @@ import nebulosa.log.loggerFor import java.time.Duration import java.util.* import java.util.concurrent.Executor +import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicReference @@ -33,7 +35,7 @@ import java.util.concurrent.atomic.AtomicReference data class SequencerTask( @JvmField val camera: Camera, - @JvmField val plan: SequencePlanRequest, + @JvmField val plan: SequencerPlanRequest, @JvmField val guider: Guider? = null, @JvmField val mount: Mount? = null, @JvmField val wheel: FilterWheel? = null, @@ -41,15 +43,17 @@ data class SequencerTask( @JvmField val rotator: Rotator? = null, private val executor: Executor? = null, private val calibrationFrameProvider: CalibrationFrameProvider? = null, -) : AbstractTask(), Consumer, CameraEventAware, WheelEventAware { +) : AbstractTask(), Consumer, CameraEventAware, WheelEventAware, PauseListener { - private val usedEntries = plan.entries.filter { it.enabled } + private val sequences = plan.sequences.filter { it.enabled } private val initialDelayTask = DelayTask(plan.initialDelay) private val sequencerId = AtomicInteger() private val tasks = LinkedList() private val currentTask = AtomicReference() + private val pausing = AtomicBoolean() + private val paused = AtomicBoolean() @Volatile private var estimatedCaptureTime = initialDelayTask.duration @@ -59,21 +63,24 @@ data class SequencerTask( @Volatile private var progress = 0.0 init { - require(usedEntries.isNotEmpty()) { "no entries found" } + require(sequences.isNotEmpty()) { "no entries found" } initialDelayTask.subscribe(this) tasks.add(initialDelayTask) - fun mapRequest(request: CameraStartCaptureRequest): CameraStartCaptureRequest { - return request.copy(savePath = plan.savePath, autoSave = true, autoSubFolderMode = plan.autoSubFolderMode) - } + fun mapRequest(request: CameraStartCaptureRequest) = request.copy( + savePath = plan.savePath, autoSave = true, + autoSubFolderMode = plan.autoSubFolderMode, + dither = plan.dither, + namingFormat = plan.namingFormat, + ) - if (plan.captureMode == SequenceCaptureMode.FULLY || usedEntries.size == 1) { - for (i in usedEntries.indices) { - val request = mapRequest(usedEntries[i]) + if (plan.captureMode == SequencerCaptureMode.FULLY || sequences.size == 1) { + for (i in sequences.indices) { + val request = mapRequest(sequences[i]) // ID. - tasks.add(SequencerIdTask(plan.entries.indexOfFirst { it === usedEntries[i] } + 1)) + tasks.add(SequencerIdTask(plan.sequences.indexOfFirst { it === sequences[i] } + 1)) // FILTER WHEEL. request.wheelMoveTask()?.also(tasks::add) @@ -87,23 +94,22 @@ data class SequencerTask( cameraCaptureTask.subscribe(this) estimatedCaptureTime += cameraCaptureTask.estimatedCaptureTime - tasks.add(SequenceCaptureModeCameraCaptureTask(cameraCaptureTask, SequenceCaptureMode.FULLY, i)) + tasks.add(SequenceCaptureModeCameraCaptureTask(cameraCaptureTask, SequencerCaptureMode.FULLY, i)) } } else { - val sequenceIdTasks = usedEntries.map { req -> SequencerIdTask(plan.entries.indexOfFirst { it === req } + 1) } - val requests = usedEntries.map { mapRequest(it) } - val cameraCaptureTasks = requests - .mapIndexed { i, req -> - val task = CameraCaptureTask( - camera, req, guider, - i > 0, executor, calibrationFrameProvider, - mount, wheel, focuser, rotator - ) - - SequenceCaptureModeCameraCaptureTask(task, SequenceCaptureMode.INTERLEAVED, i) - } + val sequenceIdTasks = sequences.map { req -> SequencerIdTask(plan.sequences.indexOfFirst { it === req } + 1) } + val requests = sequences.map { mapRequest(it) } + val cameraCaptureTasks = requests.mapIndexed { i, req -> + val task = CameraCaptureTask( + camera, req, guider, + i > 0, executor, calibrationFrameProvider, + mount, wheel, focuser, rotator + ) + + SequenceCaptureModeCameraCaptureTask(task, SequencerCaptureMode.INTERLEAVED, i) + } val wheelMoveTasks = requests.map { it.wheelMoveTask() } - val count = IntArray(requests.size) { usedEntries[it].exposureAmount } + val count = IntArray(requests.size) { sequences[it].exposureAmount } for ((cameraCaptureTask) in cameraCaptureTasks) { cameraCaptureTask.subscribe(this) @@ -111,14 +117,14 @@ data class SequencerTask( } while (count.sum() > 0) { - for (i in usedEntries.indices) { + for (i in sequences.indices) { if (count[i] > 0) { tasks.add(sequenceIdTasks[i]) wheelMoveTasks[i]?.also(tasks::add) val task = cameraCaptureTasks[i] - if (count[i] == usedEntries[i].exposureAmount) { + if (count[i] == sequences[i].exposureAmount) { tasks.add(InitializeCameraCaptureTask(task.task)) } @@ -150,11 +156,22 @@ data class SequencerTask( } } + override fun onPause(paused: Boolean) { + pausing.set(paused) + + if (paused) { + sendEvent(SequencerState.PAUSING) + } + } + override fun execute(cancellationToken: CancellationToken) { LOG.info("Sequencer started. camera={}, mount={}, wheel={}, focuser={}, plan={}", camera, mount, wheel, focuser, plan) + cancellationToken.listenToPause(this) + for (task in tasks) { if (cancellationToken.isCancelled) break + currentTask.set(task) task.execute(cancellationToken) currentTask.set(null) @@ -163,9 +180,11 @@ data class SequencerTask( if (remainingTime.toMillis() > 0L) { remainingTime = Duration.ZERO progress = 1.0 - sendEvent() + sendEvent(SequencerState.IDLE) } + cancellationToken.unlistenToPause(this) + LOG.info("Sequencer finished. camera={}, mount={}, wheel={}, focuser={}, plan={}", camera, mount, wheel, focuser, plan) } @@ -189,15 +208,19 @@ data class SequencerTask( if (event.task === initialDelayTask) { elapsedTime += event.waitTime computeRemainingTimeAndProgress() - sendEvent() + sendEvent(SequencerState.RUNNING) } } is CameraCaptureEvent -> { + pausing.set(event.state == CameraCaptureState.PAUSING) + paused.set(event.state == CameraCaptureState.PAUSED) + when (event.state) { CameraCaptureState.CAPTURE_STARTED -> { prevElapsedTime = elapsedTime } - CameraCaptureState.EXPOSURING, CameraCaptureState.WAITING -> { + CameraCaptureState.EXPOSURING, + CameraCaptureState.WAITING -> { elapsedTime = prevElapsedTime + event.captureElapsedTime computeRemainingTimeAndProgress() } @@ -207,7 +230,7 @@ data class SequencerTask( else -> Unit } - sendEvent(event) + sendEvent(SequencerState.RUNNING, event) } } } @@ -219,8 +242,13 @@ data class SequencerTask( } @Suppress("NOTHING_TO_INLINE") - private inline fun sendEvent(capture: CameraCaptureEvent? = null) { - onNext(SequencerEvent(sequencerId.get(), elapsedTime, remainingTime, progress, capture)) + private inline fun sendEvent(state: SequencerState, capture: CameraCaptureEvent? = null) { + onNext( + SequencerEvent( + sequencerId.get(), elapsedTime, remainingTime, progress, capture, + if (pausing.get()) SequencerState.PAUSING else if (paused.get()) SequencerState.PAUSED else state + ) + ) } override fun close() { @@ -244,12 +272,12 @@ data class SequencerTask( private data class SequenceCaptureModeCameraCaptureTask( @JvmField val task: CameraCaptureTask, - @JvmField val mode: SequenceCaptureMode, + @JvmField val mode: SequencerCaptureMode, @JvmField val index: Int, ) : Task { override fun execute(cancellationToken: CancellationToken) { - if (mode == SequenceCaptureMode.FULLY) { + if (mode == SequencerCaptureMode.FULLY) { task.initialize(cancellationToken) task.executeInLoop(cancellationToken) task.finalize(cancellationToken) diff --git a/api/src/main/kotlin/nebulosa/api/stacker/AnalyzedTarget.kt b/api/src/main/kotlin/nebulosa/api/stacker/AnalyzedTarget.kt new file mode 100644 index 000000000..892ff518b --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/stacker/AnalyzedTarget.kt @@ -0,0 +1,23 @@ +package nebulosa.api.stacker + +import nebulosa.fits.* +import nebulosa.image.format.ReadableHeader +import nebulosa.indi.device.camera.FrameType +import nebulosa.indi.device.camera.FrameType.Companion.frameType + +data class AnalyzedTarget( + @JvmField val width: Int, + @JvmField val height: Int, + @JvmField val binX: Int, + @JvmField val binY: Int, + @JvmField val gain: Double, + @JvmField val exposureTime: Long, + @JvmField val type: FrameType, + @JvmField val group: StackerGroupType, +) { + + constructor(header: ReadableHeader) : this( + header.width, header.height, header.binX, header.binY, header.gain, header.exposureTimeInMicroseconds, + header.frameType ?: FrameType.LIGHT, StackerGroupType.from(header) + ) +} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt new file mode 100644 index 000000000..66a95ff33 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt @@ -0,0 +1,46 @@ +package nebulosa.api.stacker + +import jakarta.validation.Valid +import nebulosa.common.concurrency.cancel.CancellationToken +import org.springframework.validation.annotation.Validated +import org.springframework.web.bind.annotation.* +import java.nio.file.Path +import java.util.concurrent.atomic.AtomicReference + +@Validated +@RestController +@RequestMapping("stacker") +class StackerController( + private val stackerService: StackerService, +) { + + private val cancellationToken = AtomicReference() + + @PutMapping("start") + fun start(@RequestBody @Valid body: StackingRequest): Path? { + return if (cancellationToken.compareAndSet(null, CancellationToken())) { + try { + stackerService.stack(body, cancellationToken.get()) + } finally { + cancellationToken.getAndSet(null)?.unlistenAll() + } + } else { + null + } + } + + @GetMapping("running") + fun isRunning(): Boolean { + return cancellationToken.get() != null + } + + @PutMapping("stop") + fun stop() { + cancellationToken.get()?.cancel() + } + + @PutMapping("analyze") + fun analyze(@RequestParam path: Path): AnalyzedTarget? { + return stackerService.analyze(path) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerGroupType.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerGroupType.kt new file mode 100644 index 000000000..641984102 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/stacker/StackerGroupType.kt @@ -0,0 +1,25 @@ +package nebulosa.api.stacker + +import nebulosa.fits.filter +import nebulosa.image.format.ReadableHeader + +enum class StackerGroupType { + LUMINANCE, + RED, + GREEN, + BLUE, + MONO, + RGB; + + companion object { + + @JvmStatic + fun from(header: ReadableHeader) = header.filter?.let { + if (it.contains("RED", true) || it.equals("R", true)) RED + else if (it.contains("GREEN", true) || it.equals("G", true)) GREEN + else if (it.contains("BLUE", true) || it.equals("B", true)) BLUE + else if (it.contains("LUMINANCE", true) || it.equals("L", true)) LUMINANCE + else MONO + } ?: MONO + } +} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt new file mode 100644 index 000000000..f56f9a1fa --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt @@ -0,0 +1,113 @@ +package nebulosa.api.stacker + +import nebulosa.common.concurrency.cancel.CancellationToken +import nebulosa.fits.fits +import nebulosa.fits.isFits +import nebulosa.stacker.AutoStacker +import nebulosa.xisf.isXisf +import nebulosa.xisf.xisf +import org.springframework.stereotype.Service +import java.nio.file.Path +import kotlin.io.path.exists +import kotlin.io.path.isDirectory +import kotlin.io.path.isRegularFile + +@Service +class StackerService { + + fun stack(request: StackingRequest, cancellationToken: CancellationToken = CancellationToken.NONE): Path? { + require(request.outputDirectory != null && request.outputDirectory.exists() && request.outputDirectory.isDirectory()) + + val luminance = request.targets.filter { it.enabled && it.group == StackerGroupType.LUMINANCE } + val red = request.targets.filter { it.enabled && it.group == StackerGroupType.RED } + val green = request.targets.filter { it.enabled && it.group == StackerGroupType.GREEN } + val blue = request.targets.filter { it.enabled && it.group == StackerGroupType.BLUE } + val mono = request.targets.filter { it.enabled && it.group == StackerGroupType.MONO } + val rgb = request.targets.filter { it.enabled && it.group == StackerGroupType.RGB } + + val name = "${System.currentTimeMillis()}" + + // Combined LRGB + return if (red.size + green.size + blue.size >= 1) { + val stacker = request.get() + + cancellationToken.listen { stacker.stop() } + + val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken) + val stackedRedPath = red.stack(request, stacker, name, StackerGroupType.RED, cancellationToken) + val stackedGreenPath = green.stack(request, stacker, name, StackerGroupType.GREEN, cancellationToken) + val stackedBluePath = blue.stack(request, stacker, name, StackerGroupType.BLUE, cancellationToken) + + if (cancellationToken.isCancelled) { + null + } else { + val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") + stacker.combineLRGB(combinedPath, stackedLuminancePath, stackedRedPath, stackedGreenPath, stackedBluePath) + combinedPath + } + } + // LRGB + else if (rgb.isNotEmpty()) { + val stacker = request.get() + + val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken) + val stackedRGBPath = rgb.stack(request, stacker, name, StackerGroupType.RGB, cancellationToken) + + if (cancellationToken.isCancelled) { + null + } else if (stackedLuminancePath != null && stackedRGBPath != null) { + val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") + stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedRGBPath, false) + combinedPath + } else { + stackedLuminancePath ?: stackedRGBPath + } + } + // MONO + else if (mono.isNotEmpty() || luminance.isNotEmpty()) { + val stacker = request.get() + + val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken) + val stackedMonoPath = mono.stack(request, stacker, name, StackerGroupType.MONO, cancellationToken) + + if (cancellationToken.isCancelled) { + null + } else if (stackedLuminancePath != null && stackedMonoPath != null) { + val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") + stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedMonoPath, true) + combinedPath + } else { + stackedLuminancePath ?: stackedMonoPath + } + } else { + null + } + } + + private fun List.stack( + request: StackingRequest, stacker: AutoStacker, + name: String, group: StackerGroupType, cancellationToken: CancellationToken, + ): Path? { + return if (cancellationToken.isCancelled) { + null + } else if (size > 1) { + val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits") + if (stacker.stack(map { it.path!! }, outputPath, request.referencePath!!)) outputPath else null + } else if (isNotEmpty()) { + val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits") + if (stacker.align(request.referencePath!!, this[0].path!!, outputPath)) outputPath else null + } else { + null + } + } + + fun analyze(path: Path): AnalyzedTarget? { + if (!path.exists() || !path.isRegularFile()) return null + + val image = if (path.isFits()) path.fits() + else if (path.isXisf()) path.xisf() + else return null + + return image.use { it.firstOrNull()?.header }?.let(::AnalyzedTarget) + } +} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerType.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerType.kt new file mode 100644 index 000000000..4aee6a289 --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/stacker/StackerType.kt @@ -0,0 +1,5 @@ +package nebulosa.api.stacker + +enum class StackerType { + PIXINSIGHT, +} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackingRequest.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackingRequest.kt new file mode 100644 index 000000000..66b00312a --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/stacker/StackingRequest.kt @@ -0,0 +1,45 @@ +package nebulosa.api.stacker + +import jakarta.validation.Valid +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Size +import nebulosa.pixinsight.script.startPixInsight +import nebulosa.pixinsight.stacker.PixInsightAutoStacker +import nebulosa.stacker.AutoStacker +import java.nio.file.Files +import java.nio.file.Path +import java.util.function.Supplier +import kotlin.io.path.exists +import kotlin.io.path.isRegularFile + +data class StackingRequest( + @JvmField @field:NotNull val outputDirectory: Path? = null, + @JvmField val type: StackerType = StackerType.PIXINSIGHT, + @JvmField @field:NotNull val executablePath: Path? = null, + @JvmField val darkPath: Path? = null, + @JvmField val darkEnabled: Boolean = false, + @JvmField val flatPath: Path? = null, + @JvmField val flatEnabled: Boolean = false, + @JvmField val biasPath: Path? = null, + @JvmField val biasEnabled: Boolean = false, + @JvmField val use32Bits: Boolean = false, + @JvmField val slot: Int = 1, + @JvmField @field:NotNull val referencePath: Path? = null, + @JvmField @field:Size(min = 2) @field:Valid val targets: List = emptyList(), +) : Supplier { + + override fun get(): AutoStacker { + val workingDirectory = Files.createTempDirectory("as-") + + val darkPath = darkPath?.takeIf { darkEnabled && it.exists() && it.isRegularFile() } + val flatPath = flatPath?.takeIf { flatEnabled && it.exists() && it.isRegularFile() } + val biasPath = biasPath?.takeIf { biasEnabled && it.exists() && it.isRegularFile() } + + return when (type) { + StackerType.PIXINSIGHT -> { + val runner = startPixInsight(executablePath!!, slot) + PixInsightAutoStacker(runner, workingDirectory, darkPath, flatPath, biasPath, slot) + } + } + } +} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackingTarget.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackingTarget.kt new file mode 100644 index 000000000..a544f999f --- /dev/null +++ b/api/src/main/kotlin/nebulosa/api/stacker/StackingTarget.kt @@ -0,0 +1,11 @@ +package nebulosa.api.stacker + +import jakarta.validation.constraints.NotNull +import java.nio.file.Path + +data class StackingTarget( + @JvmField val enabled: Boolean = true, + @JvmField @field:NotNull val path: Path? = null, + @JvmField val group: StackerGroupType = StackerGroupType.MONO, + @JvmField val debayer: Boolean = true, +) diff --git a/api/src/main/kotlin/nebulosa/api/stardetector/StarDetectionRequest.kt b/api/src/main/kotlin/nebulosa/api/stardetector/StarDetectionRequest.kt index c43e42c1f..982f000a9 100644 --- a/api/src/main/kotlin/nebulosa/api/stardetector/StarDetectionRequest.kt +++ b/api/src/main/kotlin/nebulosa/api/stardetector/StarDetectionRequest.kt @@ -1,9 +1,7 @@ package nebulosa.api.stardetector import nebulosa.astap.stardetector.AstapStarDetector -import nebulosa.pixinsight.script.PixInsightIsRunning -import nebulosa.pixinsight.script.PixInsightScriptRunner -import nebulosa.pixinsight.script.PixInsightStartup +import nebulosa.pixinsight.script.startPixInsight import nebulosa.pixinsight.stardetector.PixInsightStarDetector import nebulosa.siril.stardetector.SirilStarDetector import nebulosa.stardetector.StarDetector @@ -24,14 +22,7 @@ data class StarDetectionRequest( StarDetectorType.ASTAP -> AstapStarDetector(executablePath!!, minSNR) StarDetectorType.SIRIL -> SirilStarDetector(executablePath!!, maxStars) StarDetectorType.PIXINSIGHT -> { - val runner = PixInsightScriptRunner(executablePath!!) - - if (!PixInsightIsRunning(slot).use { it.runSync(runner) }) { - if (!PixInsightStartup(slot).use { it.runSync(runner) }) { - throw IllegalStateException("unable to start PixInsight") - } - } - + val runner = startPixInsight(executablePath!!, slot) PixInsightStarDetector(runner, slot, minSNR, timeout) } } diff --git a/api/src/main/kotlin/nebulosa/api/wheels/WheelSerializer.kt b/api/src/main/kotlin/nebulosa/api/wheels/WheelSerializer.kt index 698abf566..cbadc3fc4 100644 --- a/api/src/main/kotlin/nebulosa/api/wheels/WheelSerializer.kt +++ b/api/src/main/kotlin/nebulosa/api/wheels/WheelSerializer.kt @@ -11,6 +11,7 @@ class WheelSerializer : StdSerializer(FilterWheel::class.java) { override fun serialize(value: FilterWheel, gen: JsonGenerator, provider: SerializerProvider) { gen.writeStartObject() + gen.writeStringField("type", value.type.name) gen.writeStringField("sender", value.sender.id) gen.writeStringField("id", value.id) gen.writeStringField("name", value.name) diff --git a/api/src/main/resources/HIPS_SURVEYS.json b/api/src/main/resources/HIPS_SURVEYS.json new file mode 100644 index 000000000..c9334cace --- /dev/null +++ b/api/src/main/resources/HIPS_SURVEYS.json @@ -0,0 +1,118 @@ +[ +{ "ID":"CDS/P/2MASS/H", "hips_doi":"10.26093/cds/aladin/2thy-66", "creator_did":"ivo://CDS/P/2MASS/H", "hips_initial_ra":"266.40499479", "hips_initial_dec":"-28.936173970", "hips_initial_fov":"58.63230142835039", "hips_pixel_bitpix":"-32", "data_pixel_bitpix":"-32", "hips_sampling":"bilinear", "hips_skyval_method":"SKYVAL", "hips_skyval_value":"-0.5 100.0 -3146.3114318847656 13218.762664794922", "hips_overlay":"mean", "hips_hierarchy":"median", "hips_creator":"Oberto A. (CDS)", "hips_copyright":"CNRS/Unistra", "hips_version":"1.4", "hips_release_date":"2021-02-23T18:05Z", "hips_frame":"equatorial", "hips_order":"9", "hips_order_min":"0", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"-0.5 100", "moc_access_url":"http://alasky.u-strasbg.fr/2MASS/H/Moc.fits", "hips_status":"public master clonableOnce", "obs_title":"2MASS H (1.66um)", "obs_collection":"The Two Micron All Sky Survey - H band (2MASS H)", "obs_description":"2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0\". This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF", "obs_copyright_url":"http://www.ipac.caltech.edu/2mass/", "obs_ack":"University of Massachusetts & IPAC/Caltech", "bib_reference":"2006AJ....131.1163S", "bib_reference_url":"http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2006AJ....131.1163S", "obs_copyright":"University of Massachusetts & IPAC/Caltech", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "em_min":"1.525E-6", "em_max":"1.798E-6", "hips_data_range":"-3146 13219", "prov_progenitor":"IPAC/NASA", "client_category":"Image/Infrared/2MASS", "hips_builder":"Aladin/HipsGen v11.023", "hips_pixel_scale":"2.236E-4", "s_pixel_scale":"2.777E-4", "moc_sky_fraction":"1", "hips_estsize":"4631264878", "hipsgen_date":"2020-05-29T23:00Z", "hipsgen_params":"in=2MASSh out=Hips-H creator_did=ivo://CDS-test/P/2MASS/H -f \"hips_pixel_cut=-0.5 100 log\" skyval=SKYVAL \"fitskeys=ORDATE SCANNO SCANDIR\" maxthread=64 hips_frame=equatorial TILES PNG DETAILS", "hips_creation_date":"2013-05-06T20:36Z", "hips_service_url":"https://alasky.cds.unistra.fr/2MASS/H", "hips_progenitor_url":"https://alasky.cds.unistra.fr/2MASS/H/HpxFinder", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/2MASS/H", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/2MASS/H", "hips_status_2":"public mirror unclonable", "hips_tile_format_2":"jpeg", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"266.40499479", "obs_initial_dec":"-28.936173970", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288569682"}, +{ "ID":"CDS/P/2MASS/J", "hips_doi":"10.26093/cds/aladin/3ntd-6fa", "creator_did":"ivo://CDS/P/2MASS/J", "hips_initial_ra":"266.40499479", "hips_initial_dec":"-28.936173970", "hips_initial_fov":"58.63230142835039", "hips_pixel_bitpix":"-32", "data_pixel_bitpix":"-32", "hips_sampling":"bilinear", "hips_skyval_method":"SKYVAL", "hips_skyval_value":"-0.5 100.0 -357.66149139404297 1421.6846084594727", "hips_overlay":"mean", "hips_hierarchy":[ "median", "mean"], "hips_creator":"Oberto A. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"2MASS J (1.23um)", "hips_builder":[ "Aladin/HipsGen v11.023", "Aladin/HipsGen v11.023"], "hips_version":"1.4", "hips_creation_date":"2014-02-11T11:28Z", "hips_release_date":"2021-02-24T06:06Z", "hips_frame":"equatorial", "hips_order":"9", "hips_order_min":"0", "hips_tile_width":"512", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/2MASS/J/Moc.fits", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_cut":"-0.5 40", "hips_data_range":"-357.7 1422", "obs_collection":"The Two Micron All Sky Survey - J band (2MASS J)", "obs_description":"2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0\". This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF", "obs_copyright_url":"http://www.ipac.caltech.edu/2mass/", "obs_ack":"University of Massachusetts & IPAC/Caltech", "bib_reference":"2006AJ....131.1163S", "bib_reference_url":"http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2006AJ....131.1163S", "obs_copyright":"University of Massachusetts & IPAC/Caltech", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "em_min":"1.147E-6", "em_max":"1.323E-6", "prov_progenitor":"IPAC/NASA", "client_category":"Image/Infrared/2MASS", "hips_pixel_scale":"2.236E-4", "s_pixel_scale":"2.777E-4", "moc_sky_fraction":"1", "hips_estsize":"4593684487", "hipsgen_date":[ "2020-06-16T12:47Z", "2020-06-22T09:53Z"], "hipsgen_params":"in=2MASSj out=Hips-J creator_did=ivo://CDS-test/P/2MASS/J -f \"hips_pixel_cut=-0.5 40 log\" fading=true skyval=SKYVAL maxthread=64 hips_frame=equatorial JPEG", "hips_service_url":"https://alasky.cds.unistra.fr/2MASS/J", "hips_progenitor_url":"https://alasky.cds.unistra.fr/2MASS/J/HpxFinder", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/2MASS/J", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/2MASS/J", "hips_status_2":"public mirror unclonable", "hips_tile_format_2":"jpeg", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"266.40499479", "obs_initial_dec":"-28.936173970", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288568394"}, +{ "ID":"CDS/P/2MASS/K", "hips_doi":"10.26093/cds/aladin/2ea3-abw", "creator_did":"ivo://CDS/P/2MASS/K", "hips_initial_ra":"266.40499479", "hips_initial_dec":"-28.936173970", "hips_initial_fov":"58.63230142835039", "hips_pixel_bitpix":"-32", "data_pixel_bitpix":"-32", "hips_sampling":"bilinear", "hips_skyval_method":"SKYVAL", "hips_skyval_value":"-0.5 100.0 -2660.7283935546875 9046.068969726562", "hips_overlay":"mean", "hips_hierarchy":[ "median", "mean"], "hips_creator":"Oberto A. (CDS)", "hips_copyright":"CNRS/Unistra", "hips_version":"1.4", "hips_release_date":"2021-02-23T01:29Z", "hips_frame":"equatorial", "hips_order":"9", "hips_order_min":"0", "hips_tile_width":"512", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/2MASS/K/Moc.fits", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_cut":"-0.5 40", "hips_data_range":"-2661 9046", "obs_title":"2MASS K (2.16um)", "obs_collection":"The Two Micron All Sky Survey - K band (2MASS K)", "obs_description":"2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0\". This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF", "obs_copyright_url":"http://www.ipac.caltech.edu/2mass/", "obs_ack":"University of Massachusetts & IPAC/Caltech", "bib_reference":"2006AJ....131.1163S", "bib_reference_url":"http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2006AJ....131.1163S", "obs_copyright":"University of Massachusetts & IPAC/Caltech", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "em_min":"2.015E-6", "em_max":"2.303E-6", "prov_progenitor":"IPAC/NASA", "client_category":"Image/Infrared/2MASS", "hips_builder":"Aladin/HipsGen v11.023", "hips_pixel_scale":"2.236E-4", "s_pixel_scale":"2.777E-4", "moc_sky_fraction":"1", "hips_estsize":"4706425658", "hips_creation_date":"2013-01-14T09:45Z", "hipsgen_date":"2020-08-04T10:56Z", "hipsgen_params":"cache=CACHE-TODEL-K cacheRemoveOnExit=true in=2MASSk out=Hips-K3 creator_did=ivo://CDS-test/P/2MASS/K \"hips_pixel_cut=-0.5 40 log\" maxthread=64 -f hips_frame=equatorial skyval=SKYVAL JPEG", "hips_service_url":"https://alasky.cds.unistra.fr/2MASS/K", "hips_progenitor_url":"https://alasky.cds.unistra.fr/2MASS/K/HpxFinder", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/2MASS/K", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/2MASS/K", "hips_status_2":"public mirror unclonable", "hips_tile_format_2":"jpeg", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"266.40499479", "obs_initial_dec":"-28.936173970", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288570974"}, +{ "ID":"CDS/P/2MASS/color", "hips_doi":"10.26093/cds/aladin/bzc8-nw", "creator_did":"ivo://CDS/P/2MASS/color", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"266.40499479", "hips_initial_dec":"-28.936173970", "obs_collection":"The Two Micron All Sky Survey - J-H-K bands (2MASS color)", "obs_title":"2MASS color J (1.23um), H (1.66um), K (2.16um)", "obs_description":"2MASS has uniformly scanned the entire sky in three near-infrared bands to detect and characterize point sources brighter than about 1 mJy in each band, with signal-to-noise ratio (SNR) greater than 10, using a pixel size of 2.0\". This has achieved an 80,000-fold improvement in sensitivity relative to earlier surveys. 2MASS used two highly-automated 1.3-m telescopes, one at Mt. Hopkins, AZ, and one at CTIO, Chile. Each telescope was equipped with a three-channel camera, each channel consisting of a 256x256 array of HgCdTe detectors, capable of observing the sky simultaneously at J (1.25 microns), H (1.65 microns), and Ks (2.17 microns). The University of Massachusetts (UMass) was responsible for the overall management of the project, and for developing the infrared cameras and on-site computing systems at both facilities. The Infrared Processing and Analysis Center (IPAC) is responsible for all data processing through the Production Pipeline, and construction and distribution of the data products. Funding is provided primarily by NASA and the NSF", "obs_copyright_url":"http://www.ipac.caltech.edu/2mass/", "hips_creator":"Oberto A. (CDS)", "client_application":[ "AladinLite", "AladinDesktop"], "client_sort_key":"04-001-00", "prov_progenitor":"IPAC/NASA", "client_category":"Image/Infrared/2MASS", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "em_min":"1.147E-6", "em_max":"2.303E-6", "hips_builder":"Aladin/HipsGen v11.023", "hips_version":"1.4", "hips_release_date":"2021-02-24T03:22Z", "hips_frame":"equatorial", "hips_order":"9", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"University of Massachusetts & IPAC/Caltech", "bib_reference":"2006AJ....131.1163S", "bib_reference_url":"http://cdsbib.u-strasbg.fr/cgi-bin/cdsbib?2006AJ....131.1163S", "obs_copyright":"University of Massachusetts & IPAC/Caltech", "hips_pixel_scale":"2.236E-4", "dataproduct_type":"image", "hips_rgb_red":"2MASS K [-0.5 20.0 40.0 Log]", "hips_rgb_green":"2MASSh [1.0 20.0 100.0 Log]", "hips_rgb_blue":"2MASS J [-0.5 20.0 40.0 Log]", "moc_sky_fraction":"1", "hips_estsize":"75160788", "hipsgen_date":"2020-10-01T09:28Z", "hipsgen_params":"inRed=Hips-K2 inBlue=Hips-J2 inGreen=Hips-H out=Hips-Color5 creator_did=ivo://CDS-test/P/2MASS/Color skyval=SKYVAL \"fitskeys=ORDATE SCANNO SCANDIR\" maxthread=64 hips_frame=equatorial \"cmRed=-0.5 20 40 log\" \"cmBlue=-0.5 20 40 log\" \"cmGreen=1 20 100 log\" color=jpeg RGB", "hips_creation_date":"2012-02-24T12:43Z", "hips_tile_format":"jpeg", "hipsgen_date_1":"2020-10-12T12:49Z", "hipsgen_params_1":"inRed=Hips-K2 inBlue=Hips-J2 inGreen=Hips-H out=Hips-Color5 creator_did=ivo://CDS-test/P/2MASS/Color skyval=SKYVAL \"fitskeys=ORDATE SCANNO SCANDIR\" maxthread=64 hips_frame=equatorial \"cmRed=-0.5 20 40 log\" \"cmBlue=-0.5 20 40 log\" \"cmGreen=1 20 100 log\" color=jpeg RGB", "hips_hierarchy":"median", "dataproduct_subtype":"color", "hips_service_url":"https://alasky.cds.unistra.fr/2MASS/Color", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/2MASS/Color", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://casda.csiro.au/hips/2MASS/Color", "hips_status_2":"public mirror unclonable", "hips_service_url_3":"https://irsa.ipac.caltech.edu/data/hips/CDS/2MASS/Color", "hips_status_3":"public mirror unclonable", "hips_service_url_4":"https://healpix.ias.u-psud.fr/CDS_P_2MASS_color", "hips_status_4":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"266.40499479", "obs_initial_dec":"-28.936173970", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288896092"}, +{ "ID":"CDS/P/AKARI/FIS/Color", "hips_doi":"10.26093/cds/aladin/3rag-15t", "creator_did":"ivo://CDS/P/AKARI/FIS/Color", "obs_collection":"AKARI FIS Color", "obs_title":"AKARI FIS Color WideL (140um), WideS (90um), N60 (65um)", "obs_description":"AKARI (Previously known as ASTRO-F or IRIS - InfraRed Imaging Surveyor) is the second space mission for infrared astronomy in Japan. AKARI was developed by the members of JAXA/ISAS and collaborators. IRAS (Infrared Astronomical Satellite, launched in 1983 by the United Kingdom, the United States, and the Netherlands) carried out the first all-sky survey at infrared wavelengths and made a huge impact on astronomy. The AKARI mission was an ambitious plan to make an all-sky survey with much better sensitivity, spatial resolution and wider wavelength coverage than those of IRAS. All-sky survey obtained by the Far-Infrared Surveyor (FIS) onboard the AKARI satellite, at 65um (Color), 90 um (WIDE-S), 140um (WIDE-L),and 160um (N160). See http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/Doi_AKARI_FIR_AllSkySurvey.pdf.", "obs_ack":"University of Tokyo, ISAS/JAXA, Tohoku University, University of Tsukuba, RAL, and Open University", "obs_copyright":"ISAS/JAXA", "obs_copyright_url":"http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/", "client_application":[ "AladinLite", "AladinLite", "AladinDesktop"], "client_category":"Image/Infrared/AKARI-FIS", "client_sort_key":"04-05-00", "hips_release_date":"2019-05-05T04:59Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"5", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"png jpeg", "dataproduct_type":"image", "hips_rgb_red":"WideLHiPS [0.0 250.0 500.0 Log]", "hips_rgb_green":"WideSHiPS [-1.5 91.75 185.0 Log]", "hips_rgb_blue":"N60HiPS [-1.5 69.25 140.0 Log]", "moc_access_url":"http://alasky.u-strasbg.fr/AKARI-FIS/ColorLSN60/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"ISAS/JAXA", "bib_reference":"2015PASJ...67...50D", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2015PASJ...67...50D", "t_min":"53863", "t_max":"54338", "obs_regime":"Infrared", "em_min":"6.5E-5", "em_max":"0.00014", "hips_creation_date":"2015-01-09T14:17Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.003579", "hips_initial_fov":"110.0", "hips_initial_ra":"83.5553478", "hips_initial_dec":"9.0998893", "hips_order_min":"0", "dataproduct_subtype":"color", "moc_sky_fraction":"1", "hips_estsize":"367003", "hipsgen_date":"2019-05-05T04:59Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/AKARI-FIS/ColorLSN60 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AKARI-FIS/ColorLSN60", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AKARI-FIS/ColorLSN60", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://healpix.ias.u-psud.fr/CDS_P_AKARI_FIS_Color", "hips_status_2":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"5", "obs_initial_ra":"83.5553478", "obs_initial_dec":"9.0998893", "obs_initial_fov":"1.8322594196359496", "TIMESTAMP":"1721288716997"}, +{ "ID":"CDS/P/AKARI/FIS/N160", "hips_doi":"10.26093/cds/aladin/23kb-b0r", "creator_did":"ivo://CDS/P/AKARI/FIS/N160", "obs_collection":"AKARI FIS N160", "obs_title":"AKARI FIS N160 (160um)", "obs_description":"AKARI (Previously known as ASTRO-F or IRIS - InfraRed Imaging Surveyor) is the second space mission for infrared astronomy in Japan. AKARI was developed by the members of JAXA/ISAS and collaborators. IRAS (Infrared Astronomical Satellite, launched in 1983 by the United Kingdom, the United States, and the Netherlands) carried out the first all-sky survey at infrared wavelengths and made a huge impact on astronomy. The AKARI mission was an ambitious plan to make an all-sky survey with much better sensitivity, spatial resolution and wider wavelength coverage than those of IRAS. All-sky survey obtained by the Far-Infrared Surveyor (FIS) onboard the AKARI satellite, at 65um (Color), 90 um (WIDE-S), 140um (WIDE-L),and 160um (N160). See http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/Doi_AKARI_FIR_AllSkySurvey.pdf.", "obs_ack":"University of Tokyo, ISAS/JAXA, Tohoku University, University of Tsukuba, RAL and Open University", "obs_copyright":"ISAS/JAXA", "obs_copyright_url":"http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/", "client_category":"Image/Infrared/AKARI-FIS", "client_sort_key":"04-05-04", "hips_release_date":"2019-05-05T05:01Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"5", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"0 500", "hips_data_range":"-125.7 304.6", "moc_access_url":"http://alasky.u-strasbg.fr/AKARI-FIS/N160/Moc.fits", "hips_progenitor_url":"https://alasky.cds.unistra.fr/AKARI-FIS/N160/HpxFinder", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"ISAS/JAXA", "bib_reference":"2015PASJ...67...50D", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2015PASJ...67...50D", "t_min":"53863", "t_max":"54338", "obs_regime":"Infrared", "em_min":"0.00016", "em_max":"0.00016", "hips_creation_date":"2015-01-08T14:34Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.003579", "hips_initial_fov":"110.0", "hips_initial_ra":"83.5553478", "hips_initial_dec":"9.0998893", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"17870688", "hipsgen_date":"2019-05-05T05:01Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/AKARI-FIS/N160 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AKARI-FIS/N160", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AKARI-FIS/N160", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"41", "moc_time_range":"1", "moc_order":"5", "obs_initial_ra":"83.5553478", "obs_initial_dec":"9.0998893", "obs_initial_fov":"1.8322594196359496", "TIMESTAMP":"1721288413290"}, +{ "ID":"CDS/P/AKARI/FIS/N60", "hips_doi":"10.26093/cds/aladin/2a4f-2bn", "creator_did":"ivo://CDS/P/AKARI/FIS/N60", "obs_collection":"AKARI FIS N60", "obs_title":"AKARI FIS N60 (65um)", "obs_description":"AKARI (Previously known as ASTRO-F or IRIS - InfraRed Imaging Surveyor) is the second space mission for infrared astronomy in Japan. AKARI was developed by the members of JAXA/ISAS and collaborators. IRAS (Infrared Astronomical Satellite, launched in 1983 by the United Kingdom, the United States, and the Netherlands) carried out the first all-sky survey at infrared wavelengths and made a huge impact on astronomy. The AKARI mission was an ambitious plan to make an all-sky survey with much better sensitivity, spatial resolution and wider wavelength coverage than those of IRAS. All-sky survey obtained by the Far-Infrared Surveyor (FIS) onboard the AKARI satellite, at 65um (Color), 90 um (WIDE-S), 140um (WIDE-L),and 160um (N160). See http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/Doi_AKARI_FIR_AllSkySurvey.pdf.", "obs_ack":"University of Tokyo, ISAS/JAXA, Tohoku University, University of Tsukuba, RAL and Open University", "obs_copyright":"ISAS/JAXA", "obs_copyright_url":"http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/", "client_category":"Image/Infrared/AKARI-FIS", "client_sort_key":"04-05-01", "hips_release_date":"2019-05-05T05:03Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"5", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"-1.5 140", "hips_data_range":"-104.6 251", "moc_access_url":"http://alasky.u-strasbg.fr/AKARI-FIS/N60/Moc.fits", "hips_progenitor_url":"https://alasky.cds.unistra.fr/AKARI-FIS/N60/HpxFinder", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"ISAS/JAXA", "bib_reference":"2015PASJ...67...50D", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2015PASJ...67...50D", "t_min":"53863", "t_max":"54338", "obs_regime":"Infrared", "em_min":"6.5E-5", "em_max":"6.5E-5", "hips_creation_date":"2015-01-08T16:04Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.003579", "hips_initial_fov":"110.0", "hips_initial_ra":"83.5553478", "hips_initial_dec":"9.0998893", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"17870688", "hipsgen_date":"2019-05-05T05:03Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/AKARI-FIS/N60 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AKARI-FIS/N60", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AKARI-FIS/N60", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"41", "moc_time_range":"1", "moc_order":"5", "obs_initial_ra":"83.5553478", "obs_initial_dec":"9.0998893", "obs_initial_fov":"1.8322594196359496", "TIMESTAMP":"1721288413358"}, +{ "ID":"CDS/P/AKARI/FIS/WideL", "hips_doi":"10.26093/cds/aladin/1tkx-knx", "creator_did":"ivo://CDS/P/AKARI/FIS/WideL", "obs_collection":"AKARI FIS WideL", "obs_title":"AKARI FIS WideL (140um)", "obs_description":"AKARI (Previously known as ASTRO-F or IRIS - InfraRed Imaging Surveyor) is the second space mission for infrared astronomy in Japan. AKARI was developed by the members of JAXA/ISAS and collaborators. IRAS (Infrared Astronomical Satellite, launched in 1983 by the United Kingdom, the United States, and the Netherlands) carried out the first all-sky survey at infrared wavelengths and made a huge impact on astronomy. The AKARI mission was an ambitious plan to make an all-sky survey with much better sensitivity, spatial resolution and wider wavelength coverage than those of IRAS. All-sky survey obtained by the Far-Infrared Surveyor (FIS) onboard the AKARI satellite, at 65um (Color), 90 um (WIDE-S), 140um (WIDE-L),and 160um (N160). See http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/Doi_AKARI_FIR_AllSkySurvey.pdf.", "obs_ack":"University of Tokyo, ISAS/JAXA, Tohoku University, University of Tsukuba, RAL and Open University", "obs_copyright":"ISAS/JAXA", "obs_copyright_url":"http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/", "client_category":"Image/Infrared/AKARI-FIS", "client_sort_key":"04-05-03", "hips_release_date":"2019-05-05T05:05Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"5", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"0 500", "hips_data_range":"-38.17 154", "moc_access_url":"http://alasky.u-strasbg.fr/AKARI-FIS/WideL/Moc.fits", "hips_progenitor_url":"https://alasky.cds.unistra.fr/AKARI-FIS/WideL/HpxFinder", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"ISAS/JAXA", "bib_reference":"2015PASJ...67...50D", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2015PASJ...67...50D", "t_min":"53863", "t_max":"54338", "obs_regime":"Infrared", "em_min":"0.00014", "em_max":"0.00014", "hips_creation_date":"2015-01-08T15:53Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.003579", "hips_initial_fov":"110.0", "hips_initial_ra":"83.5553478", "hips_initial_dec":"9.0998893", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"17870688", "hipsgen_date":"2019-05-05T05:05Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/AKARI-FIS/WideL UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AKARI-FIS/WideL", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AKARI-FIS/WideL", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"41", "moc_time_range":"1", "moc_order":"5", "obs_initial_ra":"83.5553478", "obs_initial_dec":"9.0998893", "obs_initial_fov":"1.8322594196359496", "TIMESTAMP":"1721288413410"}, +{ "ID":"CDS/P/AKARI/FIS/WideS", "hips_doi":"10.26093/cds/aladin/3w3d-gt8", "creator_did":"ivo://CDS/P/AKARI/FIS/WideS", "obs_collection":"AKARI FIS WideS", "obs_title":"AKARI FIS WideS (90um)", "obs_description":"AKARI (Previously known as ASTRO-F or IRIS - InfraRed Imaging Surveyor) is the second space mission for infrared astronomy in Japan. AKARI was developed by the members of JAXA/ISAS and collaborators. IRAS (Infrared Astronomical Satellite, launched in 1983 by the United Kingdom, the United States, and the Netherlands) carried out the first all-sky survey at infrared wavelengths and made a huge impact on astronomy. The AKARI mission was an ambitious plan to make an all-sky survey with much better sensitivity, spatial resolution and wider wavelength coverage than those of IRAS. All-sky survey obtained by the Far-Infrared Surveyor (FIS) onboard the AKARI satellite, at 65um (Color), 90 um (WIDE-S), 140um (WIDE-L),and 160um (N160). See http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/Doi_AKARI_FIR_AllSkySurvey.pdf.", "obs_ack":"University of Tokyo, ISAS/JAXA, Tohoku University, University of Tsukuba, RAL and Open University", "obs_copyright":"ISAS/JAXA", "obs_copyright_url":"http://www.ir.isas.jaxa.jp/AKARI/Archive/Images/FIS_AllSkyMap/", "client_category":"Image/Infrared/AKARI-FIS", "client_sort_key":"04-05-02", "hips_release_date":"2019-05-05T05:07Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"5", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"-1.5 140", "hips_data_range":"-12.06 38.42", "moc_access_url":"http://alasky.u-strasbg.fr/AKARI-FIS/WideS/Moc.fits", "hips_progenitor_url":"https://alasky.cds.unistra.fr/AKARI-FIS/WideS/HpxFinder", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"ISAS/JAXA", "bib_reference":"2015PASJ...67...50D", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2015PASJ...67...50D", "t_min":"53863", "t_max":"54338", "obs_regime":"Infrared", "em_min":"9e-5", "em_max":"9e-5", "hips_creation_date":"2015-01-08T15:45Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.003579", "hips_initial_fov":"110.0", "hips_initial_ra":"83.5553478", "hips_initial_dec":"9.0998893", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"17870688", "hipsgen_date":"2019-05-05T05:07Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/AKARI-FIS/WideS UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AKARI-FIS/WideS", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AKARI-FIS/WideS", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"41", "moc_time_range":"1", "moc_order":"5", "obs_initial_ra":"83.5553478", "obs_initial_dec":"9.0998893", "obs_initial_fov":"1.8322594196359496", "TIMESTAMP":"1721288413462"}, +{ "ID":"CDS/P/CO", "hips_doi":"10.26093/cds/aladin/1zd9-hss", "creator_did":"ivo://CDS/P/CO", "obs_collection":"CO composite survey", "obs_title":"CO composite survey", "obs_description":"This survey contains data from the composite CO map constructed by Dame, Hartmann and Thaddeus (2001) from 37 individual surveys of the Galaxy in the CO (1-0) line. Due to the composite nature of the map and processing used to render a uniform S/N appearance, the user is cautioned that angular resolution and sensitivity vary across the map. Survey data are limited to Galactic latitudes |b|<32 deg., with roughly half of that area containing observations.To create this file, the velocity-integrated brightness temperature map, W(CO), was obtained from the CfA Millimeter Wave Group website and then interpolated onto a HEALPix grid with Nside=512.", "obs_copyright":"Composite map by Dame et al (2001,ApJ,547,792), - HEALPixed by LAMBDA", "obs_copyright_url":"http://lambda.gsfc.nasa.gov/product/foreground/fg_WCO_get.cfm", "client_category":"Image/Gas-lines/CO", "client_sort_key":"06-08-01", "hips_creation_date":"2011-02-14T12:00Z", "hips_release_date":"2019-05-05T06:00Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_width":"64", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/CO/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"We acknowledge the use of the Legacy Archive for Microwave Background Data Analysis (LAMBDA), part of the High Energy Astrophysics Science Archive Center (HEASARC). HEASARC/LAMBDA is a service of the Astrophysics Science Division at the NASA Goddard Space Flight Center.\"", "prov_progenitor":"HEASARC/LAMBDA", "bib_reference":"2001ApJ...547..792D", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2001ApJ...547..792D", "t_min":"44239", "t_max":"51544", "obs_regime":"Radio", "em_min":"2.604173540653e-3", "em_max":"2.609386874402e-3", "hips_pixel_scale":"0.01431", "hips_initial_fov":"158.63230142835039", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:00Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/CO UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/CO", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/CO", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288414282"}, +{ "ID":"CDS/P/CO-Dame-2022", "hips_initial_fov":"360.0", "hips_initial_ra":"266.415009", "hips_initial_dec":"-29.0061110", "creator_did":"ivo://CDS/P/CO-Dame-2022", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Universite de Strasbourg", "obs_title":"Dame and Thaddeus 2022 Velocity Integrated CO Map", "obs_collection":"Dame CO maps", "obs_description":"Data published by Dame & Thaddeus (2022) significantly extends the Galactic plane CO survey of Dame, Hartmann & Thaddeus (2001) with complementary coverage of the entire northern sky (? > -17�). The coverage extension was carried out with the same telescope as was used for the plane survey, the CfA 1.2 m, and perfectly meshes with its irregular boundaries in latitude. The merged survey is released by the authors in the form of CO line spectral data cubes. To create the LAMBDA map, the moment-masked data cube for the combined survey was integrated over +/- 36 km/sec, the full range over which significant emission is detected (see Figure 8 of Dame & Thaddeus 2022; the combined survey does not include the high-velocity observations available from the Dame, Hartmann & Thaddeus 2001 data). The velocity integrated brightness temperature map was then interpolated from the original rectilinear projection onto pixel centers appropriate for HEALPix Nside=256. The LAMBDA map is in units of K-km/sec, and a mask is provided indicating those portions of the sky that are unobserved.", "obs_ack":"We acknowledge the use of the Legacy Archive for Microwave Background Data Analysis (LAMBDA), part of the High Energy Astrophysics Science Archive Center (HEASARC). HEASARC/LAMBDA is a service of the Astrophysics Science Division at the NASA Goddard Space Flight Center.\"", "prov_progenitor":"HEASARC/LAMBDA", "bib_reference":"2022ApJS..262....5D", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2022ApJS..262....5D", "obs_copyright":"Composite map by Dame & Thaddeus (2022 ApJS, 262, 5) - HEALPixed by LAMBDA", "obs_copyright_url":"https://lambda.gsfc.nasa.gov/product/foreground/fg_wco_dt2022_get.html", "client_category":"Image/Gas-lines/CO", "obs_regime":"Radio", "em_min":"2.604173540653e-3", "em_max":"2.609386874402e-3", "hips_builder":"Aladin/HipsGen v12.119", "hips_version":"1.4", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"32", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-1 210", "hips_data_range":"-382.9 1137", "hips_pixel_scale":"0.01431", "dataproduct_type":"image", "hipsgen_date":"2024-07-17T09:58Z", "hipsgen_params":"in=lambda_Wco_DT2022.fits out=./hips id=CDS/P/CO-Dame-2022 MAPTILES", "hips_release_date":"2024-07-17T10:09Z", "hips_creation_date":"2024-07-17T09:58Z", "moc_sky_fraction":"1", "hipsgen_date_1":"2024-07-17T10:00Z", "hipsgen_params_1":"in=lambda_Wco_DT2022.fits out=./hips id=CDS/P/CO-Dame-2022 blank=0 MAPTILES", "hipsgen_date_2":"2024-07-17T10:09Z", "hipsgen_params_2":"in=lambda_Wco_DT2022.fits out=./hips id=CDS/P/CO-Dame-2022 blank=0 \"pixelCut=-1 210 log\" PNG", "hips_service_url":"https://alasky.cds.unistra.fr/CO-maps/CDS_P_CO-Dame-2022", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/CO-maps/CDS_P_CO-Dame-2022", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"3", "obs_initial_ra":"266.415009", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288498778"}, +{ "ID":"CDS/P/DM/flux-Bp/I/345/gaia2", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/DM/flux-Bp/I/345/gaia2", "hips_creator":"Boch T. (CDS)", "obs_title":"Bp flux map for table I/345/gaia2 (Gaia DR2)", "obs_regime":"Optical", "em_min":"3.28045e-07", "em_max":"6.71903e-07", "prov_did":"ivo://CDS/I/345/gaia2", "client_category":"Ancillary/GaiaDR2", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T08:57Z", "hips_frame":"equatorial", "hips_order":"4", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"0 100000", "hips_data_range":"-2.521E8 7.565E8", "hips_pixel_scale":"0.007157", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"4486027", "hipsgen_date":"2018-04-17T07:37Z", "hipsgen_params":"in=density-maps/param-weighted/gaia-DR2-Bp-map.hpx out=density-maps/param-weighted/hips-gaia-DR2-flux-Bp -creator_did=ivo://CDS/P/DM/flux-Bp/I/345/out \"-hips_pixel_cut=0 200000 sqrt\" -method=mean MAPTILES JPEG", "hips_creation_date":"2018-04-17T07:37Z", "hipsgen_date_1":"2018-04-17T07:38Z", "hipsgen_params_1":"in=density-maps/param-weighted/gaia-DR2-Bp-map.hpx out=density-maps/param-weighted/hips-gaia-DR2-flux-Bp -creator_did=ivo://CDS/P/DM/flux-Bp/I/345/out \"-hips_pixel_cut=0 200000 sqrt\" -method=mean MAPTILES JPEG", "hipsgen_date_2":"2018-04-17T08:02Z", "hipsgen_params_2":"in=density-maps/param-weighted/gaia-DR2-Bp-map.hpx out=density-maps/param-weighted/hips-gaia-DR2-flux-Bp -creator_did=ivo://CDS/P/DM/flux-Bp/I/345/out \"-hips_pixel_cut=0 100000 sqrt\" -method=mean MAPTILES JPEG", "hipsgen_date_3":"2018-04-17T08:03Z", "hipsgen_params_3":"in=density-maps/param-weighted/gaia-DR2-Bp-map.hpx out=density-maps/param-weighted/hips-gaia-DR2-flux-Bp -creator_did=ivo://CDS/P/DM/flux-Bp/I/345/out \"-hips_pixel_cut=0 100000 sqrt\" -method=mean MAPTILES JPEG", "hipsgen_date_4":"2019-05-21T08:57Z", "hipsgen_params_4":"out=/asd-volumes/sc1-asd-volume6/ancillary/GaiaDR2/Bp-flux-map UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaDR2/Bp-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaDR2/Bp-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"9", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288478430"}, +{ "ID":"CDS/P/DM/flux-Bp/I/350/gaiaedr3", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "creator_did":"ivo://CDS/P/DM/flux-Bp/I/350/gaiaedr3", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"Bp flux map for table I/350/gaiaedr3 (Gaia EDR3)", "obs_collection":"Gaia", "obs_copyright":"ESA/Gaia mission/DPAC", "obs_copyright_url":"https://dx.doi.org/10.5270/esa-1ugzkg7", "obs_regime":"Optical", "em_min":"3.29283e-7", "em_max":"6.73811e-7", "data_bunit":"count.s-1.sr-1", "client_category":"Ancillary/GaiaEDR3", "prov_did":"ivo://CDS/I/350/gaiaedr3", "hips_builder":"Aladin/HipsGen v11.025", "hips_version":"1.4", "hips_release_date":"2020-11-27T12:53Z", "hips_frame":"equatorial", "hips_order":"7", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"-32", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_creation_date":"2019-05-21T08:57Z", "hips_pixel_scale":"8.946E-4", "hips_estsize":"287105287", "hipsgen_date":"2020-11-26T10:02Z", "hipsgen_params":"out=hips-flux/save/bp-flux TREE", "hipsgen_date_1":"2020-11-26T10:52Z", "hipsgen_params_1":"out=hips-flux/bp-flux TREE", "hipsgen_date_2":"2020-11-26T10:56Z", "hipsgen_params_2":"out=hips-flux/bp-flux TREE", "hipsgen_date_3":"2020-11-26T11:01Z", "hipsgen_params_3":"out=hips-flux/bp-flux TREE", "hipsgen_date_4":"2020-11-26T11:17Z", "hipsgen_params_4":"out=hips-flux/bp-flux TREE", "hipsgen_date_5":"2020-11-26T11:21Z", "hipsgen_params_5":"out=hips-flux/bp-flux TREE", "hipsgen_date_6":"2020-11-26T11:22Z", "hipsgen_params_6":"out=hips-flux/bp-flux TREE", "hipsgen_date_7":"2020-11-26T12:56Z", "hipsgen_params_7":"out=hips-flux/bp-flux TREE", "hipsgen_date_8":"2020-11-27T07:42Z", "hipsgen_params_8":"out=hips-flux/bp-flux TREE", "hips_pixel_cut":"0 2.0E7", "hipsgen_date_9":"2020-11-27T12:53Z", "hipsgen_params_9":"out=hips-flux/bp-flux \"pixelCut=0 2e7 log\" JPEG", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaEDR3/Bp-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaEDR3/Bp-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288487630"}, +{ "ID":"CDS/P/DM/flux-Bp/I/355/gaiadr3", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "creator_did":"ivo://CDS/P/DM/flux-Bp/I/355/gaiadr3", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Universite de Strasbourg", "obs_title":"Bp flux map for table I/355/gaiadr3 (Gaia DR3)", "obs_collection":"Gaia", "obs_copyright":"ESA/Gaia mission/DPAC", "obs_copyright_url":"https://doi.org/10.5270/esa-qa4lep3", "obs_regime":"Optical", "em_min":"3.29283e-7", "em_max":"6.73811e-7", "data_bunit":"count.s-1.sr-1", "client_category":"Ancillary/GaiaDR3", "prov_did":"ivo://CDS/I/355/gaiadr3", "hips_builder":"Aladin/HipsGen v11.071", "hips_version":"1.4", "hips_release_date":"2022-06-16T09:03Z", "hips_frame":"equatorial", "hips_order":"7", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"-32", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_creation_date":"2022-06-12T22:59Z", "hips_pixel_scale":"8.946E-4", "hips_pixel_cut":"0 2.0E7", "hips_estsize":"287105287", "hipsgen_date":"2022-06-14T14:17Z", "hipsgen_params":"\"hips_pixel_cut=0 2e7 asinh\" out=bp-flux \"hips_pixel_cut=0 20000000 asinh\" JPEG", "hipsgen_date_1":"2022-06-15T06:54Z", "hipsgen_params_1":"\"hips_pixel_cut=0 2e7 asinh\" out=bp-flux \"hips_pixel_cut=0 20000000 asinh\" method=MEAN JPEG -f", "hipsgen_date_2":"2022-06-16T06:43Z", "hipsgen_params_2":"\"hips_pixel_cut=0 2e7 asinh\" out=bp-flux \"hips_pixel_cut=0 20000000 asinh\" method=MEAN JPEG -f", "hipsgen_date_3":"2022-06-16T07:05Z", "hipsgen_params_3":"\"hips_pixel_cut=0 2e7 asinh\" out=bp-flux \"hips_pixel_cut=0 20000000 asinh\" method=MEAN JPEG -f", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaDR3/Bp-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaDR3/Bp-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288487962"}, +{ "ID":"CDS/P/DM/flux-G/I/345/gaia2", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/DM/flux-G/I/345/gaia2", "hips_creator":"Boch T. (CDS)", "obs_title":"G flux map for table I/345/gaia2 (Gaia DR2)", "obs_regime":"Optical", "em_min":"3.30660e-07", "em_max":"10.45065e-07", "prov_did":"ivo://CDS/I/345/gaia2", "client_category":"Ancillary/GaiaDR2", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T08:57Z", "hips_frame":"equatorial", "hips_order":"4", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"0 200000", "hips_data_range":"-3.760E8 1.128E9", "hips_pixel_scale":"0.007157", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"4486027", "hipsgen_date":"2018-04-17T07:05Z", "hipsgen_params":"in=density-maps/param-weighted/gaia-DR2-G-map.hpx out=density-maps/param-weighted/hips-gaia-DR2-flux-G -creator_did=ivo://CDS/P/DM/flux-G/I/345/out \"-hips_pixel_cut=0 200000 sqrt\" -method=mean MAPTILES JPEG", "hips_creation_date":"2018-04-17T07:05Z", "hipsgen_date_1":"2018-04-17T07:08Z", "hipsgen_params_1":"in=density-maps/param-weighted/gaia-DR2-G-map.hpx out=density-maps/param-weighted/hips-gaia-DR2-flux-G -creator_did=ivo://CDS/P/DM/flux-G/I/345/out \"-hips_pixel_cut=0 200000 sqrt\" -method=mean MAPTILES JPEG", "hipsgen_date_2":"2019-05-21T08:57Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/ancillary/GaiaDR2/G-flux-map UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaDR2/G-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaDR2/G-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"9", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288478570"}, +{ "ID":"CDS/P/DM/flux-G/I/350/gaiaedr3", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "creator_did":"ivo://CDS/P/DM/flux-G/I/350/gaiaedr3", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"G flux map for table I/350/gaiaedr3 (Gaia EDR3)", "obs_collection":"Gaia", "obs_copyright":"ESA/Gaia mission/DPAC", "obs_copyright_url":"https://dx.doi.org/10.5270/esa-1ugzkg7", "obs_regime":"Optical", "em_min":"3.29402e-7", "em_max":"10.30196e-7", "data_bunit":"count.s-1.sr-1", "client_category":"Ancillary/GaiaEDR3", "prov_did":"ivo://CDS/I/350/gaiaedr3", "hips_builder":"Aladin/HipsGen v11.025", "hips_version":"1.4", "hips_release_date":"2020-11-27T15:11Z", "hips_frame":"equatorial", "hips_order":"7", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"-32", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_creation_date":"2019-05-21T08:57Z", "hips_pixel_scale":"8.946E-4", "hips_estsize":"287105287", "hipsgen_date":"2020-11-26T10:02Z", "hipsgen_params":"out=hips-flux/save/bp-flux TREE", "hipsgen_date_1":"2020-11-26T10:52Z", "hipsgen_params_1":"out=hips-flux/bp-flux TREE", "hipsgen_date_2":"2020-11-26T10:56Z", "hipsgen_params_2":"out=hips-flux/bp-flux TREE", "hipsgen_date_3":"2020-11-26T11:01Z", "hipsgen_params_3":"out=hips-flux/bp-flux TREE", "hipsgen_date_4":"2020-11-26T11:17Z", "hipsgen_params_4":"out=hips-flux/bp-flux TREE", "hipsgen_date_5":"2020-11-26T11:21Z", "hipsgen_params_5":"out=hips-flux/bp-flux TREE", "hipsgen_date_6":"2020-11-26T11:22Z", "hipsgen_params_6":"out=hips-flux/bp-flux TREE", "hipsgen_date_7":"2020-11-26T12:56Z", "hipsgen_params_7":"out=hips-flux/bp-flux TREE", "hipsgen_date_8":"2020-11-26T15:09Z", "hipsgen_params_8":"out=hips-flux/g-flux TREE", "hipsgen_date_9":"2020-11-27T10:18Z", "hipsgen_params_9":"out=hips-flux/g-flux TREE", "hips_pixel_cut":"0 2.0E7", "hipsgen_date_10":"2020-11-27T15:11Z", "hipsgen_params_10":"out=hips-flux/g-flux \"pixelCut=0 2e7 log\" JPEG", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaEDR3/G-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaEDR3/G-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288487742"}, +{ "ID":"CDS/P/DM/flux-G/I/355/gaiadr3", "creator_did":"ivo://CDS/P/DM/flux-G/I/355/gaiadr3", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Universite de Strasbourg", "obs_title":"G flux map for table I/355/gaiadr3 (Gaia DR3)", "obs_collection":"Gaia", "obs_copyright":"ESA/Gaia mission/DPAC", "obs_copyright_url":"https://doi.org/10.5270/esa-qa4lep3", "obs_regime":"Optical", "em_min":"3.29402e-7", "em_max":"10.30196e-7", "data_bunit":"count.s-1.sr-1", "client_category":"Ancillary/GaiaDR3", "prov_did":"ivo://CDS/I/355/gaiadr3", "hips_builder":"Aladin/HipsGen v11.071", "hips_version":"1.4", "hips_release_date":"2022-06-16T14:31Z", "hips_frame":"equatorial", "hips_order":"7", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"-32", "hips_pixel_scale":"8.946E-4", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"287105287", "hipsgen_date":"2022-06-12T22:59Z", "hipsgen_params":"out=hips-flux/bp-flux TREE", "hips_creation_date":"2022-06-12T22:59Z", "hipsgen_date_1":"2022-06-13T01:55Z", "hipsgen_params_1":"out=hips-flux/g-flux TREE", "hipsgen_date_2":"2022-06-16T12:26Z", "hipsgen_params_2":"\"hips_pixel_cut=0 2e7 asinh\" out=g-flux \"hips_pixel_cut=0 20000000 asinh\" method=MEAN JPEG -f", "hips_pixel_cut":"0 2.0E7", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaDR3/G-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaDR3/G-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288488014"}, +{ "ID":"CDS/P/DM/flux-Rp/I/345/gaia2", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/DM/flux-Rp/I/345/gaia2", "hips_creator":"Boch T. (CDS)", "obs_title":"Rp flux map for table I/345/gaia2 (Gaia DR2)", "obs_regime":"Optical", "em_min":"6.25497e-07", "em_max":"10.60579e-07", "prov_did":"ivo://CDS/I/345/gaia2", "client_category":"Ancillary/GaiaDR2", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T08:57Z", "hips_frame":"equatorial", "hips_order":"4", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"0 100000", "hips_data_range":"-1.383E8 4.150E8", "hips_pixel_scale":"0.007157", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"4486027", "hipsgen_date":"2018-04-17T08:17Z", "hipsgen_params":"in=density-maps/param-weighted/gaia-DR2-Rp-map.hpx out=density-maps/param-weighted/hips-gaia-DR2-flux-Rp -creator_did=ivo://CDS/P/DM/flux-Rp/I/345/out \"-hips_pixel_cut=0 100000 sqrt\" -method=mean MAPTILES JPEG", "hips_creation_date":"2018-04-17T08:17Z", "hipsgen_date_1":"2018-04-17T08:18Z", "hipsgen_params_1":"in=density-maps/param-weighted/gaia-DR2-Rp-map.hpx out=density-maps/param-weighted/hips-gaia-DR2-flux-Rp -creator_did=ivo://CDS/P/DM/flux-Rp/I/345/out \"-hips_pixel_cut=0 100000 sqrt\" -method=mean MAPTILES JPEG", "hipsgen_date_2":"2019-05-21T08:57Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/ancillary/GaiaDR2/Rp-flux-map UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaDR2/Rp-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaDR2/Rp-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"9", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288478642"}, +{ "ID":"CDS/P/DM/flux-Rp/I/350/gaiaedr3", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "creator_did":"ivo://CDS/P/DM/flux-Rp/I/350/gaiaedr3", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"Rp flux map for table I/350/gaiaedr3 (Gaia EDR3)", "obs_collection":"Gaia", "obs_copyright":"ESA/Gaia mission/DPAC", "obs_copyright_url":"https://dx.doi.org/10.5270/esa-1ugzkg7", "obs_regime":"Optical", "em_min":"6.19605e-7", "em_max":"10.42296e-7", "data_bunit":"count.s-1.sr-1", "client_category":"Ancillary/GaiaEDR3", "prov_did":"ivo://CDS/I/350/gaiaedr3", "hips_builder":"Aladin/HipsGen v11.025", "hips_version":"1.4", "hips_release_date":"2020-11-27T14:11Z", "hips_frame":"equatorial", "hips_order":"7", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"-32", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_creation_date":"2019-05-21T08:57Z", "hips_pixel_scale":"8.946E-4", "hips_estsize":"287105287", "hipsgen_date":"2020-11-26T10:02Z", "hipsgen_params":"out=hips-flux/save/bp-flux TREE", "hipsgen_date_1":"2020-11-26T10:52Z", "hipsgen_params_1":"out=hips-flux/bp-flux TREE", "hipsgen_date_2":"2020-11-26T10:56Z", "hipsgen_params_2":"out=hips-flux/bp-flux TREE", "hipsgen_date_3":"2020-11-26T11:01Z", "hipsgen_params_3":"out=hips-flux/bp-flux TREE", "hipsgen_date_4":"2020-11-26T11:17Z", "hipsgen_params_4":"out=hips-flux/bp-flux TREE", "hipsgen_date_5":"2020-11-26T11:21Z", "hipsgen_params_5":"out=hips-flux/bp-flux TREE", "hipsgen_date_6":"2020-11-26T11:22Z", "hipsgen_params_6":"out=hips-flux/bp-flux TREE", "hipsgen_date_7":"2020-11-26T12:56Z", "hipsgen_params_7":"out=hips-flux/bp-flux TREE", "hipsgen_date_8":"2020-11-26T14:07Z", "hipsgen_params_8":"out=hips-flux/rp-flux TREE", "hipsgen_date_9":"2020-11-26T14:17Z", "hipsgen_params_9":"out=hips-flux/rp-flux mode=keeptile TREE", "hipsgen_date_10":"2020-11-26T14:45Z", "hipsgen_params_10":"out=hips-flux/rp-flux TREE", "hipsgen_date_11":"2020-11-27T08:52Z", "hipsgen_params_11":"out=hips-flux/rp-flux TREE", "hips_pixel_cut":"0 2.0E7", "hipsgen_date_12":"2020-11-27T14:11Z", "hipsgen_params_12":"out=hips-flux/rp-flux \"pixelCut=0 2e7 log\" JPEG", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaEDR3/Rp-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaEDR3/Rp-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288487794"}, +{ "ID":"CDS/P/DM/flux-Rp/I/355/gaiadr3", "creator_did":"ivo://CDS/P/DM/flux-Rp/I/355/gaiadr3", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Universite de Strasbourg", "obs_title":"Rp flux map for table I/355/gaiadr3 (Gaia DR3)", "obs_collection":"Gaia", "obs_copyright":"ESA/Gaia mission/DPAC", "obs_copyright_url":"https://doi.org/10.5270/esa-qa4lep3", "obs_regime":"Optical", "em_min":"6.19605e-7", "em_max":"10.42296e-7", "data_bunit":"count.s-1.sr-1", "client_category":"Ancillary/GaiaDR3", "prov_did":"ivo://CDS/I/355/gaiadr3", "hips_builder":"Aladin/HipsGen v11.071", "hips_version":"1.4", "hips_release_date":"2022-06-16T12:25Z", "hips_frame":"galactic", "hips_order":"7", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"-32", "hips_pixel_scale":"8.946E-4", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"287105287", "hipsgen_date":"2022-06-12T22:59Z", "hipsgen_params":"out=hips-flux/bp-flux TREE", "hips_creation_date":"2022-06-12T22:59Z", "hipsgen_date_1":"2022-06-13T00:27Z", "hipsgen_params_1":"out=hips-flux/rp-flux TREE", "hipsgen_date_2":"2022-06-16T09:08Z", "hipsgen_params_2":"\"hips_pixel_cut=0 2e7 asinh\" out=rp-flux \"hips_pixel_cut=0 20000000 asinh\" method=MEAN JPEG -f", "hips_pixel_cut":"0 2.0E7", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaDR3/Rp-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaDR3/Rp-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288488070"}, +{ "ID":"CDS/P/DM/flux-color-Rp-G-Bp/I/345/gaia2", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/DM/flux-color-Rp-G-Bp/I/345/gaia2", "hips_creator":"Boch T. (CDS)", "obs_title":"Color flux map for I/345/gaia2 (Gaia DR2)", "obs_regime":"Optical", "prov_did":"ivo://CDS/I/345/gaia2", "client_category":"Ancillary/GaiaDR2", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T08:57Z", "hips_frame":"equatorial", "hips_order":"4", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg", "hips_pixel_scale":"0.007157", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"73407", "hips_creation_date":"2018-04-17T07:37Z", "hips_pixel_cut":"0 100000", "hips_data_range":"-2.521E8 7.565E8", "hipsgen_date":"2018-04-19T12:45Z", "hipsgen_params":"out=density-maps/param-weighted/test -order=3 allsky", "hipsgen_date_1":"2018-04-19T12:46Z", "hipsgen_params_1":"out=density-maps/param-weighted/test color=jpeg -order=3 allsky", "hips_order_min":"0", "dataproduct_subtype":"color", "hipsgen_date_2":"2019-05-21T08:57Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/ancillary/GaiaDR2/color-Rp-G-Bp-flux-map UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaDR2/color-Rp-G-Bp-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaDR2/color-Rp-G-Bp-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"9", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288478498"}, +{ "ID":"CDS/P/DM/flux-color-Rp-G-Bp/I/350/gaiaedr3", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "creator_did":"ivo://CDS/P/DM/flux-color-Rp-G-Bp/I/350/gaiaedr3", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"Color flux map for I/350/gaiaedr3 (Gaia EDR3)", "obs_collection":"Gaia", "obs_copyright":"ESA/Gaia mission/DPAC", "obs_copyright_url":"https://dx.doi.org/10.5270/esa-1ugzkg7", "obs_regime":"Optical", "em_min":"3.29283e-7", "em_max":"10.42296e-7", "client_category":"Ancillary/GaiaEDR3", "prov_did":"ivo://CDS/I/350/gaiaedr3", "hips_version":"1.4", "hips_frame":"equatorial", "hips_order":"7", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_creation_date":"2020-11-26T16:37Z", "hips_release_date":"2020-11-27T12:53Z", "hips_order_min":"0", "dataproduct_subtype":"color", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaEDR3/color-Rp-G-Bp-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaEDR3/color-Rp-G-Bp-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288487682"}, +{ "ID":"CDS/P/DM/flux-color-Rp-G-Bp/I/355/gaiadr3", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "creator_did":"ivo://CDS/P/DM/flux-color-Rp-G-Bp/I/355/gaiadr3", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Universite de Strasbourg", "obs_title":"Color flux map for I/355/gaiadr3 (Gaia DR3)", "obs_collection":"Gaia", "obs_description":"This color flux map has been created from Gaia DR3 individual sources data (fluxes in Rp=red, G=green, Bp=blue filters)", "obs_copyright":"ESA/Gaia mission/DPAC", "obs_copyright_url":"https://doi.org/10.5270/esa-qa4lep3", "obs_regime":"Optical", "em_min":"3.29283e-7", "em_max":"10.42296e-7", "client_category":"Ancillary/GaiaDR3", "prov_did":"ivo://CDS/I/355/gaiadr3", "hips_version":"1.4", "hips_frame":"equatorial", "hips_order":"7", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_creation_date":"2022-06-12T16:37Z", "hips_release_date":"2022-06-16T09:03Z", "hips_order_min":"0", "dataproduct_subtype":"color", "hips_service_url":"https://alasky.cds.unistra.fr/ancillary/GaiaDR3/color-Rp-G-Bp-flux-map", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/ancillary/GaiaDR3/color-Rp-G-Bp-flux-map", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288487906"}, +{ "ID":"CDS/P/DSS2/NIR", "hips_doi":"10.26093/cds/aladin/3hh0-7dk", "creator_did":"ivo://CDS/P/DSS2/NIR", "obs_collection":"DSS2 NIR (XI+IS)", "obs_title":"DSS2 NIR (XI+IS)", "obs_description":"The Catalogs and Surveys Group of the Space Telescope Science Institute has digitized the photographic Sky survey plates from the Palomar and UK Schmidt telescopes to produce the \"Digitized Sky Survey\"(DSS). Each plate covers 6.5 x 6.5 degrees of the sky and have been digitized using a modified PDS microdensitometer. The DSS NIT HiPS is a combination of DSS2-XI and DSS2-IS. DSS2-XI north is the digitalization of the POSS-II N (1987-2002 - filter: IV-N +RG9) from Caltech, DSS2-IS south is the digitalization of the SERC-IS (1990-2002 - filter: IV-N +RG175). The all-sky HEALPix resampling has been done by the CDS with the help of CADC.", "obs_ack":"The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions. The National Geographic Society - Palomar Observatory Sky Atlas (POSS-I) was made by the California Institute of Technology with grants from the National Geographic Society. The Second Palomar Observatory Sky Survey (POSS-II) was made by the California Institute of Technology with funds from the National Science Foundation, the National Geographic Society, the Sloan Foundation, the Samuel Oschin Foundation, and the Eastman Kodak Corporation. The Oschin Schmidt Telescope is operated by the California Institute of Technology and Palomar Observatory. The UK Schmidt Telescope was operated by the Royal Observatory Edinburgh, with funding from the UK Science and Engineering Research Council (later the UK Particle Physics and Astronomy Research Council), until 1988 June, and thereafter by the Anglo-Australian Observatory. The blue plates of the southern Sky Atlas and its Equatorial Extension (together known as the SERC-J), as well as the Equatorial Red (ER), and the Second Epoch [red] Survey (SES) were all taken with the UK Schmidt. Supplemental funding for sky-survey work at the ST ScI is provided by the European Southern Observatory.", "prov_progenitor":"STScI", "bib_reference":"1996ASPC..101...88L", "bib_reference_url":"http://cdsads.u-strasbg.fr/abs/1996ASPC..101...88L", "obs_copyright":"Digitized Sky Survey - STScI/NASA, Healpixed by CDS", "obs_copyright_url":"http://archive.stsci.edu/dss/copyright.html", "client_category":"Image/Optical/DSS", "client_sort_key":"03-01-03a", "t_min":"46796", "t_max":"52620", "em_min":"7E-7", "em_max":"9.5E-7", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-20T15:03Z", "hips_creator":"Fernique P. (CDS)", "hips_frame":"equatorial", "hips_order":"9", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_hierarchy":"mean", "hips_pixel_scale":"2.236E-4", "dataproduct_type":"image", "moc_sky_fraction":"0.9955", "hips_creation_date":"2015-09-08T12:14Z", "hips_tile_format":"jpeg fits", "hips_pixel_bitpix":"16", "data_pixel_bitpix":"16", "hips_pixel_cut":"1249 10940", "hips_data_range":"-11612 34834", "hips_initial_ra":"200.0641577", "hips_initial_dec":"-62.0757716", "hips_initial_fov":"30.0", "s_pixel_scale":"2.798E-4", "obs_regime":"Optical", "hips_copyright":"CNRS/Unistra", "hips_order_min":"0", "hips_estsize":"2328304001", "hipsgen_date":"2019-05-20T15:03Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume6/DSS/DSS2-NIR UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/DSS/DSS2-NIR", "hips_progenitor_url":"https://alasky.cds.unistra.fr/DSS/DSS2-NIR/HpxFinder", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/DSS/DSS2-NIR", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"41", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"200.0641577", "obs_initial_dec":"-62.0757716", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288455126"}, +{ "ID":"CDS/P/DSS2/blue", "hips_doi":"10.26093/cds/aladin/2szd-bms", "creator_did":"ivo://CDS/P/DSS2/blue", "obs_collection":"DSS2 Blue (XJ+S)", "obs_title":"DSS2 Blue (XJ+S)", "obs_description":"The Catalogs and Surveys Group of the Space Telescope Science Institute has digitized the photographic Sky survey plates from the Palomar and UK Schmidt telescopes to produce the \"Digitized Sky Survey\"(DSS). Each plate covers 6.5 x 6.5 degrees of the sky and have been\ndigitized using a modified PDS microdensitometer. The DSS blue HiPS is a combination of DSS2-XJ and DSS2-S. DSS2-XJ north is the digitalization of the POSS-II J (1987-1998 - 0.491um) from Caltech,DSS2-S south is the digitalization of the SERC-J (1975-1987 - 0.468um) and SERC-EJ (1979-1988 - 0.468um) from ROE.The all-sky HEALPix resampling has been done by the CDS with the help of CADC.", "obs_copyright":"Digitized Sky Survey - STScI/NASA, Healpixed by CDS", "obs_copyright_url":"http://archive.stsci.edu/dss/copyright.html", "client_category":"Image/Optical/DSS", "client_sort_key":"03-01-03", "hips_release_date":"2019-06-17T07:58Z", "hips_builder":"Aladin/HipsGen v10.125", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"9", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"4286 19959", "hips_data_range":"-4736 29668", "client_application":[ "AladinLite", "AladinDesktop"], "moc_access_url":"http://alasky.u-strasbg.fr/DSS/DSS2-blue-XJ-S/Moc.fits", "hips_progenitor_url":"https://alasky.cds.unistra.fr/DSS/DSS2-blue-XJ-S/HpxFinder", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions. The National Geographic Society - Palomar Observatory Sky Atlas (POSS-I) was made by the California Institute of Technology with grants from the National Geographic Society. The Second Palomar Observatory Sky Survey (POSS-II) was made by the California Institute of Technology with funds from the National Science Foundation, the National Geographic Society, the Sloan Foundation, the Samuel Oschin Foundation, and the Eastman Kodak Corporation. The Oschin Schmidt Telescope is operated by the California Institute of Technology and Palomar Observatory. The UK Schmidt Telescope was operated by the Royal Observatory Edinburgh, with funding from the UK Science and Engineering Research Council (later the UK Particle Physics and Astronomy Research Council), until 1988 June, and thereafter by the Anglo-Australian Observatory. The blue plates of the southern Sky Atlas and its Equatorial Extension (together known as the SERC-J), as well as the Equatorial Red (ER), and the Second Epoch [red] Survey (SES) were all taken with the UK Schmidt. Supplemental funding for sky-survey work at the ST ScI is provided by the European Southern Observatory.", "prov_progenitor":"STScI", "bib_reference":"1996ASPC..101...88L", "bib_reference_url":"http://cdsads.u-strasbg.fr/abs/1996ASPC..101...88L", "t_min":"42413", "t_max":"50814", "obs_regime":"Optical", "em_min":"4.68e-7", "em_max":"4.91e-7", "hips_creation_date":"2015-02-07T11:42Z", "hips_pixel_scale":"2.236E-4", "hips_initial_fov":"23.0", "hips_initial_ra":"271.0198457", "hips_initial_dec":"-24.3603897", "hips_order_min":"0", "hips_pixel_bitpix":"16", "moc_sky_fraction":"0.9972", "hips_estsize":"2331148605", "hipsgen_date":"2019-06-17T07:58Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume6/DSS/DSS2-blue-XJ-S UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/DSS/DSS2-blue-XJ-S", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/DSS/DSS2-blue-XJ-S", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"41", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"271.0198457", "obs_initial_dec":"-24.3603897", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288414390"}, +{ "ID":"CDS/P/DSS2/color", "hips_doi":"10.26093/cds/aladin/ht9n-7r", "creator_did":"ivo://CDS/P/DSS2/color", "obs_collection":"DSS colored", "obs_title":"DSS colored", "obs_description":"Color composition generated by CDS. This HiPS survey is based on 2 others HiPS surveys, respectively DSS2-red and DSS2-blue HiPS, both of them directly generated from original scanned plates downloaded from STScI site. The red component has been built from POSS-II F, AAO-SES,SR and SERC-ER plates. The blue component has been build from POSS-II J and SERC-J,EJ. The green component is based on the mean of other components. Three missing plates from red survey (253, 260, 359) has been replaced by pixels from the DSSColor STScI jpeg survey. The 11 missing blue plates (mainly in galactic plane) have not been replaced (only red component).", "obs_copyright":"Digitized Sky Survey - STScI/NASA, Colored & Healpixed by CDS", "obs_copyright_url":"http://archive.stsci.edu/dss/copyright.html", "client_category":"Image/Optical/DSS", "client_sort_key":"03-00", "hips_builder":"Aladin/HipsGen v10.123", "hips_creation_date":"2010-05-01T19:05Z", "hips_release_date":"2019-05-07T10:55Z", "hips_creator":"Oberto A. (CDS) , Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"9", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg", "dataproduct_type":"image", "client_application":[ "AladinLite", "AladinDesktop"], "moc_access_url":"http://alasky.u-strasbg.fr/DSS/DSSColor/Moc.fits", "hips_status":"public master clonableOnce", "hips_rgb_red":"DSS2Merged [1488.0 8488.8125 14666.0 Linear]", "hips_rgb_blue":"DSS2-blue-XJ-S [4286.0 12122.5 19959.0 Linear]", "hips_hierarchy":"median", "hips_pixel_scale":"2.236E-4", "hips_initial_ra":"085.30251", "hips_initial_dec":"-02.25468", "hips_initial_fov":"2", "moc_sky_fraction":"1", "hips_copyright":"CNRS/Unistra", "obs_ack":"The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions. The National Geographic Society - Palomar Observatory Sky Atlas (POSS-I) was made by the California Institute of Technology with grants from the National Geographic Society. The Second Palomar Observatory Sky Survey (POSS-II) was made by the California Institute of Technology with funds from the National Science Foundation, the National Geographic Society, the Sloan Foundation, the Samuel Oschin Foundation, and the Eastman Kodak Corporation. The Oschin Schmidt Telescope is operated by the California Institute of Technology and Palomar Observatory. The UK Schmidt Telescope was operated by the Royal Observatory Edinburgh, with funding from the UK Science and Engineering Research Council (later the UK Particle Physics and Astronomy Research Council), until 1988 June, and thereafter by the Anglo-Australian Observatory. The blue plates of the southern Sky Atlas and its Equatorial Extension (together known as the SERC-J), as well as the Equatorial Red (ER), and the Second Epoch [red] Survey (SES) were all taken with the UK Schmidt. Supplemental funding for sky-survey work at the ST ScI is provided by the European Southern Observatory.", "prov_progenitor":"STScI", "bib_reference":"1996ASPC..101...88L", "bib_reference_url":"http://cdsads.u-strasbg.fr/abs/1996ASPC..101...88L", "t_min":"42413", "t_max":"51179", "obs_regime":"Optical", "em_min":"4e-7", "em_max":"6e-7", "hips_order_min":"0", "dataproduct_subtype":"color", "hips_estsize":"37580398", "hipsgen_date":"2019-05-07T10:55Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume8/DSS/DSSColor UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/DSS/DSSColor", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/DSS/DSSColor", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://casda.csiro.au/hips/DSS2/color", "hips_status_2":"public mirror unclonable", "hips_service_url_3":"https://irsa.ipac.caltech.edu/data/hips/CDS/DSS2/color", "hips_status_3":"public mirror unclonable", "hips_service_url_4":"https://healpix.ias.u-psud.fr/CDS_P_DSS2_color", "hips_status_4":"public mirror unclonable", "hips_service_url_5":"http://skies.esac.esa.int/DSSColor", "hips_status_5":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"085.30251", "obs_initial_dec":"-02.25468", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288896984"}, +{ "ID":"CDS/P/DSS2/red", "hips_doi":"10.26093/cds/aladin/wenz-vg", "creator_did":"ivo://CDS/P/DSS2/red", "obs_collection":"DSS2 Red (F+R)", "obs_title":"DSS2 Red (F+R)", "obs_description":"The Catalogs and Surveys Group of the Space Telescope Science Institute has digitized the photographic Sky survey plates from the Palomar and UK Schmidt telescopes to produce the \"Digitized Sky Survey\" (DSS). Each plate covers 6.5 x 6.5 degrees of the sky and have been digitized using a modified PDS microdensitometer.\nDSS2F north is the digitalization of the POSS2/UKSTU Red survey (0.658um)\nDSS2R south is the digitalization of the AAO Red survey (0.64um)\nThe all-sky HEALPix resampling has been done by the CDS", "obs_copyright":"Digitized Sky Survey - STScI/NASA, Healpixed by CDS", "obs_copyright_url":"http://archive.stsci.edu/dss/copyright.html", "client_category":"Image/Optical/DSS", "client_sort_key":"03-01-02", "hips_creation_date":"2012-07-13T14:03Z", "hips_release_date":"2019-05-07T10:59Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"9", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "hips_pixel_cut":"1000 10000", "dataproduct_type":"image", "client_application":[ "AladinLite", "AladinDesktop"], "moc_access_url":"http://alasky.u-strasbg.fr/DSS/DSS2Merged/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"The Digitized Sky Surveys were produced at the Space Telescope Science Institute under U.S. Government grant NAG W-2166. The images of these surveys are based on photographic data obtained using the Oschin Schmidt Telescope on Palomar Mountain and the UK Schmidt Telescope. The plates were processed into the present compressed digital form with the permission of these institutions. The National Geographic Society - Palomar Observatory Sky Atlas (POSS-I) was made by the California Institute of Technology with grants from the National Geographic Society. The Second Palomar Observatory Sky Survey (POSS-II) was made by the California Institute of Technology with funds from the National Science Foundation, the National Geographic Society, the Sloan Foundation, the Samuel Oschin Foundation, and the Eastman Kodak Corporation. The Oschin Schmidt Telescope is operated by the California Institute of Technology and Palomar Observatory. The UK Schmidt Telescope was operated by the Royal Observatory Edinburgh, with funding from the UK Science and Engineering Research Council (later the UK Particle Physics and Astronomy Research Council), until 1988 June, and thereafter by the Anglo-Australian Observatory. The blue plates of the southern Sky Atlas and its Equatorial Extension (together known as the SERC-J), as well as the Equatorial Red (ER), and the Second Epoch [red] Survey (SES) were all taken with the UK Schmidt. Supplemental funding for sky-survey work at the ST ScI is provided by the European Southern Observatory.", "prov_progenitor":"STScI", "bib_reference":"1996ASPC..101...88L", "bib_reference_url":"http://cdsads.u-strasbg.fr/abs/1996ASPC..101...88L", "t_min":"45700", "t_max":"51179", "obs_regime":"Optical", "em_min":"6.4e-7", "em_max":"6.58e-7", "hips_pixel_scale":"2.236E-4", "hips_initial_fov":"33.0", "hips_initial_ra":"82.9368880", "hips_initial_dec":"-3.3581890", "hips_order_min":"0", "hips_pixel_bitpix":"16", "moc_sky_fraction":"1", "hips_estsize":"2301246266", "hipsgen_date":"2019-05-07T10:59Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume8/DSS/DSS2Merged UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/DSS/DSS2Merged", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/DSS/DSS2Merged", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/DSS2/red", "hips_status_2":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"82.9368880", "obs_initial_dec":"-3.3581890", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288897892"}, +{ "ID":"CDS/P/EGRET/Dif/100-150", "hips_doi":"10.26093/cds/aladin/3k8t-1gp", "creator_did":"ivo://CDS/P/EGRET/Dif/100-150", "obs_collection":"Diffuse Gamma-ray EGRET maps - 100-150MeV", "obs_title":"EGRET Dif 100-150MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-04", "hips_release_date":"2019-05-05T06:00Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_100-150/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"8.2656e-15", "em_max":"1.2398e-14", "hips_creation_date":"2014-06-05T11:13Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:00Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_100-150 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_100-150", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_100-150", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288414574"}, +{ "ID":"CDS/P/EGRET/Dif/1000-2000", "hips_doi":"10.26093/cds/aladin/3535-cdg", "creator_did":"ivo://CDS/P/EGRET/Dif/1000-2000", "obs_collection":"Diffuse Gamma-ray EGRET maps - 1000-2000MeV", "obs_title":"EGRET Dif 1000-2000MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-08", "hips_release_date":"2019-05-05T06:00Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_1000-2000/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"6.1992e-16", "em_max":"1.2398e-15", "hips_creation_date":"2014-06-05T11:18Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:00Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_1000-2000 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_1000-2000", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_1000-2000", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288414630"}, +{ "ID":"CDS/P/EGRET/Dif/150-300", "hips_doi":"10.26093/cds/aladin/34b7-dwe", "creator_did":"ivo://CDS/P/EGRET/Dif/150-300", "obs_collection":"Diffuse Gamma-ray EGRET maps - 150-300MeV", "obs_title":"EGRET Dif 150-300MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-05", "hips_release_date":"2019-05-05T06:01Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_150-300/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"4.1328e-15", "em_max":"8.2656e-15", "hips_creation_date":"2014-06-05T11:14Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:01Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_150-300 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_150-300", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_150-300", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288414682"}, +{ "ID":"CDS/P/EGRET/Dif/2000-4000", "hips_doi":"10.26093/cds/aladin/3bd9-mn9", "creator_did":"ivo://CDS/P/EGRET/Dif/2000-4000", "obs_collection":"Diffuse Gamma-ray EGRET maps - 2000-4000MeV", "obs_title":"EGRET Dif 2000-4000MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-09", "hips_release_date":"2019-05-05T06:01Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_2000-4000/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"3.0996e-16", "em_max":"6.1992e-16", "hips_creation_date":"2014-06-05T11:19Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:01Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_2000-4000 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_2000-4000", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_2000-4000", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288414734"}, +{ "ID":"CDS/P/EGRET/Dif/30-50", "hips_doi":"10.26093/cds/aladin/2nbf-ypm", "creator_did":"ivo://CDS/P/EGRET/Dif/30-50", "obs_collection":"Diffuse Gamma-ray EGRET maps - 30-50MeV", "obs_title":"EGRET Dif 30-50MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-01", "hips_release_date":"2019-05-05T06:01Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_30-50/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"2.4797e-14", "em_max":"4.1328e-14", "hips_creation_date":"2014-06-05T11:10Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:01Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_30-50 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_30-50", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_30-50", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288414794"}, +{ "ID":"CDS/P/EGRET/Dif/300-500", "hips_doi":"10.26093/cds/aladin/5prj-x6", "creator_did":"ivo://CDS/P/EGRET/Dif/300-500", "obs_collection":"Diffuse Gamma-ray EGRET maps - 300-500MeV", "obs_title":"EGRET Dif 300-500MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-06", "hips_release_date":"2019-05-05T06:01Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_300-500/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"2.4797e-15", "em_max":"4.1328e-15", "hips_creation_date":"2014-06-05T11:16Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:01Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_300-500 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_300-500", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_300-500", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288414854"}, +{ "ID":"CDS/P/EGRET/Dif/4000-10000", "hips_doi":"10.26093/cds/aladin/2ffk-vw8", "creator_did":"ivo://CDS/P/EGRET/Dif/4000-10000", "obs_collection":"Diffuse Gamma-ray EGRET maps - 4000-10000MeV", "obs_title":"EGRET Dif 4000-10000MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-10", "hips_release_date":"2019-05-05T06:02Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_4000-10000/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"1.2398e-16", "em_max":"3.0996e-16", "hips_creation_date":"2014-06-05T11:19Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:02Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_4000-10000 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_4000-10000", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_4000-10000", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288414922"}, +{ "ID":"CDS/P/EGRET/Dif/50-70", "hips_doi":"10.26093/cds/aladin/bw2v-fp", "creator_did":"ivo://CDS/P/EGRET/Dif/50-70", "obs_collection":"Diffuse Gamma-ray EGRET maps - 50-70MeV", "obs_title":"EGRET Dif 50-70MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-02", "hips_release_date":"2019-05-05T06:02Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_50-70/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"1.7712e-14", "em_max":"2.4797e-14", "hips_creation_date":"2014-06-05T11:09Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:02Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_50-70 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_50-70", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_50-70", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288414978"}, +{ "ID":"CDS/P/EGRET/Dif/500-1000", "hips_doi":"10.26093/cds/aladin/216g-av7", "creator_did":"ivo://CDS/P/EGRET/Dif/500-1000", "obs_collection":"Diffuse Gamma-ray EGRET maps - 500-1000MeV", "obs_title":"EGRET Dif 500-1000MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-07", "hips_release_date":"2019-05-05T06:02Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_500-1000/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"1.2398e-15", "em_max":"2.4797e-15", "hips_creation_date":"2014-06-05T11:17Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:02Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_500-1000 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_500-1000", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_500-1000", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288415034"}, +{ "ID":"CDS/P/EGRET/Dif/70-100", "hips_doi":"10.26093/cds/aladin/3mnf-56w", "creator_did":"ivo://CDS/P/EGRET/Dif/70-100", "obs_collection":"Diffuse Gamma-ray EGRET maps - 70-100MeV", "obs_title":"EGRET Dif 70-100MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright_url":"ftp://legacy.gsfc.nasa.gov/compton/data/egret/diffuse_maps/README.fitsmaps.html", "client_category":"Image/Gamma-ray/EGRET/Diffuse", "client_sort_key":"00-02-01-03", "hips_release_date":"2019-05-05T06:03Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"1.731E-6 6.218E-5", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-dif/EGRET_dif_70-100/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "obs_copyright":"Compton Gamma Ray Observatory (CGRO)", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"1.2398e-14", "em_max":"1.7712e-14", "hips_creation_date":"2014-06-05T11:12Z", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:03Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-dif/EGRET_dif_70-100 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_70-100", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-dif/EGRET_dif_70-100", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288415090"}, +{ "ID":"CDS/P/EGRET/inf100", "hips_doi":"10.26093/cds/aladin/35cp-apx", "creator_did":"ivo://CDS/P/EGRET/inf100", "obs_collection":"Gamma-ray EGRET maps - inf 100MeV", "obs_title":"EGRET inf 100MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright":[ "Distributed by SkyView/HEASARC - HEALPixed by CDS", "Compton Gamma Ray Observatory (CGRO)"], "client_category":"Image/Gamma-ray/EGRET", "client_sort_key":"00-02-00a", "hips_release_date":"2019-05-05T06:03Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"0 3.588E-4", "hips_data_range":"-2.037E-4 6.112E-4", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-inf100/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"1.2398e-14", "em_max":"4.1328e-14", "hips_creation_date":"2014-06-05T16:54Z", "hips_tile_width":"512", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:03Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-inf100 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-inf100", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-inf100", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288415146"}, +{ "ID":"CDS/P/EGRET/sup100", "hips_doi":"10.26093/cds/aladin/5c99-fg", "creator_did":"ivo://CDS/P/EGRET/sup100", "obs_collection":"Gamma-ray EGRET maps - sup 100MeV", "obs_title":"EGRET sup 100MeV", "obs_description":"This data presents all-sky maps of diffuse gamma radiation in energy ranges between 100 MeV to 150 MeV, based on data collected by the EGRET instrument on the Compton Gamma Ray Observatory. EGRET detected gamma rays in the energy range from 30 MeV to over 30 GeV, with an energy resolution of 20-25% over most of that range. The instrument is described in Hughes et al. (1980), Kanbach (1988, 1989), Thompson et. al (1993) and Esposito et al. (1998). The work described here started with standard EGRET all-sky maps (ftp://cossc.gsfc.nasa.gov/compton/data/egret/high_level/combined_data) of photon counts, instrument exposure, and gamma-ray intensity, binned in 0.5 degree pixels, in both Galactic and equatorial coordinates. The energy ranges in MeV are: (narrow ranges) 30-50, 50-70, 70-100, 100-150, 150-300, 300-500, 500-1000, 1000-2000, 2000-4000, 4000-10000; (broader ranges) 30-100, 100-300, 300-1000; (integral ranges) >100, >300, >1000.", "bib_reference":"2005ApJ...621..291C", "obs_copyright":[ "Distributed by SkyView/HEASARC - HEALPixed by CDS", "Compton Gamma Ray Observatory (CGRO)"], "client_category":"Image/Gamma-ray/EGRET", "client_sort_key":"00-02-00b", "hips_release_date":"2019-05-05T06:04Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"0 1.508E-4", "hips_data_range":"-8.375E-5 2.512E-4", "moc_access_url":"http://alasky.u-strasbg.fr/EGRET/EGRET-sup100/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2005ApJ...621..291C", "t_min":"48361", "t_max":"48943", "obs_regime":"Gamma-ray", "em_min":"1.2398e-16", "em_max":"1.2398e-14", "hips_creation_date":"2014-06-05T17:00Z", "hips_tile_width":"512", "hips_hierarchy":"mean", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:04Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/EGRET/EGRET-sup100 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/EGRET/EGRET-sup100", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/EGRET/EGRET-sup100", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288415202"}, +{ "ID":"CDS/P/Fermi/3", "hips_doi":"10.26093/cds/aladin/3q2w-3mk", "creator_did":"ivo://CDS/P/Fermi/3", "obs_collection":"Fermi3 300-1000MeV", "obs_title":"Fermi 300-1000MeV HEALPix survey", "obs_description":"Launched on June 11, 2008, the Fermi Gamma-ray Space Telescope observes the cosmos using the highest-energy form of light. This survey sums all data observed by the Fermi mission up to week 396. This version of the Fermi survey are intensity maps where the summed counts maps are divided by the exposure for each pixel. We anticipate using the HEASARC's Hera capabilities to update this survey on a roughly quarterly basis. Data is broken into 5 energy bands : 30-100 MeV Band 1, 100-300 MeV Band 2, 300-1000 MeV Band 3, 1-3 GeV Band 4 , 3-300 GeV Band 5. The SkyView data are based upon a Cartesian projection of the counts divided by the exposure maps. In the Cartesian projection pixels near the pole have a much smaller area than pixels on the equator, so these pixels have smaller integrated flux. When creating large scale images in other projections users may wish to make sure to compensate for this effect the flux conserving clip-resampling option.", "obs_copyright":"Distributed by SkyView/HEASARC - HEALPixed by CDS", "client_category":"Image/Gamma-ray", "client_sort_key":"00-01-04", "hips_creation_date":"2013-06-28T08:03Z", "hips_release_date":"2019-05-05T06:04Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/Fermi/300-1000MeV/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference":"2009ApJ...697.1071A", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2009ApJ...697.1071A", "obs_copyright_url":"http://skyview.gsfc.nasa.gov/current/cgi/survey.pl", "t_min":"54628", "t_max":"56291", "obs_regime":"Gamma-ray", "em_min":"1.2398e-15", "em_max":"4.1328e-15", "hips_tile_width":"512", "hips_pixel_scale":"0.01431", "hips_initial_fov":"150.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:04Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/Fermi/300-1000MeV UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/Fermi/300-1000MeV", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/Fermi/300-1000MeV", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288415254"}, +{ "ID":"CDS/P/Fermi/4", "hips_doi":"10.26093/cds/aladin/1r4n-6sg", "creator_did":"ivo://CDS/P/Fermi/4", "obs_collection":"Fermi4 1-3GeV", "obs_title":"Fermi 1-3GeV HEALPix survey.", "obs_description":"Launched on June 11, 2008, the Fermi Gamma-ray Space Telescope observes the cosmos using the highest-energy form of light. This survey sums all data observed by the Fermi mission up to week 396. This version of the Fermi survey are intensity maps where the summed counts maps are divided by the exposure for each pixel. We anticipate using the HEASARC's Hera capabilities to update this survey on a roughly quarterly basis. Data is broken into 5 energy bands : 30-100 MeV Band 1, 100-300 MeV Band 2, 300-1000 MeV Band 3, 1-3 GeV Band 4 , 3-300 GeV Band 5. The SkyView data are based upon a Cartesian projection of the counts divided by the exposure maps. In the Cartesian projection pixels near the pole have a much smaller area than pixels on the equator, so these pixels have smaller integrated flux. When creating large scale images in other projections users may wish to make sure to compensate for this effect the flux conserving clip-resampling option.", "obs_copyright":"Distributed by SkyView/HEASARC - HEALPixed by CDS", "client_category":"Image/Gamma-ray", "client_sort_key":"00-01-03", "hips_creation_date":"2013-06-28T08:28Z", "hips_release_date":"2019-05-05T06:05Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/Fermi/1-3GeV/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference":"2009ApJ...697.1071A", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2009ApJ...697.1071A", "obs_copyright_url":"http://skyview.gsfc.nasa.gov/current/cgi/survey.pl", "t_min":"54628", "t_max":"56291", "obs_regime":"Gamma-ray", "em_min":"4.1328e-16", "em_max":"1.2398e-15", "hips_tile_width":"512", "hips_pixel_scale":"0.01431", "hips_initial_fov":"150.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:05Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/Fermi/1-3GeV UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/Fermi/1-3GeV", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/Fermi/1-3GeV", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288415310"}, +{ "ID":"CDS/P/Fermi/5", "hips_doi":"10.26093/cds/aladin/3mva-x6", "creator_did":"ivo://CDS/P/Fermi/5", "obs_collection":"Fermi5 3-300GeV", "obs_title":"Fermi 3-300GeV HEALPix survey", "obs_description":"Launched on June 11, 2008, the Fermi Gamma-ray Space Telescope observes the cosmos using the highest-energy form of light. This survey sums all data observed by the Fermi mission up to week 396. This version of the Fermi survey are intensity maps where the summed counts maps are divided by the exposure for each pixel. We anticipate using the HEASARC's Hera capabilities to update this survey on a roughly quarterly basis. Data is broken into 5 energy bands : 30-100 MeV Band 1, 100-300 MeV Band 2, 300-1000 MeV Band 3, 1-3 GeV Band 4 , 3-300 GeV Band 5. The SkyView data are based upon a Cartesian projection of the counts divided by the exposure maps. In the Cartesian projection pixels near the pole have a much smaller area than pixels on the equator, so these pixels have smaller integrated flux. When creating large scale images in other projections users may wish to make sure to compensate for this effect the flux conserving clip-resampling option.", "obs_copyright":"Distributed by SkyView/HEASARC - HEALPixed by CDS", "client_category":"Image/Gamma-ray", "client_sort_key":"00-01-02", "hips_creation_date":"2013-06-28T09:09Z", "hips_release_date":"2019-05-05T06:05Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/Fermi/3-300GeV/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference":"2009ApJ...697.1071A", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2009ApJ...697.1071A", "obs_copyright_url":"http://skyview.gsfc.nasa.gov/current/cgi/survey.pl", "t_min":"54628", "t_max":"56291", "obs_regime":"Gamma-ray", "em_min":"4.1328e-18", "em_max":"4.1328e-16", "hips_tile_width":"512", "hips_pixel_scale":"0.01431", "hips_initial_fov":"150.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:05Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/Fermi/3-300GeV UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/Fermi/3-300GeV", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/Fermi/3-300GeV", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288415366"}, +{ "ID":"CDS/P/Fermi/color", "hips_doi":"10.26093/cds/aladin/276y-1xd", "creator_did":"ivo://CDS/P/Fermi/color", "obs_collection":"Fermi color", "obs_title":"Fermi Color HEALPix survey", "obs_description":"Launched on June 11, 2008, the Fermi Gamma-ray Space Telescope observes the cosmos using the highest-energy form of light. This survey sums all data observed by the Fermi mission up to week 396. This version of the Fermi survey are intensity maps where the summed counts maps are divided by the exposure for each pixel. We anticipate using the HEASARC's Hera capabilities to update this survey on a roughly quarterly basis. Data is broken into 5 energy bands : 30-100 MeV Band 1, 100-300 MeV Band 2, 300-1000 MeV Band 3, 1-3 GeV Band 4 , 3-300 GeV Band 5. The SkyView data are based upon a Cartesian projection of the counts divided by the exposure maps. In the Cartesian projection pixels near the pole have a much smaller area than pixels on the equator, so these pixels have smaller integrated flux. When creating large scale images in other projections users may wish to make sure to compensate for this effect the flux conserving clip-resampling option.", "obs_copyright":"Distributed by SkyView/HEASARC - HEALPixed by CDS", "client_category":"Image/Gamma-ray", "client_sort_key":"00-01-01", "hips_creation_date":"2013-06-28T11:09Z", "hips_release_date":"2019-05-05T06:06Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"3", "hips_frame":"equatorial", "hips_tile_format":"jpeg", "dataproduct_type":"image", "hips_rgb_red":"300-1000MeVALLSKY~1 [0.0 10.0 20.0 Sqrt]", "hips_rgb_green":"1-3GeVALLSKY~1 [0.0 5.0 10.0 Sqrt]", "hips_rgb_blue":"3-300GeVALLSKY [0.0 2.0 4.0 Sqrt]", "client_application":[ "AladinLite", "AladinDesktop"], "moc_access_url":"http://alasky.u-strasbg.fr/Fermi/Color/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"This research has made use of data, software and/or web tools obtained from the High Energy Astrophysics Science Archive Research Center (HEASARC), a service of the Astrophysics Science Division at NASA/GSFC and of the Smithsonian Astrophysical Observatory's High Energy Astrophysics Division", "prov_progenitor":"NASA/HEASARC", "bib_reference":"2009ApJ...697.1071A", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2009ApJ...697.1071A", "obs_copyright_url":"http://skyview.gsfc.nasa.gov/current/cgi/survey.pl", "t_min":"54628", "t_max":"56291", "obs_regime":"Gamma-ray", "em_min":"4.1328e-18", "em_max":"4.1328e-15", "hips_tile_width":"512", "hips_pixel_scale":"0.01431", "hips_initial_fov":"150.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "dataproduct_subtype":"color", "moc_sky_fraction":"1", "hips_estsize":"9182", "hipsgen_date":"2019-05-05T06:06Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/Fermi/Color UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/Fermi/Color", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/Fermi/Color", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://healpix.ias.u-psud.fr/CDS_P_Fermi_color", "hips_status_2":"public mirror unclonable", "hips_service_url_3":"http://skies.esac.esa.int/FermiColor", "hips_status_3":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288727333"}, +{ "ID":"CDS/P/Finkbeiner", "hips_doi":"10.26093/cds/aladin/3a4t-avb", "creator_did":"ivo://CDS/P/Finkbeiner", "obs_collection":"Finkbeiner Halpha", "obs_title":"Finkbeiner Halpha composite survey", "obs_description":"D. Finkbeiner has assembled a full sky Halpha map using data from several surveys: the Wisconsin H-Alpha Mapper (WHAM), the Virginia Tech Spectral-Line Survey (VTSS), and the Southern H-Alpha Sky Survey Atlas (SHASSA). The composite map can be used to provide limits on free-free foreground emission.", "obs_copyright":"Composite map by Douglas Finkbeiner (2004).", "client_category":"Image/Gas-lines/Halpha", "client_sort_key":"06-01", "hips_creation_date":"2010-12-14T01:07Z", "hips_release_date":"2019-05-05T06:07Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_width":"128", "hips_tile_format":"jpeg fits", "hips_pixel_cut":"-10 800", "dataproduct_type":"image", "client_application":[ "AladinLite", "AladinDesktop"], "moc_access_url":"http://alasky.u-strasbg.fr/FinkbeinerHalpha/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"All of these data products are available to the public on the World Wide Web", "prov_progenitor":"The data can be found as an on line material in the reference 2003ApJS..146..407F", "bib_reference":"2003ApJS..146..407F", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2003ApJS..146..407F", "t_min":"50753", "t_max":"51818", "obs_regime":"Optical", "em_min":"48E-8", "em_max":"73E-8", "hips_pixel_scale":"0.01431", "hips_initial_fov":"150.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:07Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/FinkbeinerHalpha UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/FinkbeinerHalpha", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/FinkbeinerHalpha", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"http://skies.esac.esa.int/FinkbeinerHa", "hips_status_2":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288732541"}, +{ "ID":"CDS/P/GalaxyCounts/2MPZ/0001-001", "creator_did":"ivo://CDS/P/GalaxyCounts/2MPZ/0001-001", "client_category":"Ancillary/GalaxyCounts/2MPZ", "obs_collection":"LIGO/Virgo probability maps (0.001 < z < 0.01, 40 to 80 Mpc)", "obs_title":"LIGO (0.001 < z < 0.01, 40 to 80 Mpc)", "obs_description":"The initial discovery of LIGO on 14 September 2015 was the in-spiral merger and ring-down of the black hole binary at a distance of about 500 Mpc or a redshift of about 0.1. The search for electromagnetic counterparts for the in-spiral of binary black holes is impeded by poor initial source localizations and a lack of a compelling model for the counterpart; therefore, rapid electromagnetic follow-up is required to understand the astrophysical context of these sources. Because astrophysical sources of gravitational radiation are likely to reside in galaxies, it would make sense to search rst in regions where the LIGO-Virgo probability is large and where the density of galaxies is large as well. Under the Bayesian prior assumption that the probability of a gravitational-wave event from a given region of space is proportional to the density of galaxies within the probed volume, one can calculate an improved localization of the position of the source simply by multiplying the LIGO-Virgo skymap by the density of galaxies in the range of redshifts. We propose using the 2-MASS Photometric Redshift Galaxy Catalogue for this purpose and demonstrate that using it can dramatically reduce the search region for electromagnetic counterparts.", "obs_ack":"The software and galaxy maps used in this paper is available at http://ubc-astrophysics.github.io . We used the VizieR Service, the NASA ADS service, the Super-COSMOS Science Archive, the NASA/IPAC Infrared Science Archive, the HEALPy libraries and arXiv.org.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"UBC-Astrophysics", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2016MNRAS.462.1085A/abstract", "obs_copyright":"UBC-Astrophysics", "obs_copyright_url":"http://copyright.ubc.ca/guidelines-and-resources/faq/", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T06:36Z", "hips_frame":"equatorial", "hips_order":"3", "hips_tile_width":"32", "hips_master_url":"http://alasky.unistra.fr/pub/arxiv.1602.07710v1/HIPS_0001_001/", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-3.735E-7 0.1841", "hips_data_range":"-0.1577 0.4732", "hips_pixel_scale":"0.229", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"6566", "hipsgen_date":"2016-10-21T14:47Z", "hipsgen_params":"in=2MPZ.gz_0.001_0.01_smoothed.fits out=HIPS_0001_001 ivorn=ivo://CDS/P/LIGO/0001 \"Publisher=M.Buga [CDS]\"", "hips_creation_date":"2016-10-21T14:47Z", "hipsgen_date_1":"2016-10-21T14:47Z", "hipsgen_params_1":"in=2MPZ.gz_0.001_0.01_smoothed.fits out=HIPS_0001_001 ivorn=ivo://CDS/P/LIGO/0001 \"Publisher=M.Buga [CDS]\"", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "bib_reference":"2016MNRAS.462.1085A", "hips_order_min":"0", "hipsgen_date_2":"2019-05-21T06:36Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/pub/arxiv.1602.07710v1/HIPS_0001_001 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_0001_001", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_0001_001", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288464706"}, +{ "ID":"CDS/P/GalaxyCounts/2MPZ/001-002", "creator_did":"ivo://CDS/P/GalaxyCounts/2MPZ/001-002", "client_category":"Ancillary/GalaxyCounts/2MPZ", "obs_collection":"LIGO/Virgo probability maps (0.01 < z < 0.02, 40 to 80 Mpc)", "obs_title":"LIGO (0.01 < z < 0.02, 40 to 80 Mpc)", "obs_description":"The initial discovery of LIGO on 14 September 2015 was the in-spiral merger and ring-down of the black hole binary at a distance of about 500 Mpc or a redshift of about 0.1. The search for electromagnetic counterparts for the in-spiral of binary black holes is impeded by poor initial source localizations and a lack of a compelling model for the counterpart; therefore, rapid electromagnetic follow-up is required to understand the astrophysical context of these sources. Because astrophysical sources of gravitational radiation are likely to reside in galaxies, it would make sense to search rst in regions where the LIGO-Virgo probability is large and where the density of galaxies is large as well. Under the Bayesian prior assumption that the probability of a gravitational-wave event from a given region of space is proportional to the density of galaxies within the probed volume, one can calculate an improved localization of the position of the source simply by multiplying the LIGO-Virgo skymap by the density of galaxies in the range of redshifts. We propose using the 2-MASS Photometric Redshift Galaxy Catalogue for this purpose and demonstrate that using it can dramatically reduce the search region for electromagnetic counterparts.", "obs_ack":"The software and galaxy maps used in this paper is available at http://ubc-astrophysics.github.io . We used the VizieR Service, the NASA ADS service, the Super-COSMOS Science Archive, the NASA/IPAC Infrared Science Archive, the HEALPy libraries and arXiv.org.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"UBC-Astrophysics", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2016MNRAS.462.1085A/abstract", "obs_copyright":"UBC-Astrophysics", "obs_copyright_url":"http://copyright.ubc.ca/guidelines-and-resources/faq/", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T06:36Z", "hips_frame":"equatorial", "hips_order":"3", "hips_tile_width":"32", "hips_master_url":"http://alasky.unistra.fr/pub/arxiv.1602.07710v1/HIPS_001_002/", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-8.864E-7 0.2865", "hips_data_range":"-0.2589 0.7766", "hips_pixel_scale":"0.229", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"6566", "hipsgen_date":"2016-10-21T14:49Z", "hipsgen_params":"in=2MPZ.gz_0.01_0.02_smoothed.fits out=HIPS_001_002 ivorn=ivo://CDS/P/LIGO/001 \"Publisher=M.Buga [CDS]\"", "hips_creation_date":"2016-10-21T14:49Z", "hipsgen_date_1":"2016-10-21T14:49Z", "hipsgen_params_1":"in=2MPZ.gz_0.01_0.02_smoothed.fits out=HIPS_001_002 ivorn=ivo://CDS/P/LIGO/001 \"Publisher=M.Buga [CDS]\"", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "bib_reference":"2016MNRAS.462.1085A", "hips_order_min":"0", "hipsgen_date_2":"2019-05-21T06:36Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/pub/arxiv.1602.07710v1/HIPS_001_002 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_001_002", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_001_002", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288464782"}, +{ "ID":"CDS/P/GalaxyCounts/2MPZ/002-003", "creator_did":"ivo://CDS/P/GalaxyCounts/2MPZ/002-003", "client_category":"Ancillary/GalaxyCounts/2MPZ", "obs_collection":"LIGO/Virgo probability maps (0.02 < z < 0.03, 40 to 80 Mpc)", "obs_title":"LIGO (0.02 < z < 0.03, 40 to 80 Mpc)", "obs_description":"The initial discovery of LIGO on 14 September 2015 was the in-spiral merger and ring-down of the black hole binary at a distance of about 500 Mpc or a redshift of about 0.1. The search for electromagnetic counterparts for the in-spiral of binary black holes is impeded by poor initial source localizations and a lack of a compelling model for the counterpart; therefore, rapid electromagnetic follow-up is required to understand the astrophysical context of these sources. Because astrophysical sources of gravitational radiation are likely to reside in galaxies, it would make sense to search rst in regions where the LIGO-Virgo probability is large and where the density of galaxies is large as well. Under the Bayesian prior assumption that the probability of a gravitational-wave event from a given region of space is proportional to the density of galaxies within the probed volume, one can calculate an improved localization of the position of the source simply by multiplying the LIGO-Virgo skymap by the density of galaxies in the range of redshifts. We propose using the 2-MASS Photometric Redshift Galaxy Catalogue for this purpose and demonstrate that using it can dramatically reduce the search region for electromagnetic counterparts.", "obs_ack":"The software and galaxy maps used in this paper is available at http://ubc-astrophysics.github.io . We used the VizieR Service, the NASA ADS service, the Super-COSMOS Science Archive, the NASA/IPAC Infrared Science Archive, the HEALPy libraries and arXiv.org.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"UBC-Astrophysics", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2016MNRAS.462.1085A/abstract", "obs_copyright":"UBC-Astrophysics", "obs_copyright_url":"http://copyright.ubc.ca/guidelines-and-resources/faq/", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T06:36Z", "hips_frame":"equatorial", "hips_order":"3", "hips_tile_width":"32", "hips_master_url":"http://alasky.unistra.fr/pub/arxiv.1602.07710v1/HIPS_002_003/", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-8.387E-7 0.3839", "hips_data_range":"-0.371 1.113", "hips_pixel_scale":"0.229", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"6566", "hipsgen_date":"2016-10-21T14:49Z", "hipsgen_params":"in=2MPZ.gz_0.02_0.03_smoothed.fits out=HIPS_002_003 ivorn=ivo://CDS/P/LIGO/002 \"Publisher=M.Buga [CDS]\"", "hips_creation_date":"2016-10-21T14:49Z", "hipsgen_date_1":"2016-10-21T14:49Z", "hipsgen_params_1":"in=2MPZ.gz_0.02_0.03_smoothed.fits out=HIPS_002_003 ivorn=ivo://CDS/P/LIGO/002 \"Publisher=M.Buga [CDS]\"", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "bib_reference":"2016MNRAS.462.1085A", "hips_order_min":"0", "hipsgen_date_2":"2019-05-21T06:36Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/pub/arxiv.1602.07710v1/HIPS_002_003 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_002_003", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_002_003", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288464846"}, +{ "ID":"CDS/P/GalaxyCounts/2MPZ/003-004", "creator_did":"ivo://CDS/P/GalaxyCounts/2MPZ/003-004", "client_category":"Ancillary/GalaxyCounts/2MPZ", "obs_collection":"LIGO/Virgo probability maps (0.03 < z < 0.04, 40 to 80 Mpc)", "obs_title":"LIGO (0.03 < z < 0.04, 40 to 80 Mpc)", "obs_description":"The initial discovery of LIGO on 14 September 2015 was the in-spiral merger and ring-down of the black hole binary at a distance of about 500 Mpc or a redshift of about 0.1. The search for electromagnetic counterparts for the in-spiral of binary black holes is impeded by poor initial source localizations and a lack of a compelling model for the counterpart; therefore, rapid electromagnetic follow-up is required to understand the astrophysical context of these sources. Because astrophysical sources of gravitational radiation are likely to reside in galaxies, it would make sense to search rst in regions where the LIGO-Virgo probability is large and where the density of galaxies is large as well. Under the Bayesian prior assumption that the probability of a gravitational-wave event from a given region of space is proportional to the density of galaxies within the probed volume, one can calculate an improved localization of the position of the source simply by multiplying the LIGO-Virgo skymap by the density of galaxies in the range of redshifts. We propose using the 2-MASS Photometric Redshift Galaxy Catalogue for this purpose and demonstrate that using it can dramatically reduce the search region for electromagnetic counterparts.", "obs_ack":"The software and galaxy maps used in this paper is available at http://ubc-astrophysics.github.io . We used the VizieR Service, the NASA ADS service, the Super-COSMOS Science Archive, the NASA/IPAC Infrared Science Archive, the HEALPy libraries and arXiv.org.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"UBC-Astrophysics", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2016MNRAS.462.1085A/abstract", "obs_copyright":"UBC-Astrophysics", "obs_copyright_url":"http://copyright.ubc.ca/guidelines-and-resources/faq/", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T06:37Z", "hips_frame":"equatorial", "hips_order":"3", "hips_tile_width":"32", "hips_master_url":"http://alasky.unistra.fr/pub/arxiv.1602.07710v1/HIPS_003_004/", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-1.356E-6 0.4006", "hips_data_range":"-0.3117 0.935", "hips_pixel_scale":"0.229", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"6566", "hipsgen_date":"2016-10-21T14:50Z", "hipsgen_params":"in=2MPZ.gz_0.03_0.04_smoothed.fits out=HIPS_003_004 ivorn=ivo://CDS/P/LIGO/003 \"Publisher=M.Buga [CDS]\"", "hips_creation_date":"2016-10-21T14:50Z", "hipsgen_date_1":"2016-10-21T14:50Z", "hipsgen_params_1":"in=2MPZ.gz_0.03_0.04_smoothed.fits out=HIPS_003_004 ivorn=ivo://CDS/P/LIGO/003 \"Publisher=M.Buga [CDS]\"", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "bib_reference":"2016MNRAS.462.1085A", "hips_order_min":"0", "hipsgen_date_2":"2019-05-21T06:37Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/pub/arxiv.1602.07710v1/HIPS_003_004 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_003_004", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_003_004", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288464898"}, +{ "ID":"CDS/P/GalaxyCounts/2MPZ/004-005", "creator_did":"ivo://CDS/P/GalaxyCounts/2MPZ/004-005", "client_category":"Ancillary/GalaxyCounts/2MPZ", "obs_collection":"LIGO/Virgo probability maps (0.04 < z < 0.05, 40 to 80 Mpc)", "obs_title":"LIGO (0.04 < z < 0.05, 40 to 80 Mpc)", "obs_description":"The initial discovery of LIGO on 14 September 2015 was the in-spiral merger and ring-down of the black hole binary at a distance of about 500 Mpc or a redshift of about 0.1. The search for electromagnetic counterparts for the in-spiral of binary black holes is impeded by poor initial source localizations and a lack of a compelling model for the counterpart; therefore, rapid electromagnetic follow-up is required to understand the astrophysical context of these sources. Because astrophysical sources of gravitational radiation are likely to reside in galaxies, it would make sense to search rst in regions where the LIGO-Virgo probability is large and where the density of galaxies is large as well. Under the Bayesian prior assumption that the probability of a gravitational-wave event from a given region of space is proportional to the density of galaxies within the probed volume, one can calculate an improved localization of the position of the source simply by multiplying the LIGO-Virgo skymap by the density of galaxies in the range of redshifts. We propose using the 2-MASS Photometric Redshift Galaxy Catalogue for this purpose and demonstrate that using it can dramatically reduce the search region for electromagnetic counterparts.", "obs_ack":"The software and galaxy maps used in this paper is available at http://ubc-astrophysics.github.io . We used the VizieR Service, the NASA ADS service, the Super-COSMOS Science Archive, the NASA/IPAC Infrared Science Archive, the HEALPy libraries and arXiv.org.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"UBC-Astrophysics", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2016MNRAS.462.1085A/abstract", "obs_copyright":"UBC-Astrophysics", "obs_copyright_url":"http://copyright.ubc.ca/guidelines-and-resources/faq/", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T06:37Z", "hips_frame":"equatorial", "hips_order":"3", "hips_tile_width":"32", "hips_master_url":"http://alasky.unistra.fr/pub/arxiv.1602.07710v1/HIPS_004_005/", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-7.349E-7 0.4508", "hips_data_range":"-0.3217 0.965", "hips_pixel_scale":"0.229", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"6566", "hipsgen_date":"2016-10-21T14:50Z", "hipsgen_params":"in=2MPZ.gz_0.04_0.05_smoothed.fits out=HIPS_004_005 ivorn=ivo://CDS/P/LIGO/004 \"Publisher=M.Buga [CDS]\"", "hips_creation_date":"2016-10-21T14:50Z", "hipsgen_date_1":"2016-10-21T14:50Z", "hipsgen_params_1":"in=2MPZ.gz_0.04_0.05_smoothed.fits out=HIPS_004_005 ivorn=ivo://CDS/P/LIGO/004 \"Publisher=M.Buga [CDS]\"", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "bib_reference":"2016MNRAS.462.1085A", "hips_order_min":"0", "hipsgen_date_2":"2019-05-21T06:37Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/pub/arxiv.1602.07710v1/HIPS_004_005 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_004_005", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_004_005", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288464954"}, +{ "ID":"CDS/P/GalaxyCounts/2MPZ/005-007", "creator_did":"ivo://CDS/P/GalaxyCounts/2MPZ/005-007", "client_category":"Ancillary/GalaxyCounts/2MPZ", "obs_collection":"LIGO/Virgo probability maps (0.05 < z < 0.07, 40 to 80 Mpc)", "obs_title":"LIGO (0.05 < z < 0.07, 40 to 80 Mpc)", "obs_description":"The initial discovery of LIGO on 14 September 2015 was the in-spiral merger and ring-down of the black hole binary at a distance of about 500 Mpc or a redshift of about 0.1. The search for electromagnetic counterparts for the in-spiral of binary black holes is impeded by poor initial source localizations and a lack of a compelling model for the counterpart; therefore, rapid electromagnetic follow-up is required to understand the astrophysical context of these sources. Because astrophysical sources of gravitational radiation are likely to reside in galaxies, it would make sense to search rst in regions where the LIGO-Virgo probability is large and where the density of galaxies is large as well. Under the Bayesian prior assumption that the probability of a gravitational-wave event from a given region of space is proportional to the density of galaxies within the probed volume, one can calculate an improved localization of the position of the source simply by multiplying the LIGO-Virgo skymap by the density of galaxies in the range of redshifts. We propose using the 2-MASS Photometric Redshift Galaxy Catalogue for this purpose and demonstrate that using it can dramatically reduce the search region for electromagnetic counterparts.", "obs_ack":"The software and galaxy maps used in this paper is available at http://ubc-astrophysics.github.io . We used the VizieR Service, the NASA ADS service, the Super-COSMOS Science Archive, the NASA/IPAC Infrared Science Archive, the HEALPy libraries and arXiv.org.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"UBC-Astrophysics", "bib_reference_url":"https://ui.adsabs.harvard.edu/ads/2016MNRAS.462.1085A/abstract", "obs_copyright":"UBC-Astrophysics", "obs_copyright_url":"http://copyright.ubc.ca/guidelines-and-resources/faq/", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T06:37Z", "hips_frame":"equatorial", "hips_order":"3", "hips_tile_width":"32", "hips_master_url":"http://alasky.unistra.fr/pub/arxiv.1602.07710v1/HIPS_005_007/", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-8.968E-7 0.6313", "hips_data_range":"-0.4141 1.242", "hips_pixel_scale":"0.229", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"6566", "hipsgen_date":"2016-10-21T14:51Z", "hipsgen_params":"in=2MPZ.gz_0.05_0.07_smoothed.fits out=HIPS_005_007 ivorn=ivo://CDS/P/LIGO/005 \"Publisher=M.Buga [CDS]\"", "hips_creation_date":"2016-10-21T14:51Z", "hipsgen_date_1":"2016-10-21T14:51Z", "hipsgen_params_1":"in=2MPZ.gz_0.05_0.07_smoothed.fits out=HIPS_005_007 ivorn=ivo://CDS/P/LIGO/005 \"Publisher=M.Buga [CDS]\"", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "bib_reference":"2016MNRAS.462.1085A", "hips_order_min":"0", "hipsgen_date_2":"2019-05-21T06:37Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/pub/arxiv.1602.07710v1/HIPS_005_007 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_005_007", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_005_007", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288465030"}, +{ "ID":"CDS/P/GalaxyCounts/2MPZ/007-01", "creator_did":"ivo://CDS/P/GalaxyCounts/2MPZ/007-01", "client_category":"Ancillary/GalaxyCounts/2MPZ", "obs_collection":"LIGO/Virgo probability maps (0.07 < z < 0.1, 40 to 80 Mpc)", "obs_title":"LIGO (0.07 < z < 0.1, 40 to 80 Mpc)", "obs_description":"The initial discovery of LIGO on 14 September 2015 was the in-spiral merger and ring-down of the black hole binary at a distance of about 500 Mpc or a redshift of about 0.1. The search for electromagnetic counterparts for the in-spiral of binary black holes is impeded by poor initial source localizations and a lack of a compelling model for the counterpart; therefore, rapid electromagnetic follow-up is required to understand the astrophysical context of these sources. Because astrophysical sources of gravitational radiation are likely to reside in galaxies, it would make sense to search rst in regions where the LIGO-Virgo probability is large and where the density of galaxies is large as well. Under the Bayesian prior assumption that the probability of a gravitational-wave event from a given region of space is proportional to the density of galaxies within the probed volume, one can calculate an improved localization of the position of the source simply by multiplying the LIGO-Virgo skymap by the density of galaxies in the range of redshifts. We propose using the 2-MASS Photometric Redshift Galaxy Catalogue for this purpose and demonstrate that using it can dramatically reduce the search region for electromagnetic counterparts.", "obs_ack":"The software and galaxy maps used in this paper is available at http://ubc-astrophysics.github.io . We used the VizieR Service, the NASA ADS service, the Super-COSMOS Science Archive, the NASA/IPAC Infrared Science Archive, the HEALPy libraries and arXiv.org.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"UBC-Astrophysics", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2016MNRAS.462.1085A/abstract", "obs_copyright":"UBC-Astrophysics", "obs_copyright_url":"http://copyright.ubc.ca/guidelines-and-resources/faq/", "t_min":"50600", "t_max":"51941", "obs_regime":"Infrared", "hips_builder":"Aladin/HipsGen v10.125", "hips_version":"1.4", "hips_release_date":"2019-05-21T06:37Z", "hips_frame":"equatorial", "hips_order":"3", "hips_tile_width":"32", "hips_master_url":"http://alasky.unistra.fr/pub/arxiv.1602.07710v1/HIPS_007_01/", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-1.468E-6 0.688", "hips_data_range":"-0.4203 1.261", "hips_pixel_scale":"0.229", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"6566", "hipsgen_date":"2016-10-21T14:51Z", "hipsgen_params":"in=2MPZ.gz_0.07_0.1_smoothed.fits out=HIPS_007_01 ivorn=ivo://CDS/P/LIGO/007 \"Publisher=M.Buga [CDS]\"", "hips_creation_date":"2016-10-21T14:51Z", "hipsgen_date_1":"2016-10-21T14:51Z", "hipsgen_params_1":"in=2MPZ.gz_0.07_0.1_smoothed.fits out=HIPS_007_01 ivorn=ivo://CDS/P/LIGO/007 \"Publisher=M.Buga [CDS]\"", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "bib_reference":"2016MNRAS.462.1085A", "hips_order_min":"0", "hipsgen_date_2":"2019-05-21T06:37Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume6/pub/arxiv.1602.07710v1/HIPS_007_01 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_007_01", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/pub/arxiv.1602.07710v1/HIPS_007_01", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288465098"}, +{ "ID":"CDS/P/HI", "creator_did":"ivo://CDS/P/HI", "obs_collection":"HI", "obs_title":"HI composite survey", "obs_description":"Composite all-sky map of neutral hydrogen column density (N_HI), formed from the Leiden/Dwingeloo HI survey data (Hartmann & Burton 1997) and the composite N_HI map of Dickey and Lockman (1990). The two datasets are not matched in sensitivity or resolution: note that discontinuities exist in the constructed composite map. A pixel mask is provided to indicate which dataset was used for each location on the sky. Hartmann & Burton provide a velocity integrated (-450 km/s < V_lsr < +400 km/s) HI brightness temperature map in Galactic coordinates, sampled every 0.5 degrees. This entire data set was converted to N_HI by multiplying by their factor of 1.8224e18 K km s-1 cm-2 and then interpolated to pixel centers appropriate for HEALPix Nside=512. Since the Leiden/Dwingeloo survey does not have sky coverage for declinations < -30 deg., the lower resolution Dickey & Lockman map was also interpolated to HEALPix and used to fill in the coverage gap. The Dickey & Lockman N_HI map is itself a composite of several surveys which had been merged and averaged onto 1 deg. bins in Galactic coordinates. Their map includes emission between -250 km/s < V_lsr < 250 km/s (excluding the LMC and SMC). The Leiden/Dwingeloo Survey data were obtained from the CDS. The Dickey & Lockman map was obtained from NCSA ADIL. This original HEALPix file is distributed and maintained by LAMBDA.", "obs_copyright":"Composite HI map by LAMBDA", "obs_copyright_url":"http://lambda.gsfc.nasa.gov/product/foreground/fg_HI_get.cfm", "client_category":"Image/Gas-lines/HI", "client_sort_key":"06-05", "hips_creation_date":"2011-02-14T12:00Z", "hips_release_date":"2019-05-05T06:29Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_width":"64", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/HI/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "obs_ack":"We acknowledge the use of the Legacy Archive for Microwave Background Data Analysis (LAMBDA), part of the High Energy Astrophysics Science Archive Center (HEASARC). HEASARC/LAMBDA is a service of the Astrophysics Science Division at the NASA Goddard Space Flight Center.", "prov_progenitor":"HEASARC/LAMBDA", "bib_reference":"1990ARA&A..28..215D", "bib_reference_url":"http://adsabs.harvard.edu/abs/1990ARA%26A..28..215D", "t_min":"44239", "t_max":"47892", "obs_regime":"Radio", "em_min":"0.21", "em_max":"0.21", "hips_pixel_scale":"0.01431", "hips_initial_fov":"120.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1112337", "hipsgen_date":"2019-05-05T06:29Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/HI UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/HI", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/HI", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288415898"}, +{ "ID":"CDS/P/HI4PI/NHI", "creator_did":"ivo://CDS/P/HI4PI/NHI", "client_category":"Image/Gas-lines/HI", "obs_collection":"HI4PI NHI", "obs_title":"HI4PI NHI survey (full-sky HI column density distribution)", "obs_description":"The HI4PI data release comprises 21-cm neutral atomic hydrogen data of the Milky Way (-600km/s0deg; -470km/s=10.040 AladinDesktop>=11.125", "moc_type":"smoc", "moc_order":"11", "obs_initial_ra":"282.3154012", "obs_initial_dec":"-6.75", "obs_initial_fov":"0.028629053431811713", "TIMESTAMP":"1721288493318"}, +{ "ID":"CDS/P/Mellinger/color", "creator_did":"ivo://CDS/P/Mellinger/color", "obs_collection":"Mellinger color", "obs_title":"Mellinger color optical survey", "obs_description":"Using a portable low-cost CCD camera system, 70 fields (each covering 40deg x 27deg) were imaged over a time span of 22 months from dark-sky locations in South Africa, Texas, and Michigan. The fields were photometrically calibrated against standard catalog stars. Using sky background data from the Pioneer 10 and 11 space probes, gradients resulting from artificial light pollution, airglow, and zodiacal light were eliminated, while the large-scale galactic and extragalactic background resulting from unresolved sources was preserved. The 648 megapixel image is a valuable educational tool, being able to fully utilize the resolution and dynamic range of modern full-dome planetarium projection systems.", "prov_progenitor":"Axel Mellinger", "bib_reference":"2009PASP..121.1180M", "bib_reference_url":"http://adsabs.harvard.edu/cgi-bin/nph-bib_query?db_key=AST&bibcode=2009PASP..121.1180M", "obs_copyright":"Copyright 2000-2017 Axel Mellinger. All rights reserved.", "obs_copyright_url":"http://www.milkywaysky.com/", "client_category":"Image/Optical", "client_sort_key":"03-03", "hips_creation_date":"2010-07-12T00:00Z", "hips_release_date":"2019-05-05T06:40Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"4", "hips_frame":"galactic", "hips_tile_width":"512", "hips_tile_format":"jpeg", "dataproduct_type":"image", "client_application":[ "AladinLite", "AladinDesktop"], "moc_access_url":"http://alasky.u-strasbg.fr/MellingerRGB/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "t_min":"54374", "t_max":"55044", "obs_regime":"Optical", "em_min":"4e-7", "em_max":"8e-7", "hips_pixel_scale":"0.007157", "hips_initial_fov":"360.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "dataproduct_subtype":"color", "moc_sky_fraction":"1", "hips_estsize":"36707", "hipsgen_date":"2019-05-05T06:40Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/MellingerRGB UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/MellingerRGB", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/MellingerRGB", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://healpix.ias.u-psud.fr/CDS_P_Mellinger_color", "hips_status_2":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"4", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"3.6645188392718993", "TIMESTAMP":"1721288719181"}, +{ "ID":"CDS/P/NEOWISER/Color", "creator_did":"ivo://CDS/P/NEOWISER/Color", "obs_collection":"NEOWISER W2-W1", "obs_title":"NEOWISER color Red (W2) , Blue (W1)", "obs_description":"The NEOWISE project is the asteroid-hunting portion of the Wide-field Infrared Survey Explorer (WISE) mission. Funded by NASA's Planetary Science Division, NEOWISE harvests measurements of asteroids and comets from the WISE images and provides a rich archive for searching WISE data for solar system objects. Here we update our full-depth coadds by folding in the most recently published year of W1/W2 exposures released by NEOWISER. These new single-frame data were acquired between 2015 December 13 and 2016 December 13, and became public in 2017 June. In the present work, we simply re-ran the latest unWISE coaddition code (Meisner et al. 2017a) on inputs including this additional year of publicly available NEOWISER frames. The resulting set of full-depth coadds uniformly incorporates all publicly available W1 and W2 exposures, with observation dates ranging from 2010 January 7 to 2016 December 13. The inputs consisted of ~ 10.5 million frames per band, totaling ~ 140 terabytes of single-exposure pixel data.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_ack":"This publication also makes use of data products from NEOWISE, which is a project of the Jet Propulsion Laboratory/California Institute of Technology, funded by the Planetary Science Division of the National Aeronautics and Space Administration", "prov_progenitor":"IPAC/NASA", "bib_reference":"2014ApJ...792...30M", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2014ApJ...792...30M/abstract", "obs_copyright":"University of Massachusetts & IPAC/Caltech", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allsky/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE/NEOWISER", "t_min":"55203", "t_max":"57735", "obs_regime":"Infrared", "em_min":"2.754e-6", "em_max":"5.3413e-6", "hips_builder":"Aladin/HipsGen v10.123", "hips_initial_fov":"20.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "hips_version":"1.4", "hips_release_date":"2019-05-05T08:29Z", "hips_frame":"equatorial", "hips_order":"8", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_pixel_scale":"4.473E-4", "dataproduct_type":"image", "hips_rgb_red":"NEOWISER W2 [0.0 NaN 190.0 Linear]", "hips_rgb_blue":"NEOWISER W1 [0.0 NaN 190.0 Linear]", "moc_sky_fraction":"1", "hips_estsize":"14092654", "hipsgen_date":"2018-03-04T12:58Z", "hipsgen_params":"inRed=/var/www/NEOWISER/W2/ inBlue=/var/www/NEOWISER/W1/ out=W1W2Color2 creator_did=CDS/P/NEOWISER/Color \"cmRed=0 190\" \"cmBlue=0 190\" method=FIRST color=png RGB verbose=4", "hips_creation_date":"2018-03-04T12:58Z", "hips_hierarchy":"first", "hips_tile_format":"png", "hipsgen_date_1":"2018-03-05T05:57Z", "hipsgen_params_1":"inRed=/var/www/NEOWISER/W2/ inBlue=/var/www/NEOWISER/W1/ out=W1W2Color2 creator_did=CDS/P/NEOWISER/Color \"cmRed=0 190\" \"cmBlue=0 190\" method=FIRST color=png RGB verbose=4", "hips_order_min":"0", "hipsgen_date_2":"2019-05-05T08:23Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume10/NEOWISER/W1W2 UPDATE", "dataproduct_subtype":"color", "hipsgen_date_3":"2019-05-05T08:29Z", "hipsgen_params_3":"out=/asd-volumes/sc1-asd-volume10/NEOWISER/W1W2 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/NEOWISER/W1W2", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/NEOWISER/W1W2", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://healpix.ias.u-psud.fr/CDS_P_NEOWISER_Color", "hips_status_2":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288719557"}, +{ "ID":"CDS/P/NEOWISER/W1", "creator_did":"ivo://CDS/P/NEOWISER/W1", "obs_collection":"NEOWISER W1 (3.4um)", "obs_title":"NEOWISER W1", "obs_description":"The NEOWISE project is the asteroid-hunting portion of the Wide-field Infrared Survey Explorer (WISE) mission. Funded by NASA's Planetary Science Division, NEOWISE harvests measurements of asteroids and comets from the WISE images and provides a rich archive for searching WISE data for solar system objects. Here we update our full-depth coadds by folding in the most recently published year of W1/W2 exposures released by NEOWISER. These new single-frame data were acquired between 2015 December 13 and 2016 December 13, and became public in 2017 June. In the present work, we simply re-ran the latest unWISE coaddition code (Meisner et al. 2017a) on inputs including this additional year of publicly available NEOWISER frames. The resulting set of full-depth coadds uniformly incorporates all publicly available W1 and W2 exposures, with observation dates ranging from 2010 January 7 to 2016 December 13. The inputs consisted of ~ 10.5 million frames per band, totaling ~ 140 terabytes of single-exposure pixel data.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_ack":"This publication also makes use of data products from NEOWISE, which is a project of the Jet Propulsion Laboratory/California Institute of Technology, funded by the Planetary Science Division of the National Aeronautics and Space Administration", "prov_progenitor":"IPAC/NASA", "bib_reference":"2014ApJ...792...30M", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2014ApJ...792...30M/abstract", "obs_copyright":"University of Massachusetts & IPAC/Caltech", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allsky/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE/NEOWISER", "t_min":"55203", "t_max":"57735", "obs_regime":"Infrared", "em_min":"2.754e-6", "em_max":"3.8723e-6", "hips_builder":"Aladin/HipsGen v10.123", "hips_version":"1.4", "hips_release_date":"2019-05-05T08:11Z", "hips_frame":"equatorial", "hips_order":"8", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_cut":"0 230", "hips_data_range":"-890.6 2675", "hips_pixel_scale":"4.473E-4", "s_pixel_scale":"7.638E-4", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"1139026029", "hipsgen_date":"2018-01-29T12:31Z", "hips_initial_fov":"20.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "hips_pixel_bitpix":"-32", "data_pixel_bitpix":"-32", "hips_sampling":"bilinear", "hips_skyval_method":"TRUE", "hips_skyval_value":"0.774662435054779 31.438140829062462 -890.6055234372616 2674.915220052004", "hips_overlay":"mean", "hips_hierarchy":"first", "hipsgen_params":"in=w1_std_u out=/data1/buga/NEOWISER/NEOWISER/NEOWISER_W1 skyval=true maxRatio=0 maxthread=40 method=FIRST creator_did=CDS/C/NEOWISER/W1 verbose=4 INDEX TILES PNG DETAILS", "hips_creation_date":"2018-01-29T12:31Z", "hipsgen_date_1":"2018-01-29T20:37Z", "hipsgen_params_1":"in=w1_std_u out=/data1/buga/NEOWISER/NEOWISER/NEOWISER_W1 skyval=true maxRatio=0 maxthread=40 method=FIRST creator_did=CDS/C/NEOWISER/W1 verbose=4 INDEX TILES PNG DETAILS", "hipsgen_date_2":"2018-01-30T21:24Z", "hipsgen_params_2":"in=w1_std_u out=/data1/buga/NEOWISER/NEOWISER/NEOWISER_W1 skyval=true maxRatio=0 maxthread=40 \"pixelCut=0 230\" method=FIRST creator_did=CDS/C/NEOWISER/W1 verbose=4 JPEG", "hips_order_min":"0", "hipsgen_date_3":"2019-05-05T08:11Z", "hipsgen_params_3":"out=/asd-volumes/sc1-asd-volume10/NEOWISER/W1 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/NEOWISER/W1", "hips_progenitor_url":"https://alasky.cds.unistra.fr/NEOWISER/W1/HpxFinder", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/NEOWISER/W1", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288475558"}, +{ "ID":"CDS/P/NEOWISER/W2", "creator_did":"ivo://CDS/P/NEOWISER/W2", "obs_collection":"NEOWISER W2 (4.6um)", "obs_title":"NEOWISER W2", "obs_description":"The NEOWISE project is the asteroid-hunting portion of the Wide-field Infrared Survey Explorer (WISE) mission. Funded by NASA's Planetary Science Division, NEOWISE harvests measurements of asteroids and comets from the WISE images and provides a rich archive for searching WISE data for solar system objects. Here we update our full-depth coadds by folding in the most recently published year of W1/W2 exposures released by NEOWISER. These new single-frame data were acquired between 2015 December 13 and 2016 December 13, and became public in 2017 June. In the present work, we simply re-ran the latest unWISE coaddition code (Meisner et al. 2017a) on inputs including this additional year of publicly available NEOWISER frames. The resulting set of full-depth coadds uniformly incorporates all publicly available W1 and W2 exposures, with observation dates ranging from 2010 January 7 to 2016 December 13. The inputs consisted of a ~ 10.5 million frames per band, totaling a ~ 140 terabytes of single-exposure pixel data.", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_ack":"This publication also makes use of data products from NEOWISE, which is a project of the Jet Propulsion Laboratory/California Institute of Technology, funded by the Planetary Science Division of the National Aeronautics and Space Administration", "prov_progenitor":"IPAC/NASA", "bib_reference":"2014ApJ...792...30M", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2014ApJ...792...30M/abstract", "obs_copyright":"University of Massachusetts & IPAC/Caltech", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allsky/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE/NEOWISER", "t_min":"55203", "t_max":"57735", "obs_regime":"Infrared", "em_min":"3.9633e-6", "em_max":"5.3413e-6", "hips_builder":"Aladin/HipsGen v10.123", "hips_version":"1.4", "hips_release_date":"2019-05-05T08:18Z", "hips_frame":"equatorial", "hips_order":"8", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_cut":"0 230", "hips_data_range":"-4234 12717", "hips_pixel_scale":"4.473E-4", "s_pixel_scale":"7.638E-4", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"1139026029", "hipsgen_date":"2018-02-08T13:07Z", "hips_initial_fov":"20.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "hips_pixel_bitpix":"-32", "data_pixel_bitpix":"-32", "hips_sampling":"bilinear", "hips_skyval_method":"TRUE", "hips_skyval_value":"0.0 230.0 -4234.388455033302 12716.9615162611", "hips_overlay":"mean", "hips_hierarchy":"first", "hipsgen_params":"cache=Temp/ cachesize=500000 in=w2_std_u/ out=/data1/buga/NEOWISER/NEOWISER/NEOWISER_W2 skyval=true maxRatio=0 maxthread=40 \"pixelCut=0 230\" method=FIRST creator_did=CDS/C/NEOWISER/W2 verbose=4 INDEX TILES", "hips_creation_date":"2018-02-08T13:07Z", "hipsgen_date_1":"2018-02-09T00:59Z", "hipsgen_params_1":"cache=Temp/ cachesize=500000 in=w2_std_u/ out=/data1/buga/NEOWISER/NEOWISER/NEOWISER_W2 skyval=true maxRatio=0 maxthread=40 \"pixelCut=0 230\" method=FIRST creator_did=CDS/C/NEOWISER/W2 verbose=4 JPEG DETAILS", "hips_order_min":"0", "hipsgen_date_2":"2019-05-05T08:18Z", "hipsgen_params_2":"out=/asd-volumes/sc1-asd-volume10/NEOWISER/W2 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/NEOWISER/W2", "hips_progenitor_url":"https://alasky.cds.unistra.fr/NEOWISER/W2/HpxFinder", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/NEOWISER/W2", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"9", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.11451621372724685", "TIMESTAMP":"1721288475614"}, +{ "ID":"CDS/P/PLANCK/R2/CMB", "creator_did":"ivo://CDS/P/PLANCK/R2/CMB", "obs_collection":"PLANCK R2 CMB", "obs_title":"PLANCK Maps of the CMB fluctuations.", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2", "client_sort_key":"05-04-03", "hips_release_date":"2019-05-05T06:48Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"-3.057E-4 3.607E-4", "hips_data_range":"-0.002454 0.002741", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/COM_CMB_IQU-smica-field-Int_2048_R2.00/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"3E-4", "em_max":"1.11E-2", "hips_tile_width":"256", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-06T13:52Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:48Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/COM_CMB_IQU-smica-field-Int_2048_R2.00 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/COM_CMB_IQU-smica-field-Int_2048_R2.00", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/COM_CMB_IQU-smica-field-Int_2048_R2.00", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288417526"}, +{ "ID":"CDS/P/PLANCK/R2/HFI/color", "creator_did":"ivo://CDS/P/PLANCK/R2/HFI/color", "obs_collection":"PLANCK R2 HFI color", "obs_title":"PLANCK R2 HFI color composition 353-545-857 GHz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/HFI", "client_sort_key":"05-04-xR2-02-00", "hips_release_date":"2019-05-05T06:49Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"jpeg", "dataproduct_type":"image", "hips_rgb_red":"HFI_SkyMap_353_2048_R2.00 [1.368E-4 0.0111184 0.0221 Linear]", "hips_rgb_green":"HFI_SkyMap_545_2048_R2.00 [-0.5 12.305 25.11 Linear]", "hips_rgb_blue":"HFI_SkyMap_857_2048_R2.00 [-2.0 52.8 107.6 Linear]", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/HFI_Color_353_545_857/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"3.49E-4", "em_max":"8.49E-4", "hips_tile_width":"256", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"9182", "hips_creation_date":"2015-02-06T13:38Z", "hips_order_min":"0", "dataproduct_subtype":"color", "hipsgen_date":"2019-05-05T06:49Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/HFI_Color_353_545_857 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/HFI_Color_353_545_857", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/HFI_Color_353_545_857", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/PLANCK/R2/HFI/color", "hips_status_2":"public mirror unclonable", "hips_service_url_3":"https://healpix.ias.u-psud.fr/CDS_P_PLANCK_R2_HFI_color", "hips_status_3":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288719873"}, +{ "ID":"CDS/P/PLANCK/R2/HFI100", "creator_did":"ivo://CDS/P/PLANCK/R2/HFI100", "obs_collection":"PLANCK R2 HFI100", "obs_title":"PLANCK R2 nominal frequency HFI map 100Ghz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/HFI", "client_sort_key":"05-04-xR2-01-08", "hips_release_date":"2019-05-05T06:49Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"-2.681E-4 0.00487", "hips_data_range":"-0.05606 0.1659", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/HFI_SkyMap_100_2048_R2.00/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"2.99E-3", "em_max":"2.99E-3", "hips_tile_width":"256", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-05T16:46Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:49Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/HFI_SkyMap_100_2048_R2.00 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_100_2048_R2.00", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_100_2048_R2.00", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288417690"}, +{ "ID":"CDS/P/PLANCK/R2/HFI143", "creator_did":"ivo://CDS/P/PLANCK/R2/HFI143", "obs_collection":"PLANCK R2 HFI143", "obs_title":"PLANCK R2 nominal frequency HFI map 143Ghz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/HFI", "client_sort_key":"05-04-xR2-01-07", "hips_release_date":"2019-05-05T06:49Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"-2.370E-4 0.005372", "hips_data_range":"-0.061 0.1813", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/HFI_SkyMap_143_2048_R2.00/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"2.09E-3", "em_max":"2.09E-3", "hips_tile_width":"256", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-05T16:49Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:49Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/HFI_SkyMap_143_2048_R2.00 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_143_2048_R2.00", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_143_2048_R2.00", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288417750"}, +{ "ID":"CDS/P/PLANCK/R2/HFI217", "creator_did":"ivo://CDS/P/PLANCK/R2/HFI217", "obs_collection":"PLANCK R2 HFI217", "obs_title":"PLANCK R2 nominal frequency HFI map 217Ghz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/HFI", "client_sort_key":"05-04-xR2-01-06", "hips_release_date":"2019-05-05T06:49Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"-1.571E-4 0.019", "hips_data_range":"-0.1518 0.4534", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/HFI_SkyMap_217_2048_R2.00/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"1.38E-3", "em_max":"1.38E-3", "hips_tile_width":"256", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-05T16:54Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:49Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/HFI_SkyMap_217_2048_R2.00 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_217_2048_R2.00", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_217_2048_R2.00", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288417802"}, +{ "ID":"CDS/P/PLANCK/R2/HFI353", "creator_did":"ivo://CDS/P/PLANCK/R2/HFI353", "obs_collection":"PLANCK R2 HFI353", "obs_title":"PLANCK R2 nominal frequency HFI map 353Ghz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/HFI", "client_sort_key":"05-04-xR2-01-05", "hips_release_date":"2019-05-05T06:50Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"2.591E-4 0.1375", "hips_data_range":"-0.8131 2.439", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/HFI_SkyMap_353_2048_R2.00/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"8.49E-4", "em_max":"8.49E-4", "hips_tile_width":"256", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-05T17:37Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:50Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/HFI_SkyMap_353_2048_R2.00 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_353_2048_R2.00", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_353_2048_R2.00", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288417854"}, +{ "ID":"CDS/P/PLANCK/R2/HFI545", "creator_did":"ivo://CDS/P/PLANCK/R2/HFI545", "obs_collection":"PLANCK R2 HFI545", "obs_title":"PLANCK R2 nominal frequency HFI map 545Ghz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/HFI", "client_sort_key":"05-04-xR2-01-04", "hips_release_date":"2019-05-05T06:50Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"0.2338 141.2", "hips_data_range":"-884.2 2654", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/HFI_SkyMap_545_2048_R2.00/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"5.50E-4", "em_max":"5.50E-4", "hips_tile_width":"256", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-05T17:41Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:50Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/HFI_SkyMap_545_2048_R2.00 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_545_2048_R2.00", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_545_2048_R2.00", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288417906"}, +{ "ID":"CDS/P/PLANCK/R2/HFI857", "creator_did":"ivo://CDS/P/PLANCK/R2/HFI857", "obs_collection":"PLANCK R2 HFI857", "obs_title":"PLANCK R2 nominal frequency HFI map 857Ghz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/HFI", "client_sort_key":"05-04-xR2-01-03", "hips_release_date":"2019-05-05T06:50Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"0.4857 467.3", "hips_data_range":"-3826 11480", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/HFI_SkyMap_857_2048_R2.00/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"3.49E-4", "em_max":"3.49E-4", "hips_tile_width":"256", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-05T17:52Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:50Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/HFI_SkyMap_857_2048_R2.00 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_857_2048_R2.00", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/HFI_SkyMap_857_2048_R2.00", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288417966"}, +{ "ID":"CDS/P/PLANCK/R2/LFI/color", "creator_did":"ivo://CDS/P/PLANCK/R2/LFI/color", "obs_collection":"PLANCK R2 LFI color", "obs_title":"PLANCK R2 LFI color composition 30-44-70 GHz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/LFI", "client_sort_key":"05-04-xR2-02-00", "hips_release_date":"2019-05-05T06:51Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"jpeg", "dataproduct_type":"image", "hips_rgb_red":"LFI_SkyMap_030_1024_R2.01 [-3.6E-4 0.0011315 0.002623 Linear]", "hips_rgb_green":"LFI_SkyMap_044_1024_R2.01 [-5.916E-4 0.0020472000000000003 0.004686 Sqrt]", "hips_rgb_blue":"LFI_SkyMap_070_1024_R2.01 [-7.446E-4 0.0015612000000000002 0.003867 Sqrt]", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/LFI_Color_30_44_70/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"4.28E-3", "em_max":"9.99E-3", "hips_tile_width":"128", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"9182", "hips_creation_date":"2015-02-06T12:09Z", "hips_order_min":"0", "dataproduct_subtype":"color", "hipsgen_date":"2019-05-05T06:51Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/LFI_Color_30_44_70 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/LFI_Color_30_44_70", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/LFI_Color_30_44_70", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/PLANCK/R2/LFI/color", "hips_status_2":"public mirror unclonable", "hips_service_url_3":"https://healpix.ias.u-psud.fr/CDS_P_PLANCK_R2_LFI_color", "hips_status_3":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"3", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"7.3290376785437985", "TIMESTAMP":"1721288902748"}, +{ "ID":"CDS/P/PLANCK/R2/LFI030", "creator_did":"ivo://CDS/P/PLANCK/R2/LFI030", "obs_collection":"PLANCK R2 LFI030", "obs_title":"PLANCK R2 nominal frequency LFI map 30 Ghz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/LFI", "client_sort_key":"05-04-xR2-02-03", "hips_release_date":"2019-05-05T06:51Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"-2.004E-4 0.05758", "hips_data_range":"-0.1084 0.3236", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/LFI_SkyMap_030_1024_R2.01/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"9.99E-3", "em_max":"9.99E-3", "hips_tile_width":"128", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-05T16:40Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:51Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/LFI_SkyMap_030_1024_R2.01 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/LFI_SkyMap_030_1024_R2.01", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/LFI_SkyMap_030_1024_R2.01", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288418074"}, +{ "ID":"CDS/P/PLANCK/R2/LFI044", "creator_did":"ivo://CDS/P/PLANCK/R2/LFI044", "obs_collection":"PLANCK R2 LFI044", "obs_title":"PLANCK R2 nominal frequency LFI map 44 Ghz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/LFI", "client_sort_key":"05-04-xR2-02-02", "hips_release_date":"2019-05-05T06:51Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"-2.356E-4 0.0233", "hips_data_range":"-0.05806 0.1724", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/LFI_SkyMap_044_1024_R2.01/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"6.81E-3", "em_max":"6.81E-3", "hips_tile_width":"128", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-05T16:42Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:51Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/LFI_SkyMap_044_1024_R2.01 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/LFI_SkyMap_044_1024_R2.01", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/LFI_SkyMap_044_1024_R2.01", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288418126"}, +{ "ID":"CDS/P/PLANCK/R2/LFI070", "creator_did":"ivo://CDS/P/PLANCK/R2/LFI070", "obs_collection":"PLANCK R2 LFI070", "obs_title":"PLANCK R2 nominal frequency LFI map 70Ghz", "obs_description":"The Planck mission will collect and characterise radiation from the Cosmic Microwave Background (CMB) using sensitive radio receivers operating at extremely low temperatures. These receivers will determine the black body equivalent temperature of the background radiation and will be capable of distinguishing temperature variations of about one microkelvin. These measurements will be used to produce the best ever maps of anisotropies in the CMB radiation field.", "obs_copyright_url":"http://pla.esac.esa.int/pla", "prov_progenitor":"ESA", "client_category":"Deprecated/HiPS/CDS/Radio/PLANCK/R2/LFI", "client_sort_key":"05-04-xR2-02-01", "hips_release_date":"2019-05-05T06:52Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png fits", "dataproduct_type":"image", "hips_pixel_cut":"-3.952E-4 0.004807", "hips_data_range":"-0.05934 0.1745", "moc_access_url":"http://alasky.u-strasbg.fr/PLANCK/R2/LFI_SkyMap_070_2048_R2.01/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"300.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "obs_copyright":"Planck Legacy Archive", "t_min":"55056", "t_max":"56507", "obs_regime":"Radio", "em_min":"4.28E-3", "em_max":"4.28E-3", "hips_tile_width":"256", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1116925", "hips_creation_date":"2015-02-05T16:29Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T06:52Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/PLANCK/R2/LFI_SkyMap_070_2048_R2.01 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R2/LFI_SkyMap_070_2048_R2.01", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R2/LFI_SkyMap_070_2048_R2.01", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288418178"}, +{ "ID":"CDS/P/PLANCK/R3/CMB", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/CMB", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK Map of the CMB fluctuations", "obs_collection":"PLANCK R3 CMB", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.0003498161703617", "em_max":"0.009993081933333", "client_category":"Image/Radio/PLANCK/R3", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-11-04T16:44Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"256", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-3.037E-4 3.660E-4", "hips_data_range":"-0.00648 0.004229", "hips_pixel_scale":"0.02863", "dataproduct_type":"image", "hipsgen_date":"2022-11-04T16:44Z", "hipsgen_params":"in=COM_CMB_IQU-smica_2048_R3.00_full.fits out=CMB-smica-R3 creator_did=CDS/P/PLANCK/R3/CMB", "hips_creation_date":"2022-11-04T16:44Z", "hips_estsize":"363479", "hips_nb_tiles":"2042", "hips_check_code":"png:3762770760 fits:269935101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/CMB-smica-R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/CMB-smica-R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288492618"}, +{ "ID":"CDS/P/PLANCK/R3/HFI/color", "hips_initial_fov":"140.0", "hips_initial_ra":"99.6310748", "hips_initial_dec":"+2.5518726", "creator_did":"ivo://CDS/P/PLANCK/R3/HFI/color", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 HFI color composition 353-545-857 GHz", "obs_collection":"PLANCK R3 HFI color composition 353-545-857 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.0003498161703617", "em_max":"0.0008492704192635", "client_category":"Image/Radio/PLANCK/R3/HFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-11-07T13:34Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"128", "hips_status":"public master clonableOnce", "hips_pixel_scale":"0.05726", "dataproduct_type":"image", "hips_rgb_red":"PLANCK R3 frequency HFI map 353 GHz [1.368E-4 0.0111184 0.0221 Linear]", "hips_rgb_green":"PLANCK R3 frequency HFI map 545 GHz [-0.5 12.305 25.11 Linear]", "hips_rgb_blue":"PLANCK R3 frequency HFI map 857 GHz [-2.0 52.8 107.6 Linear]", "hipsgen_date":"2022-11-07T13:34Z", "hipsgen_params":"inRed=HFI_SkyMap_353_R3 inGreen=HFI_SkyMap_545_R3 inBlue=HFI_SkyMap_857_R3 out=couleurHFI/ creator_did=CDS/P/PLANCK/R3/HFI/color RGB \"cmRed=1.368E-4 0.0111184 0.0221 Linear\" \"cmGreen=-0.5 12.305 25.11 Linear\" \"cmBlue=-2.0 52.8 107.6 Linear\" hips_tile_width=128 RGB", "hips_creation_date":"2022-11-07T13:34Z", "hips_hierarchy":"median", "hips_tile_format":"png", "dataproduct_subtype":"color", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/HFI_Color_353_545_857", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/HFI_Color_353_545_857", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"99.6310748", "obs_initial_dec":"+2.5518726", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288492674"}, +{ "ID":"CDS/P/PLANCK/R3/HFI100", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/HFI100", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 frequency HFI map 100 GHz", "obs_collection":"PLANCK R3 HFI 100 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.00299792458", "em_max":"0.00299792458", "client_category":"Image/Radio/PLANCK/R3/HFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-10-26T09:55Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"256", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-2.676E-4 0.004868", "hips_data_range":"-0.05603 0.1658", "hips_pixel_scale":"0.02863", "dataproduct_type":"image", "hipsgen_date":"2022-10-26T09:55Z", "hipsgen_params":"in=../Frequency-maps_Single-frequency/HFI_SkyMap_100_2048_R3.01_full.fits out=HiPS_HFI_SkyMap_100/ creator_did=CDS/P/PLANCK/R3/HFI100", "hips_creation_date":"2022-10-26T09:55Z", "hips_estsize":"334736", "hips_nb_tiles":"2042", "hips_check_code":"png:2495301502 fits:269935101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_100_R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_100_R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288492786"}, +{ "ID":"CDS/P/PLANCK/R3/HFI143", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/HFI143", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 frequency HFI map 143 GHz", "obs_collection":"PLANCK R3 HFI 143 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.002096450755245", "em_max":"0.002096450755245", "client_category":"Image/Radio/PLANCK/R3/HFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-10-26T10:09Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"256", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-2.371E-4 0.005359", "hips_data_range":"-0.06099 0.1813", "hips_pixel_scale":"0.02863", "dataproduct_type":"image", "hipsgen_date":"2022-10-26T10:09Z", "hipsgen_params":"in=../Frequency-maps_Single-frequency/HFI_SkyMap_143_2048_R3.01_full.fits out=HiPS_HFI_SkyMap_143/ creator_did=CDS/P/PLANCK/R3/HFI143", "hips_creation_date":"2022-10-26T10:09Z", "hips_estsize":"324689", "hips_nb_tiles":"2042", "hips_check_code":"png:3377468219 fits:269935101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_143_R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_143_R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288492842"}, +{ "ID":"CDS/P/PLANCK/R3/HFI217", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/HFI217", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 frequency HFI map 217 GHz", "obs_collection":"PLANCK R3 HFI 217 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.001381532064516", "em_max":"0.001381532064516", "client_category":"Image/Radio/PLANCK/R3/HFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-10-26T10:12Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"256", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-1.540E-4 0.01882", "hips_data_range":"-0.1515 0.4527", "hips_pixel_scale":"0.02863", "dataproduct_type":"image", "hipsgen_date":"2022-10-26T10:12Z", "hipsgen_params":"in=../../Frequency-maps_Single-frequency/HFI_SkyMap_217_2048_R3.01_full.fits out=HiPS_HFI_SkyMap_217/ creator_did=CDS/P/PLANCK/R3/HFI217", "hips_creation_date":"2022-10-26T10:12Z", "hips_estsize":"310664", "hips_nb_tiles":"2042", "hips_check_code":"png:3589847422 fits:269935101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_217_R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_217_R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288492894"}, +{ "ID":"CDS/P/PLANCK/R3/HFI353", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/HFI353", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 frequency HFI map 353 GHz", "obs_collection":"PLANCK R3 HFI 353 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.0008492704192635", "em_max":"0.0008492704192635", "client_category":"Image/Radio/PLANCK/R3/HFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-10-26T10:14Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"256", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"9.754E-5 0.1335", "hips_data_range":"-0.8379 2.513", "hips_pixel_scale":"0.02863", "dataproduct_type":"image", "hipsgen_date":"2022-10-26T10:14Z", "hipsgen_params":"in=../../Frequency-maps_Single-frequency/HFI_SkyMap_353-psb_2048_R3.01_full.fits out=HiPS_HFI_SkyMap_353/ creator_did=CDS/P/PLANCK/R3/HFI353", "hips_creation_date":"2022-10-26T10:14Z", "hips_estsize":"303717", "hips_nb_tiles":"2042", "hips_check_code":"png:1815710823 fits:269935101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_353_R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_353_R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288492954"}, +{ "ID":"CDS/P/PLANCK/R3/HFI545", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/HFI545", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 frequency HFI map 545 GHz", "obs_collection":"PLANCK R3 HFI 545 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.0005500779045872", "em_max":"0.0005500779045872", "client_category":"Image/Radio/PLANCK/R3/HFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-10-26T10:16Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"256", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"0.2317 142.6", "hips_data_range":"-890.5 2672", "hips_pixel_scale":"0.02863", "dataproduct_type":"image", "hipsgen_date":"2022-10-26T10:16Z", "hipsgen_params":"in=../../Frequency-maps_Single-frequency/HFI_SkyMap_545_2048_R3.01_full.fits out=HiPS_HFI_SkyMap_545/ creator_did=CDS/P/PLANCK/R3/HFI545", "hips_creation_date":"2022-10-26T10:16Z", "hips_estsize":"293347", "hips_nb_tiles":"2042", "hips_check_code":"png:1830480926 fits:269935101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_545_R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_545_R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288493006"}, +{ "ID":"CDS/P/PLANCK/R3/HFI857", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/HFI857", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 frequency HFI map 857 GHz", "obs_collection":"PLANCK R3 HFI 857 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.0003498161703617", "em_max":"0.0003498161703617", "client_category":"Image/Radio/PLANCK/R3/HFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-10-26T10:18Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"256", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"0.4803 469.8", "hips_data_range":"-3834 11505", "hips_pixel_scale":"0.02863", "dataproduct_type":"image", "hipsgen_date":"2022-10-26T10:17Z", "hipsgen_params":"in=../../Frequency-maps_Single-frequency/HFI_SkyMap_857_2048_R3.01_full.fits out=HiPS_HFI_SkyMap_857/ creator_did=CDS/P/PLANCK/R3/HFI857", "hips_creation_date":"2022-10-26T10:17Z", "hips_estsize":"291476", "hips_nb_tiles":"2042", "hips_check_code":"png:3351337634 fits:269935101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_857_R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/HFI_SkyMap_857_R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288493062"}, +{ "ID":"CDS/P/PLANCK/R3/LFI/color", "hips_initial_fov":"0.4580648549089874", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/LFI/color", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 LFI Color composition 30-44-70 GHz", "obs_collection":"PLANCK R3 LFI Color composition 30-44-70 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.0042827494", "em_max":"0.009993081933333", "client_category":"Image/Radio/PLANCK/R3/HFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-11-07T13:38Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"128", "hips_status":"public master clonableOnce", "hips_pixel_scale":"0.05726", "dataproduct_type":"image", "hips_rgb_red":"PLANCK R3 frequency LFI map 30 GHz [-3.6E-4 0.0011315 0.002623 Linear]", "hips_rgb_green":"PLANCK R3 frequency LFI map 44 GHz [-5.916E-4 0.0020472000000000003 0.004686 Sqrt]", "hips_rgb_blue":"PLANCK R3 frequency LFI map 70 GHz [-7.446E-4 0.0015612000000000002 0.003867 Sqrt]", "hipsgen_date":"2022-11-07T13:38Z", "hipsgen_params":"inRed=LFI_SkyMap_30_R3 inGreen=LFI_SkyMap_44_R3 inBlue=LFI_SkyMap_70_R3 out=couleurLFI/ creator_did=CDS/P/PLANCK/R3/LFI/color RGB \"cmRed=-3.6E-4 0.0011315 0.002623 Linear\" \"cmGreen=-5.916E-4 0.0020472000000000003 0.004686 Sqrt\" \"cmBlue=-7.446E-4 0.0015612000000000002 0.003867 Sqrt\" hips_tile_width=128 RGB", "hips_creation_date":"2022-11-07T13:38Z", "hips_hierarchy":"median", "hips_tile_format":"png", "dataproduct_subtype":"color", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/LFI_Color_30_44_70", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/LFI_Color_30_44_70", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288492730"}, +{ "ID":"CDS/P/PLANCK/R3/LFI30", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/LFI30", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 frequency LFI map 30 GHz", "obs_collection":"PLANCK R3 LFI 30 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.009993081933333", "em_max":"0.009993081933333", "client_category":"Image/Radio/PLANCK/R3/LFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-10-26T13:18Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"128", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-1.856E-4 0.05743", "hips_data_range":"-0.1081 0.3228", "hips_pixel_scale":"0.05726", "dataproduct_type":"image", "hipsgen_date":"2022-10-26T13:17Z", "hipsgen_params":"in=../../Frequency-maps_Single-frequency/LFI_SkyMap_030-BPassCorrected_1024_R3.00_full.fits out=HiPS_LFI_SkyMap_30/ creator_did=CDS/P/PLANCK/R3/LFI30", "hips_creation_date":"2022-10-26T13:17Z", "hips_estsize":"86960", "hips_nb_tiles":"2042", "hips_check_code":"png:2686887806 fits:4278383101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/LFI_SkyMap_30_R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/LFI_SkyMap_30_R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288493118"}, +{ "ID":"CDS/P/PLANCK/R3/LFI44", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/LFI44", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 frequency LFI map 44 GHz", "obs_collection":"PLANCK R3 LFI 44 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.006813464954545", "em_max":"0.006813464954545", "client_category":"Image/Radio/PLANCK/R3/LFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-10-26T13:18Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"128", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-2.378E-4 0.02326", "hips_data_range":"-0.05799 0.1721", "hips_pixel_scale":"0.05726", "dataproduct_type":"image", "hipsgen_date":"2022-10-26T13:18Z", "hipsgen_params":"in=../../Frequency-maps_Single-frequency/LFI_SkyMap_044-BPassCorrected_1024_R3.00_full.fits out=HiPS_LFI_SkyMap_44/ creator_did=CDS/P/PLANCK/R3/LFI44", "hips_creation_date":"2022-10-26T13:18Z", "hips_estsize":"91012", "hips_nb_tiles":"2042", "hips_check_code":"png:1098360562 fits:4278383101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/LFI_SkyMap_44_R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/LFI_SkyMap_44_R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288493190"}, +{ "ID":"CDS/P/PLANCK/R3/LFI70", "hips_initial_fov":"58.63230142835039", "hips_initial_ra":"0", "hips_initial_dec":"+0", "creator_did":"ivo://CDS/P/PLANCK/R3/LFI70", "hips_creator":"Buga M. (CDS)", "hips_copyright":"CNRS/Unistra", "obs_title":"PLANCK R3 frequency LFI map 70 GHz", "obs_collection":"PLANCK R3 LFI 70 GHz", "obs_description":"Planck is ESA's mission to observe the first light in the Universe. Planck was launched on 14 May 2009, and the minimum requirement for success was for the spacecraft to complete two whole surveys of the sky. In the end, Planck worked perfectly for 30 months, about twice the span originally required, and completed five full-sky surveys with both instruments. Able to work at slightly higher temperatures than HFI, the Low Frequency Instrument (LFI) continued to survey the sky for a large part of 2013, providing even more data to improve the Planck final results. Planck was turned off on 23 October 2013. The high-quality data the mission has produced will continue to be scientifically explored in the years to come.", "obs_ack":"ESA and the Planck Collaboration", "prov_progenitor":"ESA", "bib_reference":"2020A&A...641A...1P", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2020A%26A...641A...1P/abstract", "obs_copyright":"EUROPEAN SPACE AGENCY. ALL RIGHTS RESERVED", "obs_copyright_url":"https://www.cosmos.esa.int/web/planck", "t_min":"55054.9166667", "t_max":"56587.9166667", "obs_regime":"Radio", "em_min":"0.0042827494", "em_max":"0.0042827494", "client_category":"Image/Radio/PLANCK/R3/LFI", "hips_builder":"Aladin/HipsGen v12.013", "hips_version":"1.4", "hips_release_date":"2022-10-26T13:19Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"128", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-2.741E-4 0.00991", "hips_data_range":"-0.07587 0.2254", "hips_pixel_scale":"0.05726", "dataproduct_type":"image", "hipsgen_date":"2022-10-26T13:19Z", "hipsgen_params":"in=../../Frequency-maps_Single-frequency/LFI_SkyMap_070-BPassCorrected_1024_R3.00_full.fits out=HiPS_LFI_SkyMap_70/ creator_did=CDS/P/PLANCK/R3/LFI70", "hips_creation_date":"2022-10-26T13:19Z", "hips_estsize":"94756", "hips_nb_tiles":"2042", "hips_check_code":"png:150953168 fits:4278383101", "hips_service_url":"https://alasky.cds.unistra.fr/PLANCK/R3/LFI_SkyMap_70_R3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/PLANCK/R3/LFI_SkyMap_70_R3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_sky_fraction":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288493258"}, +{ "ID":"CDS/P/QUIJOTE/DR1/MFI/I/11GHz", "hips_initial_fov":"360.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "creator_did":"ivo://CDS/P/QUIJOTE/DR1/MFI/I/11GHz", "hips_creator":"QUIJOTE collaboration", "hips_copyright":"CNRS/Unistra", "obs_title":"QUIJOTE MFI DR1 11GHz Intensity", "obs_collection":"QUIJOTE MFI DR1", "obs_description":"The QUIJOTE (Q-U-I JOint TEnerife) CMB Experiment is a scientific collaboration between the Instituto de Astrofisica de Canarias (Tenerife, Spain), the Instituto de Fisica de Cantabria (Santander, Spain), the Departamento de Ingenieria de COMunicaciones (Santander, Spain), the Jodrell Bank Observatory (Manchester, UK), the Cavendish Laboratory (Cambridge, UK), and the IDOM company (Spain). It started operations in November 2012, and it consists in two telescopes and three instruments dedicated to measure the polarization of the microwave sky in the frequency range between 10 GHz and 40GHz, and at angular scales of one degree. We present QUIJOTE intensity and polarization maps in four frequency bands centred around 11, 13, 17 and 19 GHz, and covering approximately 30 000 deg2, including most of the Northern sky region. These maps result from 9 000 hours of observations taken between May 2013 and June 2018 with the first QUIJOTE instrument (MFI), and have angular resolutions of around one degree, and sensitivities in polarization within the range 35-40 microkelvin per 1-degree beam, being a factor 2-4 worse in intensity.", "obs_ack":"Please acknowledge the use of the QUIJOTE MFI wide survey data products by: citing the main QUIJOTE MFI wide survey paper ( Rubino-Martin et al. 2023 ), and if using derived products, the relevant associated paper(s); and adding an acknowledgment statement: \"Some of the presented results are based on observations obtained with the QUIJOTE experiment ( http://research.iac.es/proyecto/quijote )\".", "prov_progenitor":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "bib_reference":"2023MNRAS.519.3383R", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2023MNRAS.519.3383R", "obs_copyright_url":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "obs_regime":"Radio", "em_min":"0.02727272727", "em_max":"0.02727272727", "client_category":"Image/Radio/QUIJOTE/DR1/Intensity", "hips_builder":"Aladin/HipsGen v11.024", "hips_version":"1.4", "hips_release_date":"2023-01-10T09:14Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"64", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-1.281 30", "hips_data_range":"-322.8 961.2", "hips_pixel_scale":"1.832", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"8", "hips_creation_date":"2023-01-10T08:56Z", "hips_service_url":"https://alasky.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_I_11GHz", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_I_11GHz", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288494714"}, +{ "ID":"CDS/P/QUIJOTE/DR1/MFI/I/13GHz", "hips_initial_fov":"360.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "creator_did":"ivo://CDS/P/QUIJOTE/DR1/MFI/I/13GHz", "hips_creator":"QUIJOTE collaboration", "hips_copyright":"CNRS/Unistra", "obs_title":"QUIJOTE MFI DR1 13GHz Intensity", "obs_collection":"QUIJOTE MFI DR1", "obs_description":"The QUIJOTE (Q-U-I JOint TEnerife) CMB Experiment is a scientific collaboration between the Instituto de Astrofisica de Canarias (Tenerife, Spain), the Instituto de Fisica de Cantabria (Santander, Spain), the Departamento de Ingenieria de COMunicaciones (Santander, Spain), the Jodrell Bank Observatory (Manchester, UK), the Cavendish Laboratory (Cambridge, UK), and the IDOM company (Spain). It started operations in November 2012, and it consists in two telescopes and three instruments dedicated to measure the polarization of the microwave sky in the frequency range between 10 GHz and 40GHz, and at angular scales of one degree. We present QUIJOTE intensity and polarization maps in four frequency bands centred around 11, 13, 17 and 19 GHz, and covering approximately 30 000 deg2, including most of the Northern sky region. These maps result from 9 000 hours of observations taken between May 2013 and June 2018 with the first QUIJOTE instrument (MFI), and have angular resolutions of around one degree, and sensitivities in polarization within the range 35-40 microkelvin per 1-degree beam, being a factor 2-4 worse in intensity.", "obs_ack":"Please acknowledge the use of the QUIJOTE MFI wide survey data products by: citing the main QUIJOTE MFI wide survey paper ( Rubino-Martin et al. 2023 ), and if using derived products, the relevant associated paper(s); and adding an acknowledgment statement: \"Some of the presented results are based on observations obtained with the QUIJOTE experiment ( http://research.iac.es/proyecto/quijote )\".", "prov_progenitor":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "bib_reference":"2023MNRAS.519.3383R", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2023MNRAS.519.3383R", "obs_copyright_url":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "obs_regime":"Radio", "em_min":"0.02307692307", "em_max":"0.02307692307", "client_category":"Image/Radio/QUIJOTE/DR1/Intensity", "hips_builder":"Aladin/HipsGen v11.024", "hips_version":"1.4", "hips_release_date":"2023-01-10T09:19Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"64", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-1.281 30", "hips_data_range":"-231.6 686.8", "hips_pixel_scale":"1.832", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"8", "hips_creation_date":"2023-01-10T07:57Z", "hips_service_url":"https://alasky.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_I_13GHz", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_I_13GHz", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288494778"}, +{ "ID":"CDS/P/QUIJOTE/DR1/MFI/I/17GHz", "hips_initial_fov":"360.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "creator_did":"ivo://CDS/P/QUIJOTE/DR1/MFI/I/17GHz", "hips_creator":"QUIJOTE collaboration", "hips_copyright":"CNRS/Unistra", "obs_title":"QUIJOTE MFI DR1 17GHz Intensity", "obs_collection":"QUIJOTE MFI DR1", "obs_description":"The QUIJOTE (Q-U-I JOint TEnerife) CMB Experiment is a scientific collaboration between the Instituto de Astrofisica de Canarias (Tenerife, Spain), the Instituto de Fisica de Cantabria (Santander, Spain), the Departamento de Ingenieria de COMunicaciones (Santander, Spain), the Jodrell Bank Observatory (Manchester, UK), the Cavendish Laboratory (Cambridge, UK), and the IDOM company (Spain). It started operations in November 2012, and it consists in two telescopes and three instruments dedicated to measure the polarization of the microwave sky in the frequency range between 10 GHz and 40GHz, and at angular scales of one degree. We present QUIJOTE intensity and polarization maps in four frequency bands centred around 11, 13, 17 and 19 GHz, and covering approximately 30 000 deg2, including most of the Northern sky region. These maps result from 9 000 hours of observations taken between May 2013 and June 2018 with the first QUIJOTE instrument (MFI), and have angular resolutions of around one degree, and sensitivities in polarization within the range 35-40 microkelvin per 1-degree beam, being a factor 2-4 worse in intensity.", "obs_ack":"Please acknowledge the use of the QUIJOTE MFI wide survey data products by: citing the main QUIJOTE MFI wide survey paper ( Rubino-Martin et al. 2023 ), and if using derived products, the relevant associated paper(s); and adding an acknowledgment statement: \"Some of the presented results are based on observations obtained with the QUIJOTE experiment ( http://research.iac.es/proyecto/quijote )\".", "prov_progenitor":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "bib_reference":"2023MNRAS.519.3383R", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2023MNRAS.519.3383R", "obs_copyright_url":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "obs_regime":"Radio", "em_min":"0.01764705882", "em_max":"0.01764705882", "client_category":"Image/Radio/QUIJOTE/DR1/Intensity", "hips_builder":"Aladin/HipsGen v11.024", "hips_version":"1.4", "hips_release_date":"2023-01-10T09:50Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"64", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-3.177 50", "hips_data_range":"-129.7 368.7", "hips_pixel_scale":"1.832", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"8", "hips_creation_date":"2023-01-10T07:57Z", "hips_service_url":"https://alasky.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_I_17GHz", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_I_17GHz", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288494834"}, +{ "ID":"CDS/P/QUIJOTE/DR1/MFI/I/19GHz", "hips_initial_fov":"360.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "creator_did":"ivo://CDS/P/QUIJOTE/DR1/MFI/I/19GHz", "hips_creator":"QUIJOTE collaboration", "hips_copyright":"CNRS/Unistra", "obs_title":"QUIJOTE MFI DR1 19GHz Intensity", "obs_collection":"QUIJOTE MFI DR1", "obs_description":"The QUIJOTE (Q-U-I JOint TEnerife) CMB Experiment is a scientific collaboration between the Instituto de Astrofisica de Canarias (Tenerife, Spain), the Instituto de Fisica de Cantabria (Santander, Spain), the Departamento de Ingenieria de COMunicaciones (Santander, Spain), the Jodrell Bank Observatory (Manchester, UK), the Cavendish Laboratory (Cambridge, UK), and the IDOM company (Spain). It started operations in November 2012, and it consists in two telescopes and three instruments dedicated to measure the polarization of the microwave sky in the frequency range between 10 GHz and 40GHz, and at angular scales of one degree. We present QUIJOTE intensity and polarization maps in four frequency bands centred around 11, 13, 17 and 19 GHz, and covering approximately 30 000 deg2, including most of the Northern sky region. These maps result from 9 000 hours of observations taken between May 2013 and June 2018 with the first QUIJOTE instrument (MFI), and have angular resolutions of around one degree, and sensitivities in polarization within the range 35-40 microkelvin per 1-degree beam, being a factor 2-4 worse in intensity.", "obs_ack":"Please acknowledge the use of the QUIJOTE MFI wide survey data products by: citing the main QUIJOTE MFI wide survey paper ( Rubino-Martin et al. 2023 ), and if using derived products, the relevant associated paper(s); and adding an acknowledgment statement: \"Some of the presented results are based on observations obtained with the QUIJOTE experiment ( http://research.iac.es/proyecto/quijote )\".", "prov_progenitor":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "bib_reference":"2023MNRAS.519.3383R", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2023MNRAS.519.3383R", "obs_copyright_url":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "obs_regime":"Radio", "em_min":"0.01578947368", "em_max":"0.01578947368", "client_category":"Image/Radio/QUIJOTE/DR1/Intensity", "hips_builder":"Aladin/HipsGen v11.024", "hips_version":"1.4", "hips_release_date":"2023-01-10T09:53Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"64", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"-3.041 61.26", "hips_data_range":"-100.9 282.6", "hips_pixel_scale":"1.832", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"8", "hips_creation_date":"2023-01-10T07:57Z", "hips_service_url":"https://alasky.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_I_19GHz", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_I_19GHz", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288494890"}, +{ "ID":"CDS/P/QUIJOTE/DR1/MFI/P/11GHz", "hips_initial_fov":"360.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "creator_did":"ivo://CDS/P/QUIJOTE/DR1/MFI/P/11GHz", "hips_creator":"QUIJOTE collaboration", "hips_copyright":"CNRS/Unistra", "obs_title":"QUIJOTE MFI DR1 11GHz Polarization", "obs_collection":"QUIJOTE MFI DR1", "obs_description":"The QUIJOTE (Q-U-I JOint TEnerife) CMB Experiment is a scientific collaboration between the Instituto de Astrofisica de Canarias (Tenerife, Spain), the Instituto de Fisica de Cantabria (Santander, Spain), the Departamento de Ingenieria de COMunicaciones (Santander, Spain), the Jodrell Bank Observatory (Manchester, UK), the Cavendish Laboratory (Cambridge, UK), and the IDOM company (Spain). It started operations in November 2012, and it consists in two telescopes and three instruments dedicated to measure the polarization of the microwave sky in the frequency range between 10 GHz and 40GHz, and at angular scales of one degree. We present QUIJOTE intensity and polarization maps in four frequency bands centred around 11, 13, 17 and 19 GHz, and covering approximately 30 000 deg2, including most of the Northern sky region. These maps result from 9 000 hours of observations taken between May 2013 and June 2018 with the first QUIJOTE instrument (MFI), and have angular resolutions of around one degree, and sensitivities in polarization within the range 35-40 microkelvin per 1-degree beam, being a factor 2-4 worse in intensity.", "obs_ack":"Please acknowledge the use of the QUIJOTE MFI wide survey data products by: citing the main QUIJOTE MFI wide survey paper ( Rubino-Martin et al. 2023 ), and if using derived products, the relevant associated paper(s); and adding an acknowledgment statement: \"Some of the presented results are based on observations obtained with the QUIJOTE experiment ( http://research.iac.es/proyecto/quijote )\".", "prov_progenitor":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "bib_reference":"2023MNRAS.519.3383R", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2023MNRAS.519.3383R", "obs_copyright_url":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "obs_regime":"Radio", "em_min":"0.02727272727", "em_max":"0.02727272727", "client_category":"Image/Radio/QUIJOTE/DR1/Polarization", "hips_builder":"Aladin/HipsGen v11.024", "hips_version":"1.4", "hips_release_date":"2023-01-14T08:45Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"64", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"0.01065 2.5", "hips_data_range":"-10.54 31.62", "hips_pixel_scale":"1.832", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"8", "hips_creation_date":"2023-01-10T08:56Z", "hips_rgb_red":"TFIELD1 [-0.1923 14.36 70.49 Linear]", "hips_rgb_green":"TFIELD1 [-0.1389 67.11 317.5 Linear]", "hips_rgb_blue":"TFIELD1 [-0.4765 37.97 185.9 Linear]", "hips_hierarchy":"median", "hips_service_url":"https://alasky.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_P_11GHz", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_P_11GHz", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288494958"}, +{ "ID":"CDS/P/QUIJOTE/DR1/MFI/P/13GHz", "hips_initial_fov":"360.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "creator_did":"ivo://CDS/P/QUIJOTE/DR1/MFI/P/13GHz", "hips_creator":"QUIJOTE collaboration", "hips_copyright":"CNRS/Unistra", "obs_title":"QUIJOTE MFI DR1 13GHz Polarization", "obs_collection":"QUIJOTE MFI DR1", "obs_description":"The QUIJOTE (Q-U-I JOint TEnerife) CMB Experiment is a scientific collaboration between the Instituto de Astrofisica de Canarias (Tenerife, Spain), the Instituto de Fisica de Cantabria (Santander, Spain), the Departamento de Ingenieria de COMunicaciones (Santander, Spain), the Jodrell Bank Observatory (Manchester, UK), the Cavendish Laboratory (Cambridge, UK), and the IDOM company (Spain). It started operations in November 2012, and it consists in two telescopes and three instruments dedicated to measure the polarization of the microwave sky in the frequency range between 10 GHz and 40GHz, and at angular scales of one degree. We present QUIJOTE intensity and polarization maps in four frequency bands centred around 11, 13, 17 and 19 GHz, and covering approximately 30 000 deg2, including most of the Northern sky region. These maps result from 9 000 hours of observations taken between May 2013 and June 2018 with the first QUIJOTE instrument (MFI), and have angular resolutions of around one degree, and sensitivities in polarization within the range 35-40 microkelvin per 1-degree beam, being a factor 2-4 worse in intensity.", "obs_ack":"Please acknowledge the use of the QUIJOTE MFI wide survey data products by: citing the main QUIJOTE MFI wide survey paper ( Rubino-Martin et al. 2023 ), and if using derived products, the relevant associated paper(s); and adding an acknowledgment statement: \"Some of the presented results are based on observations obtained with the QUIJOTE experiment ( http://research.iac.es/proyecto/quijote )\".", "prov_progenitor":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "bib_reference":"2023MNRAS.519.3383R", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2023MNRAS.519.3383R", "obs_copyright_url":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "obs_regime":"Radio", "em_min":"0.02307692307", "em_max":"0.02307692307", "client_category":"Image/Radio/QUIJOTE/DR1/Polarization", "hips_builder":"Aladin/HipsGen v11.024", "hips_version":"1.4", "hips_release_date":"2023-01-14T09:26Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"64", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"0.01065 2.5", "hips_data_range":"-8.084 24.25", "hips_pixel_scale":"1.832", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"8", "hips_creation_date":"2023-01-14T09:23Z", "hips_service_url":"https://alasky.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_P_13GHz", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_P_13GHz", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288495038"}, +{ "ID":"CDS/P/QUIJOTE/DR1/MFI/P/17GHz", "hips_initial_fov":"360.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "creator_did":"ivo://CDS/P/QUIJOTE/DR1/MFI/P/17GHz", "hips_creator":"QUIJOTE collaboration", "hips_copyright":"CNRS/Unistra", "obs_title":"QUIJOTE MFI DR1 17GHz Polarization", "obs_collection":"QUIJOTE MFI DR1", "obs_description":"The QUIJOTE (Q-U-I JOint TEnerife) CMB Experiment is a scientific collaboration between the Instituto de Astrofisica de Canarias (Tenerife, Spain), the Instituto de Fisica de Cantabria (Santander, Spain), the Departamento de Ingenieria de COMunicaciones (Santander, Spain), the Jodrell Bank Observatory (Manchester, UK), the Cavendish Laboratory (Cambridge, UK), and the IDOM company (Spain). It started operations in November 2012, and it consists in two telescopes and three instruments dedicated to measure the polarization of the microwave sky in the frequency range between 10 GHz and 40GHz, and at angular scales of one degree. We present QUIJOTE intensity and polarization maps in four frequency bands centred around 11, 13, 17 and 19 GHz, and covering approximately 30 000 deg2, including most of the Northern sky region. These maps result from 9 000 hours of observations taken between May 2013 and June 2018 with the first QUIJOTE instrument (MFI), and have angular resolutions of around one degree, and sensitivities in polarization within the range 35-40 microkelvin per 1-degree beam, being a factor 2-4 worse in intensity.", "obs_ack":"Please acknowledge the use of the QUIJOTE MFI wide survey data products by: citing the main QUIJOTE MFI wide survey paper ( Rubino-Martin et al. 2023 ), and if using derived products, the relevant associated paper(s); and adding an acknowledgment statement: \"Some of the presented results are based on observations obtained with the QUIJOTE experiment ( http://research.iac.es/proyecto/quijote )\".", "prov_progenitor":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "bib_reference":"2023MNRAS.519.3383R", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2023MNRAS.519.3383R", "obs_copyright_url":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "obs_regime":"Radio", "em_min":"0.01764705882", "em_max":"0.01764705882", "client_category":"Image/Radio/QUIJOTE/DR1/Polarization", "hips_builder":"Aladin/HipsGen v11.024", "hips_version":"1.4", "hips_release_date":"2023-01-14T09:09Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"64", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"0.003608 1.692", "hips_data_range":"-4.453 13.36", "hips_pixel_scale":"1.832", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"8", "hips_creation_date":"2023-01-14T09:06Z", "hips_service_url":"https://alasky.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_P_17GHz", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_P_17GHz", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288495090"}, +{ "ID":"CDS/P/QUIJOTE/DR1/MFI/P/19GHz", "hips_initial_fov":"360.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "creator_did":"ivo://CDS/P/QUIJOTE/DR1/MFI/P/19GHz", "hips_creator":"QUIJOTE collaboration", "hips_copyright":"CNRS/Unistra", "obs_title":"QUIJOTE MFI DR1 19GHz Polarization", "obs_collection":"QUIJOTE MFI DR1", "obs_description":"The QUIJOTE (Q-U-I JOint TEnerife) CMB Experiment is a scientific collaboration between the Instituto de Astrofisica de Canarias (Tenerife, Spain), the Instituto de Fisica de Cantabria (Santander, Spain), the Departamento de Ingenieria de COMunicaciones (Santander, Spain), the Jodrell Bank Observatory (Manchester, UK), the Cavendish Laboratory (Cambridge, UK), and the IDOM company (Spain). It started operations in November 2012, and it consists in two telescopes and three instruments dedicated to measure the polarization of the microwave sky in the frequency range between 10 GHz and 40GHz, and at angular scales of one degree. We present QUIJOTE intensity and polarization maps in four frequency bands centred around 11, 13, 17 and 19 GHz, and covering approximately 30 000 deg2, including most of the Northern sky region. These maps result from 9 000 hours of observations taken between May 2013 and June 2018 with the first QUIJOTE instrument (MFI), and have angular resolutions of around one degree, and sensitivities in polarization within the range 35-40 microkelvin per 1-degree beam, being a factor 2-4 worse in intensity.", "obs_ack":"Please acknowledge the use of the QUIJOTE MFI wide survey data products by: citing the main QUIJOTE MFI wide survey paper ( Rubino-Martin et al. 2023 ), and if using derived products, the relevant associated paper(s); and adding an acknowledgment statement: \"Some of the presented results are based on observations obtained with the QUIJOTE experiment ( http://research.iac.es/proyecto/quijote )\".", "prov_progenitor":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "bib_reference":"2023MNRAS.519.3383R", "bib_reference_url":"https://ui.adsabs.harvard.edu/abs/2023MNRAS.519.3383R", "obs_copyright_url":"https://research.iac.es/proyecto/quijote/pages/en/data/mfi-wide-survey.php", "obs_regime":"Radio", "em_min":"0.01578947368", "em_max":"0.01578947368", "client_category":"Image/Radio/QUIJOTE/DR1/Polarization", "hips_builder":"Aladin/HipsGen v11.024", "hips_version":"1.4", "hips_release_date":"2023-01-14T09:34Z", "hips_frame":"galactic", "hips_order":"3", "hips_order_min":"0", "hips_tile_width":"64", "hips_status":"public master clonableOnce", "hips_tile_format":"png fits", "hips_pixel_bitpix":"-32", "hips_pixel_cut":"0.003608 1.692", "hips_data_range":"-3.556 10.67", "hips_pixel_scale":"1.832", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"8", "hips_creation_date":"2023-01-14T09:32Z", "hips_service_url":"https://alasky.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_P_19GHz", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/QUIJOTE/DR1/MFI/CDS_P_QUIJOTE_DR1_MFI_P_19GHz", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"7", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288495146"}, +{ "ID":"CDS/P/RASS", "creator_did":"ivo://CDS/P/RASS", "obs_collection":"RASS", "obs_title":"ROSAT X-Ray All-Sky Survey", "obs_description":"The ROSAT All-Sky X-ray Survey was obtained during 1990/1991 using the ROSAT Position Sensitive Proportional Counter (PSPC) in combination with the ROSAT X-ray Telescope (XRT).", "obs_copyright":"Distributed by MPE - HEALPixed by CDS", "obs_copyright_url":"http://www.mpe.mpg.de/xray/home.php", "client_category":"Image/X/ROSAT", "client_sort_key":"01-02", "hips_creation_date":"2014-03-29T13:46Z", "hips_release_date":"2019-05-05T06:52Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"4", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/RASS/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"100.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"NASA/HEASARC", "bib_reference":"1999A&A...349..389V", "bib_reference_url":"http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=1999A%26A...349..389V&simbo=on", "t_min":"48058", "t_max":"48602", "obs_regime":"X-ray", "em_min":"5.1660e-10", "em_max":"1.2398e-8", "hips_pixel_scale":"0.007157", "moc_sky_fraction":"1", "hips_estsize":"2247318", "hips_order_min":"0", "hips_pixel_bitpix":"16", "hipsgen_date":"2019-05-05T06:52Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/RASS UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/RASS", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/RASS", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"4", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"3.6645188392718993", "TIMESTAMP":"1721288418230"}, +{ "ID":"CDS/P/SHASSA/DU", "creator_did":"ivo://CDS/P/SHASSA/DU", "obs_collection":"SHASSA DU", "obs_title":"SHASSA DU - Continuum", "obs_description":"The Southern H-Alpha Sky Survey Atlas is the product of a wide-angle digital imaging survey of the H-alpha emission from the warm ionized interstellar gas of our Galaxy. This atlas covers the southern hemisphere sky (declinations less than +15 degrees). The observations were taken with a robotic camera operating at Cerro Tololo Inter-American Observatory (CTIO) in Chile.", "obs_copyright":"By courtesy of Swarthmore College Incorporated", "obs_copyrigh_url":"http://amundsen.astro.swarthmore.edu/SHASSA/index.html", "client_category":"Image/Gas-lines/Halpha", "client_sort_key":"06-02-02", "hips_creation_date":"2011-02-01T12:00Z", "hips_release_date":"2019-05-05T07:17Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"4", "hips_frame":"galactic", "hips_tile_width":"512", "hips_tile_format":"png jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"-20 8000", "moc_access_url":"http://alasky.u-strasbg.fr/SHASSA-DU/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"1.0921117184376416E-7", "hips_initial_ra":"45.0", "hips_initial_dec":"7.114779961355992E-8", "hips_copyright":"CNRS/Unistra", "obs_ack":"the Southern H-Alpha Sky Survey Atlas (SHASSA), which is supported by the National Science Foundation", "prov_progenitor":"SHASSA", "bib_reference":"2001PASP..113.1326G", "bib_reference_url":"http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2001PASP..113.1326G&simbo=on", "obs_copyright_url":"http://amundsen.astro.swarthmore.edu/SHASSA/ack.html", "t_min":"50753", "t_max":"51847", "obs_regime":"Optical", "em_min":"6.56e-7", "em_max":"6.56e-7", "hips_pixel_scale":"0.007157", "moc_sky_fraction":"1", "hips_estsize":"4504376", "hips_order_min":"0", "hips_pixel_bitpix":"32", "hipsgen_date":"2019-05-05T07:17Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/SHASSA-v2/SHASSA-DU UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/SHASSA-DU", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/SHASSA-DU", "hips_status_1":"public mirror clonableOnce", "TIMESTAMP":"1721288418890"}, +{ "ID":"CDS/P/SHASSA/FL", "creator_did":"ivo://CDS/P/SHASSA/FL", "obs_collection":"SHASSA FL", "obs_title":"SHASSA FL - Continuum subtract.", "obs_description":"The Southern H-Alpha Sky Survey Atlas is the product of a wide-angle digital imaging survey of the H-alpha emission from the warm ionized interstellar gas of our Galaxy. This atlas covers the southern hemisphere sky (declinations less than +15 degrees). The observations were taken with a robotic camera operating at Cerro Tololo Inter-American Observatory (CTIO) in Chile.", "obs_copyright":"By courtesy of Swarthmore College Incorporated", "client_category":"Image/Gas-lines/Halpha", "client_sort_key":"06-02-03", "hips_creation_date":"2011-02-01T12:00Z", "hips_release_date":"2019-05-05T07:17Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"4", "hips_frame":"galactic", "hips_tile_width":"512", "hips_tile_format":"png jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"-200 5000", "moc_access_url":"http://alasky.u-strasbg.fr/SHASSA-FL/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"1.0921117184376416E-7", "hips_initial_ra":"45.0", "hips_initial_dec":"7.114779961355992E-8", "hips_copyright":"By courtesy of Swarthmore College Incorporated", "obs_ack":"the Southern H-Alpha Sky Survey Atlas (SHASSA), which is supported by the National Science Foundation", "prov_progenitor":"SHASSA", "bib_reference":"2001PASP..113.1326G", "bib_reference_url":"http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2001PASP..113.1326G&simbo=on", "obs_copyright_url":"http://amundsen.astro.swarthmore.edu/SHASSA/ack.html", "t_min":"50753", "t_max":"51847", "obs_regime":"Optical", "em_min":"6.56e-7", "em_max":"6.56e-7", "hips_pixel_scale":"0.007157", "moc_sky_fraction":"1", "hips_estsize":"4504376", "hips_order_min":"0", "hips_pixel_bitpix":"32", "hipsgen_date":"2019-05-05T07:17Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/SHASSA-v2/SHASSA-FL UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/SHASSA-FL", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/SHASSA-FL", "hips_status_1":"public mirror clonableOnce", "TIMESTAMP":"1721288418946"}, +{ "ID":"CDS/P/SHASSA/H", "creator_did":"ivo://CDS/P/SHASSA/H", "obs_collection":"SHASSA H", "obs_title":"SHASSA H - H-alpha emission", "obs_description":"The Southern H-Alpha Sky Survey Atlas is the product of a wide-angle digital imaging survey of the H-alpha emission from the warm ionized interstellar gas of our Galaxy. This atlas covers the southern hemisphere sky (declinations less than +15 degrees). The observations were taken with a robotic camera operating at Cerro Tololo Inter-American Observatory (CTIO) in Chile.", "obs_copyright":"By courtesy of Swarthmore College Incorporated", "obs_copyright_url":[ "http://amundsen.astro.swarthmore.edu/SHASSA/index.html", "http://amundsen.astro.swarthmore.edu/SHASSA/ack.html"], "client_category":"Image/Gas-lines/Halpha", "client_sort_key":"06-02-01", "hips_creation_date":"2010-12-13T12:00Z", "hips_release_date":"2019-05-05T07:18Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"4", "hips_frame":"galactic", "hips_tile_width":"512", "hips_tile_format":"png jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"-10 20000", "moc_access_url":"http://alasky.u-strasbg.fr/SHASSA-H3/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"1.0921117184376416E-7", "hips_initial_ra":"45.0", "hips_initial_dec":"7.114779961355992E-8", "hips_copyright":"CNRS/Unistra", "obs_ack":"the Southern H-Alpha Sky Survey Atlas (SHASSA), which is supported by the National Science Foundation", "prov_progenitor":"SHASSA", "bib_reference":"2001PASP..113.1326G", "bib_reference_url":"http://simbad.u-strasbg.fr/simbad/sim-ref?bibcode=2001PASP..113.1326G&simbo=on", "t_min":"50753", "t_max":"51847", "obs_regime":"Optical", "em_min":"6.56e-7", "em_max":"6.56e-7", "hips_pixel_scale":"0.007157", "moc_sky_fraction":"1", "hips_estsize":"4504376", "hips_order_min":"0", "hips_pixel_bitpix":"32", "hipsgen_date":"2019-05-05T07:18Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/SHASSA-v2/SHASSA-H3 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/SHASSA-H3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/SHASSA-H3", "hips_status_1":"public mirror clonableOnce", "TIMESTAMP":"1721288419002"}, +{ "ID":"CDS/P/SPITZER/MIPS1", "creator_did":"ivo://CDS/P/SPITZER/MIPS1", "obs_collection":"SPITZER MIPS1", "obs_title":"MIPS1 survey in Healpix", "obs_description":"Composite map from Spitzer Legacy Programs MIPSGAL: A 24 and 70 Micron Survey of the Inner Galactic Disk with MIPS (Carey S.) C2D: From Molecular Cores to Planet-Forming Disks (Evans N.) Taurus 2: Finishing the Spitzer Map of the Taurus Molecular Clouds (Padgett D.) SAGE: Spitzer Survey of the Large Magellanic Cloud: Surveying the Agents of a Galaxy's Evolution (Meixner M.) SAGE-SMC: Surveying the Agents of Galaxy Evolution in the Tidally- Disrupted, Low-Metallicity Small Magellanic Cloud (Gordon K.) SINGS: The Spitzer Infrared Nearby Galaxies Survey - Physics of the Star-Forming ISM and Galaxy Evolution (Kennicutt R.)", "obs_copyright":"Spitzer mission - JPL/NASA", "client_category":"Image/Infrared/Spitzer", "client_sort_key":"04-03-05", "hips_release_date":"2023-04-18T09:42Z", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"8", "hips_frame":"galactic", "hips_tile_width":"512", "hips_tile_format":"png jpeg fits", "dataproduct_type":"image", "moc_access_url":"http://alasky.unistra.fr/MIPS1/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"JPL/NASA", "bib_reference":[ "2009PASP..121...76C", "2003PASP..115..965E", "2006ApJ...645.1283P", "2006AJ....132.2268M", "2011AJ....142..102G", "2003PASP..115..928K"], "bib_reference_url":[ "http://adsabs.harvard.edu/abs/2009PASP..121...76C", "http://adsabs.harvard.edu/abs/2003PASP..115..965E", "http://adsabs.harvard.edu/abs/2006ApJ...645.1283P", "http://adsabs.harvard.edu/abs/2006AJ....132.2268M", "http://adsabs.harvard.edu/abs/2011AJ....142..102G", "http://adsabs.harvard.edu/abs/2003PASP..115..928K"], "obs_copyright_url":"https://www.jpl.nasa.gov/copyrights.php", "t_min":"52876", "t_max":"55195", "obs_regime":"Infrared", "em_min":"1.98889e-05", "em_max":"3.09383e-05", "hips_builder":"Aladin/HipsGen v12.044", "hips_creation_date":"2011-07-04T15:11Z", "hips_pixel_bitpix":"-32", "hips_pixel_scale":"0.229", "moc_sky_fraction":"1", "hips_pixel_cut":"0 55", "hipsgen_date":"2017-03-27T11:44Z", "hipsgen_params":"out=MIPS1 \"-pixelCut=0 55\" UPDATE", "hips_initial_fov":"0.2290324274544937", "hips_initial_ra":"45.0", "hips_initial_dec":"0.14920792779581243", "hips_order_min":"0", "hipsgen_date_1":"2019-05-05T07:21Z", "hipsgen_params_1":"out=/asd-volumes/sc1-asd-volume10/MIPS1 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/Spitzer/MIPS1", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/Spitzer/MIPS1", "hips_status_1":"public mirror clonableOnce", "TIMESTAMP":"1721288419546"}, +{ "ID":"CDS/P/WISE/W1", "creator_did":"ivo://CDS/P/WISE/W1", "obs_collection":"WISE W1", "obs_title":"WISE W1 (3.4um)", "obs_description":"Wide-field Infrared Survey Explore (WISE) is a MIDEX (medium class Explorer) mission funded by NASA. The WISE short-wavelength channels employ 4.2 and 5.4um cutoff HgCdTe arrays fabricated by Teledyne Imaging Sensors with 1024x1024 pixels each 18 um square. WISE W1 (3.4um) from raw Atlas Images (not background matched nor zodi-corrected). Resampled in Healpix by Frank Masci (IPAC). The spatial resolution is limited to 12 arcsec.", "obs_copyright":"University of Massachusetts & IPAC/Caltech", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allsky/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE/Low", "client_sort_key":"04-003-XX-01", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"5", "hips_frame":"galactic", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_status":"public master clonableOnce", "obs_ack":"This publication makes use of data products from the Wide-field Infrared Survey Explorer, which is a joint project of the University of California, Los Angeles, and the Jet Propulsion Laboratory/California Institute of Technology, funded by the National Aeronautics and Space Administration", "prov_progenitor":"IPAC/NASA", "bib_reference":"2010AJ....140.1868W", "bib_reference_url":"http://adsabs.harvard.edu/abs/2010AJ....140.1868W", "t_min":"55210", "t_max":"55530", "em_min":"2.754e-6", "em_max":"3.8723e-6", "hips_builder":"Aladin/HipsGen v10.123", "hips_release_date":"2019-05-07T11:10Z", "hips_pixel_bitpix":"-32", "hips_hierarchy":"mean", "hips_pixel_scale":"0.003579", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "hips_copyright":"CNRS/Unistra", "obs_regime":"Infrared", "moc_sky_fraction":"1", "hips_estsize":"17797289", "hips_creation_date":"2012-04-05T13:30Z", "hips_order_min":"0", "hipsgen_date":"2019-05-07T11:10Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume8/WISE/W1 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WISE/W1", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WISE/W1", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288420430"}, +{ "ID":"CDS/P/WISE/W2", "creator_did":"ivo://CDS/P/WISE/W2", "obs_collection":"WISE W2", "obs_title":"WISE W2 (4.6um)", "obs_description":"Wide-field Infrared Survey Explore (WISE) is a MIDEX (medium class Explorer) mission funded by NASA. The WISE short-wavelength channels employ 4.2 and 5.4um cutoff HgCdTe arrays fabricated by Teledyne Imaging Sensors with 1024x1024 pixels each 18 um square. WISE W2 (4.6um) from raw Atlas Images (not background matched nor zodi-corrected).Resampled in Healpix by Frank Masci (IPAC). The spatial resolution is limited to 12 arcsec.", "obs_copyright":"University of Massachusetts & IPAC/Caltech", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allsky/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE/Low", "client_sort_key":"04-003-XX-02", "hips_release_date":"2019-05-07T11:12Z", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"5", "hips_frame":"galactic", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_status":"public master clonableOnce", "obs_ack":"This publication makes use of data products from the Wide-field Infrared Survey Explorer, which is a joint project of the University of California, Los Angeles, and the Jet Propulsion Laboratory/California Institute of Technology, funded by the National Aeronautics and Space Administration", "prov_progenitor":"IPAC/NASA", "bib_reference":"2010AJ....140.1868W", "bib_reference_url":"http://adsabs.harvard.edu/abs/2010AJ....140.1868W", "t_min":"55210", "t_max":"55530", "em_min":"3.9633e-06", "em_max":"5.3413e-06", "hips_builder":"Aladin/HipsGen v10.123", "hips_creation_date":"2012-04-05T15:29Z", "hips_pixel_bitpix":"-32", "hips_hierarchy":"mean", "hips_pixel_scale":"0.003579", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "hips_copyright":"CNRS/Unistra", "obs_regime":"Infrared", "moc_sky_fraction":"1", "hips_estsize":"17797289", "hips_order_min":"0", "hipsgen_date":"2019-05-07T11:12Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume8/WISE/W2 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WISE/W2", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WISE/W2", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288420482"}, +{ "ID":"CDS/P/WISE/W3", "creator_did":"ivo://CDS/P/WISE/W3", "obs_collection":"WISE W3", "obs_title":"WISE W3 (12um)", "obs_description":"Wide-field Infrared Survey Explore (WISE) is a MIDEX (medium class Explorer) mission funded by NASA. The WISE short-wavelength channels employ 4.2 and 5.4um cutoff HgCdTe arrays fabricated by Teledyne Imaging Sensors with 1024x1024 pixels each 18 um square. WISE W3 (12um) from raw Atlas Images (not background matched nor zodi-corrected). Resampled in Healpix by Frank Masci (IPAC). The spatial resolution is limited to 12 arcsec.", "obs_copyright":"WISE acknowledgment", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allsky/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE/Low", "client_sort_key":"04-003-XX-03", "hips_release_date":"2019-05-07T11:14Z", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"5", "hips_frame":"galactic", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_status":"public master clonableOnce", "obs_ack":"This publication makes use of data products from the Wide-field Infrared Survey Explorer, which is a joint project of the University of California, Los Angeles, and the Jet Propulsion Laboratory/California Institute of Technology, funded by the National Aeronautics and Space Administration", "prov_progenitor":"IPAC/NASA", "bib_reference":"2010AJ....140.1868W", "bib_reference_url":"http://adsabs.harvard.edu/abs/2010AJ....140.1868W", "t_min":"55210", "t_max":"55530", "em_min":"7.443e-6", "em_max":"1.72613e-5", "hips_builder":"Aladin/HipsGen v10.123", "hips_creation_date":"2012-04-05T16:29Z", "hips_pixel_bitpix":"-32", "hips_hierarchy":"mean", "hips_pixel_scale":"0.003579", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "hips_copyright":"CNRS/Unistra", "obs_regime":"Infrared", "moc_sky_fraction":"1", "hips_estsize":"17797289", "hips_order_min":"0", "hipsgen_date":"2019-05-07T11:14Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume8/WISE/W3 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WISE/W3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WISE/W3", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288420534"}, +{ "ID":"CDS/P/WISE/W4", "creator_did":"ivo://CDS/P/WISE/W4", "obs_collection":"WISE W4", "obs_title":"WISE W4 (22um)", "obs_description":"Wide-field Infrared Survey Explore (WISE) is a MIDEX (medium class Explorer) mission funded by NASA. The WISE short-wavelength channels employ 4.2 and 5.4um cutoff HgCdTe arrays fabricated by Teledyne Imaging Sensors with 1024x1024 pixels each 18 um square. WISE W1 (3.4um) from raw Atlas Images (not background matched nor zodi-corrected). Resampled in Healpix by Frank Masci (IPAC). The spatial resolution is limited to 12 arcsec.", "obs_copyright":"WISE acknowledgment", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allsky/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE/Low", "client_sort_key":"04-003-XX-04", "hips_release_date":"2019-05-07T11:15Z", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"5", "hips_frame":"galactic", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_status":"public master clonableOnce", "obs_ack":"This publication makes use of data products from the Wide-field Infrared Survey Explorer, which is a joint project of the University of California, Los Angeles, and the Jet Propulsion Laboratory/California Institute of Technology, funded by the National Aeronautics and Space Administration", "prov_progenitor":"IPAC/NASA", "bib_reference":"2010AJ....140.1868W", "bib_reference_url":"http://adsabs.harvard.edu/abs/2010AJ....140.1868W", "t_min":"55210", "t_max":"55530", "em_min":"1.952e-5", "em_max":"2.79107e-5", "hips_builder":"Aladin/HipsGen v10.123", "hips_creation_date":"2012-04-10T06:02Z", "hips_pixel_bitpix":"-32", "hips_hierarchy":"mean", "hips_pixel_scale":"0.003579", "hips_initial_fov":"130.0", "hips_initial_ra":"266.4150089", "hips_initial_dec":"-29.0061110", "hips_copyright":"CNRS/Unistra", "obs_regime":"Infrared", "moc_sky_fraction":"1", "hips_estsize":"17797289", "hips_order_min":"0", "hipsgen_date":"2019-05-07T11:15Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume8/WISE/W4 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WISE/W4", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WISE/W4", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4150089", "obs_initial_dec":"-29.0061110", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288420590"}, +{ "ID":"CDS/P/WISE/WSSA/12um", "creator_did":"ivo://CDS/P/WISE/WSSA/12um", "obs_collection":"WISE WSSA 12um", "obs_title":"Diffuse dust 12um WSSA (Meisner & Finkbeiner 2013)", "obs_description":"Diffuse Galactic dust emission at 12um from the processing of the Wide-field Infrared Survey Explorer (WISE) data set by Meisner & Finkbeiner (2013). The 430 WISE Sky Survey Atlas (WSSA) tiles were resampled in Healpix by Thomas Boch (CDS). The spatial resolution is limited to 15 arcsec.", "obs_copyright":"Meisner & Finkbeiner (2013)", "obs_copyright_url":"http://faun.rc.fas.harvard.edu/ameisner/wssa/", "client_category":"Image/Infrared/WISE/WSSA", "client_sort_key":"04-003-01-01", "hips_release_date":"2019-05-05T07:49Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"CDS", "hips_version":"1.4", "hips_order":"7", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/WSSA/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"1.0921117184376416E-7", "hips_initial_ra":"0", "hips_initial_dec":"+0", "hips_copyright":"CNRS/Unistra", "obs_ack":"see 2014ApJ...781....5M", "prov_progenitor":"Meisner & Finkbeiner (2013)", "bib_reference":"2014ApJ...781....5M", "bib_reference_url":"https://ui.adsabs.harvard.edu/?#abs/2014ApJ...781....5M", "t_min":"55210", "t_max":"55530", "obs_regime":"Infrared", "em_min":"1.2e-5", "em_max":"1.2e-5", "hips_pixel_scale":"8.946E-4", "moc_sky_fraction":"1", "hips_estsize":"284756513", "hips_creation_date":"2014-04-17T22:08Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T07:49Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/WSSA UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WSSA", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WSSA", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"7", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.4580648549089874", "TIMESTAMP":"1721288420658"}, +{ "ID":"CDS/P/WMAP/K/9yr", "creator_did":"ivo://CDS/P/WMAP/K/9yr", "obs_collection":"WMAP K 9yr", "obs_title":"WMAP K - 9yr", "obs_description":"The WMAP (Wilkinson Microwave Anisotropy Probe) mission is designed to determine the geometry, content, and evolution of the universe.The K-band is centered at 13 mm (23 GHz), its beam size is 0.88 deg (square-root of the beam solid angle).", "obs_copyright_url":"http://lambda.gsfc.nasa.gov/product/map/dr5/maps_band_r9_i_9yr_get.cfm", "prov_progenitor":"HEASARC/LAMBDA", "client_category":"Image/Radio/WMAP", "client_sort_key":"05-03-05", "hips_release_date":"2019-05-05T07:50Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"0 10", "hips_data_range":"-2 200", "moc_access_url":"http://alasky.u-strasbg.fr/WMAP9yr/WMAPK9yr/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"0.2290324274544937", "hips_initial_ra":"0", "hips_initial_dec":"+0", "hips_copyright":"CNRS/Unistra", "obs_ack":"We acknowledge the use of the Legacy Archive for Microwave Background Data Analysis (LAMBDA), part of the High Energy Astrophysics Science Archive Center (HEASARC). HEASARC/LAMBDA is a service of the Astrophysics Science Division at the NASA Goddard Space Flight Center.\"", "bib_reference":"2013ApJS..208...20B", "bib_reference_url":"http://adsabs.harvard.edu/abs/2013ApJS..208...20B", "obs_copyright":"HEASARC/LAMBDA", "t_min":"52131", "t_max":"55427", "obs_regime":"Radio", "em_min":"1.17e-2", "em_max":"1.57e-2", "hips_tile_width":"64", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1126099", "hips_creation_date":"2014-11-25T18:10Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T07:50Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/WMAP9yr/WMAPK9yr UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WMAP9yr/WMAPK9yr", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WMAP9yr/WMAPK9yr", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288420714"}, +{ "ID":"CDS/P/WMAP/Ka/9yr", "creator_did":"ivo://CDS/P/WMAP/Ka/9yr", "obs_collection":"WMAP Ka 9yr", "obs_title":"WMAP Ka - 9yr", "obs_description":"The WMAP (Wilkinson Microwave Anisotropy Probe) mission is designed to determine the geometry, content, and evolution of the universe.The Ka-band is centered at 9.1 mm (33 GHz), its beam size is 0.66 deg (square-root of the beam solid angle).", "obs_copyright_url":"http://lambda.gsfc.nasa.gov/product/map/dr5/maps_band_r9_i_9yr_get.cfm", "prov_progenitor":"HEASARC/LAMBDA", "client_category":"Image/Radio/WMAP", "client_sort_key":"05-03-04", "hips_release_date":"2019-05-05T07:52Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"0 10", "hips_data_range":"-2 200", "moc_access_url":"http://alasky.u-strasbg.fr/WMAP9yr/WMAPKa9yr/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"0.2290324274544937", "hips_initial_ra":"0", "hips_initial_dec":"+0", "hips_copyright":"CNRS/Unistra", "obs_ack":"We acknowledge the use of the Legacy Archive for Microwave Background Data Analysis (LAMBDA), part of the High Energy Astrophysics Science Archive Center (HEASARC). HEASARC/LAMBDA is a service of the Astrophysics Science Division at the NASA Goddard Space Flight Center.\"", "bib_reference":"2013ApJS..208...20B", "bib_reference_url":"http://adsabs.harvard.edu/abs/2013ApJS..208...20B", "obs_copyright":"HEASARC/LAMBDA", "t_min":"52131", "t_max":"55427", "obs_regime":"Radio", "em_min":"7.94e-3", "em_max":"1.04e-2", "hips_tile_width":"64", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1126099", "hips_creation_date":"2014-11-25T18:12Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T07:52Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/WMAP9yr/WMAPKa9yr UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WMAP9yr/WMAPKa9yr", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WMAP9yr/WMAPKa9yr", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288420798"}, +{ "ID":"CDS/P/WMAP/Q/9yr", "creator_did":"ivo://CDS/P/WMAP/Q/9yr", "obs_collection":"WMAP Q 9yr", "obs_title":"WMAP Q - 9yr", "obs_description":"The WMAP (Wilkinson Microwave Anisotropy Probe) mission is designed to determine the geometry, content, and evolution of the universe.The Q-band is centered at 7.3 mm (41 GHz), its beam size is 0.51 deg (square-root of the beam solid angle).", "obs_copyright_url":"http://lambda.gsfc.nasa.gov/product/map/dr5/maps_band_r9_i_9yr_get.cfm", "prov_progenitor":"HEASARC/LAMBDA", "client_category":"Image/Radio/WMAP", "client_sort_key":"05-03-03", "hips_release_date":"2019-05-05T07:52Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"0 10", "hips_data_range":"-2 200", "moc_access_url":"http://alasky.u-strasbg.fr/WMAP9yr/WMAPQ9yr/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"0.2290324274544937", "hips_initial_ra":"0", "hips_initial_dec":"+0", "hips_copyright":"CNRS/Unistra", "obs_ack":"We acknowledge the use of the Legacy Archive for Microwave Background Data Analysis (LAMBDA), part of the High Energy Astrophysics Science Archive Center (HEASARC). HEASARC/LAMBDA is a service of the Astrophysics Science Division at the NASA Goddard Space Flight Center.\"", "bib_reference":"2013ApJS..208...20B", "bib_reference_url":"http://adsabs.harvard.edu/abs/2013ApJS..208...20B", "obs_copyright":"HEASARC/LAMBDA", "t_min":"52131", "t_max":"55427", "obs_regime":"Radio", "em_min":"6.43e-3", "em_max":"8.61e-3", "hips_tile_width":"64", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1126099", "hips_creation_date":"2014-11-25T18:14Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T07:52Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/WMAP9yr/WMAPQ9yr UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WMAP9yr/WMAPQ9yr", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WMAP9yr/WMAPQ9yr", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288420854"}, +{ "ID":"CDS/P/WMAP/V/9yr", "creator_did":"ivo://CDS/P/WMAP/V/9yr", "obs_collection":"WMAP V 9yr", "obs_title":"WMAP V - 9yr", "obs_description":"The WMAP (Wilkinson Microwave Anisotropy Probe) mission is designed to determine the geometry, content, and evolution of the universe.The V-band is centered at 4.9 mm (61 GHz), its beam size is 0.35 deg (square-root of the beam solid angle).", "obs_copyright_url":"http://lambda.gsfc.nasa.gov/product/map/dr5/maps_band_r9_i_9yr_get.cfm", "prov_progenitor":"HEASARC/LAMBDA", "client_category":"Image/Radio/WMAP", "client_sort_key":"05-03-02", "hips_release_date":"2019-05-05T07:53Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"3", "hips_tile_format":"png jpeg fits", "dataproduct_type":"image", "moc_access_url":"http://alasky.u-strasbg.fr/WMAP9yr/WMAPV9yr/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"0.2290324274544937", "hips_initial_ra":"0", "hips_initial_dec":"+0", "hips_copyright":"CNRS/Unistra", "obs_ack":"We acknowledge the use of the Legacy Archive for Microwave Background Data Analysis (LAMBDA), part of the High Energy Astrophysics Science Archive Center (HEASARC). HEASARC/LAMBDA is a service of the Astrophysics Science Division at the NASA Goddard Space Flight Center.\"", "bib_reference":"2013ApJS..208...20B", "bib_reference_url":"http://adsabs.harvard.edu/abs/2013ApJS..208...20B", "obs_copyright":"HEASARC/LAMBDA", "t_min":"52131", "t_max":"55427", "obs_regime":"Radio", "em_min":"4.31e-3", "em_max":"5.73e-3", "hips_frame":"galactic", "hips_tile_width":"64", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1126099", "hips_creation_date":"2014-11-25T18:15Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T07:53Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/WMAP9yr/WMAPV9yr UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WMAP9yr/WMAPV9yr", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WMAP9yr/WMAPV9yr", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288420906"}, +{ "ID":"CDS/P/WMAP/W/9yr", "creator_did":"ivo://CDS/P/WMAP/W/9yr", "obs_collection":"WMAP W 9yr", "obs_title":"WMAP W - 9yr", "obs_description":"The WMAP (Wilkinson Microwave Anisotropy Probe) mission is designed to determine the geometry, content, and evolution of the universe.The W-band is centered at 3.2 mm (94 GHz), its beam size is 0.22 deg (square-root of the beam solid angle).", "obs_copyright_url":"http://lambda.gsfc.nasa.gov/product/map/dr5/maps_band_r9_i_9yr_get.cfm", "prov_progenitor":"HEASARC/LAMBDA", "client_category":"Image/Radio/WMAP", "client_sort_key":"05-03-01", "hips_release_date":"2019-05-05T07:53Z", "hips_builder":"Aladin/HipsGen v10.123", "hips_creator":"Fernique P. (CDS)", "hips_version":"1.4", "hips_order":"3", "hips_frame":"galactic", "hips_tile_format":"png jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"0 10", "hips_data_range":"-2 200", "moc_access_url":"http://alasky.u-strasbg.fr/WMAP9yr/WMAPW9yr/Moc.fits", "hips_status":"public master clonableOnce", "hips_initial_fov":"0.2290324274544937", "hips_initial_ra":"0", "hips_initial_dec":"+0", "hips_copyright":"CNRS/Unistra", "obs_ack":"We acknowledge the use of the Legacy Archive for Microwave Background Data Analysis (LAMBDA), part of the High Energy Astrophysics Science Archive Center (HEASARC). HEASARC/LAMBDA is a service of the Astrophysics Science Division at the NASA Goddard Space Flight Center.\"", "bib_reference":"2013ApJS..208...20B", "bib_reference_url":"http://adsabs.harvard.edu/abs/2013ApJS..208...20B", "obs_copyright":"HEASARC/LAMBDA", "t_min":"52131", "t_max":"55427", "obs_regime":"Radio", "em_min":"2.78e-3", "em_max":"3.71e-3", "hips_tile_width":"64", "hips_pixel_scale":"0.01431", "moc_sky_fraction":"1", "hips_estsize":"1126099", "hips_creation_date":"2014-11-25T18:16Z", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "hipsgen_date":"2019-05-05T07:53Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume10/WMAP9yr/WMAPW9yr UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/WMAP9yr/WMAPW9yr", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/WMAP9yr/WMAPW9yr", "hips_status_1":"public mirror clonableOnce", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"0", "obs_initial_dec":"+0", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288420958"}, +{ "ID":"CDS/P/allWISE/W1", "hips_doi":"10.26093/cds/aladin/32ax-d9f", "creator_did":"ivo://CDS/P/allWISE/W1", "obs_collection":"The Wide-field Infrared Survey Explorer - W1 band (allWISE W1)", "obs_title":"AllWISE W1 (3.4um) from raw Atlas Images", "obs_description":"NASA's Wide-field Infrared Survey Explorer (WISE; Wright et al.2010) mapped the sky at 3.4, 4.6, 12, and 22 um (W1, W2, W3, W4) in 2010 with an angular resolution of 6.1\", 6.4\", 6.5\", & 12.0\" in the four bands. WISE achieved 5 sigma point source sensitivities better than 0.08, 0.11, 1 and 6 mJy in unconfused regions on the ecliptic in the four bands. Sensitivity improves toward the ecliptic poles due to denser coverage and lower zodiacal background.The All-Sky Release includes all data taken during the WISE full cryogenic mission phase, 7 January 2010 to 6 August 2010, that were processed with improved calibrations and reduction algorithms.", "obs_ack":"This Progressive Survey distribution makes use of data products from the Wide-field Infrared Survey Explorer, which is a joint project of the University of California, Los Angeles, and the Jet Propulsion Laboratory/California Institute of Technology, and NEOWISE, which is a project of the Jet Propulsion Laboratory/California Institute of Technology.WISE and NEOWISE are funded by the National Aeronautics and Space Administration.", "obs_copyright":"IPAC/NASA", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allsky/", "client_category":"Image/Infrared/WISE", "client_sort_key":"04-003-01", "hips_creation_date":"2014-04-15T08:59Z", "hips_release_date":"2019-05-20T08:06Z", "hips_builder":"Aladin/HipsGen v10.125", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"8", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"0 400", "hips_data_range":"4.296 2345", "moc_access_url":"http://alasky.u-strasbg.fr/AllWISE/W1/Moc.fits", "hips_progenitor_url":"https://alasky.cds.unistra.fr/AllWISE/W1/HpxFinder", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"IPAC/NASA", "bib_reference":"2010AJ....140.1868W", "bib_reference_url":"http://adsabs.harvard.edu/abs/2010AJ....140.1868W", "t_min":"55378", "t_max":"55414", "obs_regime":"Infrared", "em_min":"2.754e-6", "em_max":"3.8723e-6", "hips_hierarchy":"mean", "hips_pixel_scale":"4.473E-4", "hips_initial_fov":"140", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1148421127", "hipsgen_date":"2019-05-20T08:06Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume6/AllWISE/W1 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AllWISE/W1", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AllWISE/W1", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/AllWISE/W1", "hips_status_2":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288573526"}, +{ "ID":"CDS/P/allWISE/W2", "hips_doi":"10.26093/cds/aladin/1etf-s4n", "creator_did":"ivo://CDS/P/allWISE/W2", "obs_collection":"The Wide-field Infrared Survey Explorer - W2 band (allWISE W2)", "obs_title":"AllWISE W2 (4.6um) from raw Atlas Images", "obs_description":"NASA's Wide-field Infrared Survey Explorer (WISE; Wright et al.2010) mapped the sky at 3.4, 4.6, 12, and 22 um (W1, W2, W3, W4) in 2010 with an angular resolution of 6.1\", 6.4\", 6.5\", & 12.0\" in the four bands. WISE achieved 5 sigma point source sensitivities better than 0.08, 0.11, 1 and 6 mJy in unconfused regions on the ecliptic in the four bands. Sensitivity improves toward the ecliptic poles due to denser coverage and lower zodiacal background.The All-Sky Release includes all data taken during the WISE full cryogenic mission phase, 7 January 2010 to 6 August 2010, that were processed with improved calibrations and reduction algorithms.", "obs_ack":"This Progressive Survey distribution makes use of data products from the Wide-field Infrared Survey Explorer, which is a joint project of the University of California, Los Angeles, and the Jet Propulsion Laboratory/California Institute of Technology, and NEOWISE, which is a project of the Jet Propulsion Laboratory/California Institute of Technology.WISE and NEOWISE are funded by the National Aeronautics and Space Administration.", "obs_copyright":"IPAC/NASA", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allwise/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE", "client_sort_key":"04-003-02", "hips_release_date":"2019-05-20T08:16Z", "hips_builder":"Aladin/HipsGen v10.125", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"8", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"0 100", "hips_data_range":"7.619 143.8", "moc_access_url":"http://alasky.u-strasbg.fr/AllWISE/W2/Moc.fits", "hips_progenitor_url":"https://alasky.cds.unistra.fr/AllWISE/W2/HpxFinder", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"IPAC/NASA", "bib_reference":"2010AJ....140.1868W", "bib_reference_url":"http://adsabs.harvard.edu/abs/2010AJ....140.1868W", "t_min":"55378", "t_max":"55414", "obs_regime":"Infrared", "em_min":"3.9633e-6", "em_max":"5.3413e-6", "hips_creation_date":"2014-04-15T09:19Z", "hips_hierarchy":"mean", "hips_pixel_scale":"4.473E-4", "hips_initial_fov":"140", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1148421127", "hipsgen_date":"2019-05-20T08:16Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume6/AllWISE/W2 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AllWISE/W2", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AllWISE/W2", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/AllWISE/W2", "hips_status_2":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288574806"}, +{ "ID":"CDS/P/allWISE/W3", "hips_doi":"10.26093/cds/aladin/na1n-03", "creator_did":"ivo://CDS/P/allWISE/W3", "obs_collection":"The Wide-field Infrared Survey Explorer - W3 band (allWISE W3)", "obs_title":"AllWISE W3 (12um) from raw Atlas Images", "obs_description":"NASA's Wide-field Infrared Survey Explorer (WISE; Wright et al.2010) mapped the sky at 3.4, 4.6, 12, and 22 um (W1, W2, W3, W4) in 2010 with an angular resolution of 6.1\", 6.4\", 6.5\", & 12.0\" in the four bands. WISE achieved 5 sigma point source sensitivities better than 0.08, 0.11, 1 and 6 mJy in unconfused regions on the ecliptic in the four bands. Sensitivity improves toward the ecliptic poles due to denser coverage and lower zodiacal background.The All-Sky Release includes all data taken during the WISE full cryogenic mission phase, 7 January 2010 to 6 August 2010, that were processed with improved calibrations and reduction algorithms.", "obs_ack":"This Progressive Survey distribution makes use of data products from the Wide-field Infrared Survey Explorer, which is a joint project of the University of California, Los Angeles, and the Jet Propulsion Laboratory/California Institute of Technology, and NEOWISE, which is a project of the Jet Propulsion Laboratory/California Institute of Technology.WISE and NEOWISE are funded by the National Aeronautics and Space Administration.", "obs_copyright":"IPAC/NASA", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allwise/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE", "client_sort_key":"04-003-03", "hips_release_date":"2019-05-20T08:23Z", "hips_builder":"Aladin/HipsGen v10.125", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"8", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"260 1000", "hips_data_range":"269.6 477.3", "moc_access_url":"http://alasky.u-strasbg.fr/AllWISE/W3/Moc.fits", "hips_progenitor_url":"https://alasky.cds.unistra.fr/AllWISE/W3/HpxFinder", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"IPAC/NASA", "bib_reference":"2010AJ....140.1868W", "bib_reference_url":"http://adsabs.harvard.edu/abs/2010AJ....140.1868W", "t_min":"55378", "t_max":"55414", "obs_regime":"Infrared", "em_min":"7.443e-6", "em_max":"1.72613e-5", "hips_creation_date":"2014-04-15T09:25Z", "hips_hierarchy":"mean", "hips_pixel_scale":"4.473E-4", "hips_initial_fov":"140", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"0.9999", "hips_estsize":"1148421127", "hipsgen_date":"2019-05-20T08:23Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume6/AllWISE/W3 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AllWISE/W3", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AllWISE/W3", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/AllWISE/W3", "hips_status_2":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288576082"}, +{ "ID":"CDS/P/allWISE/W4", "hips_doi":"10.26093/cds/aladin/2tc8-gd0", "creator_did":"ivo://CDS/P/allWISE/W4", "obs_collection":"The Wide-field Infrared Survey Explorer - W4 band (allWISE W4)", "obs_title":"AllWISE W4 (22um) from raw Atlas Images", "obs_description":"NASA's Wide-field Infrared Survey Explorer (WISE; Wright et al.2010) mapped the sky at 3.4, 4.6, 12, and 22 um (W1, W2, W3, W4) in 2010 with an angular resolution of 6.1\", 6.4\", 6.5\", & 12.0\" in the four bands. WISE achieved 5 sigma point source sensitivities better than 0.08, 0.11, 1 and 6 mJy in unconfused regions on the ecliptic in the four bands. Sensitivity improves toward the ecliptic poles due to denser coverage and lower zodiacal background.The All-Sky Release includes all data taken during the WISE full cryogenic mission phase, 7 January 2010 to 6 August 2010, that were processed with improved calibrations and reduction algorithms.", "obs_ack":"This Progressive Survey distribution makes use of data products from the Wide-field Infrared Survey Explorer, which is a joint project of the University of California, Los Angeles, and the Jet Propulsion Laboratory/California Institute of Technology, and NEOWISE, which is a project of the Jet Propulsion Laboratory/California Institute of Technology.WISE and NEOWISE are funded by the National Aeronautics and Space Administration.", "obs_copyright":"IPAC/NASA", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allwise/expsup/sec1_6b.html", "client_category":"Image/Infrared/WISE", "client_sort_key":"04-003-04", "hips_release_date":"2019-05-20T08:28Z", "hips_builder":"Aladin/HipsGen v10.125", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"8", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg fits", "dataproduct_type":"image", "hips_pixel_cut":"100 200", "hips_data_range":"102.5 103.1", "moc_access_url":"http://alasky.u-strasbg.fr/AllWISE/W4/Moc.fits", "hips_progenitor_url":"https://alasky.cds.unistra.fr/AllWISE/W4/HpxFinder", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"IPAC/NASA", "bib_reference":"2010AJ....140.1868W", "bib_reference_url":"http://adsabs.harvard.edu/abs/2010AJ....140.1868W", "t_min":"55378", "t_max":"55414", "obs_regime":"Infrared", "em_min":"1.952e-5", "em_max":"2.79107e-5", "hips_creation_date":"2014-04-15T09:29Z", "hips_hierarchy":"mean", "hips_pixel_scale":"4.473E-4", "hips_initial_fov":"140", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "hips_order_min":"0", "hips_pixel_bitpix":"-32", "moc_sky_fraction":"1", "hips_estsize":"1148421127", "hipsgen_date":"2019-05-20T08:28Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume6/AllWISE/W4 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AllWISE/W4", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AllWISE/W4", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/AllWISE/W4", "hips_status_2":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288577374"}, +{ "ID":"CDS/P/allWISE/color", "hips_doi":"10.26093/cds/aladin/35rf-zj", "creator_did":"ivo://CDS/P/allWISE/color", "obs_collection":"The Wide-field Infrared Survey Explorer - W4-W2-W1 bands (allWISE color RGB-W4-W2-W1)", "obs_title":"AllWISE color Red (W4) , Green (W2) , Blue (W1) from raw Atlas Images", "obs_description":"NASA's Wide-field Infrared Survey Explorer (WISE; Wright et al. 2010) mapped the sky at 3.4, 4.6, 12, and 22 um (W1, W2, W3, W4) in 2010 with an angular resolution of 6.1\", 6.4\", 6.5\", & 12.0\" in the four bands. WISE achieved 5\\u03c3 point source sensitivities better than 0.08, 0.11, 1 and 6 mJy in unconfused regions on the ecliptic in the four bands. Sensitivity improves toward the ecliptic poles due to denser coverage and lower zodiacal background. The All-Sky Release includes all data taken during the WISE full cryogenic mission phase, 7 January 2010 to 6 August 2010, that were processed with improved calibrations and reduction algorithms.", "obs_ack":"This Progressive Survey distribution makes use of data products from the Wide-field Infrared Survey Explorer, which is a joint project of the University of California, Los Angeles, and the Jet Propulsion Laboratory/California Institute of Technology, and NEOWISE, which is a project of the Jet Propulsion Laboratory/California Institute of Technology. WISE and NEOWISE are funded by the National Aeronautics and Space Administration.", "obs_copyright":"IPAC/NASA", "obs_copyright_url":"http://wise2.ipac.caltech.edu/docs/release/allsky/", "client_application":[ "AladinLite", "AladinDesktop"], "client_category":"Image/Infrared/WISE", "client_sort_key":"04-003-00", "hips_creation_date":"2014-04-15T08:59Z", "hips_release_date":"2019-05-20T08:30Z", "hips_builder":"Aladin/HipsGen v10.125", "hips_creator":"Boch T. (CDS)", "hips_version":"1.4", "hips_order":"8", "hips_frame":"equatorial", "hips_tile_width":"512", "hips_tile_format":"jpeg", "dataproduct_type":"image", "hips_pixel_cut":"0 3", "hips_rgb_red":"w4 [102.0 151.0 200.0 Log]", "hips_rgb_green":"w2 [0.0 80.0 160.0 Log]", "hips_rgb_blue":"w1 [0.0 200.0 400.0 Log]", "moc_access_url":"http://alasky.u-strasbg.fr/AllWISE/RGB-W4-W2-W1/Moc.fits", "hips_status":"public master clonableOnce", "hips_copyright":"CNRS/Unistra", "prov_progenitor":"IPAC/NASA - healpixed by CDS", "bib_reference":"2010AJ....140.1868W", "bib_reference_url":"http://adsabs.harvard.edu/abs/2010AJ....140.1868W", "t_min":"55378", "t_max":"55414", "obs_regime":"Infrared", "em_min":"2.754e-6", "em_max":"2.79107e-5", "hips_hierarchy":"mean", "hips_pixel_scale":"4.473E-4", "hips_initial_fov":"12.0", "hips_initial_ra":"161.1024001", "hips_initial_dec":"-59.6638730", "hips_order_min":"0", "dataproduct_subtype":"color", "moc_sky_fraction":"1", "hips_estsize":"18790203", "hipsgen_date":"2019-05-20T08:30Z", "hipsgen_params":"out=/asd-volumes/sc1-asd-volume6/AllWISE/RGB-W4-W2-W1 UPDATE", "hips_service_url":"https://alasky.cds.unistra.fr/AllWISE/RGB-W4-W2-W1", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/AllWISE/RGB-W4-W2-W1", "hips_status_1":"public mirror clonableOnce", "hips_service_url_2":"https://irsa.ipac.caltech.edu/data/hips/CDS/AllWISE/RGB-W4-W2-W1", "hips_status_2":"public mirror unclonable", "hips_service_url_3":"https://healpix.ias.u-psud.fr/CDS_P_allWISE_color", "hips_status_3":"public mirror unclonable", "hips_service_url_4":"http://skies.esac.esa.int/AllWISEColor", "hips_status_4":"public mirror unclonable", "moc_type":"stmoc", "moc_time_order":"25", "moc_time_range":"1", "moc_order":"8", "obs_initial_ra":"161.1024001", "obs_initial_dec":"-59.6638730", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288899888"}, +{ "ID":"CDS/P/unWISE/W1", "hips_initial_fov":"60.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "creator_did":"ivo://CDS/P/unWISE/W1", "client_category":"Image/Infrared/WISE/unWISE", "hips_pixel_bitpix":"-32", "data_pixel_bitpix":"-32", "hips_sampling":"bilinear", "hips_overlay":"mean", "hips_hierarchy":"median", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Universite de Strasbourg", "obs_title":"unWISE W1 (3.4um)", "obs_collection":"unWISE", "obs_description":"unWISE: unofficial, unblurred coadds of the WISE imaging, that combine nine years of Wide-field Infrared Survey Explorer (WISE) and NEOWISE exposures, resulting in the deepest ever 3-5 micrometer full-sky maps.", "prov_progenitor":"https://portal.nersc.gov/project/cosmo/data/unwise/neo8/unwise-coadds/", "bib_reference":"2022RNAAS...6..188M", "obs_copyright":"IPAC/NASA - D. Lang for the reprocessing", "obs_regime":"Infrared", "em_min":"2.754e-6", "em_max":"3.8723e-6", "hips_builder":"Aladin/HipsGen v12.019", "hips_version":"1.4", "hips_release_date":"2022-12-13T14:16Z", "hips_frame":"equatorial", "hips_order":"8", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_cut":"-5 9400", "hips_data_range":"-36096 108229", "hips_pixel_scale":"0.229", "s_pixel_scale":"7.638E-4", "dataproduct_type":"image", "hipsgen_date":"2022-12-12T09:52Z", "hipsgen_params":"in=org-data/W1 out=hips/W1 creator_did=CDS/P/unWISE/W1 hips_frame=equatorial TILES", "hips_creation_date":"2019-12-17T12:05Z", "hipsgen_date_1":"2022-12-12T13:23Z", "hipsgen_params_1":"out=hips/W1 creator_did=CDS/P/unWISE/W1 hips_frame=equatorial \"hips_pixel_cut=-5 9400 log\" JPEG", "hips_estsize":"1079386335", "hips_nb_tiles":"1048573", "hips_check_code":"jpeg:0 fits:4098166269", "hipsgen_date_2":"2022-12-13T07:56Z", "hipsgen_params_2":"out=hips/W1 CHECKCODE", "hipsgen_date_3":"2022-12-13T14:16Z", "hipsgen_params_3":"out=hips/W1 CHECKCODE -f", "hips_service_url":"https://alasky.cds.unistra.fr/unWISE/W1", "hips_progenitor_url":"https://alasky.cds.unistra.fr/unWISE/W1/HpxFinder", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/unWISE/W1", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_sky_fraction":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288485942"}, +{ "ID":"CDS/P/unWISE/W2", "hips_initial_fov":"60.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "creator_did":"ivo://CDS/P/unWISE/W2", "client_category":"Image/Infrared/WISE/unWISE", "hips_pixel_bitpix":"-32", "data_pixel_bitpix":"-32", "hips_sampling":"bilinear", "hips_overlay":"mean", "hips_hierarchy":"median", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Universite de Strasbourg", "obs_title":"unWISE W2 (4.6um)", "obs_collection":"unWISE", "obs_description":"unWISE: unofficial, unblurred coadds of the WISE imaging, that combine nine years of Wide-field Infrared Survey Explorer (WISE) and NEOWISE exposures, resulting in the deepest ever 3-5 micrometer full-sky maps.", "prov_progenitor":"https://portal.nersc.gov/project/cosmo/data/unwise/neo8/unwise-coadds/", "bib_reference":"2022RNAAS...6..188M", "obs_copyright":"IPAC/NASA - D. Lang for the reprocessing", "obs_regime":"Infrared", "em_min":"3.9633e-6", "em_max":"5.3413e-6", "hips_builder":"Aladin/HipsGen v12.001", "hips_version":"1.4", "hips_release_date":"2022-12-13T08:52Z", "hips_frame":"equatorial", "hips_order":"8", "hips_order_min":"0", "hips_tile_width":"512", "hips_status":"public master clonableOnce", "hips_tile_format":"jpeg fits", "hips_pixel_cut":"-5 9400", "hips_data_range":"-82669 247939", "hips_pixel_scale":"0.229", "s_pixel_scale":"7.638E-4", "dataproduct_type":"image", "hipsgen_date":"2022-12-08T17:36Z", "hipsgen_params":"in=org-data/W2 out=hips/W2 creator_did=CDS/P/unWISE/W2/neo8 hips_frame=equatorial TILES", "hips_creation_date":"2019-12-17T12:05Z", "hipsgen_date_1":"2022-12-09T12:33Z", "hipsgen_params_1":"out=hips/W2 creator_did=CDS/P/unWISE/W2/neo8 hips_frame=equatorial \"hips_pixel_cut=-5 9400 log\" JPEG", "hips_estsize":"1079386335", "hips_nb_tiles":"1048573", "hips_check_code":"jpeg:0 fits:4098166269", "hipsgen_date_2":"2022-12-13T08:52Z", "hipsgen_params_2":"out=hips/W2 CHECKCODE", "hips_service_url":"https://alasky.cds.unistra.fr/unWISE/W2", "hips_progenitor_url":"https://alasky.cds.unistra.fr/unWISE/W2/HpxFinder", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/unWISE/W2", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_sky_fraction":"1", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288485994"}, +{ "ID":"CDS/P/unWISE/color-W2-W1W2-W1", "hips_initial_fov":"60.0", "hips_initial_ra":"266.4168166", "hips_initial_dec":"-29.0078249", "creator_did":"ivo://CDS/P/unWISE/color-W2-W1W2-W1", "client_category":"Image/Infrared/WISE/unWISE", "hips_pixel_bitpix":"-32", "data_pixel_bitpix":"-32", "hips_sampling":"bilinear", "hips_overlay":"mean", "hips_hierarchy":"median", "hips_creator":"Boch T. (CDS)", "hips_copyright":"CNRS/Universite de Strasbourg", "obs_title":"unWISE color, from W2 and W1 bands", "obs_collection":"unWISE", "obs_description":"unWISE: unofficial, unblurred coadds of the WISE imaging, that combine nine years of Wide-field Infrared Survey Explorer (WISE) and NEOWISE exposures, resulting in the deepest ever 3-5 micrometer full-sky maps.", "prov_progenitor":"http://unwise.me/", "bib_reference":"2022RNAAS...6..188M", "obs_copyright":"IPAC/NASA - D. Lang for the reprocessing", "obs_regime":"Infrared", "em_min":"2.754e-6", "em_max":"5.3413e-6", "hips_builder":"Aladin/HipsGen v11.025", "hips_version":"1.4", "hips_release_date":"2022-12-12T11:02Z", "hips_frame":"equatorial", "hips_order":"8", "hips_tile_width":"512", "hips_tile_format":"jpeg", "hips_status":"public master clonableOnce", "hips_pixel_scale":"4.473E-4", "dataproduct_type":"image", "moc_sky_fraction":"1", "hips_estsize":"18790203", "hips_creation_date":"2019-12-18T13:22Z", "hips_order_min":"0", "dataproduct_subtype":"color", "hips_service_url":"https://alasky.cds.unistra.fr/unWISE/color-W2-W1W2-W1", "hips_service_url_1":"https://alaskybis.cds.unistra.fr/unWISE/color-W2-W1W2-W1", "hips_status_1":"public mirror clonableOnce", "moc_type":"smoc", "moc_order":"8", "obs_initial_ra":"266.4168166", "obs_initial_dec":"-29.0078249", "obs_initial_fov":"0.2290324274544937", "TIMESTAMP":"1721288486050"} +] diff --git a/api/src/test/kotlin/APITest.kt b/api/src/test/kotlin/APITest.kt deleted file mode 100644 index 0166af1d2..000000000 --- a/api/src/test/kotlin/APITest.kt +++ /dev/null @@ -1,219 +0,0 @@ -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.kotlinModule -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.booleans.shouldBeTrue -import kotlinx.coroutines.delay -import nebulosa.api.autofocus.AutoFocusRequest -import nebulosa.api.beans.converters.time.DurationSerializer -import nebulosa.api.cameras.CameraStartCaptureRequest -import nebulosa.api.connection.ConnectionType -import nebulosa.api.stardetector.StarDetectionRequest -import nebulosa.common.json.PathSerializer -import nebulosa.test.AbstractFitsAndXisfTest.Companion.HTTP_CLIENT -import nebulosa.test.NonGitHubOnlyCondition -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.Request -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.toRequestBody -import java.nio.file.Path -import java.time.Duration - -@EnabledIf(NonGitHubOnlyCondition::class) -class APITest : StringSpec() { - - init { - // GENERAL. - - "Connect" { connect() } - "Disconnect" { disconnect() } - - // CAMERA. - - "Cameras" { cameras() } - "Camera Connect" { cameraConnect() } - "Camera" { camera() } - "Camera Capture Start" { cameraStartCapture() } - "Camera Capture Stop" { cameraStopCapture() } - "Camera Disconnect" { cameraDisconnect() } - - // MOUNT. - - "Mounts" { mounts() } - "Mount Connect" { mountConnect() } - "Mount" { mount() } - "Mount Remote Control Start" { mountRemoteControlStart() } - "Mount Remote Control List" { mountRemoteControlList() } - "Mount Remote Control Stop" { mountRemoteControlStop() } - "Mount Disconnect" { mountDisconnect() } - - // FOCUSER. - - "Focusers" { focusers() } - "Focuser Connect" { focuserConnect() } - "Focuser" { focuser() } - "Focuser Disconnect" { focuserDisconnect() } - - // AUTO FOCUS. - - "Auto Focus Start" { - connect("192.168.31.153", 11111, ConnectionType.ALPACA) - delay(1000) - cameraConnect() - focuserConnect() - delay(1000) - // focuserMoveTo(position = 8100) - delay(2000) - autoFocusStart() - } - "Auto Focus Stop" { autoFocusStop() } - } - - private fun connect(host: String = "0.0.0.0", port: Int = 7624, type: ConnectionType = ConnectionType.INDI) { - put("connection?host=$host&port=$port&type=$type") - } - - private fun disconnect() { - delete("connection") - } - - private fun cameras() { - get("cameras") - } - - private fun cameraConnect(camera: String = CAMERA_NAME) { - put("cameras/$camera/connect") - } - - private fun cameraDisconnect(camera: String = CAMERA_NAME) { - put("cameras/$camera/disconnect") - } - - private fun camera(camera: String = CAMERA_NAME) { - get("cameras/$camera") - } - - private fun cameraStartCapture(camera: String = CAMERA_NAME) { - putJson("cameras/$camera/capture/start", CAMERA_START_CAPTURE_REQUEST) - } - - private fun cameraStopCapture(camera: String = CAMERA_NAME) { - put("cameras/$camera/capture/abort") - } - - private fun mounts() { - get("mounts") - } - - private fun mountConnect(mount: String = MOUNT_NAME) { - put("mounts/$mount/connect") - } - - private fun mountDisconnect(mount: String = MOUNT_NAME) { - put("mounts/$mount/disconnect") - } - - private fun mount(mount: String = MOUNT_NAME) { - get("mounts/$mount") - } - - private fun mountRemoteControlStart(mount: String = MOUNT_NAME, host: String = "0.0.0.0", port: Int = 10001) { - put("mounts/$mount/remote-control/start?type=LX200&host=$host&port=$port") - } - - private fun mountRemoteControlList(mount: String = MOUNT_NAME) { - get("mounts/$mount/remote-control") - } - - private fun mountRemoteControlStop(mount: String = MOUNT_NAME) { - put("mounts/$mount/remote-control/stop?type=LX200") - } - - private fun focusers() { - get("focusers") - } - - private fun focuserConnect(focuser: String = FOCUSER_NAME) { - put("focusers/$focuser/connect") - } - - private fun focuserDisconnect(focuser: String = FOCUSER_NAME) { - put("focusers/$focuser/disconnect") - } - - private fun focuser(focuser: String = FOCUSER_NAME) { - get("focusers/$focuser") - } - - private fun focuserMoveTo(focuser: String = FOCUSER_NAME, position: Int) { - put("focusers/$focuser/move-to?steps=$position") - } - - private fun autoFocusStart(camera: String = CAMERA_NAME, focuser: String = FOCUSER_NAME) { - putJson("auto-focus/$camera/$focuser/start", AUTO_FOCUS_REQUEST) - } - - private fun autoFocusStop(camera: String = CAMERA_NAME) { - put("auto-focus/$camera/stop") - } - - companion object { - - private const val BASE_URL = "http://localhost:7000" - private const val CAMERA_NAME = "Sky Simulator" - private const val MOUNT_NAME = "Telescope Simulator" - private const val FOCUSER_NAME = "ZWO Focuser (1)" - - @JvmStatic private val EXPOSURE_TIME = Duration.ofSeconds(5) - @JvmStatic private val CAPTURES_PATH = Path.of("/home/tiagohm/Git/nebulosa/data/captures") - - @JvmStatic private val STAR_DETECTION_OPTIONS = StarDetectionRequest(executablePath = Path.of("astap")) - - @JvmStatic private val CAMERA_START_CAPTURE_REQUEST = CameraStartCaptureRequest( - exposureTime = EXPOSURE_TIME, width = 1280, height = 1024, frameFormat = "INDI_MONO", - savePath = CAPTURES_PATH, exposureAmount = 1 - ) - - @JvmStatic private val AUTO_FOCUS_REQUEST = AutoFocusRequest( - capture = CAMERA_START_CAPTURE_REQUEST, stepSize = 500, - starDetector = STAR_DETECTION_OPTIONS - ) - - @JvmStatic private val KOTLIN_MODULE = kotlinModule() - .addSerializer(PathSerializer) - .addSerializer(DurationSerializer()) - - @JvmStatic private val OBJECT_MAPPER = ObjectMapper() - .registerModule(JavaTimeModule()) - .registerModule(KOTLIN_MODULE) - - @JvmStatic private val APPLICATION_JSON = "application/json".toMediaType() - @JvmStatic private val EMPTY_BODY = ByteArray(0).toRequestBody(APPLICATION_JSON) - - @JvmStatic - private fun get(path: String) { - val request = Request.Builder().get().url("$BASE_URL/$path").build() - HTTP_CLIENT.newCall(request).execute().use { it.isSuccessful.shouldBeTrue() } - } - - @JvmStatic - private fun put(path: String, body: RequestBody = EMPTY_BODY) { - val request = Request.Builder().put(body).url("$BASE_URL/$path").build() - HTTP_CLIENT.newCall(request).execute().use { it.isSuccessful.shouldBeTrue() } - } - - @JvmStatic - private fun putJson(path: String, data: Any) { - val bytes = OBJECT_MAPPER.writeValueAsBytes(data) - val body = bytes.toRequestBody(APPLICATION_JSON) - put(path, body) - } - - @JvmStatic - private fun delete(path: String) { - val request = Request.Builder().delete().url("$BASE_URL/$path").build() - HTTP_CLIENT.newCall(request).execute().use { it.isSuccessful.shouldBeTrue() } - } - } -} diff --git a/api/src/test/kotlin/CalibrationFrameRepositoryTest.kt b/api/src/test/kotlin/CalibrationFrameRepositoryTest.kt index 8cdd27d80..602571baa 100644 --- a/api/src/test/kotlin/CalibrationFrameRepositoryTest.kt +++ b/api/src/test/kotlin/CalibrationFrameRepositoryTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldHaveSize import io.objectbox.kotlin.boxFor @@ -6,84 +5,91 @@ import nebulosa.api.calibration.CalibrationFrameEntity import nebulosa.api.calibration.CalibrationFrameRepository import nebulosa.api.database.MyObjectBox import nebulosa.indi.device.camera.FrameType +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import java.util.* -class CalibrationFrameRepositoryTest : StringSpec() { +class CalibrationFrameRepositoryTest { - init { - val boxStore = MyObjectBox.builder() - .inMemory(UUID.randomUUID().toString()) - .build() - - afterSpec { - boxStore.close() - } + @Test + fun findAll() { + REPOSITORY.findAll().shouldHaveSize(17) + } - val box = boxStore.boxFor() - val repository = CalibrationFrameRepository(box) + @Test + fun findDarks() { + REPOSITORY.darkFrames(NAME, 1280, 1024, 1, 1L, 0.0).shouldHaveSize(1) + REPOSITORY.darkFrames(NAME, 1280, 1024, 1, 60L, 0.0).shouldHaveSize(4) + REPOSITORY.darkFrames(NAME, 1280, 1024, 1, 60L, 100.0).shouldHaveSize(2) + REPOSITORY.darkFrames(NAME, 1280, 1024, 1, 60L, 50.0).shouldBeEmpty() + REPOSITORY.darkFrames(NAME, 1280, 1024, 2, 60L, 100.0).shouldBeEmpty() + REPOSITORY.darkFrames(NAME, 4092, 2800, 1, 60L, 100.0).shouldBeEmpty() + REPOSITORY.darkFrames("ZW", 1280, 1024, 1, 1L, 0.0).shouldBeEmpty() + } - repository.save(FrameType.DARK, 1L) - repository.save(FrameType.DARK, 2L) - repository.save(FrameType.DARK, 5L) - repository.save(FrameType.DARK, 10L) - repository.save(FrameType.DARK, 30L) - repository.save(FrameType.DARK, 60L) - repository.save(FrameType.DARK, 60L, gain = 100.0) - repository.save(FrameType.DARK, 10L, temperature = -10.0) - repository.save(FrameType.DARK, 30L, temperature = -10.0) - repository.save(FrameType.DARK, 60L, temperature = -10.0) - repository.save(FrameType.DARK, 60L, temperature = -10.0, gain = 100.0) - repository.save(FrameType.BIAS, 0L) - repository.save(FrameType.BIAS, 0L, gain = 100.0) - repository.save(FrameType.FLAT, 0L, filter = "RED") - repository.save(FrameType.FLAT, 0L, filter = "GREEN") - repository.save(FrameType.FLAT, 0L, filter = "BLUE") - repository.save(FrameType.FLAT, 0L, filter = null) + @Test + fun findBias() { + REPOSITORY.biasFrames(NAME, 1280, 1024, 1, 0.0).shouldHaveSize(2) + REPOSITORY.biasFrames(NAME, 1280, 1024, 1, 100.0).shouldHaveSize(1) + REPOSITORY.biasFrames(NAME, 1280, 1024, 1, 50.0).shouldBeEmpty() + REPOSITORY.biasFrames(NAME, 1280, 1024, 2, 0.0).shouldBeEmpty() + REPOSITORY.biasFrames(NAME, 4092, 2800, 1, 0.0).shouldBeEmpty() + REPOSITORY.biasFrames("ZW", 1280, 1024, 2, 0.0).shouldBeEmpty() + } - "find all" { - repository.findAll().shouldHaveSize(17) - } - "find darks" { - repository.darkFrames(NAME, 1280, 1024, 1, 1L, 0.0).shouldHaveSize(1) - repository.darkFrames(NAME, 1280, 1024, 1, 60L, 0.0).shouldHaveSize(4) - repository.darkFrames(NAME, 1280, 1024, 1, 60L, 100.0).shouldHaveSize(2) - repository.darkFrames(NAME, 1280, 1024, 1, 60L, 50.0).shouldBeEmpty() - repository.darkFrames(NAME, 1280, 1024, 2, 60L, 100.0).shouldBeEmpty() - repository.darkFrames(NAME, 4092, 2800, 1, 60L, 100.0).shouldBeEmpty() - repository.darkFrames("ZW", 1280, 1024, 1, 1L, 0.0).shouldBeEmpty() - } - "find bias" { - repository.biasFrames(NAME, 1280, 1024, 1, 0.0).shouldHaveSize(2) - repository.biasFrames(NAME, 1280, 1024, 1, 100.0).shouldHaveSize(1) - repository.biasFrames(NAME, 1280, 1024, 1, 50.0).shouldBeEmpty() - repository.biasFrames(NAME, 1280, 1024, 2, 0.0).shouldBeEmpty() - repository.biasFrames(NAME, 4092, 2800, 1, 0.0).shouldBeEmpty() - repository.biasFrames("ZW", 1280, 1024, 2, 0.0).shouldBeEmpty() - } - "find flats" { - repository.flatFrames(NAME, null, 1280, 1024, 1).shouldHaveSize(1) - repository.flatFrames(NAME, "RED", 1280, 1024, 1).shouldHaveSize(1) - repository.flatFrames(NAME, "green", 1280, 1024, 1).shouldHaveSize(1) - repository.flatFrames(NAME, "BLUE", 1280, 1024, 1).shouldHaveSize(1) - repository.flatFrames(NAME, "RED", 1280, 1024, 2).shouldBeEmpty() - repository.flatFrames(NAME, "RED", 4092, 2800, 2).shouldBeEmpty() - repository.flatFrames(NAME, "HA", 1280, 1024, 2).shouldBeEmpty() - repository.flatFrames("ZW", "RED", 1280, 1024, 2).shouldBeEmpty() - } + @Test + fun findFlats() { + REPOSITORY.flatFrames(NAME, null, 1280, 1024, 1).shouldHaveSize(1) + REPOSITORY.flatFrames(NAME, "RED", 1280, 1024, 1).shouldHaveSize(1) + REPOSITORY.flatFrames(NAME, "green", 1280, 1024, 1).shouldHaveSize(1) + REPOSITORY.flatFrames(NAME, "BLUE", 1280, 1024, 1).shouldHaveSize(1) + REPOSITORY.flatFrames(NAME, "RED", 1280, 1024, 2).shouldBeEmpty() + REPOSITORY.flatFrames(NAME, "RED", 4092, 2800, 2).shouldBeEmpty() + REPOSITORY.flatFrames(NAME, "HA", 1280, 1024, 2).shouldBeEmpty() + REPOSITORY.flatFrames("ZW", "RED", 1280, 1024, 2).shouldBeEmpty() } companion object { private const val NAME = "CCD Simulator" + @JvmStatic private val BOX_STORE = MyObjectBox.builder() + .inMemory(UUID.randomUUID().toString()) + .build() + + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() + } + + @JvmStatic private val BOX = BOX_STORE.boxFor() + @JvmStatic private val REPOSITORY = CalibrationFrameRepository(BOX).apply { + save(FrameType.DARK, 1L) + save(FrameType.DARK, 2L) + save(FrameType.DARK, 5L) + save(FrameType.DARK, 10L) + save(FrameType.DARK, 30L) + save(FrameType.DARK, 60L) + save(FrameType.DARK, 60L, gain = 100.0) + save(FrameType.DARK, 10L, temperature = -10.0) + save(FrameType.DARK, 30L, temperature = -10.0) + save(FrameType.DARK, 60L, temperature = -10.0) + save(FrameType.DARK, 60L, temperature = -10.0, gain = 100.0) + save(FrameType.BIAS, 0L) + save(FrameType.BIAS, 0L, gain = 100.0) + save(FrameType.FLAT, 0L, filter = "RED") + save(FrameType.FLAT, 0L, filter = "GREEN") + save(FrameType.FLAT, 0L, filter = "BLUE") + save(FrameType.FLAT, 0L, filter = null) + } + @JvmStatic internal fun CalibrationFrameRepository.save( type: FrameType, exposureTime: Long, temperature: Double = 25.0, width: Int = 1280, height: Int = 1024, bin: Int = 1, gain: Double = 0.0, filter: String? = null, - ) { - save(CalibrationFrameEntity(0L, type, NAME, filter, exposureTime, temperature, width, height, bin, bin, gain)) - } + ) = save(CalibrationFrameEntity(0L, type, NAME, filter, exposureTime, temperature, width, height, bin, bin, gain)) } } diff --git a/api/src/test/kotlin/CameraCaptureNamingFormatterTest.kt b/api/src/test/kotlin/CameraCaptureNamingFormatterTest.kt new file mode 100644 index 000000000..ba9b8e1c6 --- /dev/null +++ b/api/src/test/kotlin/CameraCaptureNamingFormatterTest.kt @@ -0,0 +1,483 @@ +import io.kotest.matchers.shouldBe +import nebulosa.api.cameras.CameraCaptureNamingFormatter +import nebulosa.api.cameras.CameraCaptureNamingFormatter.Companion.BIAS_FORMAT +import nebulosa.api.cameras.CameraCaptureNamingFormatter.Companion.DARK_FORMAT +import nebulosa.api.cameras.CameraCaptureNamingFormatter.Companion.FLAT_FORMAT +import nebulosa.api.cameras.CameraCaptureNamingFormatter.Companion.LIGHT_FORMAT +import nebulosa.fits.FitsHeader +import nebulosa.fits.FitsKeyword +import nebulosa.image.algorithms.transformation.CfaPattern +import nebulosa.image.format.HeaderCard +import nebulosa.indi.device.Device +import nebulosa.indi.device.MessageSender +import nebulosa.indi.device.PropertyVector +import nebulosa.indi.device.camera.Camera +import nebulosa.indi.device.camera.FrameType +import nebulosa.indi.device.filterwheel.FilterWheel +import nebulosa.indi.device.focuser.Focuser +import nebulosa.indi.device.mount.* +import nebulosa.indi.device.rotator.Rotator +import nebulosa.indi.protocol.INDIProtocol +import nebulosa.indi.protocol.PropertyState +import nebulosa.math.* +import nebulosa.time.SystemClock +import org.junit.jupiter.api.Test +import java.time.* +import java.util.* + +class CameraCaptureNamingFormatterTest { + + private val header = FitsHeader() + + init { + header.add("FRAME", "Light") + header.add(FitsKeyword.EXPTIME, 1.5) + header.add(FitsKeyword.FILTER, "Red") + header.add(FitsKeyword.GAIN, 80) + header.add(FitsKeyword.XBINNING, 2) + header.add(FitsKeyword.NAXIS1, 1280) + header.add(FitsKeyword.NAXIS2, 1024) + header.add(FitsKeyword.CCD_TEMP, -15.0) + header.add(FitsKeyword.RA, "06 45 08.91728".hours.toDegrees) + header.add(FitsKeyword.DEC, "-16 42 58.0171".deg.toDegrees) + } + + @Test + fun type() { + FORMATTER.format("[type]", header) shouldBe "LIGHT" + } + + @Test + fun year() { + FORMATTER.format("[year]", header) shouldBe "2024" + FORMATTER.format("[year:2]", header) shouldBe "24" + FORMATTER.format("[year:4]", header) shouldBe "2024" + FORMATTER.format("[year:3]", header) shouldBe "2024" + } + + @Test + fun month() { + FORMATTER.format("[month]", header) shouldBe "07" + } + + @Test + fun day() { + FORMATTER.format("[day]", header) shouldBe "04" + } + + @Test + fun hour() { + FORMATTER.format("[hour]", header) shouldBe "17" + } + + @Test + fun minute() { + FORMATTER.format("[min]", header) shouldBe "37" + FORMATTER.format("[minute]", header) shouldBe "37" + } + + @Test + fun second() { + FORMATTER.format("[sec]", header) shouldBe "36" + FORMATTER.format("[second]", header) shouldBe "36" + } + + @Test + fun millisecond() { + FORMATTER.format("[ms]", header) shouldBe "369" + } + + @Test + fun exposureTime() { + FORMATTER.format("[exp]", header) shouldBe "1500000" + FORMATTER.format("[exp:s]", header) shouldBe "1s" + FORMATTER.format("[exp:ms]", header) shouldBe "1500ms" + FORMATTER.format("[exp:us]", header) shouldBe "1500000" + FORMATTER.format("[exposure]", header) shouldBe "1500000" + } + + @Test + fun filter() { + FORMATTER.format("[filter]", header) shouldBe "Red" + } + + @Test + fun gain() { + FORMATTER.format("[gain]", header) shouldBe "80" + } + + @Test + fun bin() { + FORMATTER.format("[bin]", header) shouldBe "2" + } + + @Test + fun width() { + FORMATTER.format("[width]", header) shouldBe "1280" + FORMATTER.format("[w]", header) shouldBe "1280" + } + + @Test + fun height() { + FORMATTER.format("[height]", header) shouldBe "1024" + FORMATTER.format("[h]", header) shouldBe "1024" + } + + @Test + fun temperature() { + FORMATTER.format("[temp]", header) shouldBe "-15" + FORMATTER.format("[temperature]", header) shouldBe "-15" + } + + @Test + fun rightAscension() { + FORMATTER.format("[ra]", header) shouldBe "06h45m09s" + } + + @Test + fun declination() { + FORMATTER.format("[dec]", header) shouldBe "-016d42m58s" + } + + @Test + fun camera() { + FORMATTER.format("[camera]", header) shouldBe "Camera Simulator" + } + + @Test + fun mount() { + FORMATTER.format("[mount]", header) shouldBe "Mount Simulator" + } + + @Test + fun focuser() { + FORMATTER.format("[focuser]", header) shouldBe "Focuser Simulator" + } + + @Test + fun wheel() { + FORMATTER.format("[wheel]", header) shouldBe "Wheel Simulator" + } + + @Test + fun rotator() { + FORMATTER.format("[rotator]", header) shouldBe "Rotator Simulator" + } + + @Test + fun n() { + FORMATTER.format("[n]", header) shouldBe "0001" + FORMATTER.format("[n:1]", header) shouldBe "2" + FORMATTER.format("[n:2]", header) shouldBe "03" + FORMATTER.format("[n:3]", header) shouldBe "004" + FORMATTER.format("[n:6]", header) shouldBe "000005" + } + + @Test + fun light() { + with(header) { + add("FRAME", "Light") + FORMATTER.format(LIGHT_FORMAT, this) shouldBe "Camera Simulator_LIGHT_240704173736369_Red_1280_1024_1500000_2_80" + } + } + + @Test + fun dark() { + with(header) { + add("FRAME", "Dark") + FORMATTER.format(DARK_FORMAT, this) shouldBe "Camera Simulator_DARK_1280_1024_1500000_2_80" + } + } + + @Test + fun flat() { + with(header) { + add("FRAME", "Flat") + FORMATTER.format(FLAT_FORMAT, this) shouldBe "Camera Simulator_FLAT_Red_1280_1024_2" + } + } + + @Test + fun bias() { + with(header) { + add("FRAME", "Bias") + FORMATTER.format(BIAS_FORMAT, this) shouldBe "Camera Simulator_BIAS_1280_1024_2_80" + } + } + + @Test + fun unknown() { + FORMATTER.format("[abc]_[camera]_[123]", header) shouldBe "abc_Camera Simulator_123" + } + + @Test + fun notFound() { + with(header) { + delete(FitsKeyword.RA) + FORMATTER.format("[ra]", this) shouldBe "" + FORMATTER.format("[ra]_[ra]", this) shouldBe "_" + } + } + + @Test + fun illegalChars() { + FORMATTER.format("[???]", header) shouldBe "[]" + FORMATTER.format("[???a]", header) shouldBe "[a]" + FORMATTER.format("[a???]", header) shouldBe "[a]" + FORMATTER.format("[][/\\:*?\"<>|]", header) shouldBe "[][]" + } + + @Suppress("LeakingThis") + private abstract class Simulator(override val name: String) : Device, MessageSender { + + final override val sender = this + final override val id = UUID.randomUUID().toString() + final override val connected = true + final override val properties = emptyMap>() + final override val messages = emptyList() + final override val snoopedDevices = emptyList() + + final override fun sendMessageToServer(message: INDIProtocol) = Unit + + final override fun snoop(devices: Iterable) = Unit + + final override fun handleMessage(message: INDIProtocol) = Unit + + final override fun close() = Unit + + final override fun connect() = Unit + + final override fun disconnect() = Unit + } + + private data object CameraSim : Simulator("Camera Simulator"), Camera { + + override val exposuring = true + override val hasCoolerControl = true + override val coolerPower = 0.0 + override val cooler = true + override val hasDewHeater = true + override val dewHeater = true + override val frameFormats = listOf("RAW16") + override val canAbort = true + override val cfaOffsetX = 0 + override val cfaOffsetY = 0 + override val cfaType = CfaPattern.RGGB + override val exposureMin: Duration = Duration.ofNanos(1000) + override val exposureMax: Duration = Duration.ofMinutes(10) + override val exposureState = PropertyState.IDLE + override val exposureTime: Duration = Duration.ofSeconds(1) + override val hasCooler = true + override val canSetTemperature = true + override val canSubFrame = true + override val x = 0 + override val minX = 0 + override val maxX = 1279 + override val y = 0 + override val minY = 0 + override val maxY = 1023 + override val width = 0 + override val minWidth = 0 + override val maxWidth = 1280 + override val height = 0 + override val minHeight = 0 + override val maxHeight = 1024 + override val canBin = true + override val maxBinX = 4 + override val maxBinY = 4 + override val binX = 1 + override val binY = 1 + override val gain = 0 + override val gainMin = 0 + override val gainMax = 100 + override val offset = 0 + override val offsetMin = 0 + override val offsetMax = 100 + override val guideHead = null + override val pixelSizeX = 2.8 + override val pixelSizeY = 2.8 + override val canPulseGuide = true + override val pulseGuiding = true + override val hasThermometer = true + override val temperature = 15.0 + + override fun cooler(enabled: Boolean) = Unit + + override fun dewHeater(enabled: Boolean) = Unit + + override fun temperature(value: Double) = Unit + + override fun frameFormat(format: String?) = Unit + + override fun frameType(type: FrameType) = Unit + + override fun frame(x: Int, y: Int, width: Int, height: Int) = Unit + + override fun bin(x: Int, y: Int) = Unit + + override fun gain(value: Int) = Unit + + override fun offset(value: Int) = Unit + + override fun startCapture(exposureTime: Duration) = Unit + + override fun abortCapture() = Unit + + override fun fitsKeywords(vararg cards: HeaderCard) = Unit + + override fun guideNorth(duration: Duration) = Unit + + override fun guideSouth(duration: Duration) = Unit + + override fun guideEast(duration: Duration) = Unit + + override fun guideWest(duration: Duration) = Unit + } + + private data object MountSim : Simulator("Mount Simulator"), Mount { + + override val slewing = false + override val tracking = true + override val canSync = true + override val canGoTo = true + override val canAbort = true + override val canHome = true + override val slewRates = emptyList() + override val slewRate = null + override val mountType = MountType.EQ_GEM + override val trackModes = TrackMode.entries + override val trackMode = TrackMode.SIDEREAL + override val pierSide = PierSide.WEST + override val guideRateWE = 0.0 + override val guideRateNS = 0.0 + override val rightAscension = 0.0 + override val declination = 0.0 + override val hasGPS = true + override val longitude = 0.0 + override val latitude = 0.0 + override val elevation = 0.0 + override val dateTime: OffsetDateTime = OffsetDateTime.now(SystemClock) + override val canPark = true + override val parking = false + override val parked = false + override val canPulseGuide = true + override val pulseGuiding = true + + override fun tracking(enable: Boolean) = Unit + + override fun sync(ra: Angle, dec: Angle) = Unit + + override fun syncJ2000(ra: Angle, dec: Angle) = Unit + + override fun slewTo(ra: Angle, dec: Angle) = Unit + + override fun slewToJ2000(ra: Angle, dec: Angle) = Unit + + override fun goTo(ra: Angle, dec: Angle) = Unit + + override fun goToJ2000(ra: Angle, dec: Angle) = Unit + + override fun home() = Unit + + override fun abortMotion() = Unit + + override fun trackMode(mode: TrackMode) = Unit + + override fun slewRate(rate: SlewRate) = Unit + + override fun moveNorth(enabled: Boolean) = Unit + + override fun moveSouth(enabled: Boolean) = Unit + + override fun moveWest(enabled: Boolean) = Unit + + override fun moveEast(enabled: Boolean) = Unit + + override fun coordinates(longitude: Angle, latitude: Angle, elevation: Distance) = Unit + + override fun dateTime(dateTime: OffsetDateTime) = Unit + + override fun park() = Unit + + override fun unpark() = Unit + + override fun guideNorth(duration: Duration) = Unit + + override fun guideSouth(duration: Duration) = Unit + + override fun guideEast(duration: Duration) = Unit + + override fun guideWest(duration: Duration) = Unit + } + + private data object FocuserSim : Simulator("Focuser Simulator"), Focuser { + + override val moving = false + override val canSync = true + override val canAbort = true + override val position = 1 + override val canAbsoluteMove = true + override val canRelativeMove = true + override val canReverse = true + override val reversed = true + override val hasBacklash = true + override val maxPosition = 100000 + override val hasThermometer = true + override val temperature = 15.0 + + override fun moveFocusIn(steps: Int) = Unit + + override fun moveFocusOut(steps: Int) = Unit + + override fun moveFocusTo(steps: Int) = Unit + + override fun abortFocus() = Unit + + override fun reverseFocus(enable: Boolean) = Unit + + override fun syncFocusTo(steps: Int) = Unit + } + + private data object WheelSim : Simulator("Wheel Simulator"), FilterWheel { + + override val moving = false + override val count = 5 + override val names = listOf("Luminance", "Red", "Green", "Blue", "Dark") + override val position = 1 + + override fun moveTo(position: Int) = Unit + + override fun names(names: Iterable) = Unit + } + + private data object RotatorSim : Simulator("Rotator Simulator"), Rotator { + + override val moving = false + override val canSync = true + override val canAbort = true + override val canReverse = true + override val canHome = true + override val hasBacklashCompensation = false + override val backslash = 0 + override val angle = 0.0 + override val minAngle = 0.0 + override val maxAngle = 360.0 + override val reversed = false + + override fun moveRotator(angle: Double) = Unit + + override fun syncRotator(angle: Double) = Unit + + override fun homeRotator() = Unit + + override fun reverseRotator(enable: Boolean) = Unit + + override fun abortRotator() = Unit + } + + companion object { + + @JvmStatic private val CLOCK = Clock.fixed(Instant.ofEpochSecond(1720114656, 369000000), ZoneOffset.UTC) + @JvmStatic private val FORMATTER = CameraCaptureNamingFormatter(CameraSim, MountSim, WheelSim, FocuserSim, RotatorSim, CLOCK) + } +} diff --git a/api/src/test/kotlin/PreferenceRepositoryTest.kt b/api/src/test/kotlin/PreferenceRepositoryTest.kt index 81bb5d59f..8e68479e9 100644 --- a/api/src/test/kotlin/PreferenceRepositoryTest.kt +++ b/api/src/test/kotlin/PreferenceRepositoryTest.kt @@ -1,5 +1,4 @@ import com.fasterxml.jackson.module.kotlin.jsonMapper -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.shouldBeExactly @@ -15,97 +14,117 @@ import nebulosa.api.preference.PreferenceEntity import nebulosa.api.preference.PreferenceRepository import nebulosa.api.preference.PreferenceService import nebulosa.indi.device.camera.FrameType +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import java.util.* -class PreferenceRepositoryTest : StringSpec() { +class PreferenceRepositoryTest { - init { - val boxStore = MyObjectBox.builder() + @Test + fun boolean() { + SERVICE.contains("b").shouldBeFalse() + SERVICE.putBoolean("b", true) + SERVICE.contains("b").shouldBeTrue() + SERVICE.getBoolean("b").shouldNotBeNull().shouldBeTrue() + SERVICE.putBoolean("b", false) + SERVICE.getBoolean("b").shouldNotBeNull().shouldBeFalse() + SERVICE.delete("b") + SERVICE.contains("b").shouldBeFalse() + SERVICE.getBoolean("b").shouldBeNull() + } + + @Test + fun int() { + SERVICE.contains("i").shouldBeFalse() + SERVICE.putInt("i", 22) + SERVICE.contains("i").shouldBeTrue() + SERVICE.getInt("i").shouldNotBeNull() shouldBeExactly 22 + SERVICE.delete("i") + SERVICE.contains("i").shouldBeFalse() + SERVICE.getInt("i").shouldBeNull() + } + + @Test + fun long() { + SERVICE.contains("l").shouldBeFalse() + SERVICE.putLong("l", 22L) + SERVICE.contains("l").shouldBeTrue() + SERVICE.getLong("l").shouldNotBeNull() shouldBeExactly 22L + SERVICE.delete("l") + SERVICE.contains("l").shouldBeFalse() + SERVICE.getLong("l").shouldBeNull() + } + + @Test + fun double() { + SERVICE.contains("d").shouldBeFalse() + SERVICE.putDouble("d", 22.0) + SERVICE.contains("d").shouldBeTrue() + SERVICE.getDouble("d").shouldNotBeNull() shouldBeExactly 22.0 + SERVICE.delete("d") + SERVICE.contains("d").shouldBeFalse() + SERVICE.getDouble("d").shouldBeNull() + } + + @Test + fun text() { + SERVICE.contains("s").shouldBeFalse() + SERVICE.putText("s", "Texto") + SERVICE.contains("s").shouldBeTrue() + SERVICE.getText("s").shouldNotBeNull() shouldBe "Texto" + SERVICE.delete("s") + SERVICE.contains("s").shouldBeFalse() + SERVICE.getText("s").shouldBeNull() + } + + @Test + fun enum() { + SERVICE.contains("e").shouldBeFalse() + SERVICE.putEnum("e", FrameType.DARK) + SERVICE.contains("e").shouldBeTrue() + SERVICE.getEnum("e").shouldNotBeNull() shouldBe FrameType.DARK + SERVICE.delete("e") + SERVICE.contains("e").shouldBeFalse() + SERVICE.getEnum("e").shouldBeNull() + } + + @Test + fun json() { + SERVICE.contains("j").shouldBeFalse() + SERVICE.putJSON("j", Location(longitude = 123.456)) + SERVICE.contains("j").shouldBeTrue() + SERVICE.getJSON("j").shouldNotBeNull() shouldBe Location(longitude = 123.456) + SERVICE.delete("j") + SERVICE.contains("j").shouldBeFalse() + SERVICE.getJSON("j").shouldBeNull() + } + + @Test + fun clear() { + SERVICE.putLong("l", 22L) + SERVICE.putDouble("d", 22.0) + SERVICE.putText("s", "Texto") + SERVICE.putEnum("e", FrameType.DARK) + SERVICE.putJSON("j", Location(longitude = 123.456)) + SERVICE.size shouldBeExactly 5 + SERVICE.clear() + SERVICE.isEmpty().shouldBeTrue() + } + + companion object { + + @JvmStatic private val BOX_STORE = MyObjectBox.builder() .inMemory(UUID.randomUUID().toString()) .build() - afterSpec { - boxStore.close() + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() } - val box = boxStore.boxFor() - val repository = PreferenceRepository(box) - val service = PreferenceService(repository, jsonMapper { }) - - "boolean" { - service.contains("b").shouldBeFalse() - service.putBoolean("b", true) - service.contains("b").shouldBeTrue() - service.getBoolean("b").shouldNotBeNull().shouldBeTrue() - service.putBoolean("b", false) - service.getBoolean("b").shouldNotBeNull().shouldBeFalse() - service.delete("b") - service.contains("b").shouldBeFalse() - service.getBoolean("b").shouldBeNull() - } - "int" { - service.contains("i").shouldBeFalse() - service.putInt("i", 22) - service.contains("i").shouldBeTrue() - service.getInt("i").shouldNotBeNull() shouldBeExactly 22 - service.delete("i") - service.contains("i").shouldBeFalse() - service.getInt("i").shouldBeNull() - } - "long" { - service.contains("l").shouldBeFalse() - service.putLong("l", 22L) - service.contains("l").shouldBeTrue() - service.getLong("l").shouldNotBeNull() shouldBeExactly 22L - service.delete("l") - service.contains("l").shouldBeFalse() - service.getLong("l").shouldBeNull() - } - "double" { - service.contains("d").shouldBeFalse() - service.putDouble("d", 22.0) - service.contains("d").shouldBeTrue() - service.getDouble("d").shouldNotBeNull() shouldBeExactly 22.0 - service.delete("d") - service.contains("d").shouldBeFalse() - service.getDouble("d").shouldBeNull() - } - "text" { - service.contains("s").shouldBeFalse() - service.putText("s", "Texto") - service.contains("s").shouldBeTrue() - service.getText("s").shouldNotBeNull() shouldBe "Texto" - service.delete("s") - service.contains("s").shouldBeFalse() - service.getText("s").shouldBeNull() - } - "enum" { - service.contains("e").shouldBeFalse() - service.putEnum("e", FrameType.DARK) - service.contains("e").shouldBeTrue() - service.getEnum("e").shouldNotBeNull() shouldBe FrameType.DARK - service.delete("e") - service.contains("e").shouldBeFalse() - service.getEnum("e").shouldBeNull() - } - "json" { - service.contains("j").shouldBeFalse() - service.putJSON("j", Location(longitude = 123.456)) - service.contains("j").shouldBeTrue() - service.getJSON("j").shouldNotBeNull() shouldBe Location(longitude = 123.456) - service.delete("j") - service.contains("j").shouldBeFalse() - service.getJSON("j").shouldBeNull() - } - "clear" { - service.putLong("l", 22L) - service.putDouble("d", 22.0) - service.putText("s", "Texto") - service.putEnum("e", FrameType.DARK) - service.putJSON("j", Location(longitude = 123.456)) - service.size shouldBeExactly 5 - service.clear() - service.isEmpty().shouldBeTrue() - } + @JvmStatic private val BOX = BOX_STORE.boxFor() + @JvmStatic private val REPOSITORY = PreferenceRepository(BOX) + @JvmStatic private val SERVICE = PreferenceService(REPOSITORY, jsonMapper { }) } } diff --git a/api/src/test/kotlin/SatelliteEntityRepositoryTest.kt b/api/src/test/kotlin/SatelliteEntityRepositoryTest.kt index 3450687ef..d667bf606 100644 --- a/api/src/test/kotlin/SatelliteEntityRepositoryTest.kt +++ b/api/src/test/kotlin/SatelliteEntityRepositoryTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldHaveSize import io.objectbox.kotlin.boxFor @@ -6,41 +5,34 @@ import nebulosa.api.atlas.SatelliteEntity import nebulosa.api.atlas.SatelliteGroupType import nebulosa.api.atlas.SatelliteRepository import nebulosa.api.database.MyObjectBox +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import java.util.* -class SatelliteEntityRepositoryTest : StringSpec() { +class SatelliteEntityRepositoryTest { - init { - val boxStore = MyObjectBox.builder() - .inMemory(UUID.randomUUID().toString()) - .build() - - afterSpec { - boxStore.close() - } + @Test + fun findAll() { + REPOSITORY.search().shouldHaveSize(2) + } - val box = boxStore.boxFor() - val repository = SatelliteRepository(box) + @Test + fun findByName() { + REPOSITORY.search("iss").shouldHaveSize(1) + } - repository.save("ISS (ZARYA)", ISS_TLE, SatelliteGroupType.ACTIVE, SatelliteGroupType.EDUCATION) - repository.save("StarLink", "", SatelliteGroupType.ACTIVE, SatelliteGroupType.STARLINK) + @Test + fun findByGroups() { + REPOSITORY.search(groups = listOf(SatelliteGroupType.ACTIVE)).shouldHaveSize(2) + REPOSITORY.search(groups = listOf(SatelliteGroupType.STARLINK)).shouldHaveSize(1) + REPOSITORY.search(groups = listOf(SatelliteGroupType.AMATEUR)).shouldBeEmpty() + } - "find all" { - repository.search().shouldHaveSize(2) - } - "find by name" { - repository.search("iss").shouldHaveSize(1) - } - "find by groups" { - repository.search(groups = listOf(SatelliteGroupType.ACTIVE)).shouldHaveSize(2) - repository.search(groups = listOf(SatelliteGroupType.STARLINK)).shouldHaveSize(1) - repository.search(groups = listOf(SatelliteGroupType.AMATEUR)).shouldBeEmpty() - } - "find by name and groups" { - repository.search(text = "iss", groups = listOf(SatelliteGroupType.ACTIVE)).shouldHaveSize(1) - repository.search(text = "iss", groups = listOf(SatelliteGroupType.STARLINK)).shouldBeEmpty() - repository.search(text = "starlink", groups = listOf(SatelliteGroupType.EDUCATION)).shouldBeEmpty() - } + @Test + fun findByNameAndGroups() { + REPOSITORY.search(text = "iss", groups = listOf(SatelliteGroupType.ACTIVE)).shouldHaveSize(1) + REPOSITORY.search(text = "iss", groups = listOf(SatelliteGroupType.STARLINK)).shouldBeEmpty() + REPOSITORY.search(text = "starlink", groups = listOf(SatelliteGroupType.EDUCATION)).shouldBeEmpty() } companion object { @@ -51,6 +43,22 @@ class SatelliteEntityRepositoryTest : StringSpec() { 2 25544 51.6392 250.6622 0011086 22.0936 34.8107 15.49934787460571 """.trimIndent() + @JvmStatic private val BOX_STORE = MyObjectBox.builder() + .inMemory(UUID.randomUUID().toString()) + .build() + + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() + } + + @JvmStatic private val BOX = BOX_STORE.boxFor() + @JvmStatic private val REPOSITORY = SatelliteRepository(BOX).apply { + save("ISS (ZARYA)", ISS_TLE, SatelliteGroupType.ACTIVE, SatelliteGroupType.EDUCATION) + save("StarLink", "", SatelliteGroupType.ACTIVE, SatelliteGroupType.STARLINK) + } + @JvmStatic internal fun SatelliteRepository.save(name: String, tle: String = "", vararg groups: SatelliteGroupType) { save(SatelliteEntity(0L, name, tle, groups.map { it.name }.toMutableList())) diff --git a/api/src/test/kotlin/SimbadDatabaseGenerator.kt b/api/src/test/kotlin/SimbadDatabaseGenerator.kt index c960c968d..2d9dfe376 100644 --- a/api/src/test/kotlin/SimbadDatabaseGenerator.kt +++ b/api/src/test/kotlin/SimbadDatabaseGenerator.kt @@ -12,6 +12,7 @@ import nebulosa.simbad.SimbadService import nebulosa.skycatalog.SkyObject import nebulosa.skycatalog.SkyObjectType import nebulosa.skycatalog.stellarium.Nebula +import nebulosa.test.concat import okhttp3.OkHttpClient import okio.sink import okio.source @@ -214,7 +215,7 @@ object SimbadDatabaseGenerator { for ((_, entity) in entities) { if (writer == null || count > 10000) { writer?.close() - writer = SimbadDatabaseWriter(Path.of("$SIMBAD_DATABASE_PATH", "simbad.%02d.dat".format(index++)).sink()) + writer = SimbadDatabaseWriter(SIMBAD_DATABASE_PATH.concat("simbad.%02d.dat".format(index++)).sink()) count = 0 } diff --git a/api/src/test/kotlin/SimbadEntityRepositoryTest.kt b/api/src/test/kotlin/SimbadEntityRepositoryTest.kt index 3ddd4487d..10df681ce 100644 --- a/api/src/test/kotlin/SimbadEntityRepositoryTest.kt +++ b/api/src/test/kotlin/SimbadEntityRepositoryTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.shouldBeExactly @@ -12,62 +11,75 @@ import nebulosa.math.deg import nebulosa.math.hours import nebulosa.nova.astrometry.Constellation import nebulosa.skycatalog.SkyObjectType +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import java.util.* -class SimbadEntityRepositoryTest : StringSpec() { +class SimbadEntityRepositoryTest { - init { - val boxStore = MyObjectBox.builder() - .inMemory(UUID.randomUUID().toString()) - .build() + @Test + fun findAll() { + REPOSITORY.search().shouldHaveSize(4).first().magnitude shouldBeExactly -1.45 + } - afterSpec { - boxStore.close() - } + @Test + fun findByName() { + REPOSITORY.search(name = "dolphin").shouldHaveSize(1).first().name shouldBe "Dolphin Nebula" + REPOSITORY.search(name = "andromeda").shouldBeEmpty() + REPOSITORY.search(name = "nebula").shouldHaveSize(2).first().magnitude shouldBeExactly 5.0 + } - val box = boxStore.boxFor() - val repository = SimbadEntityRepository(box) + @Test + fun findByConstellation() { + REPOSITORY.search(constellation = Constellation.CMA).shouldHaveSize(2).first().magnitude shouldBeExactly -1.45 + REPOSITORY.search(constellation = Constellation.AND).shouldBeEmpty() + } - repository.save("Sirius", SkyObjectType.STAR, Constellation.CMA, -1.45, "06 45 06".hours, "-16 43 33".deg) - repository.save("Dolphin Nebula", SkyObjectType.NEBULA, Constellation.CMA, 6.91, "06 54 11".hours, "-23 55 47".deg) - repository.save("75 Tucanae", SkyObjectType.GLOBULAR_CLUSTER, Constellation.TUC, 6.58, "01 03 12".hours, "-70 50 39".deg) - repository.save("Car Nebula", SkyObjectType.NEBULA, Constellation.CAR, 5.0, "10 45 15".hours, "-59 43 35".deg) + @Test + fun findByRegion() { + REPOSITORY.search(rightAscension = "06 45 59".hours, declination = "-20 45 29".deg, radius = 4.5.deg).shouldHaveSize(2) + .first().magnitude shouldBeExactly -1.45 + REPOSITORY.search(rightAscension = "06 45 59".hours, declination = "-20 45 29".deg, radius = 4.0.deg).shouldHaveSize(1) + .first().name shouldBe "Dolphin Nebula" + REPOSITORY.search(rightAscension = "00 42 43".hours, declination = "41 15 53".deg, radius = 10.deg).shouldBeEmpty() + } - "find all" { - repository.search().shouldHaveSize(4).first().magnitude shouldBeExactly -1.45 - } - "find by name" { - repository.search(name = "dolphin").shouldHaveSize(1).first().name shouldBe "Dolphin Nebula" - repository.search(name = "andromeda").shouldBeEmpty() - repository.search(name = "nebula").shouldHaveSize(2).first().magnitude shouldBeExactly 5.0 - } - "find by constellation" { - repository.search(constellation = Constellation.CMA).shouldHaveSize(2).first().magnitude shouldBeExactly -1.45 - repository.search(constellation = Constellation.AND).shouldBeEmpty() - } - "find by region" { - repository.search(rightAscension = "06 45 59".hours, declination = "-20 45 29".deg, radius = 4.5.deg).shouldHaveSize(2) - .first().magnitude shouldBeExactly -1.45 - repository.search(rightAscension = "06 45 59".hours, declination = "-20 45 29".deg, radius = 4.0.deg).shouldHaveSize(1) - .first().name shouldBe "Dolphin Nebula" - repository.search(rightAscension = "00 42 43".hours, declination = "41 15 53".deg, radius = 10.deg).shouldBeEmpty() - } - "find by magnitude" { - repository.search(magnitudeMin = 5.0).shouldHaveSize(3) - repository.search(magnitudeMax = 4.9).shouldHaveSize(1).first().name shouldBe "Sirius" - repository.search(magnitudeMin = 6.6, magnitudeMax = 6.99).shouldHaveSize(1).first().name shouldBe "Dolphin Nebula" - repository.search(magnitudeMax = -2.0).shouldBeEmpty() - repository.search(magnitudeMin = 7.0).shouldBeEmpty() - repository.search(magnitudeMin = 5.1, magnitudeMax = 6.0).shouldBeEmpty() - } - "find by type" { - repository.search(type = SkyObjectType.NEBULA).shouldHaveSize(2).first().magnitude shouldBeExactly 5.0 - repository.search(type = SkyObjectType.GALAXY).shouldBeEmpty() - } + @Test + fun findByMagnitude() { + REPOSITORY.search(magnitudeMin = 5.0).shouldHaveSize(3) + REPOSITORY.search(magnitudeMax = 4.9).shouldHaveSize(1).first().name shouldBe "Sirius" + REPOSITORY.search(magnitudeMin = 6.6, magnitudeMax = 6.99).shouldHaveSize(1).first().name shouldBe "Dolphin Nebula" + REPOSITORY.search(magnitudeMax = -2.0).shouldBeEmpty() + REPOSITORY.search(magnitudeMin = 7.0).shouldBeEmpty() + REPOSITORY.search(magnitudeMin = 5.1, magnitudeMax = 6.0).shouldBeEmpty() + } + + @Test + fun findByType() { + REPOSITORY.search(type = SkyObjectType.NEBULA).shouldHaveSize(2).first().magnitude shouldBeExactly 5.0 + REPOSITORY.search(type = SkyObjectType.GALAXY).shouldBeEmpty() } companion object { + @JvmStatic private val BOX_STORE = MyObjectBox.builder() + .inMemory(UUID.randomUUID().toString()) + .build() + + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() + } + + @JvmStatic private val BOX = BOX_STORE.boxFor() + @JvmStatic private val REPOSITORY = SimbadEntityRepository(BOX).apply { + save("Sirius", SkyObjectType.STAR, Constellation.CMA, -1.45, "06 45 06".hours, "-16 43 33".deg) + save("Dolphin Nebula", SkyObjectType.NEBULA, Constellation.CMA, 6.91, "06 54 11".hours, "-23 55 47".deg) + save("75 Tucanae", SkyObjectType.GLOBULAR_CLUSTER, Constellation.TUC, 6.58, "01 03 12".hours, "-70 50 39".deg) + save("Car Nebula", SkyObjectType.NEBULA, Constellation.CAR, 5.0, "10 45 15".hours, "-59 43 35".deg) + } + @JvmStatic internal fun SimbadEntityRepository.save( name: String, type: SkyObjectType, constellation: Constellation, diff --git a/api/src/test/kotlin/SkyAtlasServiceTest.kt b/api/src/test/kotlin/SkyAtlasServiceTest.kt index 6a25036af..b4a655de1 100644 --- a/api/src/test/kotlin/SkyAtlasServiceTest.kt +++ b/api/src/test/kotlin/SkyAtlasServiceTest.kt @@ -1,7 +1,6 @@ import SatelliteEntityRepositoryTest.Companion.ISS_TLE import SatelliteEntityRepositoryTest.Companion.save import SimbadEntityRepositoryTest.Companion.save -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainAll import io.kotest.matchers.collections.shouldHaveAtLeastSize import io.kotest.matchers.collections.shouldHaveSize @@ -19,141 +18,161 @@ import nebulosa.math.* import nebulosa.nova.astrometry.Constellation import nebulosa.sbd.SmallBodyDatabaseService import nebulosa.skycatalog.SkyObjectType -import nebulosa.test.AbstractFitsAndXisfTest.Companion.HTTP_CLIENT +import nebulosa.test.HTTP_CLIENT +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor import java.time.LocalDateTime import java.util.* -class SkyAtlasServiceTest : StringSpec() { +class SkyAtlasServiceTest { - init { - val boxStore = MyObjectBox.builder() - .inMemory(UUID.randomUUID().toString()) - .build() + @Test + fun objectTypes() { + SERVICE.objectTypes.shouldHaveSize(2).shouldContainAll(SkyObjectType.STAR, SkyObjectType.GLOBULAR_CLUSTER) + } - afterSpec { - boxStore.close() - } + @Test + fun positionOfSun() { + val position = SERVICE.positionOfSun(LOCATION, DATE_TIME, false) + position.rightAscensionJ2000.formatHMS() shouldBe "06h38m40.2s" + position.declinationJ2000.formatSignedDMS() shouldBe "+023°08'24.6\"" + position.rightAscension.formatHMS() shouldBe "06h40m07.7s" + position.declination.formatSignedDMS() shouldBe "+023°07'10.5\"" + position.azimuth.formatDMS() shouldBe "037°55'12.8\"" + position.altitude.formatSignedDMS() shouldBe "+036°39'01.4\"" + position.magnitude shouldBeExactly -26.706 + position.constellation shouldBe Constellation.GEM + position.distance shouldBe (1.017 plusOrMinus 1e-3) + position.distanceUnit shouldBe "AU" + position.illuminated shouldBeExactly 100.0 + position.elongation.toDegrees shouldBeExactly 0.0 + } - val threadPoolTaskExecutor = ThreadPoolTaskExecutor() - val horizonsService = HorizonsService(httpClient = HTTP_CLIENT) - val horizonsEphemerisProvider = HorizonsEphemerisProvider(horizonsService) - val bodyEphemerisProvider = BodyEphemerisProvider(threadPoolTaskExecutor) - val smallBodyDatabaseService = SmallBodyDatabaseService() - val satelliteBox = boxStore.boxFor() - val satelliteRepository = SatelliteRepository(satelliteBox) - val simbadBox = boxStore.boxFor() - val simbadEntityRepository = SimbadEntityRepository(simbadBox) + @Test + fun positionOfMoon() { + val position = SERVICE.positionOfMoon(LOCATION, DATE_TIME, false) + position.rightAscensionJ2000.formatHMS() shouldBe "01h47m21.7s" + position.declinationJ2000.formatSignedDMS() shouldBe "+013°32'49.4\"" + position.rightAscension.formatHMS() shouldBe "01h48m39.8s" + position.declination.formatSignedDMS() shouldBe "+013°40'06.4\"" + position.azimuth.formatDMS() shouldBe "306°54'06.0\"" + position.altitude.formatSignedDMS() shouldBe "+037°58'29.7\"" + position.magnitude shouldBeExactly -9.302 + position.constellation shouldBe Constellation.ARI + position.distance shouldBe (367798.938 plusOrMinus 1e-3) + position.distanceUnit shouldBe "km" + position.illuminated shouldBe (32.301 plusOrMinus 1e-3) + position.elongation.toDegrees shouldBe (69.142 plusOrMinus 1e-3) + } - simbadEntityRepository.save("Sirius", SkyObjectType.STAR, Constellation.CMA, -1.45, "06 45 06".hours, "-16 43 33".deg) - simbadEntityRepository.save("75 Tucanae", SkyObjectType.GLOBULAR_CLUSTER, Constellation.TUC, 6.58, "01 03 12".hours, "-70 50 39".deg) + @Test + fun positionOfJupiter() { + val position = SERVICE.positionOfPlanet(LOCATION, "599", DATE_TIME, false) + position.rightAscensionJ2000.formatHMS() shouldBe "04h24m28.8s" + position.declinationJ2000.formatSignedDMS() shouldBe "+020°55'01.7\"" + position.rightAscension.formatHMS() shouldBe "04h25m54.0s" + position.declination.formatSignedDMS() shouldBe "+020°58'23.7\"" + position.azimuth.formatDMS() shouldBe "358°22'34.0\"" + position.altitude.formatSignedDMS() shouldBe "+049°09'53.9\"" + position.magnitude shouldBeExactly -2.025 + position.constellation shouldBe Constellation.TAU + position.distance shouldBe (5.870 plusOrMinus 1e-3) + position.distanceUnit shouldBe "AU" + position.illuminated shouldBe (99.726 plusOrMinus 1e-3) + position.elongation.toDegrees shouldBe (31.110 plusOrMinus 1e-3) + } - satelliteRepository.save("ISS (ZARYA)", ISS_TLE, SatelliteGroupType.ACTIVE, SatelliteGroupType.EDUCATION) + @Test + fun positionOfApophis() { + val apophis = SERVICE.searchMinorPlanet("apophis").spkId + val position = SERVICE.positionOfPlanet(LOCATION, "DES=$apophis;", DATE_TIME, false) + position.rightAscensionJ2000.formatHMS() shouldBe "06h33m49.8s" + position.declinationJ2000.formatSignedDMS() shouldBe "+021°37'20.6\"" + position.rightAscension.formatHMS() shouldBe "06h35m16.4s" + position.declination.formatSignedDMS() shouldBe "+021°36'16.5\"" + position.azimuth.formatDMS() shouldBe "038°00'41.8\"" + position.altitude.formatSignedDMS() shouldBe "+038°32'04.2\"" + position.magnitude shouldBeExactly 20.825 + position.constellation shouldBe Constellation.GEM + position.distance shouldBe (2.018 plusOrMinus 1e-3) + position.distanceUnit shouldBe "AU" + position.illuminated shouldBe (99.972 plusOrMinus 1e-3) + position.elongation.toDegrees shouldBe (1.885 plusOrMinus 1e-3) + } - threadPoolTaskExecutor.initialize() + @Test + fun positionOfSirius() { + val sirius = SERVICE.searchSkyObject("Sirius").shouldNotBeEmpty().first().id + val position = SERVICE.positionOfSkyObject(LOCATION, sirius, DATE_TIME) + position.rightAscensionJ2000.formatHMS() shouldBe "06h45m06.0s" + position.declinationJ2000.formatSignedDMS() shouldBe "-016°43'33.0\"" + position.rightAscension.formatHMS() shouldBe "06h46m11.5s" + position.declination.formatSignedDMS() shouldBe "-016°45'01.6\"" + position.azimuth.formatDMS() shouldBe "090°08'45.7\"" + position.altitude.formatSignedDMS() shouldBe "+057°41'06.5\"" + position.magnitude shouldBeExactly -1.45 + position.constellation shouldBe Constellation.CMA + } - val service = SkyAtlasService( - horizonsEphemerisProvider, bodyEphemerisProvider, smallBodyDatabaseService, - satelliteRepository, simbadEntityRepository, HTTP_CLIENT - ) + @Test + fun positionOfIss() { + val iss = SERVICE.searchSatellites("ISS", emptyList()).shouldNotBeEmpty().first() + val position = SERVICE.positionOfSatellite(LOCATION, iss, DATE_TIME) + position.rightAscensionJ2000.formatHMS() shouldBe "14h47m37.8s" + position.declinationJ2000.formatSignedDMS() shouldBe "-017°22'47.2\"" + position.rightAscension.formatHMS() shouldBe "14h49m00.4s" + position.declination.formatSignedDMS() shouldBe "-017°29'00.1\"" + position.azimuth.formatDMS() shouldBe "144°36'58.9\"" + position.altitude.formatSignedDMS() shouldBe "-045°07'44.9\"" + position.constellation shouldBe Constellation.LIB + position.distance shouldBe (9633.950 plusOrMinus 1e-3) + position.distanceUnit shouldBe "km" + position.illuminated shouldBe (79.282 plusOrMinus 1e-3) + position.elongation.toDegrees shouldBe (125.849 plusOrMinus 1e-3) + } - val location = Location("-19.846616".deg, "-43.96872".deg, 852.0.m, -180) - val dateTime = LocalDateTime.of(2024, 6, 30, 9, 50, 0) + @Test + fun closeApproaches() { + val phas = SERVICE.closeApproachesForMinorPlanets(7, 10.0, DATE_TIME.toLocalDate()) + phas.shouldHaveAtLeastSize(2).map { it.name }.shouldContainAll("(2017 MB3)", "(2024 LH)") + } - "object types" { - service.objectTypes.shouldHaveSize(2).shouldContainAll(SkyObjectType.STAR, SkyObjectType.GLOBULAR_CLUSTER) - } - "position of sun" { - val position = service.positionOfSun(location, dateTime, false) - position.rightAscensionJ2000.formatHMS() shouldBe "06h38m40.2s" - position.declinationJ2000.formatSignedDMS() shouldBe "+023°08'24.6\"" - position.rightAscension.formatHMS() shouldBe "06h40m07.7s" - position.declination.formatSignedDMS() shouldBe "+023°07'10.5\"" - position.azimuth.formatDMS() shouldBe "037°55'12.8\"" - position.altitude.formatSignedDMS() shouldBe "+036°39'01.4\"" - position.magnitude shouldBeExactly -26.706 - position.constellation shouldBe Constellation.GEM - position.distance shouldBe (1.017 plusOrMinus 1e-3) - position.distanceUnit shouldBe "AU" - position.illuminated shouldBeExactly 100.0 - position.elongation.toDegrees shouldBeExactly 0.0 - } - "position of moon" { - val position = service.positionOfMoon(location, dateTime, false) - position.rightAscensionJ2000.formatHMS() shouldBe "01h47m21.7s" - position.declinationJ2000.formatSignedDMS() shouldBe "+013°32'49.4\"" - position.rightAscension.formatHMS() shouldBe "01h48m39.8s" - position.declination.formatSignedDMS() shouldBe "+013°40'06.4\"" - position.azimuth.formatDMS() shouldBe "306°54'06.0\"" - position.altitude.formatSignedDMS() shouldBe "+037°58'29.7\"" - position.magnitude shouldBeExactly -9.302 - position.constellation shouldBe Constellation.ARI - position.distance shouldBe (367798.938 plusOrMinus 1e-3) - position.distanceUnit shouldBe "km" - position.illuminated shouldBe (32.301 plusOrMinus 1e-3) - position.elongation.toDegrees shouldBe (69.142 plusOrMinus 1e-3) - } - "position of jupiter" { - val position = service.positionOfPlanet(location, "599", dateTime, false) - position.rightAscensionJ2000.formatHMS() shouldBe "04h24m28.8s" - position.declinationJ2000.formatSignedDMS() shouldBe "+020°55'01.7\"" - position.rightAscension.formatHMS() shouldBe "04h25m54.0s" - position.declination.formatSignedDMS() shouldBe "+020°58'23.7\"" - position.azimuth.formatDMS() shouldBe "358°22'34.0\"" - position.altitude.formatSignedDMS() shouldBe "+049°09'53.9\"" - position.magnitude shouldBeExactly -2.025 - position.constellation shouldBe Constellation.TAU - position.distance shouldBe (5.870 plusOrMinus 1e-3) - position.distanceUnit shouldBe "AU" - position.illuminated shouldBe (99.726 plusOrMinus 1e-3) - position.elongation.toDegrees shouldBe (31.110 plusOrMinus 1e-3) - } - "position of apophis" { - val apophis = service.searchMinorPlanet("apophis").spkId - val position = service.positionOfPlanet(location, "DES=$apophis;", dateTime, false) - position.rightAscensionJ2000.formatHMS() shouldBe "06h33m49.8s" - position.declinationJ2000.formatSignedDMS() shouldBe "+021°37'20.6\"" - position.rightAscension.formatHMS() shouldBe "06h35m16.4s" - position.declination.formatSignedDMS() shouldBe "+021°36'16.5\"" - position.azimuth.formatDMS() shouldBe "038°00'41.8\"" - position.altitude.formatSignedDMS() shouldBe "+038°32'04.2\"" - position.magnitude shouldBeExactly 20.825 - position.constellation shouldBe Constellation.GEM - position.distance shouldBe (2.018 plusOrMinus 1e-3) - position.distanceUnit shouldBe "AU" - position.illuminated shouldBe (99.972 plusOrMinus 1e-3) - position.elongation.toDegrees shouldBe (1.885 plusOrMinus 1e-3) - } - "position of sirius" { - val sirius = service.searchSkyObject("Sirius").shouldNotBeEmpty().first().id - val position = service.positionOfSkyObject(location, sirius, dateTime) - position.rightAscensionJ2000.formatHMS() shouldBe "06h45m06.0s" - position.declinationJ2000.formatSignedDMS() shouldBe "-016°43'33.0\"" - position.rightAscension.formatHMS() shouldBe "06h46m11.5s" - position.declination.formatSignedDMS() shouldBe "-016°45'01.6\"" - position.azimuth.formatDMS() shouldBe "090°08'45.7\"" - position.altitude.formatSignedDMS() shouldBe "+057°41'06.5\"" - position.magnitude shouldBeExactly -1.45 - position.constellation shouldBe Constellation.CMA + companion object { + + @JvmStatic private val BOX_STORE = MyObjectBox.builder() + .inMemory(UUID.randomUUID().toString()) + .build() + + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() } - "position of iss" { - val iss = service.searchSatellites("ISS", emptyList()).shouldNotBeEmpty().first() - val position = service.positionOfSatellite(location, iss, dateTime) - position.rightAscensionJ2000.formatHMS() shouldBe "14h47m37.8s" - position.declinationJ2000.formatSignedDMS() shouldBe "-017°22'47.2\"" - position.rightAscension.formatHMS() shouldBe "14h49m00.4s" - position.declination.formatSignedDMS() shouldBe "-017°29'00.1\"" - position.azimuth.formatDMS() shouldBe "144°36'58.8\"" - position.altitude.formatSignedDMS() shouldBe "-045°07'44.9\"" - position.constellation shouldBe Constellation.LIB - position.distance shouldBe (9633.950 plusOrMinus 1e-3) - position.distanceUnit shouldBe "km" - position.illuminated shouldBe (79.282 plusOrMinus 1e-3) - position.elongation.toDegrees shouldBe (125.849 plusOrMinus 1e-3) + + @JvmStatic private val THREAD_POOL_TASK_EXECUTOR = ThreadPoolTaskExecutor().also { it.initialize() } + @JvmStatic private val HORIZONS_SERVICE = HorizonsService(httpClient = HTTP_CLIENT) + @JvmStatic private val HORIZONS_EPHEMERIS_PROVIDER = HorizonsEphemerisProvider(HORIZONS_SERVICE) + @JvmStatic private val BODY_EPHEMERIS_PROVIDER = BodyEphemerisProvider(THREAD_POOL_TASK_EXECUTOR) + @JvmStatic private val SMALL_BODY_DATABASE_SERVICE = SmallBodyDatabaseService() + @JvmStatic private val SATELLITE_BOX = BOX_STORE.boxFor() + @JvmStatic private val SIMBAD_BOX = BOX_STORE.boxFor() + + @JvmStatic private val SIMBAD_ENTITY_REPOSITORY = SimbadEntityRepository(SIMBAD_BOX).apply { + save("Sirius", SkyObjectType.STAR, Constellation.CMA, -1.45, "06 45 06".hours, "-16 43 33".deg) + save("75 Tucanae", SkyObjectType.GLOBULAR_CLUSTER, Constellation.TUC, 6.58, "01 03 12".hours, "-70 50 39".deg) } - "close approaches" { - val phas = service.closeApproachesForMinorPlanets(7, 10.0, dateTime.toLocalDate()) - phas.shouldHaveAtLeastSize(2).map { it.name }.shouldContainAll("(2017 MB3)", "(2024 LH)") + + @JvmStatic private val SATELLITE_REPOSITORY = SatelliteRepository(SATELLITE_BOX).apply { + save("ISS (ZARYA)", ISS_TLE, SatelliteGroupType.ACTIVE, SatelliteGroupType.EDUCATION) } + + @JvmStatic private val SERVICE = SkyAtlasService( + HORIZONS_EPHEMERIS_PROVIDER, BODY_EPHEMERIS_PROVIDER, SMALL_BODY_DATABASE_SERVICE, + SATELLITE_REPOSITORY, SIMBAD_ENTITY_REPOSITORY, HTTP_CLIENT + ) + + @JvmStatic private val LOCATION = Location("-19.846616".deg, "-43.96872".deg, 852.0.m, -180) + @JvmStatic private val DATE_TIME = LocalDateTime.of(2024, 6, 30, 9, 50, 0) } } diff --git a/api/src/test/kotlin/StackerServiceTest.kt b/api/src/test/kotlin/StackerServiceTest.kt new file mode 100644 index 000000000..e27fa0a0a --- /dev/null +++ b/api/src/test/kotlin/StackerServiceTest.kt @@ -0,0 +1,69 @@ +import io.kotest.matchers.ints.shouldBeExactly +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import nebulosa.api.stacker.* +import nebulosa.fits.fits +import nebulosa.image.Image +import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction +import nebulosa.test.NonGitHubOnly +import nebulosa.test.save +import org.junit.jupiter.api.Test +import java.nio.file.Path +import kotlin.io.path.createDirectories + +@NonGitHubOnly +class StackerServiceTest { + + @Test + fun stackLRGB() { + val targets = PATHS.map { + val analyzed = SERVICE.analyze(it)!! + StackingTarget(true, it, analyzed.group, true) + } + + targets.count { it.group == StackerGroupType.LUMINANCE } shouldBeExactly 10 + targets.count { it.group == StackerGroupType.RED } shouldBeExactly 3 + targets.count { it.group == StackerGroupType.GREEN } shouldBeExactly 3 + targets.count { it.group == StackerGroupType.BLUE } shouldBeExactly 3 + + val request = StackingRequest( + Path.of(BASE_DIR, "stacker").createDirectories(), StackerType.PIXINSIGHT, + Path.of("PixInsight"), DARK_PATH, true, null, false, null, false, false, + 1, PATHS[0], targets + ) + + val image = SERVICE.stack(request).shouldNotBeNull().fits().use(Image::open) + image.transform(AutoScreenTransformFunction).save("stacker-lrgb").second shouldBe "465a296bb4582ab2f938757347500eb8" + } + + companion object { + + const val BASE_DIR = "/home/tiagohm/Imagens/Astrophotos/Light/Algieba/2024-05-13" + + @JvmStatic private val SERVICE = StackerService() + + @JvmStatic private val PATHS = listOf( + Path.of("$BASE_DIR/20240513.213424625-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213436506-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213448253-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213500627-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213512554-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213524278-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213535967-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213547683-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213559416-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213611421-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213624939-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213636654-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213648389-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213701880-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213713546-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213725316-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213738803-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213750501-LIGHT.fits"), + Path.of("$BASE_DIR/20240513.213802188-LIGHT.fits"), + ) + + @JvmStatic private val DARK_PATH = Path.of("/home/tiagohm/Imagens/Astrophotos/Dark/2024-06-08/ASI294_BIN4_G120_O80/10-DARK.fits") + } +} diff --git a/build.gradle.kts b/build.gradle.kts index bc5b13abd..f87cbc51c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,3 @@ -import com.adarshr.gradle.testlogger.TestLoggerExtension -import com.adarshr.gradle.testlogger.theme.ThemeType import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -8,7 +6,6 @@ buildscript { dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0") classpath("org.jetbrains.kotlin:kotlin-allopen:2.0.0") - classpath("com.adarshr:gradle-test-logger-plugin:4.0.0") classpath("io.objectbox:objectbox-gradle-plugin:4.0.1") } @@ -44,22 +41,6 @@ subprojects { val project = this@subprojects if (project.name == "nebulosa-bom") return@subprojects - apply { - plugin("com.adarshr.test-logger") - } - - configure { - theme = ThemeType.STANDARD - slowThreshold = 2000L - showStackTraces = false - showSkipped = true - showStandardStreams = true - showPassedStandardStreams = true - showSkippedStandardStreams = false - showFailedStandardStreams = true - logLevel = LogLevel.QUIET - } - tasks.withType { compilerOptions { jvmTarget.set(JvmTarget.JVM_17) @@ -84,8 +65,11 @@ subprojects { exceptionFormat = TestExceptionFormat.FULL } - systemProperty("junit.jupiter.extensions.autodetection.enabled", "true") + systemProperty("junit.jupiter.extensions.autodetection.enabled", "false") systemProperty("github", System.getProperty("github", "false")) + systemProperty("root.dir", "$rootDir") + systemProperty("project.dir", "$projectDir") + systemProperty("app.dir", "$rootDir/data") // LOGBACK } tasks.withType { diff --git a/desktop/.editorconfig b/desktop/.editorconfig index 350957ed8..ccc726593 100644 --- a/desktop/.editorconfig +++ b/desktop/.editorconfig @@ -11,6 +11,7 @@ trim_trailing_whitespace = true [*.ts, *.js] quote_type = single +insert_final_newline = true [*.md] trim_trailing_whitespace = false diff --git a/desktop/.prettierignore b/desktop/.prettierignore new file mode 100644 index 000000000..cff9d990c --- /dev/null +++ b/desktop/.prettierignore @@ -0,0 +1,6 @@ +**/node_modules +**/dist +**/release +**/*.svg + +package-lock.json diff --git a/desktop/.vscode/extensions.json b/desktop/.vscode/extensions.json index 3537a1c79..4ed06f5d0 100644 --- a/desktop/.vscode/extensions.json +++ b/desktop/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "angular.ng-template"] + "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "angular.ng-template", "editorconfig.editorconfig"] } diff --git a/desktop/README.md b/desktop/README.md index 2f0bae907..244054bc3 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -58,6 +58,10 @@ The complete integrated solution for all of your astronomical imaging needs. ![](sequencer.png) +## Stacker + +![](stacker.png) + ## INDI ![](indi.png) diff --git a/desktop/app/local.storage.ts b/desktop/app/local.storage.ts deleted file mode 100644 index a57ec7d76..000000000 --- a/desktop/app/local.storage.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as fs from 'fs' -import { dirname } from 'path' -import type { Undefinable } from '../src/shared/utils/types' - -export class LocalStorage> { - private readonly data = Object.create(null) as T - - constructor(private readonly path: string) { - try { - console.info(`loading config file at ${path}`) - - const parsedData = JSON.parse(fs.readFileSync(path, 'utf8')) as unknown - - if (typeof parsedData === 'object' && !Array.isArray(parsedData) && parsedData) { - Object.assign(this.data, parsedData) - } - } catch (e) { - console.error(e) - - this.ensureDirectory() - - fs.writeFileSync(path, '{}', { mode: 0o666 }) - } - } - - get(key: K): Undefinable { - return this.data[key] - } - - set(key: K, value: T[K]) { - this.data[key] = value - } - - has(key: K | string) { - return key in this.data - } - - save() { - try { - this.ensureDirectory() - fs.writeFileSync(this.path, JSON.stringify(this.data)) - } catch (e) { - console.error(e) - } - } - - private ensureDirectory(): void { - const dir = dirname(this.path) - - if (!fs.existsSync(dir)) { - // Ensure the directory exists as it could have been deleted in the meantime. - fs.mkdirSync(dir, { recursive: true }) - } - } -} diff --git a/desktop/app/main.ts b/desktop/app/main.ts index 2df6e432c..4f3c7b2ad 100644 --- a/desktop/app/main.ts +++ b/desktop/app/main.ts @@ -2,23 +2,29 @@ import { Menu, app, ipcMain } from 'electron' import * as fs from 'fs' import type { ChildProcessWithoutNullStreams } from 'node:child_process' import { spawn } from 'node:child_process' -import { join, resolve } from 'path' +import { join } from 'path' import { WebSocket } from 'ws' -import type { InternalEventType, JsonFile, StoredWindowData } from '../src/shared/types/app.types' +import type { InternalEventType, JsonFile } from '../src/shared/types/app.types' import { ArgumentParser } from './argument.parser' -import { LocalStorage } from './local.storage' import { WindowManager } from './window.manager' Object.assign(global, { WebSocket }) -app.commandLine.appendSwitch('disable-http-cache') - const argParser = new ArgumentParser() const parsedArgs = argParser.parse(process.argv.slice(1)) -const configPath = resolve(app.getPath('userData'), 'config.json') -const storage = new LocalStorage(configPath) + +app.commandLine.appendSwitch('disable-http-cache') + +if (parsedArgs.apiMode) { + // https://github.com/electron/electron/issues/32760#issuecomment-2227575986 + app.commandLine.appendSwitch('ignore-gpu-blacklist') + app.commandLine.appendSwitch('disable-gpu') + app.commandLine.appendSwitch('disable-gpu-compositing') + app.disableHardwareAcceleration() +} + const appIcon = join(__dirname, parsedArgs.serve ? `../src/assets/icons/nebulosa.png` : `assets/icons/nebulosa.png`) -const windowManager = new WindowManager(parsedArgs, storage, appIcon) +const windowManager = new WindowManager(parsedArgs, appIcon) let apiProcess: ChildProcessWithoutNullStreams | null process.on('beforeExit', () => { @@ -63,7 +69,7 @@ async function startApp() { if (text) { const regex = /server is started at port: (\d+)/i - const match = text.match(regex) + const match = regex.exec(text) if (match) { const port = parseInt(match[1]) diff --git a/desktop/app/package-lock.json b/desktop/app/package-lock.json index 70ead49e0..11a11b8bf 100644 --- a/desktop/app/package-lock.json +++ b/desktop/app/package-lock.json @@ -10,7 +10,8 @@ "license": "MIT", "dependencies": { "@stomp/stompjs": "7.0.0", - "ws": "8.17.1" + "electron-store": "8.2.0", + "ws": "8.18.0" } }, "node_modules/@stomp/stompjs": { @@ -18,10 +19,283 @@ "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.0.0.tgz", "integrity": "sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw==" }, - "node_modules/ws": { + "node_modules/ajv": { "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/conf": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", + "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", + "dependencies": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dependencies": { + "mimic-fn": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/electron-store": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.2.0.tgz", + "integrity": "sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==", + "dependencies": { + "conf": "^10.2.0", + "type-fest": "^2.17.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/json-schema-typed": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", + "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-fn": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", + "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dependencies": { + "find-up": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "engines": { "node": ">=10.0.0" }, diff --git a/desktop/app/package.json b/desktop/app/package.json index cd83ee94f..dae08a583 100644 --- a/desktop/app/package.json +++ b/desktop/app/package.json @@ -12,6 +12,7 @@ "private": true, "dependencies": { "@stomp/stompjs": "7.0.0", - "ws": "8.17.1" + "electron-store": "8.2.0", + "ws": "8.18.0" } } diff --git a/desktop/app/window.manager.ts b/desktop/app/window.manager.ts index 9d25ba4ca..8e7770918 100644 --- a/desktop/app/window.manager.ts +++ b/desktop/app/window.manager.ts @@ -1,12 +1,20 @@ import { Client } from '@stomp/stompjs' +import type { Point, Size } from 'electron' import { BrowserWindow, Notification, dialog, screen, shell } from 'electron' +import Store from 'electron-store' import type { ChildProcessWithoutNullStreams } from 'node:child_process' import { join } from 'path' import type { MessageEvent } from '../src/shared/types/api.types' -import type { CloseWindow, ConfirmationEvent, FullscreenWindow, NotificationEvent, OpenDirectory, OpenFile, OpenWindow, ResizeWindow, StoredWindowData, WindowCommand } from '../src/shared/types/app.types' +import type { CloseWindow, ConfirmationEvent, FullscreenWindow, NotificationEvent, OpenDirectory, OpenFile, OpenWindow, ResizeWindow, WindowCommand } from '../src/shared/types/app.types' import type { Nullable } from '../src/shared/utils/types' import type { ParsedArgument } from './argument.parser' -import type { LocalStorage } from './local.storage' + +// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style +export interface WindowInfo { + [key: `window.${string}`]: (Size & Point) | undefined +} + +const store = new Store({ name: 'nebulosa' }) export class ApplicationWindow { constructor( @@ -77,7 +85,6 @@ export class WindowManager { constructor( public readonly args: ParsedArgument, - public readonly storage: LocalStorage, defaultAppIcon: string = 'nebulosa.png', ) { this.appIcon = join(__dirname, args.serve ? `../src/assets/icons/${defaultAppIcon}` : `assets/icons/${defaultAppIcon}`) @@ -90,7 +97,6 @@ export class WindowManager { if (appWindow) { if (open.data) { - console.info('window data changed. id=%s, data=%s', open.id, open.data) appWindow.browserWindow.webContents.send('DATA.CHANGED', open.data) } @@ -108,12 +114,12 @@ export class WindowManager { const computedHeight = preference.height ? Math.trunc(this.computeHeight(preference.height, computedWidth)) : 416 const screenSize = screen.getPrimaryDisplay().workAreaSize - const storedData = this.storage.get(`window.${open.id}`) + const data = store.get(`window.${open.id}`) const resizable = preference.resizable - const width = resizable ? Math.max(minWidth, Math.min(storedData?.width ?? computedWidth, screenSize.width)) : computedWidth - const height = resizable ? Math.max(minHeight, Math.min(storedData?.height ?? computedHeight, screenSize.height)) : computedHeight - const x = Math.max(0, Math.min(storedData?.x ?? 0, screenSize.width - width)) - const y = Math.max(0, Math.min(storedData?.y ?? 0, screenSize.height - height)) + const width = resizable ? Math.max(minWidth, Math.min(data?.width ?? computedWidth, screenSize.width)) : computedWidth + const height = resizable ? Math.max(minHeight, Math.min(data?.height ?? computedHeight, screenSize.height)) : computedHeight + const x = Math.max(0, Math.min(data?.x ?? 0, screenSize.width - width)) + const y = Math.max(0, Math.min(data?.y ?? 0, screenSize.height - height)) const browserWindow = new BrowserWindow({ title: 'Nebulosa', @@ -129,6 +135,7 @@ export class WindowManager { resizable: this.args.serve || resizable, autoHideMenuBar: true, icon: preference.icon ? join(__dirname, this.args.serve ? `../src/assets/icons/${preference.icon}.png` : `assets/icons/${preference.icon}.png`) : this.appIcon, + show: false, webPreferences: { nodeIntegration: true, allowRunningInsecureContent: this.args.serve, @@ -139,9 +146,13 @@ export class WindowManager { }, }) - if (!storedData) { - browserWindow.center() - } + browserWindow.on('ready-to-show', () => { + browserWindow.show() + + if (!data) { + browserWindow.center() + } + }) if (this.args.serve) { await browserWindow.loadURL(`http://localhost:4200/${open.path}?data=${encodedData}`) @@ -193,8 +204,7 @@ export class WindowManager { saveWindowData(window: ApplicationWindow) { const [x, y] = window.browserWindow.getPosition() const [width, height] = window.browserWindow.getSize() - this.storage.set(`window.${window.data.id}`, { x, y, width, height }) - this.storage.save() + store.set(`window.${window.data.id}`, { x, y, width, height }) } async createMainWindow(apiProcess?: ChildProcessWithoutNullStreams, port: number = this.port, host: string = this.host) { @@ -254,9 +264,12 @@ export class WindowManager { const url = new URL(join('file:', __dirname, 'assets', 'images', 'splash.png')) + browserWindow.on('ready-to-show', () => { + browserWindow.show() + browserWindow.center() + }) + await browserWindow.loadURL(url.href) - browserWindow.show() - browserWindow.center() return browserWindow } else { @@ -284,24 +297,34 @@ export class WindowManager { return undefined } + findWindowWith(command: WindowCommand, sender: Electron.WebContents) { + return this.findWindow(command.windowId) ?? this.findWindow(sender.id) + } + async handleFileOpen(event: Electron.IpcMainInvokeEvent, command: OpenFile) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) if (window) { + const properties: Electron.OpenDialogOptions['properties'] = ['openFile'] + + if (command.multiple) { + properties.push('multiSelections') + } + const ret = await dialog.showOpenDialog(window.browserWindow, { filters: command.filters, - properties: ['openFile'], + properties, defaultPath: command.defaultPath || undefined, }) - return !ret.canceled && ret.filePaths[0] + return !ret.canceled && (command.multiple ? ret.filePaths : ret.filePaths[0]) } else { return false } } async handleFileSave(event: Electron.IpcMainInvokeEvent, command: OpenFile) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) if (window) { const ret = await dialog.showSaveDialog(window.browserWindow, { @@ -317,7 +340,7 @@ export class WindowManager { } async handleDirectoryOpen(event: Electron.IpcMainInvokeEvent, command: OpenDirectory) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) if (window) { const ret = await dialog.showOpenDialog(window.browserWindow, { @@ -333,7 +356,7 @@ export class WindowManager { async handleWindowOpen(event: Electron.IpcMainInvokeEvent, command: OpenWindow) { if (command.preference.modal) { - const parentWindow = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const parentWindow = this.findWindowWith(command, event.sender) const appWindow = await this.createWindow(command, parentWindow?.browserWindow) return new Promise((resolve) => { @@ -353,7 +376,7 @@ export class WindowManager { } handleWindowClose(event: Electron.IpcMainInvokeEvent, command: CloseWindow) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) if (window) { window.resolver?.(command.data) @@ -366,7 +389,7 @@ export class WindowManager { } handleWindowResize(event: Electron.IpcMainInvokeEvent, command: ResizeWindow) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) if (window && !window.data.preference.resizable && window.data.preference.autoResizable !== false) { const [width] = window.browserWindow.getSize() @@ -385,30 +408,30 @@ export class WindowManager { } handleWindowMinimize(event: Electron.IpcMainInvokeEvent, command: WindowCommand) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) window?.browserWindow.minimize() return !!window && window.browserWindow.isMinimized() } handleWindowMaximize(event: Electron.IpcMainInvokeEvent, command: WindowCommand) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) return !!window && window.toggleMaximize() } handleWindowPin(event: Electron.IpcMainInvokeEvent, command: WindowCommand) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) window?.browserWindow.setAlwaysOnTop(true) return !!window && window.browserWindow.isAlwaysOnTop() } handleWindowUnpin(event: Electron.IpcMainInvokeEvent, command: WindowCommand) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) window?.browserWindow.setAlwaysOnTop(false) return !!window && window.browserWindow.isAlwaysOnTop() } handleWindowFullscreen(event: Electron.IpcMainInvokeEvent, command: FullscreenWindow) { - const window = this.findWindow(command.windowId) ?? this.findWindow(event.sender.id) + const window = this.findWindowWith(command, event.sender) if (window) { if (command.enabled) window.browserWindow.setFullScreen(true) @@ -431,9 +454,9 @@ export class WindowManager { this.dispatchEvent(event) } - dispatchEvent(event: MessageEvent) { + dispatchEvent(event: MessageEvent, parentOnly: boolean = false) { for (const [, window] of this.windows) { - if (window.isParent) { + if (!parentOnly || window.isParent) { window.sendMessage(event) } } diff --git a/desktop/camera.png b/desktop/camera.png index 8cff67580..0d9668ac1 100644 Binary files a/desktop/camera.png and b/desktop/camera.png differ diff --git a/desktop/eslint.config.mjs b/desktop/eslint.config.mjs index 6176d33fc..900b0beff 100644 --- a/desktop/eslint.config.mjs +++ b/desktop/eslint.config.mjs @@ -2,6 +2,9 @@ import eslint from '@eslint/js' import tseslint from 'typescript-eslint' export default tseslint.config( + { + ignores: ['**/*.mjs'], + }, { files: ['**/*.ts'], ...eslint.configs.recommended, @@ -33,6 +36,7 @@ export default tseslint.config( rules: { 'no-unused-vars': 'off', 'no-loss-of-precision': 'off', + 'no-extra-semi': 'warn', '@typescript-eslint/no-unused-vars': 'warn', '@typescript-eslint/no-loss-of-precision': 'off', '@typescript-eslint/restrict-template-expressions': 'off', @@ -54,6 +58,13 @@ export default tseslint.config( ignorePrimitives: true, }, ], + '@typescript-eslint/no-unused-expressions': [ + 'error', + { + allowShortCircuit: true, + allowTernary: true, + }, + ], }, }, ) diff --git a/desktop/focuser.png b/desktop/focuser.png index dde69a2d4..262acf026 100644 Binary files a/desktop/focuser.png and b/desktop/focuser.png differ diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 04718c8e8..118e713cc 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -10,56 +10,54 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@angular/animations": "18.0.3", - "@angular/cdk": "18.0.3", - "@angular/common": "18.0.3", - "@angular/compiler": "18.0.3", - "@angular/core": "18.0.3", - "@angular/forms": "18.0.3", - "@angular/platform-browser": "18.0.3", - "@angular/platform-browser-dynamic": "18.0.3", - "@angular/router": "18.0.3", + "@angular/animations": "18.1.3", + "@angular/cdk": "18.1.3", + "@angular/common": "18.1.3", + "@angular/compiler": "18.1.3", + "@angular/core": "18.1.3", + "@angular/forms": "18.1.3", + "@angular/platform-browser": "18.1.3", + "@angular/platform-browser-dynamic": "18.1.3", + "@angular/router": "18.1.3", "@mdi/font": "7.4.47", "chart.js": "4.4.3", "chartjs-plugin-zoom": "2.0.1", "hotkeys-js": "3.13.7", - "interactjs": "1.10.27", "leaflet": "1.9.4", - "moment": "2.30.1", + "ngx-moveable": "0.50.0", "nuid": "2.0.1-2", "panzoom": "9.4.3", "primeflex": "3.3.1", "primeicons": "7.0.0", - "primeng": "17.18.1", - "primeng-sass-theme": "github:primefaces/primeng-sass-theme#17.18.0", + "primeng": "17.18.7", + "primeng-sass-theme": "github:primefaces/primeng-sass-theme#17.18.3", "rxjs": "7.8.1", "tslib": "2.6.3", - "zone.js": "0.14.7" + "zone.js": "0.14.8" }, "devDependencies": { "@angular-builders/custom-webpack": "18.0.0", - "@angular-devkit/build-angular": "18.0.4", - "@angular/cli": "18.0.4", - "@angular/compiler-cli": "18.0.3", - "@angular/language-service": "18.0.3", - "@eslint/js": "9.5.0", + "@angular-devkit/build-angular": "18.1.3", + "@angular/cli": "18.1.3", + "@angular/compiler-cli": "18.1.3", + "@angular/language-service": "18.1.3", + "@eslint/js": "9.8.0", "@types/eslint__js": "8.42.3", "@types/leaflet": "1.9.12", - "@types/node": "20.14.6", - "@types/uuid": "9.0.8", - "electron": "31.0.1", + "@types/node": "20.14.11", + "electron": "31.3.1", "electron-builder": "24.13.3", - "eslint": "8.57.0", + "eslint": "9.8.0", "node-polyfill-webpack-plugin": "4.0.0", "npm-run-all": "4.1.5", - "prettier": "3.3.2", + "prettier": "3.3.3", "ts-node": "10.9.2", - "typescript": "5.4.5", - "typescript-eslint": "7.13.1", + "typescript": "5.5.4", + "typescript-eslint": "8.0.0", "wait-on": "7.2.0" }, "engines": { - "node": ">= 22.0.0" + "node": ">= 20.11.1" } }, "node_modules/@ampproject/remapping": { @@ -67,6 +65,7 @@ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -80,6 +79,7 @@ "resolved": "https://registry.npmjs.org/@angular-builders/common/-/common-2.0.0.tgz", "integrity": "sha512-O5YJc++DtJVJhqA/OomRKN2jGYzvU/YXtfrPAqcA9Is3Ob5jvV0L0JHSAjSw/KaLvk/FjBIqoRVcYdLp5LKddA==", "dev": true, + "license": "MIT", "dependencies": { "@angular-devkit/core": "^18.0.0", "ts-node": "^10.0.0", @@ -94,6 +94,7 @@ "resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-18.0.0.tgz", "integrity": "sha512-XSynPSXHq5+nrh7J2snfrcbvm6YGwUGQRzr7OuO3wURJ6CHOD9C+xEAmvEUWW8c1YjEslVNG7aLtCGz7LA4ymw==", "dev": true, + "license": "MIT", "dependencies": { "@angular-builders/common": "2.0.0", "@angular-devkit/architect": ">=0.1800.0 < 0.1900.0", @@ -110,12 +111,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1800.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1800.4.tgz", - "integrity": "sha512-82TKhYnSO8aGIBo5TxPtyUQnZFcbV+qB2bIIYOAKsJgxAVxLeFD6QA6gTmHOZPXw5pBEPUO/+PUwq+Uk5xesgw==", + "version": "0.1801.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1801.3.tgz", + "integrity": "sha512-4yba7x315GKim7OuBgv89ZtG50hE3hw64KuRLSGuW+RvwcwLV24VanmdWmFiLC4RKYNSH13E0wZqDNJkrMQepw==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.0.4", + "@angular-devkit/core": "18.1.3", "rxjs": "7.8.1" }, "engines": { @@ -125,71 +127,71 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.0.4.tgz", - "integrity": "sha512-lFu1NDEUPIUxY+CmZJ3JspqVZDesrvdae5RbqQXCl87RfSy+ZDIa7rOtQxyBQtt2BuQIB9pWQSzCMii5kTHd6w==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.1.3.tgz", + "integrity": "sha512-1avnneitUEfC2A9HX24X6a7Ag8sHkxomVEBsggITFNQoGnZAZHCOBRzm3b9QiqTi1c1eH3p8teW8EAufEjFPKQ==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1800.4", - "@angular-devkit/build-webpack": "0.1800.4", - "@angular-devkit/core": "18.0.4", - "@angular/build": "18.0.4", - "@babel/core": "7.24.5", - "@babel/generator": "7.24.5", - "@babel/helper-annotate-as-pure": "7.22.5", - "@babel/helper-split-export-declaration": "7.24.5", - "@babel/plugin-transform-async-generator-functions": "7.24.3", - "@babel/plugin-transform-async-to-generator": "7.24.1", - "@babel/plugin-transform-runtime": "7.24.3", - "@babel/preset-env": "7.24.5", - "@babel/runtime": "7.24.5", + "@angular-devkit/architect": "0.1801.3", + "@angular-devkit/build-webpack": "0.1801.3", + "@angular-devkit/core": "18.1.3", + "@angular/build": "18.1.3", + "@babel/core": "7.24.7", + "@babel/generator": "7.24.7", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-transform-async-generator-functions": "7.24.7", + "@babel/plugin-transform-async-to-generator": "7.24.7", + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.24.7", + "@babel/runtime": "7.24.7", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "18.0.4", + "@ngtools/webpack": "18.1.3", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.19", "babel-loader": "9.1.3", - "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.21.5", - "copy-webpack-plugin": "11.0.0", - "critters": "0.0.22", - "css-loader": "7.1.1", - "esbuild-wasm": "0.21.3", + "copy-webpack-plugin": "12.0.2", + "critters": "0.0.24", + "css-loader": "7.1.2", + "esbuild-wasm": "0.21.5", "fast-glob": "3.3.2", "http-proxy-middleware": "3.0.0", - "https-proxy-agent": "7.0.4", - "inquirer": "9.2.22", - "jsonc-parser": "3.2.1", + "https-proxy-agent": "7.0.5", + "istanbul-lib-instrument": "6.0.2", + "jsonc-parser": "3.3.1", "karma-source-map-support": "1.4.0", "less": "4.2.0", "less-loader": "12.2.0", "license-webpack-plugin": "4.0.2", - "loader-utils": "3.2.1", + "loader-utils": "3.3.1", "magic-string": "0.30.10", "mini-css-extract-plugin": "2.9.0", "mrmime": "2.0.0", - "open": "8.4.2", + "open": "10.1.0", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.2", - "piscina": "4.5.0", + "piscina": "4.6.1", "postcss": "8.4.38", "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.77.2", + "sass": "1.77.6", "sass-loader": "14.2.1", "semver": "7.6.2", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.31.0", + "terser": "5.29.2", "tree-kill": "1.2.2", - "tslib": "2.6.2", - "undici": "6.18.0", - "vite": "5.2.11", + "tslib": "2.6.3", + "undici": "6.19.2", + "vite": "5.3.2", "watchpack": "2.4.1", - "webpack": "5.91.0", + "webpack": "5.92.1", "webpack-dev-middleware": "7.2.1", "webpack-dev-server": "5.0.4", "webpack-merge": "5.10.0", @@ -201,7 +203,7 @@ "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.21.3" + "esbuild": "0.21.5" }, "peerDependencies": { "@angular/compiler-cli": "^18.0.0", @@ -216,7 +218,7 @@ "ng-packagr": "^18.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.4 <5.5" + "typescript": ">=5.4 <5.6" }, "peerDependenciesMeta": { "@angular/localize": { @@ -259,6 +261,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -275,27 +278,55 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } }, + "node_modules/@angular-devkit/build-angular/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "license": "MIT" }, "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 + "dev": true, + "license": "MIT" }, "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, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -309,17 +340,12 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/@angular-devkit/build-angular/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, "node_modules/@angular-devkit/build-angular/node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "version": "5.92.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", + "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", @@ -327,10 +353,10 @@ "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -363,12 +389,13 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1800.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1800.4.tgz", - "integrity": "sha512-EtWyWH3Hb7Rh8u0Jb4cWJKRxlqiUo4qhHKjU+62E8XplWlajbuld3ltL50a3t8lkZQYYgl7nPt53E5kM/zFVrw==", + "version": "0.1801.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1801.3.tgz", + "integrity": "sha512-JezRR72P4QAc4mnkT60/+kVANCYNKcr2sZyX0/9aBHJsR7lIqgOKz5Dft3FgWHwAJcQFtsZ7OLGVOW3P1LpFkw==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1800.4", + "@angular-devkit/architect": "0.1801.3", "rxjs": "7.8.1" }, "engines": { @@ -382,14 +409,15 @@ } }, "node_modules/@angular-devkit/core": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.0.4.tgz", - "integrity": "sha512-8vYvJ5FF2NjFUia00hv8KWakOjOZ+09PbnNqd+lntJBekIg1lHDOF/vNMlVHtU5LiE1aNi9P/69/VXTckPfU9g==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.1.3.tgz", + "integrity": "sha512-S0UzNNVLbHPaiSVXHjCd2wX+eERj/YR7jJCc40PHs1gINA7Gtd2q3VDm3bUEWe4P6fP6GNp43qSXmWJFQD0+Yg==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "8.13.0", + "ajv": "8.16.0", "ajv-formats": "3.0.1", - "jsonc-parser": "3.2.1", + "jsonc-parser": "3.3.1", "picomatch": "4.0.2", "rxjs": "7.8.1", "source-map": "0.7.4" @@ -409,13 +437,14 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.0.4.tgz", - "integrity": "sha512-hCHmuu/Z1teOQPx1AMJa/gcK6depk+XgU5dIpEvflC+ApW3hglNe2QKaqajDZ+34s+PKAVWa86M8IOV7o/mHuA==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.1.3.tgz", + "integrity": "sha512-ElzCfiYW9P3xPRNRbPRSrOTGm+G7X8ta1ce3srqi00yPX39Y0WSM95SACqqF8j9dxL6BqazBMyAgNQUaVSbWjw==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.0.4", - "jsonc-parser": "3.2.1", + "@angular-devkit/core": "18.1.3", + "jsonc-parser": "3.3.1", "magic-string": "0.30.10", "ora": "5.4.1", "rxjs": "7.8.1" @@ -427,9 +456,10 @@ } }, "node_modules/@angular/animations": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.0.3.tgz", - "integrity": "sha512-Wlll6y7euIXYsOHpTh0hvVTBs7lVnbKDHiyd4Dz7kAMSeE2zyQo6OcRN+FFH3GH9BUi5UooAICNX8dJDfps6Mw==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.1.3.tgz", + "integrity": "sha512-jF4jGHZxV/REnymB11wg5q/DMXewJ0byihmvNQ3OPLHGkWnvE9MdrX44vUzI7RkzqO0suaAg8shxJlkY3OHjeA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -437,39 +467,42 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.0.3" + "@angular/core": "18.1.3" } }, "node_modules/@angular/build": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.0.4.tgz", - "integrity": "sha512-70HQQnbCOXFT5F3ROyWNNfS9A63Fzts5ANJKJY1MJLrn+dgNEG7jdIWjTtvohL3RZz97rlzSq3qRZnfxqf1lsQ==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.1.3.tgz", + "integrity": "sha512-jmTQC7lecJ6c2mJobb5nY2CN6jvdeFFHXN/jif0RkNI8dP60uV1QdMKJtTGbxEtAKXdMgOTReYICVYl6m9Q56Q==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1800.4", - "@babel/core": "7.24.5", - "@babel/helper-annotate-as-pure": "7.22.5", - "@babel/helper-split-export-declaration": "7.24.5", + "@angular-devkit/architect": "0.1801.3", + "@babel/core": "7.24.7", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-syntax-import-attributes": "7.24.7", + "@inquirer/confirm": "3.1.11", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", "browserslist": "^4.23.0", - "critters": "0.0.22", - "esbuild": "0.21.3", + "critters": "0.0.24", + "esbuild": "0.21.5", "fast-glob": "3.3.2", - "https-proxy-agent": "7.0.4", - "inquirer": "9.2.22", - "lmdb": "3.0.8", + "https-proxy-agent": "7.0.5", + "lmdb": "3.0.12", "magic-string": "0.30.10", "mrmime": "2.0.0", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", "picomatch": "4.0.2", - "piscina": "4.5.0", - "sass": "1.77.2", + "piscina": "4.6.1", + "rollup": "4.18.0", + "sass": "1.77.6", "semver": "7.6.2", - "undici": "6.18.0", - "vite": "5.2.11", + "undici": "6.19.2", + "vite": "5.3.2", "watchpack": "2.4.1" }, "engines": { @@ -485,7 +518,7 @@ "less": "^4.2.0", "postcss": "^8.4.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.4 <5.5" + "typescript": ">=5.4 <5.6" }, "peerDependenciesMeta": { "@angular/localize": { @@ -509,9 +542,10 @@ } }, "node_modules/@angular/cdk": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.0.3.tgz", - "integrity": "sha512-F6kXAoIWUbvhvbSGnMxrKJZsI7rhGKDXVFFf9m+ZQAbqboSyDbbszux7Em4CEh27LaXYNFltUcKdCQ3hdp7Kig==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.1.3.tgz", + "integrity": "sha512-u14xbuXQz+36nBeHSwRcwRoS64WNhOdK97H47nI1WaIZZaGGvKHR1Wwk2XletDRtIHv2622sJm8h+dbaBNeTGQ==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -525,23 +559,24 @@ } }, "node_modules/@angular/cli": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.0.4.tgz", - "integrity": "sha512-i7DLVIc4HN0CFZZKbEeVeQSADRG1Dt2CwXh/wTUzglRLu/tE7Q+WMrqJ2+lGTT2edZp2KKysM4Gxp+ATAzP8AQ==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.1.3.tgz", + "integrity": "sha512-vsEc3cGDUYcc+adfvBHSqKdI8uiaa86Y9pLWGHfqaD+N0q/k17d/47AFvXTDKLmKucMZrto/4088Y1y+yM9eOg==", "dev": true, + "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1800.4", - "@angular-devkit/core": "18.0.4", - "@angular-devkit/schematics": "18.0.4", - "@schematics/angular": "18.0.4", + "@angular-devkit/architect": "0.1801.3", + "@angular-devkit/core": "18.1.3", + "@angular-devkit/schematics": "18.1.3", + "@inquirer/prompts": "5.0.7", + "@listr2/prompt-adapter-inquirer": "2.0.13", + "@schematics/angular": "18.1.3", "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.3", - "ini": "4.1.2", - "inquirer": "9.2.22", - "jsonc-parser": "3.2.1", + "ini": "4.1.3", + "jsonc-parser": "3.3.1", + "listr2": "8.2.3", "npm-package-arg": "11.0.2", "npm-pick-manifest": "9.0.1", - "ora": "5.4.1", "pacote": "18.0.6", "resolve": "1.22.8", "semver": "7.6.2", @@ -558,9 +593,10 @@ } }, "node_modules/@angular/common": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.0.3.tgz", - "integrity": "sha512-lmT9QbWHduqzpsB0osQFHeSwvQB1iUeNwTVUyMtcs6i46l4qOPtAt2/9DvHUWEUp01EBDxyi385ZI3vD+FHH/w==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.1.3.tgz", + "integrity": "sha512-TC71jVph4L+QaXlyJTrW27nbqis4sWwr9hD/RDSNkfY9XCvYDb2MjYjKrpbN03FWiv7lmcKT9zgse1fYENFsKQ==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -568,14 +604,15 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.0.3", + "@angular/core": "18.1.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.0.3.tgz", - "integrity": "sha512-wrXxgBsZX4yTrj/oZ8PDGmvhqj9S2TZfcuivaUitprNC2uBWTVb1UcOS45Qw9YlLB0sYa2AmBudICDqYpb8lfw==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.1.3.tgz", + "integrity": "sha512-Mrcd+YGsz02GVnVlVbzYp7EJIVoPOIHMvhll1OiylhjQElNVeJCLPIvjVYdylzOUDctXNlchkGf/LbA7BYMbXg==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -583,7 +620,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.0.3" + "@angular/core": "18.1.3" }, "peerDependenciesMeta": { "@angular/core": { @@ -592,12 +629,13 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.0.3.tgz", - "integrity": "sha512-mxwQEeP94YBM6C9A2YfkV7ug1sHgh0fU/TSBpQcm5ni4cZiVPu6q/+Ft7hyFTKe2p3tKQme33+xVjsWhtOCx0A==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.1.3.tgz", + "integrity": "sha512-e9t5v/L1KqPLUQL+WU+d70MBBFcSRuwqbkluZgdDjdW5VelYjzlVzXdrzV6jFElP48T3kQCxJN1dAJkAvKjdOg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/core": "7.24.7", + "@babel/core": "7.24.9", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", @@ -615,26 +653,27 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.0.3", - "typescript": ">=5.4 <5.5" + "@angular/compiler": "18.1.3", + "typescript": ">=5.4 <5.6" } }, "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", - "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", + "version": "7.24.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", + "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helpers": "^7.24.7", - "@babel/parser": "^7.24.7", + "@babel/generator": "^7.24.9", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-module-transforms": "^7.24.9", + "@babel/helpers": "^7.24.8", + "@babel/parser": "^7.24.8", "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -653,24 +692,27 @@ "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 + "dev": true, + "license": "MIT" }, "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, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@angular/compiler-cli/node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.25.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -680,9 +722,10 @@ } }, "node_modules/@angular/core": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.0.3.tgz", - "integrity": "sha512-376hijhEqNpeA+qKncpVTIaZXRdBT6RctEBnFhJ2l57aHPH5S3oaSBQu1k3TEi07FlKOD4XF1+NzX9dvdup1eg==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.1.3.tgz", + "integrity": "sha512-1tFTyGLwio5oYAP2sMVDiOvy5wl/v0a4om7RTCpP2Bjro0ynuYe8FK7ilcmdyPXR1DF7GVdo/0R/eCIQJZ2PwA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -695,9 +738,10 @@ } }, "node_modules/@angular/forms": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.0.3.tgz", - "integrity": "sha512-+CjDiooUi5FkTP3YQmdO8YRbjZicgLGZonvCdz3mSucLrTY6w3oBocNs6+Kc7fLuO1NKSkFmAfYApBwK3fKBMg==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.1.3.tgz", + "integrity": "sha512-4kic/9hpS0HkbTORIkrdox7K40EcVT9VIbBruPoxX7jbfiW5jFaJ/05hLRvRt9RF8Sd9G+g5Uohmkcq/5hmsng==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -705,25 +749,27 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.0.3", - "@angular/core": "18.0.3", - "@angular/platform-browser": "18.0.3", + "@angular/common": "18.1.3", + "@angular/core": "18.1.3", + "@angular/platform-browser": "18.1.3", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.0.3.tgz", - "integrity": "sha512-urENnMjhSO4Jia7CnbchqN236dOIU6TC3CazwsQoj1Odch9x+iSFkx9Y0jXsiR5r/suK4uqKpK5N8MJ1PxDG1g==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-18.1.3.tgz", + "integrity": "sha512-1s1VQHJ6Gh84lCqgSEU6pNuPBpvee1mhfIZEE2lqxFu/tLe5gqvtTescFaTFLWY6I4e2RGAOU8WtRnFgFNxzGg==", "dev": true, + "license": "MIT", "engines": { "node": "^18.19.1 || ^20.11.1 || >=22.0.0" } }, "node_modules/@angular/platform-browser": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.0.3.tgz", - "integrity": "sha512-1fl/oJOca8BLxLxN0EjwxQZ3xzn3PCCN96ytM54bjdEMiELz+0AcQe5GNKcVjXlwMkibRLl1BP5GIdvnQYqJRA==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.1.3.tgz", + "integrity": "sha512-/k5Xt/WjOk6OlRqb1Wd0ZUQ3NjSbafQyDC9Icy0Mb8qJtiXZjA4VCMkZIiQD7cBxO0F/BsAiYnYNjWrIkCZICA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -731,9 +777,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "18.0.3", - "@angular/common": "18.0.3", - "@angular/core": "18.0.3" + "@angular/animations": "18.1.3", + "@angular/common": "18.1.3", + "@angular/core": "18.1.3" }, "peerDependenciesMeta": { "@angular/animations": { @@ -742,9 +788,10 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.0.3.tgz", - "integrity": "sha512-+kHMn7P552YKk1gkVQNO1QXzHVaIeFiVa1rV1MNvX4DvumKT3puknx1SzcmtxZTX+9ee22OuPuyLNSAKREDAQQ==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.1.3.tgz", + "integrity": "sha512-VhYfyPcdKrsLrkd5Lq7W+pqE49DZBpUeCqM/Q+s9rhTSiCCKe9Ikktq8yPZ9iHDpFr203P+T1EMHmILnLvf+gQ==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -752,16 +799,17 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.0.3", - "@angular/compiler": "18.0.3", - "@angular/core": "18.0.3", - "@angular/platform-browser": "18.0.3" + "@angular/common": "18.1.3", + "@angular/compiler": "18.1.3", + "@angular/core": "18.1.3", + "@angular/platform-browser": "18.1.3" } }, "node_modules/@angular/router": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.0.3.tgz", - "integrity": "sha512-/cglLev0USxUNMc4M+EBFGrqw1EpKq87LUJL3+0Ztr012sVSeOU38ad41fs6pPcMBePBDZIw7KmSXypvUJJFMA==", + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.1.3.tgz", + "integrity": "sha512-6fXiTgdUnaGGF32Un4+7LttG1N9rziansigvLBzFG//qYU0Ihk49phqDdWxz11iaJ+uK1YVafkjSFvV7z9cgDA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -769,9 +817,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.0.3", - "@angular/core": "18.0.3", - "@angular/platform-browser": "18.0.3", + "@angular/common": "18.1.3", + "@angular/core": "18.1.3", + "@angular/platform-browser": "18.1.3", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -780,6 +828,7 @@ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/highlight": "^7.24.7", "picocolors": "^1.0.0" @@ -789,30 +838,32 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.7.tgz", - "integrity": "sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", - "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.7.tgz", + "integrity": "sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.2", - "@babel/generator": "^7.24.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.24.5", - "@babel/helpers": "^7.24.5", - "@babel/parser": "^7.24.5", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.5", - "@babel/types": "^7.24.5", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helpers": "^7.24.7", + "@babel/parser": "^7.24.7", + "@babel/template": "^7.24.7", + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -831,24 +882,27 @@ "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 + "dev": true, + "license": "MIT" }, "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, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", - "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", + "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5", + "@babel/types": "^7.24.7", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -858,12 +912,13 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -874,6 +929,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" @@ -883,14 +939,15 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.7.tgz", - "integrity": "sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -903,24 +960,24 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.7.tgz", - "integrity": "sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz", + "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.8", "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-replace-supers": "^7.25.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/traverse": "^7.25.0", "semver": "^6.3.1" }, "engines": { @@ -930,44 +987,22 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-create-class-features-plugin/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, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "regexpu-core": "^5.3.1", @@ -980,23 +1015,12 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-create-regexp-features-plugin/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, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -1006,6 +1030,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -1022,31 +1047,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dev": true, - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.24.7" }, @@ -1055,13 +1056,14 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.7.tgz", - "integrity": "sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", + "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1072,6 +1074,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" @@ -1081,16 +1084,16 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.7.tgz", - "integrity": "sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -1099,23 +1102,12 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-optimise-call-expression": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.24.7" }, @@ -1124,23 +1116,25 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.7.tgz", - "integrity": "sha512-Rq76wjt7yz9AAc1KnlRKNAi/dMSVWgDRx43FHoJEbcYU6xOWaE2dVPwcdTukJrjxS65GITyfbvEYHvkirZ6uEg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1149,27 +1143,16 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1183,6 +1166,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" @@ -1196,6 +1180,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.24.7", "@babel/types": "^7.24.7" @@ -1205,22 +1190,24 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", - "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.7.tgz", - "integrity": "sha512-7MbVt6xrwFQbunH2DNQsAP5sTGxfqQtErvBIvIMi6EQnbgUOuVYanvREcmFrOPhoXBrTtjhhP+lW+o5UfK+tDg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -1230,42 +1217,45 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.7.tgz", - "integrity": "sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.7.tgz", - "integrity": "sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1276,6 +1266,7 @@ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", @@ -1287,10 +1278,14 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz", - "integrity": "sha512-9uUYRm6OqQrCqQdG1iCBwBPZgN8ciDBro2nIOFaiRz1/BCxaI7CNvQbDHvsArAC7Tw9Hda/B3U+6ui9u4HWXPw==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -1299,13 +1294,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" }, "engines": { "node": ">=6.9.0" @@ -1315,12 +1311,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "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.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.0", + "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.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1334,6 +1331,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", @@ -1347,13 +1345,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1367,6 +1366,7 @@ "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, + "license": "MIT", "engines": { "node": ">=6.9.0" }, @@ -1379,6 +1379,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1391,6 +1392,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -1403,6 +1405,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1418,6 +1421,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1430,6 +1434,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, @@ -1442,6 +1447,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -1457,6 +1463,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -1472,6 +1479,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -1484,6 +1492,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1496,6 +1505,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -1508,6 +1518,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1520,6 +1531,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -1532,6 +1544,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1544,6 +1557,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1556,6 +1570,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1568,6 +1583,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1583,6 +1599,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -1598,6 +1615,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -1614,6 +1632,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -1625,14 +1644,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", - "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", + "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/helper-environment-visitor": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7", "@babel/plugin-syntax-async-generators": "^7.8.4" }, "engines": { @@ -1643,14 +1663,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", - "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", + "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1664,6 +1685,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -1675,12 +1697,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1694,6 +1717,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -1710,6 +1734,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7", @@ -1723,18 +1748,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.7.tgz", - "integrity": "sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", + "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.0", "globals": "^11.1.0" }, "engines": { @@ -1744,35 +1768,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/template": "^7.24.7" @@ -1785,12 +1786,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.7.tgz", - "integrity": "sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", + "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1804,6 +1806,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -1820,6 +1823,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -1835,6 +1839,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-dynamic-import": "^7.8.3" @@ -1851,6 +1856,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -1867,6 +1873,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" @@ -1883,6 +1890,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" @@ -1895,14 +1903,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -1916,6 +1925,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-json-strings": "^7.8.3" @@ -1928,12 +1938,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1947,6 +1958,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" @@ -1963,6 +1975,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -1978,6 +1991,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -1990,13 +2004,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.7.tgz", - "integrity": "sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", + "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-module-transforms": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-simple-access": "^7.24.7" }, "engines": { @@ -2007,15 +2022,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -2029,6 +2045,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -2045,6 +2062,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -2061,6 +2079,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -2076,6 +2095,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" @@ -2092,6 +2112,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-numeric-separator": "^7.10.4" @@ -2108,6 +2129,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7", @@ -2126,6 +2148,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/helper-replace-supers": "^7.24.7" @@ -2142,6 +2165,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" @@ -2154,12 +2178,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.7.tgz", - "integrity": "sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", + "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, @@ -2175,6 +2200,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -2190,6 +2216,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -2206,6 +2233,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "@babel/helper-create-class-features-plugin": "^7.24.7", @@ -2219,23 +2247,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", - "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-property-literals": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -2251,6 +2268,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "regenerator-transform": "^0.15.2" @@ -2267,6 +2285,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -2278,13 +2297,14 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.3.tgz", - "integrity": "sha512-J0BuRPNlNqlMTRJ72eVptpt9VcInbxO6iP3jaxr+1NPhC0UkKL+6oeX6VXMEYdADnuqmMmsBspt4d5w8Y/TCbQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.3", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.1", "babel-plugin-polyfill-regenerator": "^0.6.1", @@ -2302,6 +2322,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -2311,6 +2332,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -2326,6 +2348,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" @@ -2342,6 +2365,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -2357,6 +2381,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -2368,12 +2393,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.7.tgz", - "integrity": "sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", + "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2387,6 +2413,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.24.7" }, @@ -2402,6 +2429,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -2418,6 +2446,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -2434,6 +2463,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.24.7", "@babel/helper-plugin-utils": "^7.24.7" @@ -2446,27 +2476,28 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.5.tgz", - "integrity": "sha512-UGK2ifKtcC8i5AI4cH+sbLLuLc2ktYSFJgBAXorKAsHUZmrQ1q6aQ6i3BvU24wWs2AAKqQB6kq3N9V9Gw1HiMQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.24.4", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.7.tgz", + "integrity": "sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-validator-option": "^7.24.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", "@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", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.1", - "@babel/plugin-syntax-import-attributes": "^7.24.1", + "@babel/plugin-syntax-import-assertions": "^7.24.7", + "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -2478,54 +2509,54 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.1", - "@babel/plugin-transform-async-generator-functions": "^7.24.3", - "@babel/plugin-transform-async-to-generator": "^7.24.1", - "@babel/plugin-transform-block-scoped-functions": "^7.24.1", - "@babel/plugin-transform-block-scoping": "^7.24.5", - "@babel/plugin-transform-class-properties": "^7.24.1", - "@babel/plugin-transform-class-static-block": "^7.24.4", - "@babel/plugin-transform-classes": "^7.24.5", - "@babel/plugin-transform-computed-properties": "^7.24.1", - "@babel/plugin-transform-destructuring": "^7.24.5", - "@babel/plugin-transform-dotall-regex": "^7.24.1", - "@babel/plugin-transform-duplicate-keys": "^7.24.1", - "@babel/plugin-transform-dynamic-import": "^7.24.1", - "@babel/plugin-transform-exponentiation-operator": "^7.24.1", - "@babel/plugin-transform-export-namespace-from": "^7.24.1", - "@babel/plugin-transform-for-of": "^7.24.1", - "@babel/plugin-transform-function-name": "^7.24.1", - "@babel/plugin-transform-json-strings": "^7.24.1", - "@babel/plugin-transform-literals": "^7.24.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", - "@babel/plugin-transform-member-expression-literals": "^7.24.1", - "@babel/plugin-transform-modules-amd": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-modules-systemjs": "^7.24.1", - "@babel/plugin-transform-modules-umd": "^7.24.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.24.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", - "@babel/plugin-transform-numeric-separator": "^7.24.1", - "@babel/plugin-transform-object-rest-spread": "^7.24.5", - "@babel/plugin-transform-object-super": "^7.24.1", - "@babel/plugin-transform-optional-catch-binding": "^7.24.1", - "@babel/plugin-transform-optional-chaining": "^7.24.5", - "@babel/plugin-transform-parameters": "^7.24.5", - "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.5", - "@babel/plugin-transform-property-literals": "^7.24.1", - "@babel/plugin-transform-regenerator": "^7.24.1", - "@babel/plugin-transform-reserved-words": "^7.24.1", - "@babel/plugin-transform-shorthand-properties": "^7.24.1", - "@babel/plugin-transform-spread": "^7.24.1", - "@babel/plugin-transform-sticky-regex": "^7.24.1", - "@babel/plugin-transform-template-literals": "^7.24.1", - "@babel/plugin-transform-typeof-symbol": "^7.24.5", - "@babel/plugin-transform-unicode-escapes": "^7.24.1", - "@babel/plugin-transform-unicode-property-regex": "^7.24.1", - "@babel/plugin-transform-unicode-regex": "^7.24.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoped-functions": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.24.7", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.24.7", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.7", + "@babel/plugin-transform-dotall-regex": "^7.24.7", + "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-dynamic-import": "^7.24.7", + "@babel/plugin-transform-exponentiation-operator": "^7.24.7", + "@babel/plugin-transform-export-namespace-from": "^7.24.7", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-json-strings": "^7.24.7", + "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-member-expression-literals": "^7.24.7", + "@babel/plugin-transform-modules-amd": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.7", + "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-umd": "^7.24.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-new-target": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-object-super": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.7", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-property-literals": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-reserved-words": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-template-literals": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.7", + "@babel/plugin-transform-unicode-escapes": "^7.24.7", + "@babel/plugin-transform-unicode-property-regex": "^7.24.7", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.10.4", @@ -2545,6 +2576,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -2554,6 +2586,7 @@ "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, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -2567,13 +2600,15 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/runtime": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "dev": true, + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2582,33 +2617,32 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.7.tgz", - "integrity": "sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2617,12 +2651,13 @@ } }, "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.7.tgz", - "integrity": "sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7", + "@babel/types": "^7.25.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -2631,25 +2666,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/types": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.7.tgz", - "integrity": "sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.7", + "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, @@ -2657,11 +2681,21 @@ "node": ">=6.9.0" } }, + "node_modules/@cfcs/core": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@cfcs/core/-/core-0.0.6.tgz", + "integrity": "sha512-FxfJMwoLB8MEMConeXUCqtMGqxdtePQxRBOiGip9ULcYYam3WfCgoY6xdnMaSkYvRvmosp5iuG+TiPofm65+Pw==", + "license": "MIT", + "dependencies": { + "@egjs/component": "^3.0.2" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2674,16 +2708,24 @@ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@daybrush/utils": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@daybrush/utils/-/utils-1.13.0.tgz", + "integrity": "sha512-ALK12C6SQNNHw1enXK+UO8bdyQ+jaWNQ1Af7Z3FNxeAwjYhQT7do+TRE4RASAJ3ObaS2+TJ7TXR3oz2Gzbw0PQ==", + "license": "MIT" + }, "node_modules/@develar/schema-utils": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" @@ -2701,6 +2743,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2717,6 +2760,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } @@ -2725,22 +2769,52 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/@discoveryjs/json-ext": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" } }, + "node_modules/@egjs/agent": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@egjs/agent/-/agent-2.4.3.tgz", + "integrity": "sha512-XvksSENe8wPeFlEVouvrOhKdx8HMniJ3by7sro2uPF3M6QqWwjzVcmvwoPtdjiX8O1lfRoLhQMp1a7NGlVTdIA==", + "license": "MIT" + }, + "node_modules/@egjs/children-differ": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@egjs/children-differ/-/children-differ-1.0.1.tgz", + "integrity": "sha512-DRvyqMf+CPCOzAopQKHtW+X8iN6Hy6SFol+/7zCUiE5y4P/OB8JP8FtU4NxtZwtafvSL4faD5KoQYPj3JHzPFQ==", + "license": "MIT", + "dependencies": { + "@egjs/list-differ": "^1.0.0" + } + }, + "node_modules/@egjs/component": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@egjs/component/-/component-3.0.5.tgz", + "integrity": "sha512-cLcGizTrrUNA2EYE3MBmEDt2tQv1joVP1Q3oDisZ5nw0MZDx2kcgEXM+/kZpfa/PAkFvYVhRUZwytIQWoN3V/w==", + "license": "MIT" + }, + "node_modules/@egjs/list-differ": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@egjs/list-differ/-/list-differ-1.0.1.tgz", + "integrity": "sha512-OTFTDQcWS+1ZREOdCWuk5hCBgYO4OsD30lXcOCyVOAjXMhgL5rBRDnt/otb6Nz8CzU0L/igdcaQBDLWc4t9gvg==", + "license": "MIT" + }, "node_modules/@electron/asar": { "version": "3.2.10", "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.10.tgz", "integrity": "sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw==", "dev": true, + "license": "MIT", "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", @@ -2758,6 +2832,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2768,6 +2843,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2780,6 +2856,7 @@ "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", @@ -2801,6 +2878,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -2810,6 +2888,7 @@ "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.1", @@ -2824,6 +2903,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -2839,6 +2919,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -2851,6 +2932,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -2860,6 +2942,7 @@ "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "compare-version": "^0.1.2", "debug": "^4.3.4", @@ -2881,6 +2964,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -2895,6 +2979,7 @@ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8.0.0" }, @@ -2907,6 +2992,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -2919,6 +3005,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -2928,6 +3015,7 @@ "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", "dev": true, + "license": "MIT", "dependencies": { "@electron/asar": "^3.2.1", "@malept/cross-spawn-promise": "^1.1.0", @@ -2946,6 +3034,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2956,6 +3045,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -2971,6 +3061,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -2983,6 +3074,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2995,18 +3087,20 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.3.tgz", - "integrity": "sha512-yTgnwQpFVYfvvo4SvRFB0SwrW8YjOxEoT7wfMT7Ol5v7v5LDNvSGo67aExmxOb87nQNeWPVvaGBNfQ7BXcrZ9w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -3016,13 +3110,14 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.3.tgz", - "integrity": "sha512-bviJOLMgurLJtF1/mAoJLxDZDL6oU5/ztMHnJQRejbJrSc9FFu0QoUoFhvi6qSKJEw9y5oGyvr9fuDtzJ30rNQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -3032,13 +3127,14 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.3.tgz", - "integrity": "sha512-c+ty9necz3zB1Y+d/N+mC6KVVkGUUOcm4ZmT5i/Fk5arOaY3i6CA3P5wo/7+XzV8cb4GrI/Zjp8NuOQ9Lfsosw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -3048,13 +3144,14 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.3.tgz", - "integrity": "sha512-JReHfYCRK3FVX4Ra+y5EBH1b9e16TV2OxrPAvzMsGeES0X2Ndm9ImQRI4Ket757vhc5XBOuGperw63upesclRw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -3064,13 +3161,14 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.3.tgz", - "integrity": "sha512-U3fuQ0xNiAkXOmQ6w5dKpEvXQRSpHOnbw7gEfHCRXPeTKW9sBzVck6C5Yneb8LfJm0l6le4NQfkNPnWMSlTFUQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -3080,13 +3178,14 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.3.tgz", - "integrity": "sha512-3m1CEB7F07s19wmaMNI2KANLcnaqryJxO1fXHUV5j1rWn+wMxdUYoPyO2TnAbfRZdi7ADRwJClmOwgT13qlP3Q==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -3096,13 +3195,14 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.3.tgz", - "integrity": "sha512-fsNAAl5pU6wmKHq91cHWQT0Fz0vtyE1JauMzKotrwqIKAswwP5cpHUCxZNSTuA/JlqtScq20/5KZ+TxQdovU/g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -3112,13 +3212,14 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.3.tgz", - "integrity": "sha512-tci+UJ4zP5EGF4rp8XlZIdq1q1a/1h9XuronfxTMCNBslpCtmk97Q/5qqy1Mu4zIc0yswN/yP/BLX+NTUC1bXA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -3128,13 +3229,14 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.3.tgz", - "integrity": "sha512-f6kz2QpSuyHHg01cDawj0vkyMwuIvN62UAguQfnNVzbge2uWLhA7TCXOn83DT0ZvyJmBI943MItgTovUob36SQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3144,13 +3246,14 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.3.tgz", - "integrity": "sha512-vvG6R5g5ieB4eCJBQevyDMb31LMHthLpXTc2IGkFnPWS/GzIFDnaYFp558O+XybTmYrVjxnryru7QRleJvmZ6Q==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3160,13 +3263,14 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.3.tgz", - "integrity": "sha512-HjCWhH7K96Na+66TacDLJmOI9R8iDWDDiqe17C7znGvvE4sW1ECt9ly0AJ3dJH62jHyVqW9xpxZEU1jKdt+29A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3176,13 +3280,14 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.3.tgz", - "integrity": "sha512-BGpimEccmHBZRcAhdlRIxMp7x9PyJxUtj7apL2IuoG9VxvU/l/v1z015nFs7Si7tXUwEsvjc1rOJdZCn4QTU+Q==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3192,13 +3297,14 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.3.tgz", - "integrity": "sha512-5rMOWkp7FQGtAH3QJddP4w3s47iT20hwftqdm7b+loe95o8JU8ro3qZbhgMRy0VuFU0DizymF1pBKkn3YHWtsw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3208,13 +3314,14 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.3.tgz", - "integrity": "sha512-h0zj1ldel89V5sjPLo5H1SyMzp4VrgN1tPkN29TmjvO1/r0MuMRwJxL8QY05SmfsZRs6TF0c/IDH3u7XYYmbAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3224,13 +3331,14 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.3.tgz", - "integrity": "sha512-dkAKcTsTJ+CRX6bnO17qDJbLoW37npd5gSNtSzjYQr0svghLJYGYB0NF1SNcU1vDcjXLYS5pO4qOW4YbFama4A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3240,13 +3348,14 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.3.tgz", - "integrity": "sha512-vnD1YUkovEdnZWEuMmy2X2JmzsHQqPpZElXx6dxENcIwTu+Cu5ERax6+Ke1QsE814Zf3c6rxCfwQdCTQ7tPuXA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3256,13 +3365,14 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.3.tgz", - "integrity": "sha512-IOXOIm9WaK7plL2gMhsWJd+l2bfrhfilv0uPTptoRoSb2p09RghhQQp9YY6ZJhk/kqmeRt6siRdMSLLwzuT0KQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3272,13 +3382,14 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.3.tgz", - "integrity": "sha512-uTgCwsvQ5+vCQnqM//EfDSuomo2LhdWhFPS8VL8xKf+PKTCrcT/2kPPoWMTs22aB63MLdGMJiE3f1PHvCDmUOw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -3288,13 +3399,14 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.3.tgz", - "integrity": "sha512-vNAkR17Ub2MgEud2Wag/OE4HTSI6zlb291UYzHez/psiKarp0J8PKGDnAhMBcHFoOHMXHfExzmjMojJNbAStrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -3304,13 +3416,14 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.3.tgz", - "integrity": "sha512-W8H9jlGiSBomkgmouaRoTXo49j4w4Kfbl6I1bIdO/vT0+0u4f20ko3ELzV3hPI6XV6JNBVX+8BC+ajHkvffIJA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -3320,13 +3433,14 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.3.tgz", - "integrity": "sha512-EjEomwyLSCg8Ag3LDILIqYCZAq/y3diJ04PnqGRgq8/4O3VNlXyMd54j/saShaN4h5o5mivOjAzmU6C3X4v0xw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3336,13 +3450,14 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.3.tgz", - "integrity": "sha512-WGiE/GgbsEwR33++5rzjiYsKyHywE8QSZPF7Rfx9EBfK3Qn3xyR6IjyCr5Uk38Kg8fG4/2phN7sXp4NPWd3fcw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3352,13 +3467,14 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.3.tgz", - "integrity": "sha512-xRxC0jaJWDLYvcUvjQmHCJSfMrgmUuvsoXgDeU/wTorQ1ngDdUBuFtgY3W1Pc5sprGAvZBtWdJX7RPg/iZZUqA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3372,6 +3488,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.3.0" }, @@ -3382,25 +3499,79 @@ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.1.tgz", - "integrity": "sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.17.1.tgz", + "integrity": "sha512-BlYOpej8AQ8Ev9xVqroV7a02JK3SkBAaN9GfMMH9W6Ch8FlQlkjGw4Ir7+FgYwfirivAf4t+GtzuAxqfukmISA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/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, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/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, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -3408,7 +3579,7 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -3419,6 +3590,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3430,60 +3602,43 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/@eslint/eslintrc/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, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/@eslint/eslintrc/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 + "dev": true, + "license": "MIT" }, "node_modules/@eslint/eslintrc/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, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3491,23 +3646,22 @@ "node": "*" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@eslint/js": { + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.8.0.tgz", + "integrity": "sha512-MfluB7EUfxXtv3i/++oh89uzAr4PDI4nn201hsp+qaXqsjAWzinlZEHEfPgAX4doIlKvPG/i0A9dpKxOLII8yA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/js": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.5.0.tgz", - "integrity": "sha512-A7+AOT2ICkodvtsWnxZP4Xxk3NbZ3VMHd8oihydLRGrJgqqdEz1qSeEgXYyT/Cu8h1TWWsQRejIx48mtjZ5y1w==", + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -3516,59 +3670,25 @@ "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@hapi/topo": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/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/@humanwhocodes/config-array/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/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -3577,1338 +3697,1939 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true - }, - "node_modules/@inquirer/figures": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.3.tgz", - "integrity": "sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==", + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@interactjs/types": { - "version": "1.10.27", - "resolved": "https://registry.npmjs.org/@interactjs/types/-/types-1.10.27.tgz", - "integrity": "sha512-BUdv0cvs4H5ODuwft2Xp4eL8Vmi3LcihK42z0Ft/FbVJZoRioBsxH+LlsBdK4tAie7PqlKGy+1oyOncu1nQ6eA==" - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@inquirer/checkbox": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.4.5.tgz", + "integrity": "sha512-+YlCyS6JBWeZugIvReh/YL5HJcowlklz5RykQuYKQfgWQeCJh5Us0nWcRddvIVkjmYa0I/8bwWioSLu850J8sA==", "dev": true, + "license": "MIT", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@inquirer/core": "^9.0.8", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.1", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/@inquirer/checkbox/node_modules/@inquirer/core": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.8.tgz", + "integrity": "sha512-ttnI/BGlP9SxjbQnv1nssv7dPAwiR82KmjJZx2SxSZyi2mGbaEvh4jg0I4yU/4mVQf7QvCVGGr/hGuJFEYhwnw==", "dev": true, - "engines": { - "node": ">=12" + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.0.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/@inquirer/checkbox/node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "license": "MIT", + "dependencies": { + "undici-types": "~6.13.0" } }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "node_modules/@inquirer/checkbox/node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", + "dev": true, + "license": "MIT" }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/@inquirer/confirm": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.11.tgz", + "integrity": "sha512-3wWw10VPxQP279FO4bzWsf8YjIAq7NdwATJ4xS2h1uwsXZu/RmtOVV95rZ7yllS1h/dzu+uLewjMAzNDEj8h2w==", "dev": true, + "license": "MIT", "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "@inquirer/core": "^8.2.4", + "@inquirer/type": "^1.3.3" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/@inquirer/core": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-8.2.4.tgz", + "integrity": "sha512-7vsXSfxtrrbwMTirfaKwPcjqJy7pzeuF/bP62yo1NQrRJ5HjmMlrhZml/Ljm9ODc1RnbhJlTeSnCkjtFddKjwA==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "@inquirer/figures": "^1.0.3", + "@inquirer/type": "^1.3.3", + "@types/mute-stream": "^0.0.4", + "@types/node": "^20.14.9", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "picocolors": "^1.0.1", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/@inquirer/editor": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.1.20.tgz", + "integrity": "sha512-vtIN9NwXldX8SWbPt5biJhnTpHJCzF5nSymcv4hcOxiCrOpXmgOvFYGpAY729KODF+5e1OLqPbJ8ApiwPu/peQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "@inquirer/core": "^9.0.8", + "@inquirer/type": "^1.5.1", + "external-editor": "^3.1.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=18" } }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@inquirer/editor/node_modules/@inquirer/core": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.8.tgz", + "integrity": "sha512-ttnI/BGlP9SxjbQnv1nssv7dPAwiR82KmjJZx2SxSZyi2mGbaEvh4jg0I4yU/4mVQf7QvCVGGr/hGuJFEYhwnw==", "dev": true, + "license": "MIT", "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.0.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@inquirer/editor/node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "dependencies": { + "undici-types": "~6.13.0" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "node_modules/@inquirer/editor/node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/expand": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.1.20.tgz", + "integrity": "sha512-ruUTCUGKhe6TvDM3/gKjX9v7D5cWbiuawFE6aF/cFmNO79R/zMjrFFVoueDM8FRw8yXqnREb0jFkYF1LUxnDNA==", + "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "@inquirer/core": "^9.0.8", + "@inquirer/type": "^1.5.1", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "node_modules/@inquirer/expand/node_modules/@inquirer/core": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.8.tgz", + "integrity": "sha512-ttnI/BGlP9SxjbQnv1nssv7dPAwiR82KmjJZx2SxSZyi2mGbaEvh4jg0I4yU/4mVQf7QvCVGGr/hGuJFEYhwnw==", "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.0.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" + "node": ">=18" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "node_modules/@inquirer/expand/node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" + "undici-types": "~6.13.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "node_modules/@inquirer/expand/node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } + "license": "MIT" }, - "node_modules/@jsonjoy.com/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", + "node_modules/@inquirer/figures": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.5.tgz", + "integrity": "sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" + "node": ">=18" } }, - "node_modules/@jsonjoy.com/json-pack": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.0.4.tgz", - "integrity": "sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg==", + "node_modules/@inquirer/input": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.2.7.tgz", + "integrity": "sha512-QFk31Gq4Wr+Ve9ilMiFGGrSjGZQBilV0cgTN1zubD98Bx65fsNrh8++Biy/9mjNKRaqHFbZBw5baAcQvOmW8OQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jsonjoy.com/base64": "^1.1.1", - "@jsonjoy.com/util": "^1.1.2", - "hyperdyperid": "^1.2.0", - "thingies": "^1.20.0" + "@inquirer/core": "^9.0.8", + "@inquirer/type": "^1.5.1" }, "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" + "node": ">=18" } }, - "node_modules/@jsonjoy.com/util": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.2.0.tgz", - "integrity": "sha512-4B8B+3vFsY4eo33DMKyJPlQ3sBMpPFUZK2dr3O3rXrOGKKbYG44J0XSFkDo1VOQiri5HFEhIeVvItjR2xcazmg==", + "node_modules/@inquirer/input/node_modules/@inquirer/core": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.8.tgz", + "integrity": "sha512-ttnI/BGlP9SxjbQnv1nssv7dPAwiR82KmjJZx2SxSZyi2mGbaEvh4jg0I4yU/4mVQf7QvCVGGr/hGuJFEYhwnw==", "dev": true, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.0.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, - "peerDependencies": { - "tslib": "2" + "engines": { + "node": ">=18" } }, - "node_modules/@kurkle/color": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "dev": true - }, - "node_modules/@ljharb/through": { - "version": "2.3.13", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.13.tgz", - "integrity": "sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==", + "node_modules/@inquirer/input/node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" + "undici-types": "~6.13.0" } }, - "node_modules/@lmdb/lmdb-darwin-arm64": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.8.tgz", - "integrity": "sha512-+lFwFvU+zQ9zVIFETNtmW++syh3Ps5JS8MPQ8zOYtQZoU+dTR8ivWHTaE2QVk1JG2payGDLUAvpndLAjGMdeeA==", - "cpu": [ - "arm64" - ], + "node_modules/@inquirer/input/node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", "dev": true, - "optional": true, - "os": [ - "darwin" - ] + "license": "MIT" }, - "node_modules/@lmdb/lmdb-darwin-x64": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.8.tgz", - "integrity": "sha512-T98rfsgfdQMS5/mqdsPb6oHSJ+iBYNa+PQDLtXLh6rzTEBsYP9x2uXxIj6VS4qXVDWXVi8rv85NCOG+UBOsHXQ==", - "cpu": [ - "x64" - ], + "node_modules/@inquirer/password": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.1.20.tgz", + "integrity": "sha512-il2TG7xDlfiLE3cnOCxfDfrwvsiSmXjVd26hvf4tdzHvdisgLiEjbN6mi51/TnlSQ+2Qc69+9jIq3ws93nhS2w==", "dev": true, - "optional": true, - "os": [ - "darwin" - ] + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.8", + "@inquirer/type": "^1.5.1", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } }, - "node_modules/@lmdb/lmdb-linux-arm": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.8.tgz", - "integrity": "sha512-gVNCi3bYWatdPMeFpFjuZl6bzVL55FkeZU3sPeU+NsMRXC+Zl3qOx3M6cM4OMlJWbhHjYjf2b8q83K0mczaiWQ==", - "cpu": [ - "arm" - ], + "node_modules/@inquirer/password/node_modules/@inquirer/core": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.8.tgz", + "integrity": "sha512-ttnI/BGlP9SxjbQnv1nssv7dPAwiR82KmjJZx2SxSZyi2mGbaEvh4jg0I4yU/4mVQf7QvCVGGr/hGuJFEYhwnw==", "dev": true, - "optional": true, - "os": [ - "linux" - ] + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.0.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } }, - "node_modules/@lmdb/lmdb-linux-arm64": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.8.tgz", - "integrity": "sha512-uEBGCQIChsixpykL0pjCxfF64btv64vzsb1NoM5u0qvabKvKEvErhXGoqovyldDu9u1T/fswD8Kf6ih0vJEvDQ==", - "cpu": [ - "arm64" - ], + "node_modules/@inquirer/password/node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", "dev": true, - "optional": true, - "os": [ - "linux" - ] + "license": "MIT", + "dependencies": { + "undici-types": "~6.13.0" + } }, - "node_modules/@lmdb/lmdb-linux-x64": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.8.tgz", - "integrity": "sha512-6v0B4sa9ulNezmDZtVpLjNHmA0qZzUl3001YJ2RF0naxsuv/Jq/xEwNYpOzfcdizHfpCE0oBkWzk/r+Slr+0zw==", - "cpu": [ - "x64" - ], + "node_modules/@inquirer/password/node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", "dev": true, - "optional": true, - "os": [ - "linux" - ] + "license": "MIT" }, - "node_modules/@lmdb/lmdb-win32-x64": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.8.tgz", - "integrity": "sha512-lDLGRIMqdwYD39vinwNqqZUxCdL2m2iIdn+0HyQgIHEiT0g5rIAlzaMKzoGWon5NQumfxXFk9y0DarttkR7C1w==", - "cpu": [ - "x64" - ], + "node_modules/@inquirer/prompts": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.0.7.tgz", + "integrity": "sha512-GFcigCxJTKCH3aECzMIu4FhgLJWnFvMXzpI4CCSoELWFtkOOU2P+goYA61+OKpGrB8fPE7q6n8zAXBSlZRrHjQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ] + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^2.3.7", + "@inquirer/confirm": "^3.1.11", + "@inquirer/editor": "^2.1.11", + "@inquirer/expand": "^2.1.11", + "@inquirer/input": "^2.1.11", + "@inquirer/password": "^2.1.11", + "@inquirer/rawlist": "^2.1.11", + "@inquirer/select": "^2.3.7" + }, + "engines": { + "node": ">=18" + } }, - "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", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "node_modules/@inquirer/rawlist": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.2.2.tgz", + "integrity": "sha512-U4OsvqjdLB6nmf5ZDshPYMq0b+qd6JWxTrvRTiMfwUY6cFxkR9YWKarLXFhndf7tawQ8f3DwU9P9wryDc2ESSA==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/malept" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" - } - ], + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.1" + "@inquirer/core": "^9.0.8", + "@inquirer/type": "^1.5.1", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": ">= 10" + "node": ">=18" } }, - "node_modules/@malept/flatpak-bundler": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", - "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "node_modules/@inquirer/rawlist/node_modules/@inquirer/core": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.8.tgz", + "integrity": "sha512-ttnI/BGlP9SxjbQnv1nssv7dPAwiR82KmjJZx2SxSZyi2mGbaEvh4jg0I4yU/4mVQf7QvCVGGr/hGuJFEYhwnw==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.0", - "lodash": "^4.17.15", - "tmp-promise": "^3.0.2" + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.0.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": ">= 10.0.0" + "node": ">=18" } }, - "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "node_modules/@inquirer/rawlist/node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.13.0" + } + }, + "node_modules/@inquirer/rawlist/node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/select": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.4.5.tgz", + "integrity": "sha512-DbCthH3l7vrrK+Ewll3bgzxC3dzMle8xkWYta4if31p9NOmFNhZKhSfdYMjaOtGFBCUEwo4D5LMgN6sPKgUWIw==", + "dev": true, + "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@inquirer/core": "^9.0.8", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.1", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/@inquirer/select/node_modules/@inquirer/core": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.8.tgz", + "integrity": "sha512-ttnI/BGlP9SxjbQnv1nssv7dPAwiR82KmjJZx2SxSZyi2mGbaEvh4jg0I4yU/4mVQf7QvCVGGr/hGuJFEYhwnw==", "dev": true, + "license": "MIT", "dependencies": { - "universalify": "^2.0.0" + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.1", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.0.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": ">=18" } }, - "node_modules/@malept/flatpak-bundler/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/@inquirer/select/node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", "dev": true, - "engines": { - "node": ">= 10.0.0" + "license": "MIT", + "dependencies": { + "undici-types": "~6.13.0" } }, - "node_modules/@mdi/font": { - "version": "7.4.47", - "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz", - "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==" + "node_modules/@inquirer/select/node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", + "dev": true, + "license": "MIT" }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", - "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", - "cpu": [ - "arm64" - ], + "node_modules/@inquirer/type": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.1.tgz", + "integrity": "sha512-m3YgGQlKNS0BM+8AFiJkCsTqHEFCWn6s/Rqye3mYwvqY6LdfUv12eSwbsgNzrYyrLXiy7IrrjDLPysaSBwEfhw==", "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", - "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", - "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@ngtools/webpack": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.0.4.tgz", - "integrity": "sha512-eWQkAuHEnLme01Ey4Z0FoG6upJHYhnJfsCTBnyEB2LTfdyBUk+PC0gwPXInK8oltWjFfiMnCwxrUQvQsvPW7Hg==", - "dev": true, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" }, - "peerDependencies": { - "@angular/compiler-cli": "^18.0.0", - "typescript": ">=5.4 <5.5", - "webpack": "^5.54.0" + "engines": { + "node": ">=18" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, + "license": "ISC", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, + "license": "MIT", "engines": { - "node": ">= 8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@npmcli/agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", - "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, + "license": "MIT", "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.3" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@npmcli/agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^4.3.4" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 14" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/@npmcli/agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "license": "MIT", "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">= 14" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, + "license": "MIT", "engines": { - "node": "14 || >=16.14" + "node": ">=8" } }, - "node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, + "license": "MIT", "dependencies": { - "semver": "^7.3.5" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6.0.0" } }, - "node_modules/@npmcli/git": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.7.tgz", - "integrity": "sha512-WaOVvto604d5IpdCRV2KjQu8PzkfE96d50CQGKgywXh2GxXmDeUO5EWcBC4V57uFyrNqx83+MewuJh3WTR3xPA==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, - "dependencies": { - "@npmcli/promise-spawn": "^7.0.0", - "lru-cache": "^10.0.1", - "npm-pick-manifest": "^9.0.0", - "proc-log": "^4.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=6.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==", + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=16" + "node": ">=6.0.0" } }, - "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/@npmcli/git/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/@npmcli/installed-package-contents": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", - "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, + "license": "MIT", "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "bin": { - "installed-package-contents": "bin/index.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@npmcli/node-gyp": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", - "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/@npmcli/package-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.0.tgz", - "integrity": "sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==", + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, + "license": "MIT", "dependencies": { - "@npmcli/git": "^5.0.0", - "glob": "^10.2.2", - "hosted-git-info": "^7.0.0", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "proc-log": "^4.0.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@npmcli/package-json/node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, + "license": "Apache-2.0", "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=10.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/package-json/node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", - "dev": true, - "dependencies": { - "lru-cache": "^10.0.1" + "type": "github", + "url": "https://github.com/sponsors/streamich" }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/package-json/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" + "peerDependencies": { + "tslib": "2" } }, - "node_modules/@npmcli/package-json/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.0.4.tgz", + "integrity": "sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@npmcli/promise-spawn": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", - "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", - "dev": true, - "dependencies": { - "which": "^4.0.0" + "type": "github", + "url": "https://github.com/sponsors/streamich" }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "peerDependencies": { + "tslib": "2" } }, - "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==", + "node_modules/@jsonjoy.com/util": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.3.0.tgz", + "integrity": "sha512-Cebt4Vk7k1xHy87kHY7KSPLT77A7Ev7IfOblyLZhtYEhrdQ6fX4EoLq3xOQ3O/DRMEh2ok5nyC180E+ABS8Wmw==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=16" - } - }, - "node_modules/@npmcli/promise-spawn/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" + "node": ">=10.0" }, - "bin": { - "node-which": "bin/which.js" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" }, - "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/redact": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", - "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", - "dev": true, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "peerDependencies": { + "tslib": "2" } }, - "node_modules/@npmcli/run-script": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", - "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", - "dev": true, - "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/package-json": "^5.0.0", - "@npmcli/promise-spawn": "^7.0.0", - "node-gyp": "^10.0.0", - "proc-log": "^4.0.0", - "which": "^4.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", + "license": "MIT" }, - "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==", + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true, - "engines": { - "node": ">=16" - } + "license": "MIT" }, - "node_modules/@npmcli/run-script/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.13.tgz", + "integrity": "sha512-nAl6teTt7EWSjttNavAnv3uFR3w3vPP3OTYmHyPNHzKhAj2NoBDHmbS3MGpvvO8KXXPASnHjEGrrKrdKTMKPnQ==", "dev": true, + "license": "MIT", "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" + "@inquirer/type": "^1.3.3" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">=18.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 6" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.12.tgz", + "integrity": "sha512-vgTwzNUD3Hy4aqtGhX2+nV/usI0mwy3hDRuTjs8VcK0BLiMVEpNQXgzwlWEgPmA8AAPloUgyOs2nK5clJF5oIg==", + "cpu": [ + "arm64" + ], "dev": true, + "license": "MIT", "optional": true, - "engines": { - "node": ">=14" - } + "os": [ + "darwin" + ] }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.12.tgz", + "integrity": "sha512-qOt0hAhj2ZLY6aEWu85rzt5zcyCAQITMhCMEPNlo1tuYekpVAdkQNiwXxEkCjBYvwTskvXuwXOOUpjuSc+aJnA==", "cpu": [ - "arm" + "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "android" + "darwin" ] }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.12.tgz", + "integrity": "sha512-Ggd/UXpE+alMncbELCXA3OKpDj9bDBR3qVO7WRTxstloDglRAHfZmUJgTkeaNKjFO1JHqS7AKy0jba9XebZB1w==", "cpu": [ - "arm64" + "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "android" + "linux" ] }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.12.tgz", + "integrity": "sha512-Qy4cFXFe9h1wAWMsojex8x1ifvw2kqiZv686YiRTdQEzAfc3vJASHFcD/QejHUCx7YHMYdnUoCS45rG2AiGDTQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "darwin" + "linux" ] }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.12.tgz", + "integrity": "sha512-c+noT9IofktxktFllKHFmci8ka2SYGSLN17pj/KSl1hg7mmfAiGp4xxFxEwMLTb+SX95vP1DFiR++1I3WLVxvA==", "cpu": [ "x64" ], "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", - "cpu": [ - "arm" - ], - "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.12.tgz", + "integrity": "sha512-CO3MFV8gUx16NU/CyyuumAKblESwvoGVA2XhQKZ976OTOxaTbb8F8D3f0iiZ4MYqsN74jIrFuCmXpPnpjbhfOQ==", "cpu": [ - "arm" + "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "win32" ] }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", - "cpu": [ - "arm64" + "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", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", "dev": true, - "optional": true, - "os": [ - "linux" - ] + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", - "cpu": [ - "arm64" - ], + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ] + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", - "cpu": [ - "ppc64" - ], + "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ] + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", - "cpu": [ - "riscv64" - ], + "node_modules/@malept/flatpak-bundler/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "optional": true, - "os": [ - "linux" - ] + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "node_modules/@mdi/font": { + "version": "7.4.47", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz", + "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==", + "license": "Apache-2.0" + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", "cpu": [ - "s390x" + "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ] }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ] }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", "cpu": [ - "x64" + "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ] }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", "cpu": [ - "ia32" + "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ] }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, - "node_modules/@schematics/angular": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.0.4.tgz", - "integrity": "sha512-fN4whuym9ZmcQFdTfwLZr4j+NcZ4LzbdLk8XYrYdxt1z8c9ujs5LqJYn0LYc3UWiYl7z2RVc9NOxzNrkiXdwlw==", + "node_modules/@ngtools/webpack": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.1.3.tgz", + "integrity": "sha512-VmqOO8CcXKL06anNYlL0OkrqIuBNZQu5n0YVP4z8oneJhDBqwK2++dK0WpcNyIFcg3HsQ7w3BuqUWJ4iPiWxEQ==", "dev": true, - "dependencies": { - "@angular-devkit/core": "18.0.4", - "@angular-devkit/schematics": "18.0.4", - "jsonc-parser": "3.2.1" - }, + "license": "MIT", "engines": { "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "typescript": ">=5.4 <5.6", + "webpack": "^5.54.0" } }, - "node_modules/@sideway/address": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", - "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { - "@hapi/hoek": "^9.0.0" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@sideway/formula": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", - "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", - "dev": true - }, - "node_modules/@sideway/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", - "dev": true - }, - "node_modules/@sigstore/bundle": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", - "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, - "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2" - }, + "license": "MIT", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 8" } }, - "node_modules/@sigstore/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", - "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 8" } }, - "node_modules/@sigstore/protobuf-specs": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz", - "integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==", + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", "dev": true, + "license": "ISC", + "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.3" + }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@sigstore/sign": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", - "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", + "node_modules/@npmcli/agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, + "license": "MIT", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.2", - "make-fetch-happen": "^13.0.1", - "proc-log": "^4.2.0", - "promise-retry": "^2.0.1" + "debug": "^4.3.4" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 14" } }, - "node_modules/@sigstore/tuf": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", - "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", + "node_modules/@npmcli/agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, + "license": "MIT", "dependencies": { - "@sigstore/protobuf-specs": "^0.3.2", - "tuf-js": "^2.2.1" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 14" } }, - "node_modules/@sigstore/verify": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", - "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", "dev": true, + "license": "ISC", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.1.0", - "@sigstore/protobuf-specs": "^0.3.2" + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", + "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^4.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "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, + "license": "ISC", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" + "node": ">=16" } }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, + "license": "ISC", "dependencies": { - "defer-to-connect": "^2.0.0" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">=10" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, "engines": { - "node": ">= 10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@tufjs/canonical-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", - "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", "dev": true, + "license": "ISC", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/@tufjs/models": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", - "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "node_modules/@npmcli/package-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.0.tgz", + "integrity": "sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==", "dev": true, + "license": "ISC", "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.4" + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^4.0.0", + "semver": "^7.5.3" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=16 || 14 >=14.17" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "node_modules/@npmcli/package-json/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, + "license": "ISC", "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "node_modules/@npmcli/package-json/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "dependencies": { - "@types/node": "*" - } + "license": "ISC" }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "node_modules/@npmcli/promise-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", + "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", "dev": true, + "license": "ISC", "dependencies": { - "@types/node": "*" - } + "which": "^4.0.0" + }, + "engines": { + "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, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", + "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", + "which": "^4.0.0" + }, + "engines": { + "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, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", + "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", + "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", + "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", + "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", + "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", + "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", + "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", + "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", + "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", + "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", + "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", + "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", + "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", + "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", + "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", + "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@scena/dragscroll": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scena/dragscroll/-/dragscroll-1.4.0.tgz", + "integrity": "sha512-3O8daaZD9VXA9CP3dra6xcgt/qrm0mg0xJCwiX6druCteQ9FFsXffkF8PrqxY4Z4VJ58fFKEa0RlKqbsi/XnRA==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.6.0", + "@scena/event-emitter": "^1.0.2" + } + }, + "node_modules/@scena/event-emitter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@scena/event-emitter/-/event-emitter-1.0.5.tgz", + "integrity": "sha512-AzY4OTb0+7ynefmWFQ6hxDdk0CySAq/D4efljfhtRHCOP7MBF9zUfhKG3TJiroVjASqVgkRJFdenS8ArZo6Olg==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.1.1" + } + }, + "node_modules/@scena/matrix": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scena/matrix/-/matrix-1.1.1.tgz", + "integrity": "sha512-JVKBhN0tm2Srl+Yt+Ywqu0oLgLcdemDQlD1OxmN9jaCTwaFPZ7tY8n6dhVgMEaR9qcR7r+kAlMXnSfNyYdE+Vg==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.4.0" + } + }, + "node_modules/@schematics/angular": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.1.3.tgz", + "integrity": "sha512-VyoL7O+3eL+BazmoWzexFpVy9k0MoOAmff3XqKLhP3/V7eXPc9s7znIDpPp28QF0V/Y2xMaGDWhqTx2CFcz4Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "18.1.3", + "@angular-devkit/schematics": "18.1.3", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@sigstore/bundle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", + "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", + "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz", + "integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", + "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", + "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", + "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tufjs/canonical-json": { + "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, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", + "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/body-parser": { + "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, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/connect-history-api-fallback": { "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, + "license": "MIT", "dependencies": { "@types/express-serve-static-core": "*", "@types/node": "*" @@ -4919,15 +5640,17 @@ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/ms": "*" } }, "node_modules/@types/eslint": { - "version": "8.56.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.10.tgz", - "integrity": "sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.0.tgz", + "integrity": "sha512-gi6WQJ7cHRgZxtkQEoyHMppPjq9Kxo5Tjn2prSKDSmZrCz8TZ3jSRCeTJm+WoM+oB0WG37bRqLzaaU3q7JypGg==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4938,6 +5661,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz", "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint": "*" } @@ -4947,6 +5671,7 @@ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -4956,13 +5681,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/@types/express": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -4975,6 +5702,7 @@ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz", "integrity": "sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/qs": "*", @@ -4987,6 +5715,7 @@ "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -4995,25 +5724,29 @@ "version": "7946.0.14", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/http-cache-semantics": { "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 + "dev": true, + "license": "MIT" }, "node_modules/@types/http-errors": { "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 + "dev": true, + "license": "MIT" }, "node_modules/@types/http-proxy": { "version": "1.17.14", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -5022,13 +5755,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -5038,6 +5773,7 @@ "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.12.tgz", "integrity": "sha512-BK7XS+NyRI291HIo0HCfE18Lp8oA30H1gpi1tf0mF3TgiCEzanQjOqNZ4x126SXzzi2oNSZhZ5axJp1k0iM6jg==", "dev": true, + "license": "MIT", "dependencies": { "@types/geojson": "*" } @@ -5046,19 +5782,32 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/node": { - "version": "20.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.6.tgz", - "integrity": "sha512-JbA0XIJPL1IiNnU7PFxDXyfAwcwVVrOoqyzzyQTyMeVhBzkJVMSkC1LlVsRQ2lpqiY4n6Bb9oCS6lzDKVQxbZw==", + "version": "20.14.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", + "integrity": "sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==", "dev": true, + "license": "MIT", "dependencies": { "undici-types": "~5.26.4" } @@ -5068,6 +5817,7 @@ "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -5077,6 +5827,7 @@ "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "@types/node": "*", @@ -5087,19 +5838,22 @@ "version": "6.9.15", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/range-parser": { "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 + "dev": true, + "license": "MIT" }, "node_modules/@types/responselike": { "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, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -5108,13 +5862,15 @@ "version": "0.12.2", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, + "license": "MIT", "dependencies": { "@types/mime": "^1", "@types/node": "*" @@ -5125,6 +5881,7 @@ "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, + "license": "MIT", "dependencies": { "@types/express": "*" } @@ -5134,6 +5891,7 @@ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-errors": "*", "@types/node": "*", @@ -5145,28 +5903,32 @@ "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true - }, "node_modules/@types/verror": { "version": "1.10.10", "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz", "integrity": "sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==", "dev": true, + "license": "MIT", "optional": true }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -5176,37 +5938,39 @@ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.13.1.tgz", - "integrity": "sha512-kZqi+WZQaZfPKnsflLJQCz6Ze9FFSMfXrrIOcyargekQxG37ES7DJNpJUE9Q/X5n3yTIP/WPutVNzgknQ7biLg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.0.0.tgz", + "integrity": "sha512-STIZdwEQRXAHvNUS6ILDf5z3u95Gc8jzywunxSNqX00OooIemaaNIA0vEgynJlycL5AjabYLLrIyHd4iazyvtg==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/type-utils": "7.13.1", - "@typescript-eslint/utils": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/scope-manager": "8.0.0", + "@typescript-eslint/type-utils": "8.0.0", + "@typescript-eslint/utils": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -5215,26 +5979,27 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.13.1.tgz", - "integrity": "sha512-1ELDPlnLvDQ5ybTSrMhRTFDfOQEOXNM+eP+3HT/Yq7ruWpciQw+Avi73pdEbA4SooCawEWo3dtYbF68gN7Ed1A==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.0.0.tgz", + "integrity": "sha512-pS1hdZ+vnrpDIxuFXYQpLTILglTjSYJ9MbetZctrUawogUsPdz31DIIRZ9+rab0LhYNTsk88w4fIzVheiTbWOQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/scope-manager": "8.0.0", + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/typescript-estree": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0", "debug": "^4.3.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -5243,16 +6008,17 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.13.1.tgz", - "integrity": "sha512-adbXNVEs6GmbzaCpymHQ0MB6E4TqoiVbC0iqG3uijR8ZYfpAXMGttouQzF4Oat3P2GxDVIrg7bMI/P65LiQZdg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.0.0.tgz", + "integrity": "sha512-V0aa9Csx/ZWWv2IPgTfY7T4agYwJyILESu/PVqFtTFz9RIS823mAze+NbnBI8xiwdX3iqeQbcTYlvB04G9wyQw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1" + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -5260,26 +6026,24 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.13.1.tgz", - "integrity": "sha512-aWDbLu1s9bmgPGXSzNCxELu+0+HQOapV/y+60gPXafR8e2g1Bifxzevaa+4L2ytCWm+CHqpELq4CSoN9ELiwCg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.0.0.tgz", + "integrity": "sha512-mJAFP2mZLTBwAn5WI4PMakpywfWFH5nQZezUQdSKV23Pqo6o9iShQg1hP2+0hJJXP2LnZkWPphdIq4juYYwCeg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/utils": "7.13.1", + "@typescript-eslint/typescript-estree": "8.0.0", + "@typescript-eslint/utils": "8.0.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependencies": { - "eslint": "^8.56.0" - }, "peerDependenciesMeta": { "typescript": { "optional": true @@ -5287,12 +6051,13 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.13.1.tgz", - "integrity": "sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.0.0.tgz", + "integrity": "sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==", "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -5300,13 +6065,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.13.1.tgz", - "integrity": "sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.0.0.tgz", + "integrity": "sha512-5b97WpKMX+Y43YKi4zVcCVLtK5F98dFls3Oxui8LbnmRsseKenbbDinmvxrWegKDMmlkIq/XHuyy0UGLtpCDKg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/visitor-keys": "8.0.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -5315,7 +6081,7 @@ "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -5332,6 +6098,7 @@ "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, + "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", @@ -5348,10 +6115,11 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -5367,60 +6135,71 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@typescript-eslint/utils": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.13.1.tgz", - "integrity": "sha512-h5MzFBD5a/Gh/fvNdp9pTfqJAbuQC4sCN2WzuXme71lqFJsZtLbjxfSk4r3p02WIArOF9N94pdsLiGutpDbrXQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.0.0.tgz", + "integrity": "sha512-k/oS/A/3QeGLRvOWCg6/9rATJL5rec7/5s1YmdS0ZU6LHveJyGFwBvLhSRBv6i9xaj7etmosp+l+ViN1I9Aj/Q==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1" + "@typescript-eslint/scope-manager": "8.0.0", + "@typescript-eslint/types": "8.0.0", + "@typescript-eslint/typescript-estree": "8.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.13.1.tgz", - "integrity": "sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.0.0.tgz", + "integrity": "sha512-oN0K4nkHuOyF3PVMyETbpP5zp6wfyOvm7tWhTMfoqxSSsPmJIh6JNASuZDlODE8eE+0EB9uar+6+vxr9DBTYOA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.13.1", + "@typescript-eslint/types": "8.0.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, "node_modules/@vitejs/plugin-basic-ssl": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.6.0" }, @@ -5433,6 +6212,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -5442,25 +6222,29 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.11.6", "@webassemblyjs/helper-api-error": "1.11.6", @@ -5471,13 +6255,15 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -5490,6 +6276,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -5499,6 +6286,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } @@ -5507,13 +6295,15 @@ "version": "1.11.6", "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -5530,6 +6320,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", @@ -5543,6 +6334,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-buffer": "1.12.1", @@ -5555,6 +6347,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", @@ -5569,6 +6362,7 @@ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, + "license": "MIT", "dependencies": { "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" @@ -5579,6 +6373,7 @@ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -5587,31 +6382,36 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/7zip-bin": { "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 + "dev": true, + "license": "MIT" }, "node_modules/abbrev": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -5621,6 +6421,7 @@ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "dev": true, + "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" }, @@ -5633,6 +6434,7 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, + "license": "MIT", "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -5642,10 +6444,11 @@ } }, "node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -5653,21 +6456,12 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-import-attributes": { "version": "1.9.5", "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "dev": true, - "peer": true, + "license": "MIT", "peerDependencies": { "acorn": "^8" } @@ -5677,6 +6471,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -5686,6 +6481,7 @@ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", "dev": true, + "license": "MIT", "dependencies": { "acorn": "^8.11.0" }, @@ -5698,6 +6494,7 @@ "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", "dev": true, + "license": "MIT", "dependencies": { "loader-utils": "^2.0.0", "regex-parser": "^2.2.11" @@ -5711,6 +6508,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "license": "MIT", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -5725,6 +6523,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, + "license": "MIT", "dependencies": { "debug": "4" }, @@ -5737,6 +6536,7 @@ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, + "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -5746,10 +6546,11 @@ } }, "node_modules/ajv": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", - "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", + "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", @@ -5766,6 +6567,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -5783,6 +6585,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -5794,6 +6597,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/amator/-/amator-1.1.0.tgz", "integrity": "sha512-V5+aH8pe+Z3u/UG3L3pG3BaFQGXAyXHVQDroRwjPHdh08bcUEchAVsU1MCuJSCaU5o60wTK6KaE6te5memzgYw==", + "license": "MIT", "dependencies": { "bezier-easing": "^2.0.3" } @@ -5803,6 +6607,7 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -5812,6 +6617,7 @@ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -5830,6 +6636,7 @@ "engines": [ "node >= 0.8.0" ], + "license": "Apache-2.0", "bin": { "ansi-html": "bin/ansi-html" } @@ -5839,6 +6646,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5848,6 +6656,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -5860,6 +6669,7 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -5873,6 +6683,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -5884,13 +6695,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/app-builder-lib": { "version": "24.13.3", "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", "dev": true, + "license": "MIT", "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/notarize": "2.2.1", @@ -5928,17 +6741,12 @@ "electron-builder-squirrel-windows": "24.13.3" } }, - "node_modules/app-builder-lib/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/app-builder-lib/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -5948,23 +6756,12 @@ "node": ">=12" } }, - "node_modules/app-builder-lib/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/app-builder-lib/node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -5977,6 +6774,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -5986,6 +6784,7 @@ "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "archiver-utils": "^2.1.0", @@ -6005,6 +6804,7 @@ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "glob": "^7.1.4", @@ -6027,6 +6827,7 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/archiver-utils/node_modules/readable-stream": { @@ -6034,6 +6835,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "core-util-is": "~1.0.0", @@ -6050,6 +6852,7 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/archiver-utils/node_modules/string_decoder": { @@ -6057,6 +6860,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "safe-buffer": "~5.1.0" @@ -6066,22 +6870,22 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } + "license": "Python-2.0" }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "is-array-buffer": "^3.0.4" @@ -6097,13 +6901,15 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6113,6 +6919,7 @@ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.5", @@ -6135,6 +6942,7 @@ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.0.0", "inherits": "^2.0.1", @@ -6145,13 +6953,15 @@ "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "is-nan": "^1.3.2", @@ -6165,6 +6975,7 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.8" @@ -6175,6 +6986,7 @@ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=8" @@ -6184,13 +6996,15 @@ "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/async-exit-hook": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -6199,13 +7013,15 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, + "license": "ISC", "engines": { "node": ">= 4.0.0" } @@ -6229,6 +7045,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "browserslist": "^4.23.0", "caniuse-lite": "^1.0.30001599", @@ -6252,6 +7069,7 @@ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -6267,6 +7085,7 @@ "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dev": true, + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -6278,6 +7097,7 @@ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", "dev": true, + "license": "MIT", "dependencies": { "find-cache-dir": "^4.0.0", "schema-utils": "^4.0.0" @@ -6290,27 +7110,12 @@ "webpack": ">=5" } }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.22.6", "@babel/helper-define-polyfill-provider": "^0.6.2", @@ -6325,6 +7130,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -6334,6 +7140,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.1", "core-js-compat": "^3.36.1" @@ -6347,6 +7154,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.2" }, @@ -6358,7 +7166,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -6378,24 +7187,28 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/bezier-easing": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", - "integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==" + "integrity": "sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==", + "license": "MIT" }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -6405,6 +7218,7 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -6417,6 +7231,7 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, + "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -6427,13 +7242,15 @@ "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/bluebird-lst": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", "dev": true, + "license": "MIT", "dependencies": { "bluebird": "^3.5.5" } @@ -6442,13 +7259,15 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, + "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -6473,6 +7292,7 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6482,6 +7302,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -6491,6 +7312,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -6502,13 +7324,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/body-parser/node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.4" }, @@ -6524,6 +7348,7 @@ "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" @@ -6533,13 +7358,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/brace-expansion": { @@ -6547,6 +7374,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -6556,6 +7384,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -6567,13 +7396,15 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true + "dev": true, + "license": "MIT" }, "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, + "license": "MIT", "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", @@ -6588,6 +7419,7 @@ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, + "license": "MIT", "dependencies": { "browserify-aes": "^1.0.4", "browserify-des": "^1.0.0", @@ -6599,6 +7431,7 @@ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, + "license": "MIT", "dependencies": { "cipher-base": "^1.0.1", "des.js": "^1.0.0", @@ -6611,6 +7444,7 @@ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^5.0.0", "randombytes": "^2.0.1" @@ -6621,6 +7455,7 @@ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", "integrity": "sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==", "dev": true, + "license": "ISC", "dependencies": { "bn.js": "^5.2.1", "browserify-rsa": "^4.1.0", @@ -6641,13 +7476,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/browserify-sign/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -6662,13 +7499,15 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/browserify-sign/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } @@ -6677,21 +7516,23 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/browserify-zlib": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, + "license": "MIT", "dependencies": { "pako": "~1.0.5" } }, "node_modules/browserslist": { - "version": "4.23.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.1.tgz", - "integrity": "sha512-TUfofFo/KsK/bWZ9TWQ5O26tsWW4Uhmt8IYklbnUa70udB6P2wA7w7o4PY4muaEPBQaAX+CEnmmIA41NVHtPVw==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, "funding": [ { @@ -6707,11 +7548,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001629", - "electron-to-chromium": "^1.4.796", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.16" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -6739,6 +7581,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -6749,6 +7592,7 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -6758,6 +7602,7 @@ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4" }, @@ -6769,19 +7614,22 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/builder-util": { "version": "24.13.1", "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", "dev": true, + "license": "MIT", "dependencies": { "@types/debug": "^4.1.6", "7zip-bin": "~5.2.0", @@ -6806,6 +7654,7 @@ "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" @@ -6819,6 +7668,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -6829,17 +7679,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/builder-util/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/builder-util/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, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -6856,6 +7701,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -6867,13 +7713,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/builder-util/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -6888,6 +7736,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6897,6 +7746,7 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "6", "debug": "4" @@ -6905,23 +7755,12 @@ "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", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/builder-util/node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -6934,6 +7773,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -6946,6 +7786,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -6954,13 +7795,15 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, + "license": "MIT", "dependencies": { "run-applescript": "^7.0.0" }, @@ -6976,15 +7819,17 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/cacache": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.3.tgz", - "integrity": "sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==", + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", "dev": true, + "license": "ISC", "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", @@ -7004,10 +7849,11 @@ } }, "node_modules/cacache/node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -7019,27 +7865,23 @@ "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/cacache/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "engines": { - "node": "14 || >=16.14" - } + "license": "ISC" }, "node_modules/cacache/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -7055,6 +7897,7 @@ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.6.0" } @@ -7064,6 +7907,7 @@ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", "dev": true, + "license": "MIT", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -7082,6 +7926,7 @@ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -7098,26 +7943,18 @@ }, "node_modules/callsites": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001636", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz", - "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==", + "version": "1.0.30001646", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001646.tgz", + "integrity": "sha512-dRg00gudiBDDTmUhClSdv3hqRfpbOnU28IpI1T6PBTLWa+kOj0681C8uML3PifYfREuBrVjDGhL3adYpBT6spw==", "dev": true, "funding": [ { @@ -7132,13 +7969,15 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -7152,12 +7991,14 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/chart.js": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "license": "MIT", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -7169,6 +8010,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/chartjs-plugin-zoom/-/chartjs-plugin-zoom-2.0.1.tgz", "integrity": "sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==", + "license": "MIT", "dependencies": { "hammerjs": "^2.0.8" }, @@ -7181,6 +8023,7 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -7205,6 +8048,7 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -7214,6 +8058,7 @@ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0" } @@ -7222,7 +8067,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ci-info": { "version": "3.9.0", @@ -7235,6 +8081,7 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } @@ -7244,6 +8091,7 @@ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -7254,20 +8102,25 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, + "license": "MIT", "dependencies": { - "restore-cursor": "^3.1.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-spinners": { @@ -7275,6 +8128,7 @@ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -7287,6 +8141,7 @@ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "slice-ansi": "^3.0.0", @@ -7304,6 +8159,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "license": "ISC", "engines": { "node": ">= 12" } @@ -7313,6 +8169,7 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -7327,6 +8184,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -7342,6 +8200,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -7353,13 +8212,15 @@ "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 + "dev": true, + "license": "MIT" }, "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, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -7377,6 +8238,7 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } @@ -7386,6 +8248,7 @@ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, + "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -7400,6 +8263,7 @@ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" }, @@ -7412,6 +8276,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -7420,19 +8285,22 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -7445,6 +8313,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } @@ -7453,13 +8322,15 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/compare-version": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -7469,6 +8340,7 @@ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "buffer-crc32": "^0.2.13", @@ -7485,6 +8357,7 @@ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -7497,6 +8370,7 @@ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.5", "bytes": "3.0.0", @@ -7515,6 +8389,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -7523,35 +8398,40 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/compression/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/config-file-ts": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", "dev": true, + "license": "MIT", "dependencies": { "glob": "^10.3.10", "typescript": "^5.3.3" } }, "node_modules/config-file-ts/node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -7563,18 +8443,16 @@ "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/config-file-ts/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -7590,6 +8468,7 @@ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } @@ -7604,13 +8483,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" }, @@ -7623,6 +8504,7 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7631,13 +8513,15 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7646,13 +8530,15 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/copy-anything": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "dev": true, + "license": "MIT", "dependencies": { "is-what": "^3.14.1" }, @@ -7661,20 +8547,21 @@ } }, "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", "dev": true, + "license": "MIT", "dependencies": { - "fast-glob": "^3.2.11", + "fast-glob": "^3.3.2", "glob-parent": "^6.0.1", - "globby": "^13.1.1", + "globby": "^14.0.0", "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", @@ -7689,6 +8576,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -7701,6 +8589,7 @@ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", "dev": true, + "license": "MIT", "dependencies": { "browserslist": "^4.23.0" }, @@ -7713,13 +8602,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, + "license": "MIT", "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -7741,29 +8632,12 @@ } } }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/crc": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "buffer": "^5.1.0" @@ -7774,6 +8648,7 @@ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "dev": true, + "license": "Apache-2.0", "peer": true, "bin": { "crc32": "bin/crc32.njs" @@ -7787,6 +8662,7 @@ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "crc-32": "^1.2.0", @@ -7801,6 +8677,7 @@ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.1.0", "elliptic": "^6.5.3" @@ -7810,13 +8687,15 @@ "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, + "license": "MIT", "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", @@ -7830,6 +8709,7 @@ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, + "license": "MIT", "dependencies": { "cipher-base": "^1.0.3", "create-hash": "^1.1.0", @@ -7843,13 +8723,15 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/critters": { - "version": "0.0.22", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.22.tgz", - "integrity": "sha512-NU7DEcQZM2Dy8XTKFHxtdnIM/drE312j2T4PCVaSUcS0oBeyT/NImpRw/Ap0zOr/1SE7SgPK9tGPg1WK/sVakw==", + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.24.tgz", + "integrity": "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==", "dev": true, + "license": "Apache-2.0", "dependencies": { "chalk": "^4.1.0", "css-select": "^5.1.0", @@ -7865,6 +8747,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -7880,6 +8763,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -7896,6 +8780,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -7907,13 +8792,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/critters/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, + "license": "MIT", "engines": { "node": ">=8" } @@ -7923,6 +8810,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -7930,11 +8818,58 @@ "node": ">=8" } }, + "node_modules/croact": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/croact/-/croact-1.0.4.tgz", + "integrity": "sha512-9GhvyzTY/IVUrMQ2iz/mzgZ8+NcjczmIo/t4FkC1CU0CEcau6v6VsEih4jkTa4ZmRgYTF0qXEZLObCzdDFplpw==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.13.0", + "@egjs/list-differ": "^1.0.0" + } + }, + "node_modules/croact-css-styled": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/croact-css-styled/-/croact-css-styled-1.1.9.tgz", + "integrity": "sha512-G7yvRiVJ3Eoj0ov2h2xR4312hpOzATay2dGS9clK8yJQothjH1sBXIyvOeRP5wBKD9mPcKcoUXPCPsl0tQog4w==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.13.0", + "css-styled": "~1.0.8", + "framework-utils": "^1.1.0" + } + }, + "node_modules/croact-moveable": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/croact-moveable/-/croact-moveable-0.9.0.tgz", + "integrity": "sha512-fc3bieV6CdqqZFtzsSLi9KmvUMFW3oakUfhPCls1BxKjOfUfn8rktteGED2341A/Qghy8tI3Hm6SdocIc68IKg==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.13.0", + "@egjs/agent": "^2.2.1", + "@egjs/children-differ": "^1.0.1", + "@egjs/list-differ": "^1.0.0", + "@scena/dragscroll": "^1.4.0", + "@scena/event-emitter": "^1.0.5", + "@scena/matrix": "^1.1.1", + "croact-css-styled": "^1.1.9", + "css-to-mat": "^1.1.1", + "framework-utils": "^1.1.0", + "gesto": "^1.19.3", + "overlap-area": "^1.1.0", + "react-css-styled": "^1.1.9", + "react-moveable": "~0.56.0" + }, + "peerDependencies": { + "croact": "^1.0.4" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -7949,6 +8884,7 @@ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, + "license": "MIT", "dependencies": { "browserify-cipher": "^1.0.0", "browserify-sign": "^4.0.0", @@ -7967,10 +8903,11 @@ } }, "node_modules/css-loader": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz", - "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", @@ -8006,6 +8943,7 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -8017,11 +8955,31 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-styled": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/css-styled/-/css-styled-1.0.8.tgz", + "integrity": "sha512-tCpP7kLRI8dI95rCh3Syl7I+v7PP+2JYOzWkl0bUEoSbJM+u8ITbutjlQVf0NC2/g4ULROJPi16sfwDIO8/84g==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.13.0" + } + }, + "node_modules/css-to-mat": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/css-to-mat/-/css-to-mat-1.1.1.tgz", + "integrity": "sha512-kvpxFYZb27jRd2vium35G7q5XZ2WJ9rWjDUMNT36M3Hc41qCrLXFM5iEKMGXcrPsKfXEN+8l/riB4QzwwwiEyQ==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.13.0", + "@scena/matrix": "^1.0.0" + } + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">= 6" }, @@ -8034,6 +8992,7 @@ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, + "license": "MIT", "bin": { "cssesc": "bin/cssesc" }, @@ -8046,6 +9005,7 @@ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -8063,6 +9023,7 @@ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -8080,6 +9041,7 @@ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -8093,10 +9055,11 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -8114,6 +9077,7 @@ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -8129,6 +9093,7 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8140,13 +9105,15 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/default-browser": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", "dev": true, + "license": "MIT", "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" @@ -8163,6 +9130,7 @@ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -8175,6 +9143,7 @@ "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "execa": "^5.0.0" }, @@ -8187,6 +9156,7 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, + "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -8199,6 +9169,7 @@ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } @@ -8208,6 +9179,7 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -8221,12 +9193,16 @@ } }, "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { @@ -8234,6 +9210,7 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -8251,6 +9228,7 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -8260,6 +9238,7 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -8269,6 +9248,7 @@ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" @@ -8279,6 +9259,7 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -8289,6 +9270,7 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=8" } @@ -8297,13 +9279,15 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -8313,6 +9297,7 @@ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.1.0", "miller-rabin": "^4.0.0", @@ -8323,13 +9308,15 @@ "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/dir-compare": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", "dev": true, + "license": "MIT", "dependencies": { "buffer-equal": "^1.0.0", "minimatch": "^3.0.4" @@ -8340,6 +9327,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8350,6 +9338,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -8362,6 +9351,7 @@ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, + "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -8369,11 +9359,22 @@ "node": ">=8" } }, + "node_modules/dir-glob/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/dmg-builder": { "version": "24.13.3", "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", "dev": true, + "license": "MIT", "dependencies": { "app-builder-lib": "24.13.3", "builder-util": "24.13.1", @@ -8386,17 +9387,12 @@ "dmg-license": "^1.0.11" } }, - "node_modules/dmg-builder/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/dmg-builder/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -8406,23 +9402,12 @@ "node": ">=12" } }, - "node_modules/dmg-builder/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/dmg-builder/node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -8435,6 +9420,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -8444,6 +9430,7 @@ "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -8470,6 +9457,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -8487,6 +9475,7 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/dns-packet": { @@ -8494,6 +9483,7 @@ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, + "license": "MIT", "dependencies": { "@leichtgewicht/ip-codec": "^2.0.1" }, @@ -8501,23 +9491,12 @@ "node": ">=6" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -8532,6 +9511,7 @@ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-5.7.0.tgz", "integrity": "sha512-edTFu0M/7wO1pXY6GDxVNVW086uqwWYIHP98txhcPyV995X21JIH2DtYp33sQJOupYoXKe9RwTw2Ya2vWaquTQ==", "dev": true, + "license": "Artistic-2.0", "engines": { "node": ">=4" }, @@ -8549,13 +9529,15 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -8571,6 +9553,7 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -8585,6 +9568,7 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=10" } @@ -8593,25 +9577,29 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" }, @@ -8623,11 +9611,12 @@ } }, "node_modules/electron": { - "version": "31.0.1", - "resolved": "https://registry.npmjs.org/electron/-/electron-31.0.1.tgz", - "integrity": "sha512-2eBcp4iqLkTsml6mMq+iqrS5u3kJ/2mpOLP7Mj7lo0uNK3OyfNqRS9z1ArsHjBF2/HV250Te/O9nKrwQRTX/+g==", + "version": "31.3.1", + "resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz", + "integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^20.9.0", @@ -8645,6 +9634,7 @@ "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", "dev": true, + "license": "MIT", "dependencies": { "app-builder-lib": "24.13.3", "builder-util": "24.13.1", @@ -8671,6 +9661,7 @@ "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "app-builder-lib": "24.13.3", @@ -8684,6 +9675,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -8699,6 +9691,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "universalify": "^2.0.0" @@ -8712,6 +9705,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">= 10.0.0" @@ -8722,6 +9716,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -8737,6 +9732,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8753,6 +9749,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -8764,13 +9761,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/electron-builder/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -8785,6 +9784,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -8794,6 +9794,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -8806,6 +9807,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -8818,6 +9820,7 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -8827,6 +9830,7 @@ "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", "dev": true, + "license": "MIT", "dependencies": { "@types/fs-extra": "^9.0.11", "builder-util": "24.13.1", @@ -8842,6 +9846,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -8857,6 +9862,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8873,6 +9879,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -8884,13 +9891,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/electron-publish/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -8905,6 +9914,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -8914,6 +9924,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -8926,6 +9937,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -8938,21 +9950,24 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10.0.0" } }, "node_modules/electron-to-chromium": { - "version": "1.4.806", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.806.tgz", - "integrity": "sha512-nkoEX2QIB8kwCOtvtgwhXWy2IHVcOLQZu9Qo36uaGB835mdX/h8uLRlosL6QIhLVUnAiicXRW00PwaPZC74Nrg==", - "dev": true + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.4.tgz", + "integrity": "sha512-orzA81VqLyIGUEA77YkVA1D+N+nNfl2isJVjjmOyrlxuooZ19ynb+dOlaDTqd/idKRS9lDCSBmtzM+kyCsMnkA==", + "dev": true, + "license": "ISC" }, "node_modules/elliptic": { - "version": "6.5.5", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.5.tgz", - "integrity": "sha512-7EjbcmUm17NQFu4Pmgmq2olYMj8nwMnpcddByChSUjArp8F5DQWcIcpriwO4ZToLNAJig0yiyjswfyGNje/ixw==", + "version": "6.5.6", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.6.tgz", + "integrity": "sha512-mpzdtpeCLuS3BmE3pO3Cpp5bbjlOPY2Q0PgoF+Od1XZrHLYI28Xe3ossCmYCQt11FQKEYd9+PF8jymTvtWJSHQ==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.11.9", "brorand": "^1.1.0", @@ -8967,19 +9982,22 @@ "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emojis-list": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -8989,6 +10007,7 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -8998,6 +10017,7 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "iconv-lite": "^0.6.2" @@ -9008,15 +10028,17 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, + "license": "MIT", "dependencies": { "once": "^1.4.0" } }, "node_modules/enhanced-resolve": { - "version": "5.17.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", - "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -9030,6 +10052,7 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "devOptional": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -9042,21 +10065,37 @@ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "prr": "~1.0.1" @@ -9070,6 +10109,7 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } @@ -9079,6 +10119,7 @@ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", @@ -9139,6 +10180,7 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -9151,21 +10193,24 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-module-lexer": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.3.tgz", - "integrity": "sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==", - "dev": true + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.4.tgz", + "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", + "dev": true, + "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -9178,6 +10223,7 @@ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4", "has-tostringtag": "^1.0.2", @@ -9192,6 +10238,7 @@ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -9209,14 +10256,16 @@ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/esbuild": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.3.tgz", - "integrity": "sha512-Kgq0/ZsAPzKrbOjCQcjoSmPoWhlcVnGAUo7jvaLHoxW1Drto0KGkR1xBNg2Cp43b9ImvxmPEJZ9xkfcnqPsfBw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -9224,36 +10273,37 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.3", - "@esbuild/android-arm": "0.21.3", - "@esbuild/android-arm64": "0.21.3", - "@esbuild/android-x64": "0.21.3", - "@esbuild/darwin-arm64": "0.21.3", - "@esbuild/darwin-x64": "0.21.3", - "@esbuild/freebsd-arm64": "0.21.3", - "@esbuild/freebsd-x64": "0.21.3", - "@esbuild/linux-arm": "0.21.3", - "@esbuild/linux-arm64": "0.21.3", - "@esbuild/linux-ia32": "0.21.3", - "@esbuild/linux-loong64": "0.21.3", - "@esbuild/linux-mips64el": "0.21.3", - "@esbuild/linux-ppc64": "0.21.3", - "@esbuild/linux-riscv64": "0.21.3", - "@esbuild/linux-s390x": "0.21.3", - "@esbuild/linux-x64": "0.21.3", - "@esbuild/netbsd-x64": "0.21.3", - "@esbuild/openbsd-x64": "0.21.3", - "@esbuild/sunos-x64": "0.21.3", - "@esbuild/win32-arm64": "0.21.3", - "@esbuild/win32-ia32": "0.21.3", - "@esbuild/win32-x64": "0.21.3" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/esbuild-wasm": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.21.3.tgz", - "integrity": "sha512-DMOV+eeVra0yVq3XIojfczdEQsz+RiFnpEj7lqs8Gux9mlTpN7yIbw0a4KzLspn0Uhw6UVEH3nUAidSqc/rcQg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.21.5.tgz", + "integrity": "sha512-L/FlOPMMFtw+6qPAbuPvJXdrOYOp9yx/PEwSrIZW0qghY4vgV003evdYDwqQ/9ENMQI0B6RMod9xT4FHtto6OQ==", "dev": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -9266,6 +10316,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -9274,53 +10325,52 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.8.0.tgz", + "integrity": "sha512-K8qnZ/QJzT2dLKdZJVX6W4XOwBzutMYmt0lqUS+JdXgd+HTYFlonFgkJ8s44d/zMPPCnOOk0kMWCApCPhiOy9A==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.17.1", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.8.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", @@ -9334,51 +10384,48 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/eslint/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, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -9395,6 +10442,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -9405,17 +10453,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/eslint/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, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -9426,6 +10469,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -9442,6 +10486,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -9453,54 +10498,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, + "license": "MIT", "engines": { "node": ">=10" }, @@ -9513,6 +10519,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -9520,68 +10527,29 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/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, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/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/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, "node_modules/eslint/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, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -9589,41 +10557,12 @@ "node": "*" } }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/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, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -9631,65 +10570,30 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -9697,20 +10601,12 @@ "node": ">=0.10" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -9718,20 +10614,12 @@ "node": ">=4.0" } }, - "node_modules/esrecurse/node_modules/estraverse": { + "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -9741,6 +10629,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -9750,6 +10639,7 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -9759,6 +10649,7 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -9767,13 +10658,15 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.x" } @@ -9783,6 +10676,7 @@ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, + "license": "MIT", "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" @@ -9793,6 +10687,7 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -9816,24 +10711,50 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/exponential-backoff": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/express": { "version": "4.19.2", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -9876,6 +10797,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -9884,13 +10806,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/express/node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.4" }, @@ -9906,6 +10830,7 @@ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, + "license": "MIT", "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", @@ -9920,6 +10845,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -9932,6 +10858,7 @@ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -9955,19 +10882,22 @@ "engines": [ "node >=0.6.0" ], + "license": "MIT", "optional": true }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -9983,19 +10913,22 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -10005,6 +10938,7 @@ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, + "license": "Apache-2.0", "dependencies": { "websocket-driver": ">=0.5.1" }, @@ -10017,20 +10951,22 @@ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, + "license": "MIT", "dependencies": { "pend": "~1.2.0" } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/filelist": { @@ -10038,6 +10974,7 @@ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, + "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" } @@ -10047,6 +10984,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -10059,6 +10997,7 @@ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, + "license": "MIT", "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", @@ -10077,6 +11016,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -10085,13 +11025,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/find-cache-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", "dev": true, + "license": "MIT", "dependencies": { "common-path-prefix": "^3.0.0", "pkg-dir": "^7.0.0" @@ -10104,16 +11046,20 @@ } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat": { @@ -10121,45 +11067,31 @@ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16" } }, "node_modules/flatted": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/follow-redirects": { "version": "1.15.6", @@ -10172,6 +11104,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -10186,6 +11119,7 @@ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, + "license": "MIT", "dependencies": { "is-callable": "^1.1.3" } @@ -10195,6 +11129,7 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", "dev": true, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -10206,23 +11141,12 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -10237,6 +11161,7 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -10246,6 +11171,7 @@ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, @@ -10254,11 +11180,18 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framework-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/framework-utils/-/framework-utils-1.1.0.tgz", + "integrity": "sha512-KAfqli5PwpFJ8o3psRNs8svpMGyCSAe8nmGcjQ0zZBWN2H6dZDnq+ABp3N3hdUmFeMrLtjOCTXD4yplUJIWceg==", + "license": "MIT" + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -10268,6 +11201,7 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/fs-extra": { @@ -10275,6 +11209,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -10289,6 +11224,7 @@ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, @@ -10300,7 +11236,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", @@ -10308,6 +11245,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -10321,6 +11259,7 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10330,6 +11269,7 @@ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "define-properties": "^1.2.0", @@ -10348,6 +11288,7 @@ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10357,24 +11298,50 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, + "node_modules/gesto": { + "version": "1.19.4", + "resolved": "https://registry.npmjs.org/gesto/-/gesto-1.19.4.tgz", + "integrity": "sha512-hfr/0dWwh0Bnbb88s3QVJd1ZRJeOWcgHPPwmiH6NnafDYvhTsxg+SLYu+q/oPNh9JS3V+nlr6fNs8kvPAtcRDQ==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.13.0", + "@scena/event-emitter": "^1.0.2" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -10389,20 +11356,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -10418,6 +11377,7 @@ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "es-errors": "^1.3.0", @@ -10436,6 +11396,7 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -10456,6 +11417,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -10467,13 +11429,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/glob/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, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10484,6 +11448,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -10496,6 +11461,7 @@ "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "dev": true, + "license": "BSD-3-Clause", "optional": true, "dependencies": { "boolean": "^3.0.1", @@ -10514,6 +11480,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -10523,6 +11490,7 @@ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, + "license": "MIT", "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" @@ -10535,19 +11503,21 @@ } }, "node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dev": true, + "license": "MIT", "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -10558,6 +11528,7 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -10570,6 +11541,7 @@ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", @@ -10594,18 +11566,21 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/hammerjs": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==", + "license": "MIT", "engines": { "node": ">=0.8.0" } @@ -10614,13 +11589,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10630,6 +11607,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -10639,6 +11617,7 @@ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" }, @@ -10651,6 +11630,7 @@ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -10663,6 +11643,7 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -10675,6 +11656,7 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, @@ -10690,6 +11672,7 @@ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" @@ -10703,6 +11686,7 @@ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -10713,6 +11697,7 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -10725,6 +11710,7 @@ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", "dev": true, + "license": "MIT", "dependencies": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -10736,6 +11722,7 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, + "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -10748,6 +11735,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -10759,12 +11747,14 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/hotkeys-js": { "version": "3.13.7", "resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.13.7.tgz", "integrity": "sha512-ygFIdTqqwG4fFP7kkiYlvayZppeIQX2aPpirsngkv1xM1lP0piDY5QEh68nQnIKvz64hfocxhBaD/uK3sSK1yQ==", + "license": "MIT", "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" } @@ -10774,6 +11764,7 @@ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, + "license": "MIT", "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -10785,13 +11776,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/hpack.js/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -10806,13 +11799,15 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/hpack.js/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" } @@ -10831,7 +11826,8 @@ "type": "patreon", "url": "https://patreon.com/mdevils" } - ] + ], + "license": "MIT" }, "node_modules/htmlparser2": { "version": "8.0.2", @@ -10845,6 +11841,7 @@ "url": "https://github.com/sponsors/fb55" } ], + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -10856,19 +11853,22 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, + "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -10884,13 +11884,15 @@ "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/http-proxy": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, + "license": "MIT", "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", @@ -10905,6 +11907,7 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, + "license": "MIT", "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -10919,6 +11922,7 @@ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0.tgz", "integrity": "sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-proxy": "^1.17.10", "debug": "^4.3.4", @@ -10936,6 +11940,7 @@ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", "dev": true, + "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" @@ -10948,13 +11953,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, + "license": "MIT", "dependencies": { "agent-base": "^7.0.2", "debug": "4" @@ -10968,6 +11975,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4" }, @@ -10980,6 +11988,7 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } @@ -10989,6 +11998,7 @@ "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.18" } @@ -10998,6 +12008,7 @@ "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -11015,6 +12026,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -11027,6 +12039,7 @@ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, + "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -11052,13 +12065,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -11068,6 +12083,7 @@ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", "dev": true, + "license": "ISC", "dependencies": { "minimatch": "^9.0.0" }, @@ -11076,10 +12092,11 @@ } }, "node_modules/ignore-walk/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -11095,6 +12112,7 @@ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", "dev": true, + "license": "MIT", "optional": true, "bin": { "image-size": "bin/image-size.js" @@ -11104,16 +12122,18 @@ } }, "node_modules/immutable": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", - "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", - "dev": true + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", + "dev": true, + "license": "MIT" }, "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -11125,20 +12145,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -11148,6 +12160,7 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -11158,6 +12171,7 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -11167,68 +12181,25 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ini": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", - "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/inquirer": { - "version": "9.2.22", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.22.tgz", - "integrity": "sha512-SqLLa/Oe5rZUagTR9z+Zd6izyatHglbmbvVofo1KzuVB54YHleWzeHNLoR7FOICGOeQSqeLh1cordb3MzhGcEw==", - "dev": true, - "dependencies": { - "@inquirer/figures": "^1.0.2", - "@ljharb/through": "^2.3.13", - "ansi-escapes": "^4.3.2", - "chalk": "^5.3.0", - "cli-cursor": "^3.1.0", - "cli-width": "^4.1.0", - "external-editor": "^3.1.0", - "lodash": "^4.17.21", - "mute-stream": "1.0.0", - "ora": "^5.4.1", - "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": ">=18" - } - }, - "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, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/interactjs": { - "version": "1.10.27", - "resolved": "https://registry.npmjs.org/interactjs/-/interactjs-1.10.27.tgz", - "integrity": "sha512-y/8RcCftGAF24gSp76X2JS3XpHiUvDQyhF8i7ujemBz77hwiHDuJzftHx7thY8cxGogwGiPJ+o97kWB6eAXnsA==", - "dependencies": { - "@interactjs/types": "1.10.27" - } - }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.0", @@ -11243,6 +12214,7 @@ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, + "license": "MIT", "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" @@ -11251,17 +12223,12 @@ "node": ">= 12" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true - }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" } @@ -11271,6 +12238,7 @@ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -11287,6 +12255,7 @@ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.2.1" @@ -11302,13 +12271,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, + "license": "MIT", "dependencies": { "has-bigints": "^1.0.1" }, @@ -11321,6 +12292,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -11333,6 +12305,7 @@ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -11349,6 +12322,7 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11361,6 +12335,7 @@ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, + "license": "MIT", "dependencies": { "ci-info": "^3.2.0" }, @@ -11369,12 +12344,16 @@ } }, "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", + "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "dev": true, + "license": "MIT", "dependencies": { - "hasown": "^2.0.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -11385,6 +12364,7 @@ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, + "license": "MIT", "dependencies": { "is-typed-array": "^1.1.13" }, @@ -11400,6 +12380,7 @@ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -11411,15 +12392,16 @@ } }, "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, + "license": "MIT", "bin": { "is-docker": "cli.js" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -11430,6 +12412,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11439,6 +12422,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -11448,6 +12432,7 @@ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -11463,6 +12448,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -11475,6 +12461,7 @@ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dev": true, + "license": "MIT", "dependencies": { "is-docker": "^3.0.0" }, @@ -11488,26 +12475,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-inside-container/node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -11516,13 +12489,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.0", "define-properties": "^1.1.3" @@ -11539,6 +12514,7 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11551,6 +12527,7 @@ "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -11563,6 +12540,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -11572,6 +12550,7 @@ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -11587,6 +12566,7 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -11596,6 +12576,7 @@ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -11608,6 +12589,7 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -11620,6 +12602,7 @@ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -11636,6 +12619,7 @@ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7" }, @@ -11651,6 +12635,7 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -11663,6 +12648,7 @@ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "license": "MIT", "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -11678,6 +12664,7 @@ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, + "license": "MIT", "dependencies": { "has-symbols": "^1.0.2" }, @@ -11693,6 +12680,7 @@ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, + "license": "MIT", "dependencies": { "which-typed-array": "^1.1.14" }, @@ -11708,6 +12696,7 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -11720,6 +12709,7 @@ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2" }, @@ -11731,31 +12721,38 @@ "version": "3.14.1", "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, + "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" + "is-inside-container": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isbinaryfile": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 18.0.0" }, @@ -11767,13 +12764,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -11783,46 +12782,37 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" }, "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/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": ">=10" } }, "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=14" - }, "funding": { "url": "https://github.com/sponsors/isaacs" }, @@ -11831,10 +12821,11 @@ } }, "node_modules/jake": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", - "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -11853,6 +12844,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -11868,6 +12860,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11878,6 +12871,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -11894,6 +12888,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -11905,13 +12900,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/jake/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, + "license": "MIT", "engines": { "node": ">=8" } @@ -11921,6 +12918,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -11933,6 +12931,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11945,6 +12944,7 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -11959,6 +12959,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -11968,6 +12969,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -11983,6 +12985,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true, + "license": "MIT", "bin": { "jiti": "bin/jiti.js" } @@ -11992,6 +12995,7 @@ "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.3.0", "@hapi/topo": "^5.1.0", @@ -12004,16 +13008,17 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" @@ -12023,13 +13028,15 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -12041,19 +13048,22 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", "dev": true, + "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -12062,19 +13072,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, + "license": "ISC", "optional": true }, "node_modules/json5": { @@ -12082,6 +13095,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -12090,16 +13104,18 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" }, "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, + "license": "MIT", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -12111,22 +13127,43 @@ "dev": true, "engines": [ "node >= 0.2.0" - ] + ], + "license": "MIT" }, "node_modules/karma-source-map-support": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", "dev": true, + "license": "MIT", "dependencies": { "source-map-support": "^0.5.5" } }, + "node_modules/keycode": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/keycode/-/keycode-2.2.1.tgz", + "integrity": "sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==", + "license": "MIT" + }, + "node_modules/keycon": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keycon/-/keycon-1.4.0.tgz", + "integrity": "sha512-p1NAIxiRMH3jYfTeXRs2uWbVJ1WpEjpi8ktzUyBJsX7/wn2qu2VRXktneBLNtKNxJmlUYxRi9gOJt1DuthXR7A==", + "license": "MIT", + "dependencies": { + "@cfcs/core": "^0.0.6", + "@daybrush/utils": "^1.7.1", + "@scena/event-emitter": "^1.0.2", + "keycode": "^2.2.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -12136,6 +13173,7 @@ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -12145,6 +13183,7 @@ "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.0.tgz", "integrity": "sha512-vJranOAJrI/llyWGRQqiDM+adrw+k83fvmmx3+nV47g3+36xM15jE+zyZ6Ffel02+xSvuM0b2GDRosXZkbb6wA==", "dev": true, + "license": "MIT", "dependencies": { "picocolors": "^1.0.0", "shell-quote": "^1.8.1" @@ -12154,13 +13193,15 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "readable-stream": "^2.0.5" @@ -12174,6 +13215,7 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/lazystream/node_modules/readable-stream": { @@ -12181,6 +13223,7 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "core-util-is": "~1.0.0", @@ -12197,6 +13240,7 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/lazystream/node_modules/string_decoder": { @@ -12204,6 +13248,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "safe-buffer": "~5.1.0" @@ -12212,13 +13257,15 @@ "node_modules/leaflet": { "version": "1.9.4", "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", - "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==" + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" }, "node_modules/less": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -12245,6 +13292,7 @@ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 18.12.0" }, @@ -12271,6 +13319,7 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, + "license": "MIT", "optional": true, "bin": { "mime": "cli.js" @@ -12279,62 +13328,224 @@ "node": ">=4" } }, - "node_modules/less/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==", + "node_modules/less/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, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "license": "ISC", + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": 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", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.3.tgz", + "integrity": "sha512-Lllokma2mtoniUOS94CcOErHWAug5iu7HOmDrvWgpw8jyQH2fomgB+7lZS4HWZxytUuQwkGOwe49FvwVaA85Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.0.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, - "optional": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/license-webpack-plugin": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", - "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, + "license": "MIT", "dependencies": { - "webpack-sources": "^3.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-sources": { - "optional": true - } + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, "node_modules/lmdb": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.8.tgz", - "integrity": "sha512-9rp8JT4jPhCRJUL7vRARa2N06OLSYzLwQsEkhC6Qu5XbcLyM/XBLMzDlgS/K7l7c5CdURLdDk9uE+hPFIogHTQ==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.12.tgz", + "integrity": "sha512-JnoEulTgveoC64vlYJ9sufGLuNkk6TcxSYpKxSC9aM42I61jIv3pQH0fgb6qW7HV0+FNqA3g1WCQQYfhfawGoQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { - "msgpackr": "^1.9.9", + "msgpackr": "^1.10.2", "node-addon-api": "^6.1.0", - "node-gyp-build-optional-packages": "5.1.1", + "node-gyp-build-optional-packages": "5.2.2", "ordered-binary": "^1.4.1", "weak-lru-cache": "^1.2.2" }, @@ -12342,25 +13553,27 @@ "download-lmdb-prebuilds": "bin/download-prebuilds.js" }, "optionalDependencies": { - "@lmdb/lmdb-darwin-arm64": "3.0.8", - "@lmdb/lmdb-darwin-x64": "3.0.8", - "@lmdb/lmdb-linux-arm": "3.0.8", - "@lmdb/lmdb-linux-arm64": "3.0.8", - "@lmdb/lmdb-linux-x64": "3.0.8", - "@lmdb/lmdb-win32-x64": "3.0.8" + "@lmdb/lmdb-darwin-arm64": "3.0.12", + "@lmdb/lmdb-darwin-x64": "3.0.12", + "@lmdb/lmdb-linux-arm": "3.0.12", + "@lmdb/lmdb-linux-arm64": "3.0.12", + "@lmdb/lmdb-linux-x64": "3.0.12", + "@lmdb/lmdb-win32-x64": "3.0.12" } }, "node_modules/lmdb/node_modules/node-addon-api": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^4.0.0", @@ -12376,6 +13589,7 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, + "license": "MIT", "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -12389,6 +13603,7 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -12398,48 +13613,57 @@ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.11.5" } }, "node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12.13.0" } }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/lodash.difference": { @@ -12447,6 +13671,7 @@ "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/lodash.flatten": { @@ -12454,6 +13679,7 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/lodash.isplainobject": { @@ -12461,19 +13687,22 @@ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.union": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/log-symbols": { @@ -12481,6 +13710,7 @@ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -12497,6 +13727,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -12512,6 +13743,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -12528,6 +13760,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -12539,13 +13772,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/log-symbols/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, + "license": "MIT", "engines": { "node": ">=8" } @@ -12555,6 +13790,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -12562,11 +13798,166 @@ "node": ">=8" } }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -12576,6 +13967,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -12585,6 +13977,7 @@ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -12594,6 +13987,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "pify": "^4.0.1", @@ -12608,6 +14002,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, + "license": "ISC", "optional": true, "bin": { "semver": "bin/semver" @@ -12617,13 +14012,15 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/make-fetch-happen": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", "dev": true, + "license": "ISC", "dependencies": { "@npmcli/agent": "^2.0.0", "cacache": "^18.0.0", @@ -12647,6 +14044,7 @@ "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "escape-string-regexp": "^4.0.0" @@ -12660,6 +14058,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=10" @@ -12673,6 +14072,7 @@ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, + "license": "MIT", "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", @@ -12684,18 +14084,20 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/memfs": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.9.3.tgz", - "integrity": "sha512-bsYSSnirtYTWi1+OPMFb0M048evMKyUYe0EbtuGQgq6BVQM1g1W8/KIUJCCvjgI/El0j6Q4WsmMiBwLUBSw8LA==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.1.tgz", + "integrity": "sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jsonjoy.com/json-pack": "^1.0.3", - "@jsonjoy.com/util": "^1.1.2", + "@jsonjoy.com/util": "^1.3.0", "tree-dump": "^1.0.1", "tslib": "^2.0.0" }, @@ -12720,19 +14122,22 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -12742,6 +14147,7 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -12751,6 +14157,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -12764,6 +14171,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -12776,6 +14184,7 @@ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, + "license": "MIT", "dependencies": { "bn.js": "^4.0.0", "brorand": "^1.0.1" @@ -12788,13 +14197,15 @@ "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -12807,6 +14218,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -12816,6 +14228,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -12828,15 +14241,30 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mimic-response": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -12846,6 +14274,7 @@ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", "dev": true, + "license": "MIT", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -12865,19 +14294,22 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -12890,6 +14322,7 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12899,6 +14332,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -12908,6 +14342,7 @@ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, @@ -12920,6 +14355,7 @@ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", "dev": true, + "license": "MIT", "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", @@ -12937,6 +14373,7 @@ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -12949,6 +14386,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -12960,13 +14398,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -12979,6 +14419,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -12990,13 +14431,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", "dev": true, + "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -13009,6 +14452,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -13020,13 +14464,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, + "license": "MIT", "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -13040,6 +14486,7 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -13051,13 +14498,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true, + "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" }, @@ -13065,12 +14514,17 @@ "node": ">=10" } }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "engines": { - "node": "*" + "node_modules/moveable": { + "version": "0.53.0", + "resolved": "https://registry.npmjs.org/moveable/-/moveable-0.53.0.tgz", + "integrity": "sha512-71jS9zIoQzMhnNvduhg4tUEdm23+fO/40FN7muVMbZvVwbTku2MIxxLhnU4qFvxI4oVxn75l79SbtgjuA+s7Pw==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.13.0", + "@scena/event-emitter": "^1.0.5", + "croact": "^1.0.4", + "croact-moveable": "~0.9.0", + "react-moveable": "~0.56.0" } }, "node_modules/mrmime": { @@ -13078,6 +14532,7 @@ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } @@ -13086,13 +14541,15 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/msgpackr": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.10.2.tgz", - "integrity": "sha512-L60rsPynBvNE+8BWipKKZ9jHcSGbtyJYIwjRq0VrIvQ08cRjntGXJYW/tmciZ2IHWIY8WEW32Qa2xbh5+SKBZA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.0.tgz", + "integrity": "sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==", "dev": true, + "license": "MIT", "optionalDependencies": { "msgpackr-extract": "^3.0.2" } @@ -13103,6 +14560,7 @@ "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { "node-gyp-build-optional-packages": "5.2.2" @@ -13119,26 +14577,12 @@ "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" } }, - "node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", - "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", - "dev": true, - "optional": true, - "dependencies": { - "detect-libc": "^2.0.1" - }, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, "node_modules/multicast-dns": { "version": "7.2.5", "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, + "license": "MIT", "dependencies": { "dns-packet": "^5.2.2", "thunky": "^1.0.2" @@ -13152,6 +14596,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -13167,6 +14612,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -13178,13 +14624,15 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/needle": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "iconv-lite": "^0.6.3", @@ -13202,6 +14650,7 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -13210,12 +14659,29 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ngraph.events": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.2.2.tgz", - "integrity": "sha512-JsUbEOzANskax+WSYiAPETemLWYXmixuPAlmZmhIbIj6FH/WDgEGCGnRwUQBK0GjOnVm8Ui+e5IJ+5VZ4e32eQ==" + "integrity": "sha512-JsUbEOzANskax+WSYiAPETemLWYXmixuPAlmZmhIbIj6FH/WDgEGCGnRwUQBK0GjOnVm8Ui+e5IJ+5VZ4e32eQ==", + "license": "BSD-3-Clause" + }, + "node_modules/ngx-moveable": { + "version": "0.50.0", + "resolved": "https://registry.npmjs.org/ngx-moveable/-/ngx-moveable-0.50.0.tgz", + "integrity": "sha512-P70Jk9tgZQtCkNHKXasDu0+3kyRqMaxNnhcjzzT1fovgtkClCAd1eyxUrQmTHtAVMAUbtqUjEET9A9+NjOobIw==", + "license": "MIT", + "dependencies": { + "framework-utils": "^1.1.0", + "moveable": "~0.53.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=8", + "@angular/core": ">=8" + } }, "node_modules/nice-napi": { "version": "1.0.2", @@ -13223,6 +14689,7 @@ "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "!win32" @@ -13237,19 +14704,22 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-addon-api": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", "dev": true, + "license": "MIT", "optional": true }, "node_modules/node-forge": { @@ -13257,15 +14727,17 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" } }, "node_modules/node-gyp": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.1.0.tgz", - "integrity": "sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz", + "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==", "dev": true, + "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", @@ -13273,9 +14745,9 @@ "graceful-fs": "^4.2.6", "make-fetch-happen": "^13.0.0", "nopt": "^7.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.1.0", "semver": "^7.3.5", - "tar": "^6.1.2", + "tar": "^6.2.1", "which": "^4.0.0" }, "bin": { @@ -13290,6 +14762,7 @@ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "dev": true, + "license": "MIT", "optional": true, "bin": { "node-gyp-build": "bin.js", @@ -13298,10 +14771,11 @@ } }, "node_modules/node-gyp-build-optional-packages": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", - "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", "dev": true, + "license": "MIT", "dependencies": { "detect-libc": "^2.0.1" }, @@ -13312,10 +14786,11 @@ } }, "node_modules/node-gyp/node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -13327,9 +14802,6 @@ "bin": { "glob": "dist/esm/bin.mjs" }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, "funding": { "url": "https://github.com/sponsors/isaacs" } @@ -13339,15 +14811,17 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, + "license": "ISC", "engines": { "node": ">=16" } }, "node_modules/node-gyp/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -13358,20 +14832,12 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/node-gyp/node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "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, + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -13387,6 +14853,7 @@ "resolved": "https://registry.npmjs.org/node-polyfill-webpack-plugin/-/node-polyfill-webpack-plugin-4.0.0.tgz", "integrity": "sha512-WLk77vLpbcpmTekRj6s6vYxk30XoyaY5MDZ4+9g8OaKoG3Ij+TjOqhpQjVUlfDZBPBgpNATDltaQkzuXSnnkwg==", "dev": true, + "license": "MIT", "dependencies": { "assert": "^2.1.0", "browserify-zlib": "^0.2.0", @@ -13414,2221 +14881,2048 @@ "vm-browserify": "^1.1.2" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "webpack": ">=5" - } - }, - "node_modules/node-polyfill-webpack-plugin/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/node-polyfill-webpack-plugin/node_modules/readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "dev": true, - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": { - "version": "4.20.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.20.1.tgz", - "integrity": "sha512-R6wDsVsoS9xYOpy8vgeBlqpdOyzJ12HNfQhC/aAKWM3YoCV9TtunJzh/QpkMgeDhkoynDcw5f1y+qF9yc/HHyg==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/node-releases": { - "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": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "dev": true, - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/normalize-package-data": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.1.tgz", - "integrity": "sha512-6rvCfeRW+OEZagAB4lMLSNuTNYZWLVtKccK79VSTf//yTY5VOCgcpH80O+bZK8Neps7pUnd5G+QlMg1yV/2iZQ==", - "dev": true, - "dependencies": { - "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": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/normalize-package-data/node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", - "dev": true, - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/normalize-package-data/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-bundled": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", - "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", - "dev": true, - "dependencies": { - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-install-checks": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", - "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", - "dev": true, - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-package-arg": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.2.tgz", - "integrity": "sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==", - "dev": true, - "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/npm-package-arg/node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", - "dev": true, - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=14" + }, + "peerDependencies": { + "webpack": ">=5" } }, - "node_modules/npm-package-arg/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "node_modules/node-polyfill-webpack-plugin/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "dev": true, - "engines": { - "node": "14 || >=16.14" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" } }, - "node_modules/npm-packlist": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", - "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", + "node_modules/node-polyfill-webpack-plugin/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", "dev": true, + "license": "MIT", "dependencies": { - "ignore-walk": "^6.0.4" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/npm-pick-manifest": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.1.tgz", - "integrity": "sha512-Udm1f0l2nXb3wxDpKjfohwgdFUSV50UVwzEIpDXVsbDMXVIEF81a/i0UhuQbhrPMMmdiq3+YMFLFIRVLs3hxQw==", + "node_modules/node-polyfill-webpack-plugin/node_modules/type-fest": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.23.0.tgz", + "integrity": "sha512-ZiBujro2ohr5+Z/hZWHESLz3g08BBdrdLMieYFULJO+tWc437sn8kQsWLJoZErY8alNhxre9K4p3GURAG11n+w==", "dev": true, - "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^11.0.0", - "semver": "^7.3.5" - }, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-registry-fetch": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", - "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true, - "dependencies": { - "@npmcli/redact": "^2.0.0", - "jsonparse": "^1.3.1", - "make-fetch-happen": "^13.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minizlib": "^2.1.2", - "npm-package-arg": "^11.0.0", - "proc-log": "^4.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } + "license": "MIT" }, - "node_modules/npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "dev": true, + "license": "ISC", "dependencies": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" + "abbrev": "^2.0.0" }, "bin": { - "npm-run-all": "bin/npm-run-all/index.js", - "run-p": "bin/run-p/index.js", - "run-s": "bin/run-s/index.js" + "nopt": "bin/nopt.js" }, "engines": { - "node": ">= 4" - } - }, - "node_modules/npm-run-all/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": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-run-all/node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=4.8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm-run-all/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "lru-cache": "^10.0.1" }, "engines": { - "node": "*" - } - }, - "node_modules/npm-run-all/node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true, - "engines": { - "node": ">=4" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/npm-run-all/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "bin": { - "semver": "bin/semver" - } + "license": "ISC" }, - "node_modules/npm-run-all/node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/npm-run-all/node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/npm-run-all/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "node_modules/npm-bundled": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", "dev": true, + "license": "ISC", "dependencies": { - "boolbase": "^1.0.0" + "npm-normalize-package-bin": "^3.0.0" }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nuid": { - "version": "2.0.1-2", - "resolved": "https://registry.npmjs.org/nuid/-/nuid-2.0.1-2.tgz", - "integrity": "sha512-zL7Z5+CivaZyUdBn76Ih8rffXABBSe3hzazHBk7qXsF4/o5DPDsblXjs5KQRlgjkeLa6XSCd5/GJaShArI2WMQ==", "engines": { - "node": ">= 18.x" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "node_modules/npm-install-checks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" + "semver": "^7.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", "dev": true, + "license": "ISC", "engines": { - "node": ">= 0.4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "node_modules/npm-package-arg": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.2.tgz", + "integrity": "sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==", "dev": true, + "license": "ISC", "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "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==", + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, + "license": "ISC", "dependencies": { - "ee-first": "1.1.1" + "lru-cache": "^10.0.1" }, "engines": { - "node": ">= 0.8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "node_modules/npm-package-arg/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "engines": { - "node": ">= 0.8" - } + "license": "ISC" }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/npm-packlist": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", "dev": true, + "license": "ISC", "dependencies": { - "wrappy": "1" + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "node_modules/npm-pick-manifest": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.1.tgz", + "integrity": "sha512-Udm1f0l2nXb3wxDpKjfohwgdFUSV50UVwzEIpDXVsbDMXVIEF81a/i0UhuQbhrPMMmdiq3+YMFLFIRVLs3hxQw==", "dev": true, + "license": "ISC", "dependencies": { - "mimic-fn": "^2.1.0" + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "node_modules/npm-registry-fetch": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", + "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", "dev": true, + "license": "ISC", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", "dev": true, + "license": "MIT", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 4" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "node_modules/npm-run-all/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, + "license": "MIT", "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/ora/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/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=4.8" } }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/npm-run-all/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, + "license": "ISC", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "*" } }, - "node_modules/ora/node_modules/color-convert": { + "node_modules/npm-run-all/node_modules/path-key": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, + "license": "MIT", "engines": { - "node": ">=7.0.0" + "node": ">=4" } }, - "node_modules/ora/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/ora/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==", + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "engines": { - "node": ">=8" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "node_modules/ora/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==", + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "shebang-regex": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/ordered-binary": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.1.tgz", - "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==", - "dev": true - }, - "node_modules/os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", - "dev": true - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, - "engines": { - "node": ">=8" + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "path-key": "^3.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "p-limit": "^2.2.0" + "boolbase": "^1.0.0" }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nuid": { + "version": "2.0.1-2", + "resolved": "https://registry.npmjs.org/nuid/-/nuid-2.0.1-2.tgz", + "integrity": "sha512-zL7Z5+CivaZyUdBn76Ih8rffXABBSe3hzazHBk7qXsF4/o5DPDsblXjs5KQRlgjkeLa6XSCd5/GJaShArI2WMQ==", + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": ">= 18.x" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-retry": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", - "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, + "license": "MIT", "dependencies": { - "@types/retry": "0.12.2", - "is-network-error": "^1.0.0", - "retry": "^0.13.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" }, "engines": { - "node": ">=16.17" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-retry/node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 4" + "node": ">= 0.4" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/object.assign": { + "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, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true, + "license": "MIT" }, - "node_modules/pacote": { - "version": "18.0.6", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", - "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", + "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, + "license": "MIT", "dependencies": { - "@npmcli/git": "^5.0.0", - "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/package-json": "^5.1.0", - "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^8.0.0", - "cacache": "^18.0.0", - "fs-minipass": "^3.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": "^17.0.0", - "proc-log": "^4.0.0", - "promise-retry": "^2.0.1", - "sigstore": "^2.2.0", - "ssri": "^10.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "bin/index.js" + "ee-first": "1.1.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/panzoom": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/panzoom/-/panzoom-9.4.3.tgz", - "integrity": "sha512-xaxCpElcRbQsUtIdwlrZA90P90+BHip4Vda2BC8MEb4tkI05PmR6cKECdqUCZ85ZvBHjpI9htJrZBxV5Gp/q/w==", - "dependencies": { - "amator": "^1.1.0", - "ngraph.events": "^1.2.2", - "wheel": "^1.0.0" + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" + "wrappy": "1" } }, - "node_modules/parse-asn1": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", - "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, + "license": "MIT", "dependencies": { - "asn1.js": "^4.10.1", - "browserify-aes": "^1.2.0", - "evp_bytestokey": "^1.0.3", - "hash-base": "~3.0", - "pbkdf2": "^3.1.2", - "safe-buffer": "^5.2.1" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/open": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-json/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, "engines": { - "node": ">= 0.10" + "node": ">= 0.8.0" } }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "devOptional": true, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", "dependencies": { - "entities": "^4.4.0" + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse5-html-rewriting-stream": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", - "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", + "node_modules/ora/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, + "license": "MIT", "dependencies": { - "entities": "^4.3.0", - "parse5": "^7.0.0", - "parse5-sax-parser": "^7.0.0" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" }, "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/parse5-sax-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", - "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", + "node_modules/ora/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, + "license": "MIT", "dependencies": { - "parse5": "^7.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "node_modules/ora/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, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=8" + "node": ">=7.0.0" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "node_modules/ora/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, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "node_modules/ora/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, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/ora/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "node_modules/ora/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, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, "engines": { - "node": "14 || >=16.14" + "node": ">=8" } }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/ora/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "node_modules/ora/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, + "license": "MIT", "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=0.12" + "node": ">=8" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "node_modules/ordered-binary": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.1.tgz", + "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "license": "MIT" }, - "node_modules/pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } + "license": "MIT" }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", "dev": true, - "optional": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/piscina": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.5.0.tgz", - "integrity": "sha512-iBaLWI56PFP81cfBSomWTmhOo9W2/yhIOL+Tk8O1vBCpK39cM0tGxB+wgYjG31qq4ohGvysfXSdnj8h7g4rZxA==", - "dev": true, - "optionalDependencies": { - "nice-napi": "^1.0.2" + "node_modules/overlap-area": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/overlap-area/-/overlap-area-1.1.0.tgz", + "integrity": "sha512-3dlJgJCaVeXH0/eZjYVJvQiLVVrPO4U1ZGqlATtx6QGO3b5eNM6+JgUKa7oStBTdYuGTk7gVoABCW6Tp+dhRdw==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.7.1" } }, - "node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", "dev": true, - "dependencies": { - "find-up": "^6.3.0" - }, + "license": "MIT", "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^6.0.0" + "p-limit": "^3.0.2" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir/node_modules/p-limit": { + "node_modules/p-map": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, + "license": "MIT", "dependencies": { - "yocto-queue": "^1.0.0" + "aggregate-error": "^3.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "node_modules/p-retry": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", + "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^4.0.0" + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", + "retry": "^0.13.1" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=16.17" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">= 4" } }, - "node_modules/plist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pacote": { + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", + "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", "dev": true, + "license": "ISC", "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^8.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.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": "^17.0.0", + "proc-log": "^4.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" }, "engines": { - "node": ">=10.4.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/panzoom": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/panzoom/-/panzoom-9.4.3.tgz", + "integrity": "sha512-xaxCpElcRbQsUtIdwlrZA90P90+BHip4Vda2BC8MEb4tkI05PmR6cKECdqUCZ85ZvBHjpI9htJrZBxV5Gp/q/w==", + "license": "MIT", + "dependencies": { + "amator": "^1.1.0", + "ngraph.events": "^1.2.2", + "wheel": "^1.0.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=6" } }, - "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "license": "ISC", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">= 0.10" } }, - "node_modules/postcss-loader": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", - "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { - "cosmiconfig": "^9.0.0", - "jiti": "^1.20.0", - "semver": "^7.5.4" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">= 18.12.0" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "entities": "^4.4.0" }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", + "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.3.0", + "parse5": "^7.0.0", + "parse5-sax-parser": "^7.0.0" }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", + "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, - "node_modules/postcss-media-query-parser": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", - "dev": true + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=0.10.0" } }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", - "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, + "license": "MIT", "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">=8" } }, - "node_modules/postcss-modules-scope": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", - "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "postcss-selector-parser": "^6.0.4" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": ">=16 || 14 >=14.18" }, - "peerDependencies": { - "postcss": "^8.1.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "dependencies": { - "icss-utils": "^5.0.0" - }, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "license": "MIT", "engines": { - "node": "^10 || ^12 || >= 14" + "node": ">=12" }, - "peerDependencies": { - "postcss": "^8.1.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/postcss-selector-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", - "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dev": true, + "license": "MIT", "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" }, "engines": { - "node": ">=4" + "node": ">=0.12" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true, - "engines": { - "node": ">= 0.8.0" - } + "license": "ISC" }, - "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, + "license": "MIT", "engines": { - "node": ">=14" + "node": ">=12" }, "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/primeflex": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz", - "integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ==" - }, - "node_modules/primeicons": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", - "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==" - }, - "node_modules/primeng": { - "version": "17.18.1", - "resolved": "https://registry.npmjs.org/primeng/-/primeng-17.18.1.tgz", - "integrity": "sha512-pMuXOgLQw5Xz0w9d3YTp2DAlYR8svK1Jz5gSWAhk6AyH7u7akwL1JX96RXVzLS8v2YeLUCvkMM+QROOvR3yKug==", - "dependencies": { - "tslib": "^2.3.0" + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" }, - "peerDependencies": { - "@angular/common": "^17.0.0 || ^18.0.0", - "@angular/core": "^17.0.0 || ^18.0.0", - "@angular/forms": "^17.0.0 || ^18.0.0", - "rxjs": "^6.0.0 || ^7.8.1", - "zone.js": "~0.14.0" + "engines": { + "node": ">=0.10" } }, - "node_modules/primeng-sass-theme": { - "version": "17.18.0", - "resolved": "git+ssh://git@github.com/primefaces/primeng-sass-theme.git#3309f0ec9a597109490e26d05cf35d3c4e188f60" - }, - "node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, + "license": "MIT", + "optional": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "node_modules/piscina": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.6.1.tgz", + "integrity": "sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==", "dev": true, - "engines": { - "node": ">= 0.6.0" + "license": "MIT", + "optionalDependencies": { + "nice-napi": "^1.0.2" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^6.3.0" + }, "engines": { - "node": ">=0.4.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, + "license": "MIT", "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, + "license": "MIT", "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "p-locate": "^6.0.0" }, "engines": { - "node": ">= 0.10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, "engines": { - "node": ">= 0.10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true, - "optional": true - }, - "node_modules/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, + "license": "MIT", "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/pkg-dir/node_modules/yocto-queue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/qs": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", - "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, + "license": "MIT", "dependencies": { - "side-channel": "^1.0.6" + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" }, "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10.4.0" } }, - "node_modules/querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.4.x" + "node": ">= 0.4" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "dev": true, "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" + "type": "opencollective", + "url": "https://opencollective.com/postcss/" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "github", + "url": "https://github.com/sponsors/ai" } - ] - }, - "node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^10 || ^12 || >=14" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/postcss-loader": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", "dev": true, + "license": "MIT", "dependencies": { - "safe-buffer": "^5.1.0" + "cosmiconfig": "^9.0.0", + "jiti": "^1.20.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", "dev": true, - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } + "license": "MIT" }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, + "license": "ISC", "engines": { - "node": ">= 0.6" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/raw-body": { - "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==", + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", "dev": true, + "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" }, "engines": { - "node": ">= 0.8" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "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==", + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, "engines": { - "node": ">= 0.8" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "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", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, + "license": "ISC", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "icss-utils": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/read-config-file": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", - "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", + "node_modules/postcss-selector-parser": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", + "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", "dev": true, + "license": "MIT", "dependencies": { - "config-file-ts": "^0.2.4", - "dotenv": "^9.0.2", - "dotenv-expand": "^5.1.0", - "js-yaml": "^4.1.0", - "json5": "^2.2.0", - "lazy-val": "^1.0.4" + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=4" } }, - "node_modules/read-config-file/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" }, - "node_modules/read-config-file/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "license": "MIT", + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/prettier": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, - "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=4" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "node_modules/primeflex": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/primeflex/-/primeflex-3.3.1.tgz", + "integrity": "sha512-zaOq3YvcOYytbAmKv3zYc+0VNS9Wg5d37dfxZnveKBFPr7vEIwfV5ydrpiouTft8MVW6qNjfkaQphHSnvgQbpQ==", + "license": "MIT" }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, + "node_modules/primeicons": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", + "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", + "license": "MIT" + }, + "node_modules/primeng": { + "version": "17.18.7", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-17.18.7.tgz", + "integrity": "sha512-RKbUL69uTzDrVLOKxC8Qn7tyzoAUXetBsMOnNJJaomXTirC001vNTw/wzcuLxkTQpNXAOztxcKMpMTmZQMhe5Q==", + "license": "MIT", "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^17.0.0 || ^18.0.0", + "@angular/core": "^17.0.0 || ^18.0.0", + "@angular/forms": "^17.0.0 || ^18.0.0", + "rxjs": "^6.0.0 || ^7.8.1", + "zone.js": "~0.14.0" } }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "node_modules/primeng-sass-theme": { + "version": "17.18.3", + "resolved": "git+ssh://git@github.com/primefaces/primeng-sass-theme.git#2f7b1705c8783a098381c078345eb5dbe45c4008", + "license": "MIT" + }, + "node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "dev": true, - "dependencies": { - "pify": "^3.0.0" - }, + "license": "ISC", "engines": { - "node": ">=4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/read-pkg/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.6.0" } }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true, - "bin": { - "semver": "bin/semver" - } + "license": "MIT" }, - "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==", + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=0.4.0" } }, - "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true, - "peer": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", "dependencies": { - "minimatch": "^5.1.0" + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, + "license": "MIT", "dependencies": { - "picomatch": "^2.2.1" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { - "node": ">=8.10.0" + "node": ">= 0.10" } }, - "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==", + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">= 0.10" } }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "dev": true + "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, + "license": "MIT" }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT", + "optional": true }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, + "license": "MIT", "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.8.4" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/regex-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", - "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", - "dev": true + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "side-channel": "^1.0.6" }, "engines": { - "node": ">= 0.4" + "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "node_modules/querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==", "dev": true, - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" + "node": ">=0.4.x" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" } }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, - "node_modules/resolve-alpn": { + "node_modules/range-parser": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/resolve-url-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", - "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "node_modules/raw-body": { + "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, + "license": "MIT", "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.14", - "source-map": "0.6.1" + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" }, "engines": { - "node": ">=12" + "node": ">= 0.8" } }, - "node_modules/resolve-url-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "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, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, + "license": "MIT", "engines": { - "node": ">=8.9.0" + "node": ">= 0.8" } }, - "node_modules/resolve-url-loader/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==", + "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", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, "engines": { "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==", - "dev": true, + "node_modules/react-css-styled": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/react-css-styled/-/react-css-styled-1.1.9.tgz", + "integrity": "sha512-M7fJZ3IWFaIHcZEkoFOnkjdiUFmwd8d+gTh2bpqMOcnxy/0Gsykw4dsL4QBiKsxcGow6tETUa4NAUcmJF+/nfw==", + "license": "MIT", "dependencies": { - "lowercase-keys": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "css-styled": "~1.0.8", + "framework-utils": "^1.1.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==", + "node_modules/react-moveable": { + "version": "0.56.0", + "resolved": "https://registry.npmjs.org/react-moveable/-/react-moveable-0.56.0.tgz", + "integrity": "sha512-FmJNmIOsOA36mdxbrc/huiE4wuXSRlmon/o+/OrfNhSiYYYL0AV5oObtPluEhb2Yr/7EfYWBHTxF5aWAvjg1SA==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.13.0", + "@egjs/agent": "^2.2.1", + "@egjs/children-differ": "^1.0.1", + "@egjs/list-differ": "^1.0.0", + "@scena/dragscroll": "^1.4.0", + "@scena/event-emitter": "^1.0.5", + "@scena/matrix": "^1.1.1", + "css-to-mat": "^1.1.1", + "framework-utils": "^1.1.0", + "gesto": "^1.19.3", + "overlap-area": "^1.1.0", + "react-css-styled": "^1.1.9", + "react-selecto": "^1.25.0" + } + }, + "node_modules/react-selecto": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/react-selecto/-/react-selecto-1.26.3.tgz", + "integrity": "sha512-Ubik7kWSnZyQEBNro+1k38hZaI1tJarE+5aD/qsqCOA1uUBSjgKVBy3EWRzGIbdmVex7DcxznFZLec/6KZNvwQ==", + "license": "MIT", + "dependencies": { + "selecto": "~1.26.3" + } + }, + "node_modules/read-config-file": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", "dev": true, + "license": "MIT", "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" + "config-file-ts": "^0.2.4", + "dotenv": "^9.0.2", + "dotenv-expand": "^5.1.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.0", + "lazy-val": "^1.0.4" }, "engines": { - "node": ">=8" + "node": ">=12.0.0" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, "engines": { - "node": ">= 4" + "node": ">=4" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } + "license": "ISC" }, - "node_modules/rimraf": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", - "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "pify": "^3.0.0" }, "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4" } }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "node_modules/roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "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, - "optional": true, + "license": "MIT", "dependencies": { - "boolean": "^3.0.1", - "detect-node": "^2.0.4", - "globalthis": "^1.0.1", - "json-stringify-safe": "^5.0.1", - "semver-compare": "^1.0.0", - "sprintf-js": "^1.1.2" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=8.0" + "node": ">= 6" } }, - "node_modules/roarr/node_modules/sprintf-js": { + "node_modules/readdir-glob": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "dev": true, - "optional": true + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "minimatch": "^5.1.0" + } }, - "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" + "picomatch": "^2.2.1" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", - "fsevents": "~2.3.2" + "node": ">=8.10.0" } }, - "node_modules/run-applescript": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", - "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "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, + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "dev": true, - "engines": { - "node": ">=0.12.0" - } + "license": "Apache-2.0" }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } + "license": "MIT" }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", + "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", + "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.1.0" + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" } }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", "dev": true, + "license": "MIT" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", + "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", + "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@babel/runtime": "^7.8.4" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "node_modules/regex-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", + "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "license": "MIT" }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.6", + "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "set-function-name": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -15637,2384 +16931,2563 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/sanitize-filename": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", - "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", "dev": true, + "license": "MIT", "dependencies": { - "truncate-utf8-bytes": "^1.0.0" + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/sass": { - "version": "1.77.2", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.2.tgz", - "integrity": "sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==", + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" + "jsesc": "~0.5.0" }, "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" + "regjsparser": "bin/parser" } }, - "node_modules/sass-loader": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.2.1.tgz", - "integrity": "sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==", + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", "dev": true, - "dependencies": { - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "webpack": { - "optional": true - } + "bin": { + "jsesc": "bin/jsesc" } }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "dev": true - }, - "node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, + "license": "MIT", "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=0.10.0" } }, - "node_modules/schema-utils/node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, "bin": { - "semver": "bin/semver.js" + "resolve": "bin/resolve" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", "dev": true, - "optional": true + "license": "MIT" }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", "dev": true, + "license": "MIT", "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" + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=12" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.0.0" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.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==", - "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==", + "node_modules/resolve-url-loader/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, - "bin": { - "mime": "cli.js" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "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/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, - "optional": true, + "license": "MIT", "dependencies": { - "type-fest": "^0.13.1" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, - "optional": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "node_modules/rimraf/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, + "license": "ISC", "dependencies": { - "randombytes": "^2.1.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.0.0" + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=8.0" } }, - "node_modules/serve-index/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==", + "node_modules/rollup": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", + "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", "dev": true, + "license": "MIT", "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">= 0.6" + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.18.0", + "@rollup/rollup-android-arm64": "4.18.0", + "@rollup/rollup-darwin-arm64": "4.18.0", + "@rollup/rollup-darwin-x64": "4.18.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", + "@rollup/rollup-linux-arm-musleabihf": "4.18.0", + "@rollup/rollup-linux-arm64-gnu": "4.18.0", + "@rollup/rollup-linux-arm64-musl": "4.18.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", + "@rollup/rollup-linux-riscv64-gnu": "4.18.0", + "@rollup/rollup-linux-s390x-gnu": "4.18.0", + "@rollup/rollup-linux-x64-gnu": "4.18.0", + "@rollup/rollup-linux-x64-musl": "4.18.0", + "@rollup/rollup-win32-arm64-msvc": "4.18.0", + "@rollup/rollup-win32-ia32-msvc": "4.18.0", + "@rollup/rollup-win32-x64-msvc": "4.18.0", + "fsevents": "~2.3.2" } }, - "node_modules/serve-index/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/serve-index/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/serve-index/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/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" + "queue-microtask": "^1.2.2" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", + "call-bind": "^1.0.7", "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, + "license": "MIT", "dependencies": { - "define-data-property": "^1.1.4", + "call-bind": "^1.0.6", "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" + "is-regex": "^1.1.4" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } }, - "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "node_modules/sass": { + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { - "sha.js": "bin.js" + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "node_modules/sass-loader": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.2.1.tgz", + "integrity": "sha512-G0VcnMYU18a4N7VoNDegg2OuMjYtxnqzQWARVWCIVSZwJeiL9kg8QMsuIZOplsJgTzZLF6jGxI3AClj8I9nRdQ==", "dev": true, + "license": "MIT", "dependencies": { - "kind-of": "^6.0.2" + "neo-async": "^2.6.2" }, "engines": { - "node": ">=8" + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "license": "ISC" + }, + "node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", "dev": true, + "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">=8" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/schema-utils/node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT" + }, + "node_modules/selecto": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/selecto/-/selecto-1.26.3.tgz", + "integrity": "sha512-gZHgqMy5uyB6/2YDjv3Qqaf7bd2hTDOpPdxXlrez4R3/L0GiEWDCFaUfrflomgqdb3SxHF2IXY0Jw0EamZi7cw==", + "license": "MIT", + "dependencies": { + "@daybrush/utils": "^1.13.0", + "@egjs/children-differ": "^1.0.1", + "@scena/dragscroll": "^1.4.0", + "@scena/event-emitter": "^1.0.5", + "css-styled": "^1.0.8", + "css-to-mat": "^1.1.1", + "framework-utils": "^1.1.0", + "gesto": "^1.19.4", + "keycon": "^1.2.0", + "overlap-area": "^1.1.0" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "@types/node-forge": "^1.3.0", + "node-forge": "^1" }, "engines": { - "node": ">= 0.4" + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true }, - "node_modules/sigstore": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", - "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", "dev": true, + "license": "MIT", "dependencies": { - "@sigstore/bundle": "^2.3.2", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.2", - "@sigstore/sign": "^2.3.2", - "@sigstore/tuf": "^2.3.4", - "@sigstore/verify": "^1.2.1" + "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": "^16.14.0 || >=18.0.0" + "node": ">= 0.8.0" } }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "node_modules/send/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, + "license": "MIT", "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" + "ms": "2.0.0" } }, - "node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "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==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "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==", "dev": true, - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "license": "MIT", + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/slice-ansi/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/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, + "license": "MIT" + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { - "color-convert": "^2.0.1" + "type-fest": "^0.13.1" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/slice-ansi/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==", + "node_modules/serialize-error/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "dev": true, + "license": "(MIT OR CC0-1.0)", "optional": true, - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/slice-ansi/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==", + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, - "optional": true + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "node": ">= 0.8.0" } }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "node_modules/serve-index/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, + "license": "MIT", "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" + "ms": "2.0.0" } }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "node_modules/serve-index/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, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, + "license": "MIT", "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" + "node": ">= 0.6" } }, - "node_modules/socks-proxy-agent": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", - "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", + "node_modules/serve-index/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, + "license": "MIT", "dependencies": { - "agent-base": "^7.1.1", - "debug": "^4.3.4", - "socks": "^2.7.1" + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" }, "engines": { - "node": ">= 14" + "node": ">= 0.6" } }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "node_modules/serve-index/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, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } + "license": "ISC" }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, - "engines": { - "node": ">= 8" - } + "license": "MIT" }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "node_modules/serve-index/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, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.6" } }, - "node_modules/source-map-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", - "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", + "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, + "license": "MIT", "dependencies": { - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.2" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" }, "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.72.1" + "node": ">= 0.8.0" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, + "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/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, + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, + "license": "MIT", "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } + "license": "MIT" }, - "node_modules/spdx-license-ids": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", - "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", - "dev": true + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, + "license": "(MIT AND BSD-3-Clause)", "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" }, - "engines": { - "node": ">=6.0.0" + "bin": { + "sha.js": "bin.js" } }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { - "minipass": "^7.0.3" + "shebang-regex": "^3.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/stat-mode": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", - "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", "dev": true, - "engines": { - "node": ">= 0.8" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stream-browserify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", - "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "~2.0.4", - "readable-stream": "^3.5.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/stream-http": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", - "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "dependencies": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "xtend": "^4.0.2" + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "node_modules/sigstore": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", + "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "safe-buffer": "~5.2.0" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, + "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "semver": "^7.5.3" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/string.prototype.padend": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", - "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "node_modules/slice-ansi/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, + "license": "MIT", + "optional": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "node_modules/slice-ansi/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, + "license": "MIT", + "optional": true, "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=7.0.0" } }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "node_modules/slice-ansi/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, + "license": "MIT", + "optional": true + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" } }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 10.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" }, "engines": { - "node": ">=8" + "node": ">= 14" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "debug": "^4.3.4" }, "engines": { - "node": ">=8" + "node": ">= 14" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=4" + "node": ">= 8" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/source-map-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", + "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, "engines": { - "node": ">=8" + "node": ">= 18.12.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" } }, - "node_modules/sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^4.1.0" - }, - "engines": { - "node": ">= 8.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/source-map-support/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, - "dependencies": { - "has-flag": "^3.0.0" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true, - "engines": { - "node": ">=0.10" - } + "license": "CC-BY-3.0" }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, - "engines": { - "node": ">=6" + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "node_modules/spdx-license-ids": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "dev": true, + "license": "MIT", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=6.0.0" } }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" } }, - "node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ssri": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "dev": true, + "license": "ISC", "dependencies": { - "minipass": "^3.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">= 8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "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, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/tar/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/temp-file": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", - "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", "dev": true, + "license": "MIT", "dependencies": { - "async-exit-hook": "^2.0.1", - "fs-extra": "^10.0.0" + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" } }, - "node_modules/temp-file/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" } }, - "node_modules/temp-file/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, + "license": "MIT", "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "safe-buffer": "~5.2.0" } }, - "node_modules/temp-file/node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, "engines": { - "node": ">= 10.0.0" + "node": ">=8" } }, - "node_modules/terser": { - "version": "5.31.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", - "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, + "license": "MIT", "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" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/terser-webpack-plugin/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==", + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/terser-webpack-plugin/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/terser-webpack-plugin/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==", + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/terser/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/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, - "node_modules/test-exclude/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==", + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "license": "MIT", "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/thingies": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", - "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10.18" + "node": ">=8" }, - "peerDependencies": { - "tslib": "^2" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "node_modules/timers-browserify": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", - "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "setimmediate": "^1.0.4" + "debug": "^4.1.0" }, "engines": { - "node": ">=0.6.0" + "node": ">= 8.0" } }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, + "license": "MIT", "dependencies": { - "os-tmpdir": "~1.0.2" + "has-flag": "^3.0.0" }, "engines": { - "node": ">=0.6.0" + "node": ">=4" } }, - "node_modules/tmp-promise": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, - "dependencies": { - "tmp": "^0.2.0" + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tmp-promise/node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14.14" + "node": ">=0.10" } }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, + "license": "ISC", "dependencies": { - "is-number": "^7.0.0" + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" }, "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" + "node": ">=10" } }, - "node_modules/tree-dump": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.1.tgz", - "integrity": "sha512-WCkcRBVPSlHHq1dc/px9iOfqklvzCbdRwvlNfxGZsrHqf6aZttfPrd7DJTt6oR10dwUfpFFQeVTkPbBIZxX/YA==", + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" + "license": "MIT", + "peer": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" + "engines": { + "node": ">=6" } }, - "node_modules/truncate-utf8-bytes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", "dev": true, + "license": "ISC", "dependencies": { - "utf8-byte-length": "^1.0.1" - } - }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "engines": { - "node": ">=16" + "minipass": "^3.0.0" }, - "peerDependencies": { - "typescript": ">=4.2.0" + "engines": { + "node": ">= 8" } }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "license": "ISC", "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" + "yallist": "^4.0.0" }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "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, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, + "license": "ISC", "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "node_modules/tar/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, + "license": "ISC" }, - "node_modules/tty-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", - "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", - "dev": true + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } }, - "node_modules/tuf-js": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", - "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, + "license": "MIT", "dependencies": { - "@tufjs/models": "2.0.1", - "debug": "^4.3.4", - "make-fetch-happen": "^13.0.1" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/temp-file/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1" + "universalify": "^2.0.0" }, - "engines": { - "node": ">= 0.8.0" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "node_modules/temp-file/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 10.0.0" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "node_modules/terser": { + "version": "5.29.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.29.2.tgz", + "integrity": "sha512-ZiGkhUBIM+7LwkNjXYJq8svgkd+QK3UUr0wJqY4MieaezBSAIPgbSPZyIx0idM6XWK5CMzSWa8MJIzmRcB8Caw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" }, "engines": { - "node": ">= 0.6" + "node": ">=10" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" }, "engines": { - "node": ">= 0.4" + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } } }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "node_modules/terser-webpack-plugin/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, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" + "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": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "node_modules/terser-webpack-plugin/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, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/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, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/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, + "license": "MIT", "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" }, "engines": { - "node": ">= 0.4" + "node": ">= 10.13.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "node_modules/terser/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, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, + "license": "MIT" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", + "dev": true, + "license": "Unlicense", "engines": { - "node": ">= 0.4" + "node": ">=10.18" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "tslib": "^2" } }, - "node_modules/typed-assert": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", - "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", - "dev": true + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true, + "license": "MIT" }, - "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "node_modules/timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "license": "MIT", + "dependencies": { + "setimmediate": "^1.0.4" }, "engines": { - "node": ">=14.17" + "node": ">=0.6.0" } }, - "node_modules/typescript-eslint": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.13.1.tgz", - "integrity": "sha512-pvLEuRs8iS9s3Cnp/Wt//hpK8nKc8hVa3cLljHqzaJJQYP8oys8GUyIFqtlev+2lT/fqMPcyQko+HJ6iYK3nFA==", + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "7.13.1", - "@typescript-eslint/parser": "7.13.1", - "@typescript-eslint/utils": "7.13.1" + "os-tmpdir": "~1.0.2" }, "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=0.6.0" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "tmp": "^0.2.0" } }, - "node_modules/undici": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.18.0.tgz", - "integrity": "sha512-nT8jjv/fE9Et1ilR6QoW8ingRTY2Pp4l2RUrdzV5Yz35RJDrtPc1DXvuNqcpsJSGIRHFdt3YKKktTzJA6r0fTA==", + "node_modules/tmp-promise/node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=18.17" + "node": ">=14.14" } }, - "node_modules/undici-types": { - "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": { + "node_modules/to-fast-properties": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" + "is-number": "^7.0.0" }, "engines": { - "node": ">=4" + "node": ">=8.0" } }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.6" } }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=4" + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" } }, - "node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", "dev": true, + "license": "WTFPL", "dependencies": { - "unique-slug": "^4.0.0" - }, + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" } }, - "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4" + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "node_modules/tsconfig-paths": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", + "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, "engines": { - "node": ">= 4.0.0" + "node": ">=6" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "license": "0BSD" + }, + "node_modules/tty-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.1.tgz", + "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tuf-js": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", + "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.1", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.1" + }, "engines": { - "node": ">= 0.8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/update-browserslist-db": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", - "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" + "prelude-ls": "^1.2.1" }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "dependencies": { - "punycode": "^2.1.0" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/url": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", - "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, + "license": "MIT", "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.11.2" + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "node_modules/url/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true - }, - "node_modules/utf8-byte-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", - "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", - "dev": true - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, + "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "node_modules/utils-merge": { + "node_modules/typed-array-byte-length": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, + "license": "MIT", "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", "dev": true, - "engines": { - "node": ">= 0.8" - } + "license": "MIT" }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, - "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">=0.6.0" + "node": ">=14.17" } }, - "node_modules/vite": { - "version": "5.2.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", - "integrity": "sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==", + "node_modules/typescript-eslint": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.0.0.tgz", + "integrity": "sha512-yQWBJutWL1PmpmDddIOl9/Mi6vZjqNCjqSGBMQ4vsc2Aiodk0SnbQQWPXbSy0HNuKCuGkw1+u4aQ2mO40TdhDQ==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" - }, - "bin": { - "vite": "bin/vite.js" + "@typescript-eslint/eslint-plugin": "8.0.0", + "@typescript-eslint/parser": "8.0.0", + "@typescript-eslint/utils": "8.0.0" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { + "typescript": { "optional": true } } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", - "cpu": [ - "ppc64" - ], + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", - "cpu": [ - "arm" - ], + "node_modules/undici": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.2.tgz", + "integrity": "sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18.17" } }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", - "cpu": [ - "arm64" - ], + "node_modules/undici-types": { + "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, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } + "license": "MIT" }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", - "cpu": [ - "x64" - ], + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", - "cpu": [ - "arm64" - ], + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", - "cpu": [ - "x64" - ], + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", - "cpu": [ - "arm64" - ], + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", - "cpu": [ - "x64" - ], + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", - "cpu": [ - "arm" - ], + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, "engines": { - "node": ">=12" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", - "cpu": [ - "arm64" - ], + "node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, "engines": { - "node": ">=12" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", - "cpu": [ - "ia32" - ], + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 4.0.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", - "cpu": [ - "loong64" - ], + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 0.8" } }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", - "cpu": [ - "mips64el" - ], + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, - "optional": true, - "os": [ - "linux" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], - "engines": { - "node": ">=12" + "license": "MIT", + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", - "cpu": [ - "ppc64" - ], + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", - "cpu": [ - "riscv64" - ], + "node_modules/url": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", + "integrity": "sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT", + "dependencies": { + "punycode": "^1.4.1", + "qs": "^6.12.3" + }, "engines": { - "node": ">=12" + "node": ">= 0.4" } }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", - "cpu": [ - "s390x" - ], + "node_modules/url/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "license": "MIT" + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" } }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", - "cpu": [ - "x64" - ], + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 0.4.0" } }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", - "cpu": [ - "x64" - ], + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", - "cpu": [ - "x64" - ], + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } + "license": "MIT" }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", - "cpu": [ - "x64" - ], + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", - "cpu": [ - "arm64" - ], + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "license": "ISC", "engines": { - "node": ">=12" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", - "cpu": [ - "ia32" - ], + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": ">= 0.8" } }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", - "cpu": [ - "x64" - ], + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", "dev": true, + "license": "MIT", "optional": true, - "os": [ - "win32" - ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, "engines": { - "node": ">=12" + "node": ">=0.6.0" } }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "node_modules/vite": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.2.tgz", + "integrity": "sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==", "dev": true, - "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.38", + "rollup": "^4.13.0" + }, "bin": { - "esbuild": "bin/esbuild" + "vite": "bin/vite.js" }, "engines": { - "node": ">=12" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, "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 + "dev": true, + "license": "MIT" }, "node_modules/wait-on": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==", "dev": true, + "license": "MIT", "dependencies": { "axios": "^1.6.1", "joi": "^17.11.0", @@ -18034,6 +19507,7 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "dev": true, + "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -18047,6 +19521,7 @@ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, + "license": "MIT", "dependencies": { "minimalistic-assert": "^1.0.0" } @@ -18056,6 +19531,7 @@ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, + "license": "MIT", "dependencies": { "defaults": "^1.0.3" } @@ -18064,13 +19540,15 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/webpack": { - "version": "5.92.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.92.1.tgz", - "integrity": "sha512-JECQ7IwJb+7fgUFBlrJzbyu3GEuNBcdqr1LD7IbSzwkSmIevTm8PF+wej3Oxuz/JFBUZ6O1o43zsPkwm1C4TmA==", + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -18119,6 +19597,7 @@ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.2.1.tgz", "integrity": "sha512-hRLz+jPQXo999Nx9fXVdKlg/aehsw1ajA9skAneGmT03xwmyuhvF93p6HUKKbWhXdcERtGTzUCtIQr+2IQegrA==", "dev": true, + "license": "MIT", "dependencies": { "colorette": "^2.0.10", "memfs": "^4.6.0", @@ -18148,6 +19627,7 @@ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz", "integrity": "sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==", "dev": true, + "license": "MIT", "dependencies": { "@types/bonjour": "^3.5.13", "@types/connect-history-api-fallback": "^1.5.4", @@ -18202,23 +19682,12 @@ } } }, - "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "dev": true, + "license": "MIT", "dependencies": { "@types/http-proxy": "^1.17.8", "http-proxy": "^1.18.1", @@ -18238,44 +19707,12 @@ } } }, - "node_modules/webpack-dev-server/node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "dev": true, - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/webpack-dev-server/node_modules/open": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", - "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", - "dev": true, - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/webpack-merge": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", "dev": true, + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", @@ -18290,6 +19727,7 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -18299,6 +19737,7 @@ "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", "dev": true, + "license": "MIT", "dependencies": { "typed-assert": "^1.0.8" }, @@ -18320,6 +19759,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -18337,16 +19777,44 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "license": "MIT", "peer": true, "peerDependencies": { "ajv": "^6.9.1" } }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/webpack/node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/webpack/node_modules/json-schema-traverse": { @@ -18354,6 +19822,7 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, + "license": "MIT", "peer": true }, "node_modules/webpack/node_modules/schema-utils": { @@ -18361,6 +19830,7 @@ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", @@ -18380,6 +19850,7 @@ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -18394,6 +19865,7 @@ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=0.8.0" } @@ -18401,13 +19873,15 @@ "node_modules/wheel": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wheel/-/wheel-1.0.0.tgz", - "integrity": "sha512-XiCMHibOiqalCQ+BaNSwRoZ9FDTAvOsXxGHXChBugewDj7HC8VBIER71dEOiRH1fSdLbRCQzngKTSiZ06ZQzeA==" + "integrity": "sha512-XiCMHibOiqalCQ+BaNSwRoZ9FDTAvOsXxGHXChBugewDj7HC8VBIER71dEOiRH1fSdLbRCQzngKTSiZ06ZQzeA==", + "license": "MIT" }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -18423,6 +19897,7 @@ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, + "license": "MIT", "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", @@ -18439,6 +19914,7 @@ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", @@ -18457,13 +19933,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -18473,6 +19951,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -18488,6 +19967,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -18505,6 +19985,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -18520,6 +20001,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -18531,13 +20013,15 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/wrap-ansi/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, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -18553,6 +20037,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -18564,19 +20049,22 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -18598,6 +20086,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0" } @@ -18607,6 +20096,7 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4" } @@ -18616,6 +20106,7 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -18624,13 +20115,15 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -18649,6 +20142,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -18658,6 +20152,7 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, + "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -18668,17 +20163,32 @@ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12.20" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -18689,6 +20199,7 @@ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "archiver-utils": "^3.0.4", @@ -18704,6 +20215,7 @@ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "glob": "^7.2.3", @@ -18722,9 +20234,10 @@ } }, "node_modules/zone.js": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.7.tgz", - "integrity": "sha512-0w6DGkX2BPuiK/NLf+4A8FLE43QwBfuqz2dVgi/40Rj1WmqUskCqj329O/pwrqFJLG5X8wkeG2RhIAro441xtg==" + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.8.tgz", + "integrity": "sha512-48uh7MnVp4/OQDuCHeFdXw5d8xwPqFTvlHgPJ1LBFb5GaustLSZV+YUH0to5ygNyGpqTsjpbpt141U/j3pCfqQ==", + "license": "MIT" } } } diff --git a/desktop/package.json b/desktop/package.json index fbf6ba2ec..789d2b5f3 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,5 +1,6 @@ { "name": "nebulosa", + "codename": "Ceres", "version": "0.1.0", "description": "The complete integrated solution for all of your astronomical imaging needs.", "author": { @@ -11,11 +12,12 @@ "main": "app/main.js", "private": true, "scripts": { - "postinstall": "electron-builder install-app-deps", + "postinstall": "electron-builder install-app-deps && npm run scripts", "ng": "ng", + "scripts": "node --no-warnings scripts/nebulosa.mjs", "start": "npm-run-all -p electron:serve ng:serve", "ng:serve": "ng serve -c web --hmr", - "build": "npm run electron:serve-tsc && ng build --base-href ./", + "build": "npm run scripts && npm run electron:serve-tsc && ng build --base-href ./", "build:dev": "npm run build -- -c dev", "build:prod": "npm run build -- -c production", "web:build": "npm run build -- -c web-production", @@ -30,63 +32,57 @@ "lint": "npx eslint .", "prettier:ts": "npx prettier '**/*.ts' --write", "prettier:html": "npx prettier '**/*.html' --write", - "prettier:scss": "npx prettier '**/*.scss' --write", - "prettier:json": "npx prettier '**/*.json' --write" + "prettier:scss": "npx prettier '**/*.scss' --write" }, "dependencies": { - "@angular/animations": "18.0.3", - "@angular/cdk": "18.0.3", - "@angular/common": "18.0.3", - "@angular/compiler": "18.0.3", - "@angular/core": "18.0.3", - "@angular/forms": "18.0.3", - "@angular/platform-browser": "18.0.3", - "@angular/platform-browser-dynamic": "18.0.3", - "@angular/router": "18.0.3", + "@angular/animations": "18.1.3", + "@angular/cdk": "18.1.3", + "@angular/common": "18.1.3", + "@angular/compiler": "18.1.3", + "@angular/core": "18.1.3", + "@angular/forms": "18.1.3", + "@angular/platform-browser": "18.1.3", + "@angular/platform-browser-dynamic": "18.1.3", + "@angular/router": "18.1.3", "@mdi/font": "7.4.47", "chart.js": "4.4.3", "chartjs-plugin-zoom": "2.0.1", "hotkeys-js": "3.13.7", - "interactjs": "1.10.27", "leaflet": "1.9.4", - "moment": "2.30.1", + "ngx-moveable": "0.50.0", "nuid": "2.0.1-2", "panzoom": "9.4.3", "primeflex": "3.3.1", "primeicons": "7.0.0", - "primeng": "17.18.1", - "primeng-sass-theme": "github:primefaces/primeng-sass-theme#17.18.0", + "primeng": "17.18.7", + "primeng-sass-theme": "github:primefaces/primeng-sass-theme#17.18.3", "rxjs": "7.8.1", "tslib": "2.6.3", - "zone.js": "0.14.7" + "zone.js": "0.14.8" }, "devDependencies": { "@angular-builders/custom-webpack": "18.0.0", - "@angular-devkit/build-angular": "18.0.4", - "@angular/cli": "18.0.4", - "@angular/compiler-cli": "18.0.3", - "@angular/language-service": "18.0.3", - "@eslint/js": "9.5.0", + "@angular-devkit/build-angular": "18.1.3", + "@angular/cli": "18.1.3", + "@angular/compiler-cli": "18.1.3", + "@angular/language-service": "18.1.3", + "@eslint/js": "9.8.0", "@types/eslint__js": "8.42.3", "@types/leaflet": "1.9.12", - "@types/node": "20.14.6", - "@types/uuid": "9.0.8", - "electron": "31.0.1", + "@types/node": "20.14.11", + "electron": "31.3.1", "electron-builder": "24.13.3", - "eslint": "8.57.0", + "eslint": "9.8.0", "node-polyfill-webpack-plugin": "4.0.0", "npm-run-all": "4.1.5", - "prettier": "3.3.2", + "prettier": "3.3.3", "ts-node": "10.9.2", - "typescript": "5.4.5", - "typescript-eslint": "7.13.1", + "typescript": "5.5.4", + "typescript-eslint": "8.0.0", "wait-on": "7.2.0" }, - "overrides": { - "axios": "1.6.2" - }, "engines": { - "node": ">= 22.0.0" + "node": ">= 20.11.1" }, "browserslist": ["chrome 114"] } diff --git a/desktop/scripts/nebulosa.mjs b/desktop/scripts/nebulosa.mjs new file mode 100644 index 000000000..b8951e41b --- /dev/null +++ b/desktop/scripts/nebulosa.mjs @@ -0,0 +1,47 @@ +import { execSync } from 'child_process' +import fs from 'fs' +import mainPackageJson from '../app/package.json' with { type: 'json' } +import rendererPackageJson from '../package.json' with { type: 'json' } + +const dependencies = [] + +if (rendererPackageJson.dependencies) { + for (const [name, version] of Object.entries(rendererPackageJson.dependencies).filter((e) => !e[1].includes('#'))) { + dependencies.push({ name, version }) + } +} + +if (mainPackageJson.dependencies) { + for (const [name, version] of Object.entries(mainPackageJson.dependencies).filter((e) => !e[1].includes('#'))) { + dependencies.push({ name, version }) + } +} + +if (rendererPackageJson.devDependencies) { + for (const [name, version] of Object.entries(rendererPackageJson.devDependencies).filter((e) => !e[1].includes('#'))) { + dependencies.push({ name, version }) + } +} + +if (mainPackageJson.devDependencies) { + for (const [name, version] of Object.entries(mainPackageJson.devDependencies).filter((e) => !e[1].includes('#'))) { + dependencies.push({ name, version }) + } +} + +dependencies.sort((a, b) => a.name.localeCompare(b.name)) + +const data = { + name: rendererPackageJson.name, + codename: rendererPackageJson.codename, + version: rendererPackageJson.version, + description: rendererPackageJson.description, + author: rendererPackageJson.author, + build: { + commit: execSync('git rev-parse HEAD').toString().trim(), + date: new Date().toISOString(), + }, + dependencies, +} + +fs.writeFileSync('src/assets/data/nebulosa.json', JSON.stringify(data)) diff --git a/desktop/sequencer.png b/desktop/sequencer.png index 118eb9803..6a1825304 100644 Binary files a/desktop/sequencer.png and b/desktop/sequencer.png differ diff --git a/desktop/settings.png b/desktop/settings.png index 1bcc6fdd2..84474f972 100644 Binary files a/desktop/settings.png and b/desktop/settings.png differ diff --git a/desktop/src/app/about/about.component.html b/desktop/src/app/about/about.component.html index cfddc9af2..7c59df1fe 100644 --- a/desktop/src/app/about/about.component.html +++ b/desktop/src/app/about/about.component.html @@ -17,13 +17,16 @@ + [value]="version" />

+

+ {{ description }} +

© 2022-2024 Tiago Melo -

This software is WIP, comes with absolutely no warranty and the copyright holder is not liable or responsible for anything.

+

This software is WIP, comes with absolutely no warranty and the copyright holder is not liable or responsible for anything.

+ + +
+ COMMIT: {{ commit }} + DATE: {{ date }} +
+ + +
+ @for (dep of dependencies; track $index) { + + {{ dep.name }} ({{ dep.version }}) + + }
diff --git a/desktop/src/app/about/about.component.scss b/desktop/src/app/about/about.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/app/about/about.component.ts b/desktop/src/app/about/about.component.ts index d787abeef..27d76b644 100644 --- a/desktop/src/app/about/about.component.ts +++ b/desktop/src/app/about/about.component.ts @@ -1,13 +1,62 @@ import { Component } from '@angular/core' +import nebulosa from '../../assets/data/nebulosa.json' +import { DependencyItem, FLAT_ICON_URL, IconItem } from '../../shared/types/about.types' import { AppComponent } from '../app.component' @Component({ - selector: 'app-about', + selector: 'neb-about', templateUrl: './about.component.html', - styleUrls: ['./about.component.scss'], }) export class AboutComponent { + protected readonly codename = nebulosa.codename + protected readonly version = nebulosa.version + protected readonly description = nebulosa.description + protected readonly commit = nebulosa.build.commit + protected readonly date = nebulosa.build.date + protected readonly icons: IconItem[] = [] + protected readonly dependencies: DependencyItem[] = [] + constructor(app: AppComponent) { app.title = 'About' + + this.mapDependencies() + + this.icons.push({ link: `${FLAT_ICON_URL}/information_9195785`, name: 'Information', author: 'Anggara - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/sky_3982229`, name: 'Sky', author: 'Freepik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/target_3207593`, name: 'Target', author: 'Freepik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/camera-lens_5708327`, name: 'Camera', author: 'juicy_fish - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/telescope_4011463`, name: 'Telescope', author: 'Smashicons - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/observatory_2256076`, name: 'Observatory', author: 'Nikita Golubev - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/focus_3801224`, name: 'Focus', author: 'FetchLab - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/switch_404449`, name: 'Switch', author: 'Freepik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/image_4371206`, name: 'Image', author: 'Freepik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/image-processing_6062419`, name: 'Image processing', author: 'juicy_fish - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/star_740882`, name: 'Star', author: 'Vectors Market - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/rotate_3303063`, name: 'Rotate', author: 'Freepik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/rgb-print_7664547`, name: 'Color wheel', author: 'BomSymbols - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/cogwheel_3953226`, name: 'Settings', author: 'Freepik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/contrast_439842`, name: 'Sun', author: 'DinosoftLabs - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/full-moon_9689786`, name: 'Moon', author: 'vectorsmarket15 - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/jupiter_1086078`, name: 'Planet', author: 'monkik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/asteroid_1086068`, name: 'Asteroid', author: 'monkik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/satellite_1086093`, name: 'Satellite', author: 'monkik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/witch-hat_5606276`, name: 'Witch hat', author: 'Luvdat - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/picture_2659360`, name: 'Picture', author: 'Freepik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/calculator_7182540`, name: 'Calculator', author: 'Iconic Panda - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/target_10542035`, name: 'Target', author: 'Arkinasi - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/stack_3342239`, name: 'Stack', author: 'Pixel perfect - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/blackhole_6704410`, name: 'Blackhole', author: 'Freepik - Flaticon' }) + this.icons.push({ link: `${FLAT_ICON_URL}/calibration_2364169`, name: 'Calibration', author: 'Freepik - Flaticon' }) + } + + private mapDependencies() { + for (const { name, version } of nebulosa.dependencies) { + this.dependencies.push(this.mapDependency(name, version)) + } + } + + private mapDependency(name: string, version: string): DependencyItem { + const link = `https://www.npmjs.com/package/${name}` + return { name, version, link } } } diff --git a/desktop/src/app/alignment/alignment.component.html b/desktop/src/app/alignment/alignment.component.html index 0fb4e41bc..3be7dfd3c 100644 --- a/desktop/src/app/alignment/alignment.component.html +++ b/desktop/src/app/alignment/alignment.component.html @@ -8,7 +8,7 @@ [(device)]="camera" (deviceChange)="cameraChanged()" /> @@ -41,15 +41,15 @@ [info]="status" />
+ value="RA: {{ tppaResult.rightAscension }}" + [severity]="tppaResult.failed ? 'danger' : 'info'" /> + value="DEC: {{ tppaResult.declination }}" + [severity]="tppaResult.failed ? 'danger' : 'info'" />
@@ -67,7 +67,7 @@ optionLabel="label" optionValue="value" [autoDisplayFirst]="false" - (ngModelChange)="plateSolverChanged()" /> + (ngModelChange)="savePreference()" /> @@ -80,7 +80,7 @@ [min]="1" [max]="60" (ngModelChange)="savePreference()" - scrollableNumber /> + spinnableNumber /> @@ -100,8 +100,8 @@
Azimuth - {{ tppaAzimuthError }} - {{ tppaAzimuthErrorDirection }} + {{ tppaResult.azimuthError }} + {{ tppaResult.azimuthErrorDirection }}
Altitude - {{ tppaAltitudeError }} - {{ tppaAltitudeErrorDirection }} + {{ tppaResult.altitudeError }} + {{ tppaResult.altitudeErrorDirection }}
Total - {{ tppaTotalError }} + {{ tppaResult.totalError }}
@@ -155,7 +155,7 @@ [text]="true" /> } @else if (!running) { + (ngModelChange)="savePreference()" + spinnableNumber />
- - - - +
{ - if (event.device.id === this.camera.id) { + electronService.on('CAMERA.UPDATED', (event) => { + if (event.device.id === this.camera?.id) { ngZone.run(() => { - Object.assign(this.camera, event.device) + if (this.camera) { + Object.assign(this.camera, event.device) + } }) } }) - electron.on('CAMERA.ATTACHED', (event) => { + electronService.on('CAMERA.ATTACHED', (event) => { ngZone.run(() => { this.cameras.push(event.device) this.cameras.sort(deviceComparator) }) }) - electron.on('CAMERA.DETACHED', (event) => { + electronService.on('CAMERA.DETACHED', (event) => { ngZone.run(() => { const index = this.cameras.findIndex((e) => e.id === event.device.id) if (index >= 0) { if (this.cameras[index] === this.camera) { - Object.assign(this.camera, this.cameras[0] ?? EMPTY_CAMERA) + this.camera = this.cameras[0] } this.cameras.splice(index, 1) @@ -115,28 +91,30 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy, Pingable { }) }) - electron.on('MOUNT.UPDATED', (event) => { - if (event.device.id === this.mount.id) { + electronService.on('MOUNT.UPDATED', (event) => { + if (event.device.id === this.mount?.id) { ngZone.run(() => { - Object.assign(this.mount, event.device) + if (this.mount) { + Object.assign(this.mount, event.device) + } }) } }) - electron.on('MOUNT.ATTACHED', (event) => { + electronService.on('MOUNT.ATTACHED', (event) => { ngZone.run(() => { this.mounts.push(event.device) this.mounts.sort(deviceComparator) }) }) - electron.on('MOUNT.DETACHED', (event) => { + electronService.on('MOUNT.DETACHED', (event) => { ngZone.run(() => { const index = this.mounts.findIndex((e) => e.id === event.device.id) if (index >= 0) { if (this.mounts[index] === this.mount) { - Object.assign(this.mount, this.mounts[0] ?? EMPTY_MOUNT) + this.mount = this.mounts[0] } this.mounts.splice(index, 1) @@ -144,28 +122,30 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy, Pingable { }) }) - electron.on('GUIDE_OUTPUT.UPDATED', (event) => { - if (event.device.id === this.guideOutput.id) { + electronService.on('GUIDE_OUTPUT.UPDATED', (event) => { + if (event.device.id === this.guideOutput?.id) { ngZone.run(() => { - Object.assign(this.guideOutput, event.device) + if (this.guideOutput) { + Object.assign(this.guideOutput, event.device) + } }) } }) - electron.on('GUIDE_OUTPUT.ATTACHED', (event) => { + electronService.on('GUIDE_OUTPUT.ATTACHED', (event) => { ngZone.run(() => { this.guideOutputs.push(event.device) this.guideOutputs.sort(deviceComparator) }) }) - electron.on('GUIDE_OUTPUT.DETACHED', (event) => { + electronService.on('GUIDE_OUTPUT.DETACHED', (event) => { ngZone.run(() => { const index = this.guideOutputs.findIndex((e) => e.id === event.device.id) if (index >= 0) { if (this.guideOutputs[index] === this.guideOutput) { - Object.assign(this.guideOutput, this.guideOutputs[0] ?? EMPTY_GUIDE_OUTPUT) + this.guideOutput = this.guideOutputs[0] } this.guideOutputs.splice(index, 1) @@ -173,30 +153,29 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy, Pingable { }) }) - electron.on('TPPA.ELAPSED', (event) => { - if (event.camera.id === this.camera.id) { + electronService.on('TPPA.ELAPSED', (event) => { + if (event.camera.id === this.camera?.id) { ngZone.run(() => { this.status = event.state this.running = event.state !== 'FINISHED' - this.pausingOrPaused = event.state === 'PAUSING' || event.state === 'PAUSED' if (event.state === 'COMPUTED') { - this.tppaFailed = false - this.tppaRightAscension = event.rightAscension - this.tppaDeclination = event.declination - this.tppaAzimuthError = event.azimuthError - this.tppaAltitudeError = event.altitudeError - this.tppaAzimuthErrorDirection = event.azimuthErrorDirection - this.tppaAltitudeErrorDirection = event.altitudeErrorDirection - this.tppaTotalError = event.totalError + this.tppaResult.failed = false + this.tppaResult.rightAscension = event.rightAscension + this.tppaResult.declination = event.declination + this.tppaResult.azimuthError = event.azimuthError + this.tppaResult.altitudeError = event.altitudeError + this.tppaResult.azimuthErrorDirection = event.azimuthErrorDirection + this.tppaResult.altitudeErrorDirection = event.altitudeErrorDirection + this.tppaResult.totalError = event.totalError } else if (event.state === 'FINISHED') { this.cameraExposure.reset() } else if (event.state === 'SOLVED' || event.state === 'SLEWED') { - this.tppaFailed = false - this.tppaRightAscension = event.rightAscension - this.tppaDeclination = event.declination + this.tppaResult.failed = false + this.tppaResult.rightAscension = event.rightAscension + this.tppaResult.declination = event.declination } else if (event.state === 'FAILED') { - this.tppaFailed = true + this.tppaResult.failed = true } if (event.capture && event.capture.state !== 'CAPTURE_FINISHED') { @@ -206,16 +185,16 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy, Pingable { } }) - electron.on('DARV.ELAPSED', (event) => { - if (event.camera.id === this.camera.id) { + electronService.on('DARV.ELAPSED', (event) => { + if (event.camera.id === this.camera?.id) { ngZone.run(() => { this.status = event.state this.running = this.cameraExposure.handleCameraCaptureEvent(event.capture) if (event.state === 'FORWARD' || event.state === 'BACKWARD') { - this.darvDirection = event.direction + this.darvResult.direction = event.direction } else { - this.darvDirection = undefined + this.darvResult.direction = undefined } }) } @@ -225,7 +204,7 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy, Pingable { } async ngAfterViewInit() { - this.pinger.register(this, 30000) + this.ticker.register(this, 30000) this.cameras = (await this.api.cameras()).sort(deviceComparator) this.mounts = (await this.api.mounts()).sort(deviceComparator) @@ -234,21 +213,21 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy, Pingable { @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) + this.ticker.unregister(this) void this.darvStop() void this.tppaStop() } - async ping() { - if (this.camera.id) await this.api.cameraListen(this.camera) - if (this.mount.id) await this.api.mountListen(this.mount) - if (this.guideOutput.id) await this.api.guideOutputListen(this.guideOutput) + async tick() { + if (this.camera?.id) await this.api.cameraListen(this.camera) + if (this.mount?.id) await this.api.mountListen(this.mount) + if (this.guideOutput?.id) await this.api.guideOutputListen(this.guideOutput) } - async cameraChanged() { - if (this.camera.id) { - await this.ping() + protected async cameraChanged() { + if (this.camera?.id) { + await this.tick() const camera = await this.api.camera(this.camera.id) Object.assign(this.camera, camera) @@ -256,9 +235,9 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy, Pingable { } } - async mountChanged() { - if (this.mount.id) { - await this.ping() + protected async mountChanged() { + if (this.mount?.id) { + await this.tick() const mount = await this.api.mount(this.mount.id) Object.assign(this.mount, mount) @@ -267,126 +246,94 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy, Pingable { } } - async guideOutputChanged() { - if (this.guideOutput.id) { - await this.ping() + protected async guideOutputChanged() { + if (this.guideOutput?.id) { + await this.tick() const guideOutput = await this.api.guideOutput(this.guideOutput.id) Object.assign(this.guideOutput, guideOutput) } } - async showCameraDialog() { - if (this.camera.id) { + protected async showCameraDialog() { + if (this.camera?.id) { if (this.tab === 0) { - if (await CameraComponent.showAsDialog(this.browserWindow, 'TPPA', this.camera, this.tppaRequest.capture)) { + if (await CameraComponent.showAsDialog(this.browserWindowService, 'TPPA', this.camera, this.tppaRequest.capture)) { this.savePreference() } } else if (this.tab === 1) { - this.darvRequest.capture.exposureTime = this.darvRequest.exposureTime * 1000000 - this.darvRequest.capture.exposureDelay = this.darvRequest.initialPause - - if (await CameraComponent.showAsDialog(this.browserWindow, 'DARV', this.camera, this.darvRequest.capture)) { + if (await CameraComponent.showAsDialog(this.browserWindowService, 'DARV', this.camera, this.darvRequest.capture)) { this.savePreference() } } } } - plateSolverChanged() { - this.tppaRequest.plateSolver = this.preference.plateSolverRequest(this.tppaRequest.plateSolver.type).get() - this.savePreference() - } - - initialPauseChanged() { - this.darvRequest.capture.exposureDelay = this.darvRequest.initialPause - this.savePreference() - } + protected async darvStart(direction: GuideDirection = 'EAST') { + if (this.camera?.id && this.guideOutput?.id) { + this.method = 'DARV' + this.darvRequest.direction = direction + this.darvRequest.reversed = this.preference.darvHemisphere === 'SOUTHERN' + Object.assign(this.tppaRequest.plateSolver, this.preferenceService.settings.get().plateSolver[this.tppaRequest.plateSolver.type]) - driftForChanged() { - this.darvRequest.capture.exposureTime = this.darvRequest.exposureTime * 1000000 - this.savePreference() + await this.openCameraImage() + await this.api.darvStart(this.camera, this.guideOutput, this.darvRequest) + } } - async darvStart(direction: GuideDirection = 'EAST') { - this.alignmentMethod = 'DARV' - this.darvRequest.direction = direction - this.darvRequest.reversed = this.darvHemisphere === 'SOUTHERN' - this.darvRequest.capture.exposureTime = this.darvRequest.exposureTime * 1000000 - this.darvRequest.capture.exposureDelay = this.darvRequest.initialPause - await this.openCameraImage() - await this.api.darvStart(this.camera, this.guideOutput, this.darvRequest) + protected async darvStop() { + if (this.camera?.id) { + await this.api.darvStop(this.camera) + } } - darvStop() { - return this.api.darvStop(this.camera) - } + protected async tppaStart() { + if (this.camera?.id && this.mount?.id) { + this.method = 'TPPA' - async tppaStart() { - this.alignmentMethod = 'TPPA' - await this.openCameraImage() - await this.api.tppaStart(this.camera, this.mount, this.tppaRequest) + await this.openCameraImage() + await this.api.tppaStart(this.camera, this.mount, this.tppaRequest) + } } - tppaPause() { - return this.api.tppaPause(this.camera) + protected async tppaPause() { + if (this.camera?.id) { + await this.api.tppaPause(this.camera) + } } - tppaUnpause() { - return this.api.tppaUnpause(this.camera) + protected async tppaUnpause() { + if (this.camera?.id) { + await this.api.tppaUnpause(this.camera) + } } - tppaStop() { - return this.api.tppaStop(this.camera) + protected async tppaStop() { + if (this.camera?.id) { + await this.api.tppaStop(this.camera) + } } - openCameraImage() { - return this.browserWindow.openCameraImage(this.camera, 'ALIGNMENT') + protected async openCameraImage() { + if (this.camera?.id) { + await this.browserWindowService.openCameraImage(this.camera, 'ALIGNMENT') + } } private loadPreference() { - const preference = this.preference.alignmentPreference.get() - - this.tppaRequest.startFromCurrentPosition = preference.tppaStartFromCurrentPosition - this.tppaRequest.stepDirection = preference.tppaStepDirection - this.tppaRequest.compensateRefraction = preference.tppaCompensateRefraction - this.tppaRequest.stopTrackingWhenDone = preference.tppaStopTrackingWhenDone - this.tppaRequest.stepDuration = preference.tppaStepDuration - this.tppaRequest.plateSolver.type = preference.tppaPlateSolverType - this.darvRequest.initialPause = preference.darvInitialPause - this.darvRequest.exposureTime = preference.darvExposureTime - this.darvHemisphere = preference.darvHemisphere - - if (this.camera.id) { - const cameraPreference = this.preference.cameraPreference(this.camera).get() - Object.assign(this.tppaRequest.capture, this.preference.cameraStartCaptureForTPPA(this.camera).get(cameraPreference)) - Object.assign(this.darvRequest.capture, this.preference.cameraStartCaptureForDARV(this.camera).get(cameraPreference)) + Object.assign(this.preference, this.preferenceService.alignment.get()) + this.tppaRequest = this.preference.tppaRequest + this.darvRequest = this.preference.darvRequest + if (this.camera?.id) { if (this.camera.connected) { updateCameraStartCaptureFromCamera(this.tppaRequest.capture, this.camera) updateCameraStartCaptureFromCamera(this.darvRequest.capture, this.camera) } } - - this.plateSolverChanged() } - savePreference() { - this.preference.cameraStartCaptureForTPPA(this.camera).set(this.tppaRequest.capture) - this.preference.cameraStartCaptureForDARV(this.camera).set(this.darvRequest.capture) - - const preference: AlignmentPreference = { - tppaStartFromCurrentPosition: this.tppaRequest.startFromCurrentPosition, - tppaStepDirection: this.tppaRequest.stepDirection, - tppaCompensateRefraction: this.tppaRequest.compensateRefraction, - tppaStopTrackingWhenDone: this.tppaRequest.stopTrackingWhenDone, - tppaStepDuration: this.tppaRequest.stepDuration, - tppaPlateSolverType: this.tppaRequest.plateSolver.type, - darvInitialPause: this.darvRequest.initialPause, - darvExposureTime: this.darvRequest.exposureTime, - darvHemisphere: this.darvHemisphere, - } - - this.preference.alignmentPreference.set(preference) + protected savePreference() { + this.preferenceService.alignment.set(this.preference) } } diff --git a/desktop/src/app/app-routing.module.ts b/desktop/src/app/app-routing.module.ts index 04dfdb38a..7f637d3fe 100644 --- a/desktop/src/app/app-routing.module.ts +++ b/desktop/src/app/app-routing.module.ts @@ -20,6 +20,7 @@ import { MountComponent } from './mount/mount.component' import { RotatorComponent } from './rotator/rotator.component' import { SequencerComponent } from './sequencer/sequencer.component' import { SettingsComponent } from './settings/settings.component' +import { StackerComponent } from './stacker/stacker.component' const routes: Routes = [ { @@ -91,6 +92,10 @@ const routes: Routes = [ path: 'auto-focus', component: AutoFocusComponent, }, + { + path: 'stacker', + component: StackerComponent, + }, { path: 'calculator', component: CalculatorComponent, diff --git a/desktop/src/app/app.component.html b/desktop/src/app/app.component.html index 404b64eb1..263704236 100644 --- a/desktop/src/app/app.component.html +++ b/desktop/src/app/app.component.html @@ -7,15 +7,12 @@
{{ title }}
{{ subTitle }}
- - - boolean | Promise private readonly resizeObserver?: ResizeObserver @@ -30,14 +31,14 @@ export class AppComponent implements OnDestroy { constructor( private readonly windowTitle: Title, - private readonly electron: ElectronService, - confirmation: ConfirmationService, + private readonly electronService: ElectronService, + confirmationService: ConfirmationService, ngZone: NgZone, hostElementRef: ElementRef, ) { console.info('APP_CONFIG', APP_CONFIG) - if (electron.isElectron) { + if (electronService.isElectron) { console.info('Run in electron', window.preference) } else { console.info('Run in browser', window.preference) @@ -48,7 +49,7 @@ export class AppComponent implements OnDestroy { const height = entries[0].target.clientHeight if (height) { - void this.electron.resizeWindow(height) + void this.electronService.resizeWindow(height) } }) @@ -57,34 +58,39 @@ export class AppComponent implements OnDestroy { this.resizeObserver = undefined } - electron.on('CONFIRMATION', (event) => { - if (confirmation.has(event.idempotencyKey)) { + electronService.on('CONFIRMATION', (event) => { + if (confirmationService.has(event.idempotencyKey)) { void ngZone.run(() => { - return confirmation.processConfirmationEvent(event) + return confirmationService.processConfirmationEvent(event) }) } }) } + @HostListener('window:unload') ngOnDestroy() { this.resizeObserver?.disconnect() } pin() { this.pinned = !this.pinned - if (this.pinned) return this.electron.pinWindow() - else return this.electron.unpinWindow() + if (this.pinned) return this.electronService.pinWindow() + else return this.electronService.unpinWindow() } minimize() { - return this.electron.minimizeWindow() + return this.electronService.minimizeWindow() } maximize() { - return this.electron.maximizeWindow() + return this.electronService.maximizeWindow() } - close(data?: unknown) { - return this.electron.closeWindow(data) + async close(data?: unknown, force: boolean = false) { + if (!this.beforeClose || (await this.beforeClose()) || force) { + return await this.electronService.closeWindow(data) + } else { + return undefined + } } } diff --git a/desktop/src/app/app.module.ts b/desktop/src/app/app.module.ts index b16bd44e5..d816d784b 100644 --- a/desktop/src/app/app.module.ts +++ b/desktop/src/app/app.module.ts @@ -5,6 +5,7 @@ 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 { NgxMoveableModule } from 'ngx-moveable' import { AccordionModule } from 'primeng/accordion' import { ConfirmationService, MessageService } from 'primeng/api' import { BadgeModule } from 'primeng/badge' @@ -48,6 +49,7 @@ import { DeviceChooserComponent } from '../shared/components/device-chooser/devi import { DeviceListMenuComponent } from '../shared/components/device-list-menu/device-list-menu.component' import { DialogMenuComponent } from '../shared/components/dialog-menu/dialog-menu.component' import { HistogramComponent } from '../shared/components/histogram/histogram.component' +import { LocationComponent } from '../shared/components/location/location.dialog' import { MapComponent } from '../shared/components/map/map.component' import { MenuBarComponent } from '../shared/components/menu-bar/menu-bar.component' import { MenuItemComponent } from '../shared/components/menu-item/menu-item.component' @@ -55,9 +57,8 @@ import { MoonComponent } from '../shared/components/moon/moon.component' import { PathChooserComponent } from '../shared/components/path-chooser/path-chooser.component' import { SlideMenuComponent } from '../shared/components/slide-menu/slide-menu.component' import { ConfirmDialog } from '../shared/dialogs/confirm/confirm.dialog' -import { LocationDialog } from '../shared/dialogs/location/location.dialog' -import { ScrollableNumberDirective } from '../shared/directives/input-number-scrollable' import { NoDropdownDirective } from '../shared/directives/no-dropdown.directive' +import { SpinnableNumberDirective } from '../shared/directives/spinnable-number.directive' import { StopPropagationDirective } from '../shared/directives/stop-propagation.directive' import { ConfirmationInterceptor } from '../shared/interceptors/confirmation.interceptor' import { IdempotencyKeyInterceptor } from '../shared/interceptors/idempotency-key.interceptor' @@ -68,6 +69,7 @@ import { EnumDropdownPipe } from '../shared/pipes/enum-dropdown.pipe' import { EnumPipe } from '../shared/pipes/enum.pipe' import { EnvPipe } from '../shared/pipes/env.pipe' import { ExposureTimePipe } from '../shared/pipes/exposureTime.pipe' +import { PathPipe } from '../shared/pipes/path.pipe' import { SkyObjectPipe } from '../shared/pipes/skyObject.pipe' import { WinPipe } from '../shared/pipes/win.pipe' import { AboutComponent } from './about/about.component' @@ -80,12 +82,14 @@ import { CalculatorComponent } from './calculator/calculator.component' import { FormulaComponent } from './calculator/formula/formula.component' import { CalibrationComponent } from './calibration/calibration.component' import { CameraComponent } from './camera/camera.component' +import { ExposureTimeComponent } from './camera/exposure-time.component' import { FilterWheelComponent } from './filterwheel/filterwheel.component' import { FlatWizardComponent } from './flat-wizard/flat-wizard.component' import { FocuserComponent } from './focuser/focuser.component' import { FramingComponent } from './framing/framing.component' import { GuiderComponent } from './guider/guider.component' import { HomeComponent } from './home/home.component' +import { CrossHairComponent } from './image/crosshair.component' import { ImageComponent } from './image/image.component' import { INDIComponent } from './indi/indi.component' import { INDIPropertyComponent } from './indi/property/indi-property.component' @@ -93,6 +97,7 @@ import { MountComponent } from './mount/mount.component' import { RotatorComponent } from './rotator/rotator.component' import { SequencerComponent } from './sequencer/sequencer.component' import { SettingsComponent } from './settings/settings.component' +import { StackerComponent } from './stacker/stacker.component' @NgModule({ declarations: [ @@ -108,6 +113,7 @@ import { SettingsComponent } from './settings/settings.component' CameraInfoComponent, CameraExposureComponent, ConfirmDialog, + CrossHairComponent, DeviceChooserComponent, DeviceListMenuComponent, DialogMenuComponent, @@ -115,6 +121,7 @@ import { SettingsComponent } from './settings/settings.component' EnumPipe, EnumDropdownPipe, EnvPipe, + ExposureTimeComponent, ExposureTimePipe, FilterWheelComponent, FlatWizardComponent, @@ -125,10 +132,10 @@ import { SettingsComponent } from './settings/settings.component' HistogramComponent, HomeComponent, ImageComponent, - ScrollableNumberDirective, + SpinnableNumberDirective, INDIComponent, INDIPropertyComponent, - LocationDialog, + LocationComponent, MapComponent, MenuBarComponent, MenuItemComponent, @@ -136,11 +143,13 @@ import { SettingsComponent } from './settings/settings.component' MountComponent, NoDropdownDirective, PathChooserComponent, + PathPipe, RotatorComponent, SequencerComponent, SettingsComponent, SkyObjectPipe, SlideMenuComponent, + StackerComponent, StopPropagationDirective, WinPipe, ], @@ -173,6 +182,7 @@ import { SettingsComponent } from './settings/settings.component' MenuModule, MessageModule, MultiSelectModule, + NgxMoveableModule, OverlayPanelModule, ProgressBarModule, ScrollPanelModule, diff --git a/desktop/src/app/atlas/atlas.component.html b/desktop/src/app/atlas/atlas.component.html index 2ebe2aed3..f700b6b48 100644 --- a/desktop/src/app/atlas/atlas.component.html +++ b/desktop/src/app/atlas/atlas.component.html @@ -17,12 +17,14 @@
+ [class.invisible]="!sun.image" + style="height: 225px"> + [src]="sun.image" + style="width: 223px" /> SDO/HMI @@ -43,12 +45,12 @@
+ style="height: 225px"> + [width]="200" + [height]="200" + [illuminationRatio]="moon.position.illuminated / 100" + [waning]="moon.position.leading" />
- +
@@ -109,13 +111,13 @@
@@ -168,8 +170,8 @@ [step]="1" [showButtons]="true" inputStyleClass="p-inputtext-sm border-0 w-full" - [(ngModel)]="closeApproachDays" - scrollableNumber /> + [(ngModel)]="minorPlanet.closeApproach.days" + spinnableNumber /> @@ -181,13 +183,13 @@ [showButtons]="true" inputStyleClass="p-inputtext-sm border-0 w-full" locale="en" - [(ngModel)]="closeApproachDistance" - scrollableNumber /> + [(ngModel)]="minorPlanet.closeApproach.lunarDistance" + spinnableNumber />
@@ -246,15 +248,15 @@
- +
@@ -263,7 +265,7 @@ + [(ngModel)]="skyObject.search.filter.text" />
@@ -278,7 +280,7 @@ tooltipPosition="bottom" />
+ [(ngModel)]="satellite.search.filter.text" />
@@ -376,7 +378,7 @@ tooltipPosition="bottom" />
+ [value]="position.rightAscensionJ2000" />
@@ -463,7 +465,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.declinationJ2000" /> + [value]="position.declinationJ2000" />
@@ -473,7 +475,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.rightAscension" /> + [value]="position.rightAscension" />
@@ -483,7 +485,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.declination" /> + [value]="position.declination" />
@@ -493,7 +495,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.azimuth" /> + [value]="position.azimuth" />
@@ -503,7 +505,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.altitude" /> + [value]="position.altitude" />
@@ -513,7 +515,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.magnitude < 30 ? bodyPosition.magnitude.toFixed(2) : '-'" /> + [value]="position.magnitude < 30 ? position.magnitude.toFixed(2) : '-'" />
@@ -523,7 +525,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.constellation" /> + [value]="position.constellation" />
@@ -533,8 +535,8 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.distance.toFixed(3)" /> - + [value]="position.distance.toFixed(3)" /> +
@@ -543,7 +545,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.illuminated.toFixed(3)" /> + [value]="position.illuminated.toFixed(3)" />
@@ -553,7 +555,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="bodyPosition.elongation.toFixed(3)" /> + [value]="position.elongation.toFixed(3)" />
@@ -573,7 +575,7 @@
- {{ name ?? '' }} + {{ body.name.replaceAll('|', ' · ') }}
@@ -643,12 +645,12 @@
-
+
+ [value]="skyObject.search.filter.rightAscension" />
@@ -679,9 +679,9 @@ + [value]="skyObject.search.filter.declination" />
@@ -696,16 +696,17 @@ [showButtons]="true" inputStyleClass="p-inputtext-sm border-0 w-full" locale="en" - [(ngModel)]="skyObjectFilter.radius" - scrollableNumber /> + [(ngModel)]="skyObject.search.filter.radius" + spinnableNumber />
+ @let constellations = ['ALL'].concat('CONSTELLATION' | dropdownOptions);
+ @let skyObjectTypes = ['ALL'].concat('SKY_OBJECT_TYPE' | dropdownOptions); + [(ngModel)]="skyObject.search.filter.magnitude[0]" + spinnableNumber />
@@ -756,8 +758,8 @@ [showButtons]="true" inputStyleClass="p-inputtext-sm border-0 w-full" locale="en" - [(ngModel)]="skyObjectFilter.magnitude[1]" - scrollableNumber /> + [(ngModel)]="skyObject.search.filter.magnitude[1]" + spinnableNumber />
@@ -769,26 +771,28 @@ icon="mdi mdi-filter" label="Filter" size="small" - (onClick)="filterSkyObject()" /> + (onClick)="searchSkyObject()" />
- + @let groups = 'SATELLITE_GROUP_TYPE' | dropdownOptions; + + @for (group of groups; track $index) { - + }
@@ -799,47 +803,27 @@ icon="mdi mdi-restore" label="Reset" size="small" - (onClick)="resetSatelliteFilter()" /> + (onClick)="resetSatelliteSearchGroups()" /> - -
- - -
-
-
-
- - + (onClick)="searchSatellite()" />
-
+
-
+
- Enable + Date Time + [(ngModel)]="dateTimeAndLocation.manual" + (ngModelChange)="manualDateTimeChanged()" />
-
+
-
- Hour + -
-
- Minute + spinnableNumber /> + + + -
+ spinnableNumber /> + +
+ + + +
@@ -900,5 +892,6 @@ + [header]="body.name" /> + diff --git a/desktop/src/app/atlas/atlas.component.scss b/desktop/src/app/atlas/atlas.component.scss index d1370a819..e332e3151 100644 --- a/desktop/src/app/atlas/atlas.component.scss +++ b/desktop/src/app/atlas/atlas.component.scss @@ -1,58 +1,56 @@ -:host { +neb-atlas { display: flex; flex-direction: column; height: 100vh; - ::ng-deep { - .p-tabview { - p-table.planet .p-datatable-wrapper { - height: 229px; - } + .p-tabview { + p-table.planet .p-datatable-wrapper { + height: 229px; + } - p-table.minorPlanet .p-datatable-wrapper { - height: 154px; - } + p-table.minorPlanet .p-datatable-wrapper { + height: 154px; + } - p-table.skyObject .p-datatable-wrapper, - p-table.satellite .p-datatable-wrapper { - height: 143px; - } + p-table.skyObject .p-datatable-wrapper, + p-table.satellite .p-datatable-wrapper { + height: 143px; + } - .p-tabview-nav li.main { - width: calc(100% / 6) !important; + .p-tabview-nav li.main { + width: calc(100% / 6) !important; - .p-tabview-nav-link { - display: flex; - justify-content: center; - padding: 0px; - height: 100%; + .p-tabview-nav-link { + display: flex; + justify-content: center; + padding: 0px; + height: 100%; - img { - height: 20px; - } + img { + height: 20px; } } } + } - .info.p-tabview { - padding-left: 0.21rem; - padding-right: 0.21rem; - } + .info.p-tabview { + padding-left: 0.21rem; + padding-right: 0.21rem; + } - .p-tabview .p-tabview-left-icon { - margin-right: 0px; - } + .p-tabview .p-tabview-left-icon { + margin-right: 0px; } -} -.p-input-icon-left span.pi:first-of-type { - position: absolute; - top: 50%; - margin-top: -0.5rem; - left: 0.75rem; - z-index: 1; -} + .p-input-icon-left span.pi:first-of-type { + position: absolute; + top: 50%; + margin-top: -0.5rem; + left: 0.75rem; + z-index: 1; + } -.p-input-icon-right p-checkbox { - margin-top: -0.745rem; + .p-input-icon-right p-checkbox { + margin-top: -0.745rem; + } } diff --git a/desktop/src/app/atlas/atlas.component.ts b/desktop/src/app/atlas/atlas.component.ts index d7a4eff19..dbcf90086 100644 --- a/desktop/src/app/atlas/atlas.component.ts +++ b/desktop/src/app/atlas/atlas.component.ts @@ -1,145 +1,65 @@ -import { AfterContentInit, AfterViewInit, Component, ElementRef, HostListener, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' +import { AfterContentInit, AfterViewInit, Component, HostListener, NgZone, OnDestroy, OnInit, ViewChild, ViewEncapsulation } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { Chart, ChartData, ChartOptions } from 'chart.js' import zoomPlugin from 'chartjs-plugin-zoom' -import moment from 'moment' import { UIChart } from 'primeng/chart' import { ListboxChangeEvent } from 'primeng/listbox' import { OverlayPanel } from 'primeng/overlaypanel' -import { Subscription, timer } from 'rxjs' +import { timer } from 'rxjs' import { DeviceListMenuComponent } from '../../shared/components/device-list-menu/device-list-menu.component' import { SlideMenuItem } from '../../shared/components/menu-item/menu-item.component' import { ONE_DECIMAL_PLACE_FORMATTER, TWO_DIGITS_FORMATTER } from '../../shared/constants' -import { SkyObjectPipe } from '../../shared/pipes/skyObject.pipe' +import { AngularService } from '../../shared/services/angular.service' 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 { PrimeService } from '../../shared/services/prime.service' +import { extractDate, extractTime } from '../../shared/types/angular.types' import { - CONSTELLATIONS, - CloseApproach, - Constellation, - DeepSkyObject, - EMPTY_BODY_POSITION, - EMPTY_SEARCH_FILTER, + AltitudeDataPoint, + BodyTabType, + BodyTag, + DEFAULT_BODY_TAB_REFRESH, + DEFAULT_DATE_TIME_AND_LOCATION, + DEFAULT_LOCATION, + DEFAULT_MINOR_PLANET, + DEFAULT_MOON, + DEFAULT_PLANET, + DEFAULT_SATELLITE, + DEFAULT_SKY_ATLAS_PREFERENCE, + DEFAULT_SKY_OBJECT, + DEFAULT_SUN, Location, - MinorPlanet, - MinorPlanetSearchItem, - PlanetTableItem, + MinorPlanetListItem, SATELLITE_GROUPS, - Satellite, - SatelliteGroupType, - SettingsDialog, SkyAtlasInput, - SkyAtlasTab, + resetSatelliteSearchGroup, + searchFilterWithDefault, } from '../../shared/types/atlas.types' import { Mount } from '../../shared/types/mount.types' import { AppComponent } from '../app.component' -Chart.register(zoomPlugin) - @Component({ - selector: 'app-atlas', + selector: 'neb-atlas', templateUrl: './atlas.component.html', styleUrls: ['./atlas.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class AtlasComponent implements OnInit, AfterContentInit, AfterViewInit, OnDestroy { - refreshingPosition = false - refreshingChart = false - tab = SkyAtlasTab.SUN - - get refreshing() { - return this.refreshingPosition || this.refreshingChart - } - - readonly bodyPosition = structuredClone(EMPTY_BODY_POSITION) - moonIlluminated = 1 - moonWaning = false - - useManualDateTime = false - dateTime = new Date() - dateTimeHour = this.dateTime.getHours() - dateTimeMinute = this.dateTime.getMinutes() - - planet?: PlanetTableItem - readonly planets: PlanetTableItem[] = [ - { name: 'Mercury', type: 'Planet', code: '199' }, - { name: 'Venus', type: 'Planet', code: '299' }, - { name: 'Mars', type: 'Planet', code: '499' }, - { name: 'Jupiter', type: 'Planet', code: '599' }, - { name: 'Saturn', type: 'Planet', code: '699' }, - { name: 'Uranus', type: 'Planet', code: '799' }, - { name: 'Neptune', type: 'Planet', code: '899' }, - { name: 'Pluto', type: 'Dwarf Planet', code: '999' }, - { name: 'Phobos', type: `Mars' Satellite`, code: '401' }, - { name: 'Deimos', type: `Mars' Satellite`, code: '402' }, - { name: 'Io', type: `Jupiter's Satellite`, code: '501' }, - { name: 'Europa', type: `Jupiter's Satellite`, code: '402' }, - { name: 'Ganymede', type: `Jupiter's Satellite`, code: '403' }, - { name: 'Callisto', type: `Jupiter's Satellite`, code: '504' }, - { name: 'Mimas', type: `Saturn's Satellite`, code: '601' }, - { name: 'Enceladus', type: `Saturn's Satellite`, code: '602' }, - { name: 'Tethys', type: `Saturn's Satellite`, code: '603' }, - { name: 'Dione', type: `Saturn's Satellite`, code: '604' }, - { name: 'Rhea', type: `Saturn's Satellite`, code: '605' }, - { name: 'Titan', type: `Saturn's Satellite`, code: '606' }, - { name: 'Hyperion', type: `Saturn's Satellite`, code: '607' }, - { name: 'Iapetus', type: `Saturn's Satellite`, code: '608' }, - { name: 'Ariel', type: `Uranus' Satellite`, code: '701' }, - { name: 'Umbriel', type: `Uranus' Satellite`, code: '702' }, - { name: 'Titania', type: `Uranus' Satellite`, code: '703' }, - { name: 'Oberon', type: `Uranus' Satellite`, code: '704' }, - { name: 'Miranda', type: `Uranus' Satellite`, code: '705' }, - { name: 'Triton', type: `Neptune's Satellite`, code: '801' }, - { name: 'Charon', type: `Pluto's Satellite`, code: '901' }, - { name: '1 Ceres', type: 'Dwarf Planet', code: '1;' }, - { name: '90377 Sedna', type: 'Dwarf Planet', code: '90377;' }, - { name: '136199 Eris', type: 'Dwarf Planet', code: '136199;' }, - { name: '2 Pallas', type: 'Asteroid', code: '2;' }, - { name: '3 Juno', type: 'Asteroid', code: '3;' }, - { name: '4 Vesta', type: 'Asteroid', code: '4;' }, - ] - - minorPlanetTab = 0 - minorPlanet?: MinorPlanet - minorPlanetSearchText = '' - minorPlanetChoiceItems: { name: string; pdes: string }[] = [] - showMinorPlanetChoiceDialog = false - closeApproach?: CloseApproach - closeApproaches: CloseApproach[] = [] - closeApproachDays = 7 - closeApproachDistance = 10 - - skyObject?: DeepSkyObject - skyObjectItems: DeepSkyObject[] = [] - skyObjectSearchText = '' - readonly skyObjectFilter = structuredClone(EMPTY_SEARCH_FILTER) - showSkyObjectFilter = false - readonly constellationOptions: (Constellation | 'ALL')[] = ['ALL', ...CONSTELLATIONS] - - satellite?: Satellite - satelliteItems: Satellite[] = [] - satelliteSearchText = '' - showSatelliteFilterDialog = false - readonly satelliteSearchGroup = new Map() - - name? = 'Sun' - tags: { title: string; severity: 'success' | 'info' | 'warning' | 'danger' }[] = [] - - @ViewChild('imageOfSun') - private readonly imageOfSun!: ElementRef - - @ViewChild('deviceMenu') - private readonly deviceMenu!: DeviceListMenuComponent - - @ViewChild('calendarPanel') - private readonly calendarPanel!: OverlayPanel - - @ViewChild('chart') - private readonly chart!: UIChart - - readonly altitudeData: ChartData = { + protected readonly sun = structuredClone(DEFAULT_SUN) + protected readonly moon = structuredClone(DEFAULT_MOON) + protected readonly planet = structuredClone(DEFAULT_PLANET) + protected readonly minorPlanet = structuredClone(DEFAULT_MINOR_PLANET) + protected readonly skyObject = structuredClone(DEFAULT_SKY_OBJECT) + protected readonly satellite = structuredClone(DEFAULT_SATELLITE) + protected readonly preference = structuredClone(DEFAULT_SKY_ATLAS_PREFERENCE) + protected readonly refresh = structuredClone(DEFAULT_BODY_TAB_REFRESH) + protected readonly dateTimeAndLocation = structuredClone(DEFAULT_DATE_TIME_AND_LOCATION) + + protected tab = BodyTabType.SUN + protected locations: Location[] = [structuredClone(DEFAULT_LOCATION)] + + protected readonly altitudeData: ChartData = { labels: ['12h', '13h', '14h', '15h', '16h', '17h', '18h', '19h', '20h', '21h', '22h', '23h', '0h', '1h', '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', '10h', '11h', '12h'], datasets: [ // Day. @@ -262,7 +182,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, AfterViewInit, ], } - readonly altitudeOptions: ChartOptions = { + protected readonly altitudeOptions: ChartOptions = { responsive: true, plugins: { legend: { @@ -371,84 +291,99 @@ export class AtlasComponent implements OnInit, AfterContentInit, AfterViewInit, }, } - private static readonly DEFAULT_SATELLITE_FILTERS: SatelliteGroupType[] = ['AMATEUR', 'BEIDOU', 'GALILEO', 'GLO_OPS', 'GNSS', 'GPS_OPS', 'ONEWEB', 'SCIENCE', 'STARLINK', 'STATIONS', 'VISUAL'] - - readonly ephemerisModel: SlideMenuItem[] = [ + protected readonly ephemerisModel: SlideMenuItem[] = [ { icon: 'mdi mdi-magnify', label: 'Find sky objects around this object', slideMenu: [], command: async () => { - this.skyObjectFilter.rightAscension = this.bodyPosition.rightAscensionJ2000 - this.skyObjectFilter.declination = this.bodyPosition.declinationJ2000 - if (this.skyObjectFilter.radius <= 0) this.skyObjectFilter.radius = 4 + this.skyObject.search.filter.rightAscension = this.position.rightAscensionJ2000 + this.skyObject.search.filter.declination = this.position.declinationJ2000 + if (this.skyObject.search.filter.radius <= 0) this.skyObject.search.filter.radius = 4 - this.tab = SkyAtlasTab.SKY_OBJECT + this.tab = BodyTabType.SKY_OBJECT await this.tabChanged() - await this.filterSkyObject() + await this.searchSkyObject() }, }, ] - private refreshTimer?: Subscription - private refreshTabCount = 0 + @ViewChild('deviceMenu') + private readonly deviceMenu!: DeviceListMenuComponent + + @ViewChild('dateTimeAndLocationPanel') + private readonly dateTimeAndLocationPanel!: OverlayPanel + + @ViewChild('chart') + private readonly chart!: UIChart + + get body() { + switch (this.tab) { + case BodyTabType.SUN: + return this.sun + case BodyTabType.MOON: + return this.moon + case BodyTabType.PLANET: + return this.planet + case BodyTabType.MINOR_PLANET: + return this.minorPlanet + case BodyTabType.SKY_OBJECT: + return this.skyObject + case BodyTabType.SATELLITE: + return this.satellite + default: + return this.sun + } + } - private location: Location + get position() { + return this.body.position + } - readonly settings: SettingsDialog = { - showDialog: false, + get refreshing() { + return this.refresh.position || this.refresh.chart } constructor( private readonly app: AppComponent, private readonly api: ApiService, - private readonly browserWindow: BrowserWindowService, + private readonly browserWindowService: BrowserWindowService, private readonly route: ActivatedRoute, - electron: ElectronService, - private readonly preference: PreferenceService, - private readonly skyObjectPipe: SkyObjectPipe, - private readonly prime: PrimeService, + electronService: ElectronService, + private readonly preferenceService: PreferenceService, + private readonly angularService: AngularService, ngZone: NgZone, ) { app.title = 'Sky Atlas' - app.topMenu.push({ - icon: 'mdi mdi-cog', - tooltip: 'Settings', - visible: false, - command: () => { - this.settings.showDialog = true - }, - }) app.topMenu.push({ icon: 'mdi mdi-calendar', - tooltip: 'Date & Time', + tooltip: 'Date Time and Location', command: (e) => { - this.calendarPanel.toggle(e.originalEvent) + this.dateTimeAndLocationPanel.toggle(e.originalEvent) }, }) - electron.on('LOCATION.CHANGED', async (event) => { - await ngZone.run(() => { - this.location = event - return this.refreshTab(true, true) + electronService.on('LOCATION.CHANGED', (location) => { + ngZone.run(() => { + this.loadLocations() + + if (this.dateTimeAndLocation.location.id === location.id) { + void this.refreshTab(true, true) + } }) }) - electron.on('DATA.CHANGED', async (event) => { + electronService.on('DATA.CHANGED', async (event) => { await this.loadTabFromData(event) }) - - this.location = this.preference.selectedLocation.get() - - // TODO: Refresh graph and twilight if hours past 12 (noon) } - async ngOnInit() { + ngOnInit() { + Chart.register(zoomPlugin) + this.loadPreference() - const types = await this.api.skyObjectTypes() - this.skyObjectFilter.types = ['ALL', ...types] } ngAfterContentInit() { @@ -462,8 +397,8 @@ export class AtlasComponent implements OnInit, AfterContentInit, AfterViewInit, const now = new Date() const initialDelay = 60 * 1000 - (now.getSeconds() * 1000 + now.getMilliseconds()) - this.refreshTimer = timer(initialDelay, 60 * 1000).subscribe(async () => { - if (!this.useManualDateTime) { + this.refresh.timer = timer(initialDelay, 60 * 1000).subscribe(async () => { + if (!this.dateTimeAndLocation.manual) { await this.refreshTab() } }) @@ -476,285 +411,244 @@ export class AtlasComponent implements OnInit, AfterContentInit, AfterViewInit, async ngAfterViewInit() { await this.refreshTab() - - this.calendarPanel.onOverlayClick = (e) => { - e.stopImmediatePropagation() - } } @HostListener('window:unload') ngOnDestroy() { - this.refreshTimer?.unsubscribe() + this.refresh.timer?.unsubscribe() } private async loadTabFromData(data?: SkyAtlasInput) { - if (data?.tab) { + if (data && data.tab >= BodyTabType.SUN) { this.tab = data.tab - if (this.tab === SkyAtlasTab.SKY_OBJECT) { - this.skyObjectFilter.rightAscension = data.filter?.rightAscension ?? this.skyObjectFilter.rightAscension - this.skyObjectFilter.declination = data.filter?.declination ?? this.skyObjectFilter.declination - this.skyObjectFilter.radius = (data.filter?.radius ?? this.skyObjectFilter.radius) || 4.0 - this.skyObjectFilter.constellation = data.filter?.constellation ?? this.skyObjectFilter.constellation - this.skyObjectFilter.magnitude = data.filter?.magnitude ?? this.skyObjectFilter.magnitude - this.skyObjectFilter.type = data.filter?.type ?? this.skyObjectFilter.type + if (this.tab === BodyTabType.SKY_OBJECT) { + this.skyObject.search.filter = searchFilterWithDefault(data.filter, this.skyObject.search.filter) await this.tabChanged() - await this.filterSkyObject() + await this.searchSkyObject() } } } - async tabChanged() { - await this.refreshTab(false, true) + protected tabChanged() { + return this.refreshTab(false, true) } - async planetChanged() { - await this.refreshTab(false, true) + protected async planetChanged() { + if (this.planet.selected) { + this.planet.name = this.planet.selected.name + await this.refreshTab(false, true) + } } - async searchMinorPlanet() { - this.refreshingPosition = true + protected async searchMinorPlanet() { + this.refresh.position = true try { - const minorPlanet = await this.api.searchMinorPlanet(this.minorPlanetSearchText) + const minorPlanet = await this.api.searchMinorPlanet(this.minorPlanet.search.text) if (minorPlanet.found) { - this.minorPlanet = minorPlanet + this.minorPlanet.search.result = minorPlanet + this.minorPlanet.name = minorPlanet.name + + const tags: BodyTag[] = [] + // if (minorPlanet.kind) tags.push({ label: minorPlanet.kind, severity: 'success' }) + if (minorPlanet.orbitType) tags.push({ label: minorPlanet.orbitType, severity: 'success' }) + if (minorPlanet.pha) tags.push({ label: 'PHA', severity: 'danger' }) + if (minorPlanet.neo) tags.push({ label: 'NEO', severity: 'warning' }) + this.minorPlanet.tags = tags + await this.refreshTab(false, true) - } else { - this.minorPlanetChoiceItems = minorPlanet.searchItems - this.showMinorPlanetChoiceDialog = true + } else if (minorPlanet.list.length) { + this.minorPlanet.list.items = minorPlanet.list + this.minorPlanet.list.showDialog = true } } finally { - this.refreshingPosition = false + this.refresh.position = false } } - async minorPlanetChoosen(event: ListboxChangeEvent) { - this.minorPlanetSearchText = (event.value as MinorPlanetSearchItem).pdes + protected async minorPlanetSelected(event: ListboxChangeEvent) { + const value = event.value as MinorPlanetListItem + this.minorPlanet.search.text = value.pdes + this.minorPlanet.list.showDialog = false await this.searchMinorPlanet() - this.showMinorPlanetChoiceDialog = false } - async closeApproachesForMinorPlanets() { - this.refreshingPosition = true + protected async closeApproachesOfMinorPlanets() { + this.refresh.position = true try { - this.closeApproaches = await this.api.closeApproachesForMinorPlanets(this.closeApproachDays, this.closeApproachDistance, this.dateTime) + this.minorPlanet.closeApproach.result = await this.api.closeApproachesOfMinorPlanets(this.minorPlanet.closeApproach.days, this.minorPlanet.closeApproach.lunarDistance, this.dateTimeAndLocation.dateTime) - if (!this.closeApproaches.length) { - this.prime.message('No close approaches found for the given days and lunar distance', 'warn') + if (!this.minorPlanet.closeApproach.result.length) { + this.angularService.message('No close approaches found for the given days and lunar distance', 'warn') } } finally { - this.refreshingPosition = false + this.refresh.position = false } } - async closeApproachChanged() { - if (this.closeApproach) { - this.minorPlanetSearchText = this.closeApproach.designation - this.minorPlanetTab = 0 + protected async closeApproachChanged() { + if (this.minorPlanet.closeApproach.selected) { + this.minorPlanet.search.text = this.minorPlanet.closeApproach.selected.designation + this.minorPlanet.tab = 0 await this.searchMinorPlanet() } } - starChanged() { - return this.refreshTab(false, true) - } - - dsoChanged() { - return this.refreshTab(false, true) - } - - skyObjectChanged() { - return this.refreshTab(false, true) - } - - satelliteChanged() { - return this.refreshTab(false, true) + protected async skyObjectChanged() { + if (this.skyObject.search.selected) { + this.skyObject.name = this.skyObject.search.selected.name + await this.refreshTab(false, true) + } } - showSkyObjectFilterDialog() { - this.showSkyObjectFilter = true + protected async satelliteChanged() { + if (this.satellite.search.selected) { + this.satellite.name = this.satellite.search.selected.name + await this.refreshTab(false, true) + } } - async searchSkyObject() { - const constellation = this.skyObjectFilter.constellation === 'ALL' ? undefined : this.skyObjectFilter.constellation - const type = this.skyObjectFilter.type === 'ALL' ? undefined : this.skyObjectFilter.type + protected async searchSkyObject() { + const constellation = this.skyObject.search.filter.constellation === 'ALL' ? undefined : this.skyObject.search.filter.constellation + const type = this.skyObject.search.filter.type === 'ALL' ? undefined : this.skyObject.search.filter.type - this.refreshingPosition = true + this.refresh.position = true try { - this.skyObjectItems = await this.api.searchSkyObject(this.skyObjectSearchText, this.skyObjectFilter.rightAscension, this.skyObjectFilter.declination, this.skyObjectFilter.radius, constellation, this.skyObjectFilter.magnitude[0], this.skyObjectFilter.magnitude[1], type) + const { text, rightAscension, declination, radius, magnitude } = this.skyObject.search.filter + this.skyObject.search.result = await this.api.searchSkyObject(text, rightAscension, declination, radius, constellation, magnitude[0], magnitude[1], type) } finally { - this.refreshingPosition = false + this.skyObject.search.showDialog = false + this.refresh.position = false } } - async filterSkyObject() { - await this.searchSkyObject() - this.showSkyObjectFilter = false - } - - async searchSatellite() { - this.refreshingPosition = true + protected async searchSatellite() { + this.refresh.position = true try { - this.savePreference() - const groups = SATELLITE_GROUPS.filter((e) => this.satelliteSearchGroup.get(e)) - this.satelliteItems = await this.api.searchSatellites(this.satelliteSearchText, groups) + const groups = SATELLITE_GROUPS.filter((e) => this.satellite.search.filter.groups[e]) + this.satellite.search.result = await this.api.searchSatellites(this.satellite.search.filter.text, groups) } finally { - this.refreshingPosition = false + this.satellite.search.showDialog = false + this.refresh.position = false } } - resetSatelliteFilter() { - for (const group of SATELLITE_GROUPS) { - const enabled = AtlasComponent.DEFAULT_SATELLITE_FILTERS.includes(group) - this.satelliteSearchGroup.set(group, enabled) - } - + protected resetSatelliteSearchGroups() { + resetSatelliteSearchGroup(this.satellite.search.filter.groups) this.savePreference() } - async filterSatellite() { - await this.searchSatellite() - this.showSatelliteFilterDialog = false - } - - async dateTimeChanged(dateChanged: boolean) { + protected async dateTimeChanged(dateChanged: boolean) { + this.savePreference() await this.refreshTab(dateChanged, true) } - async useManualDateTimeChanged() { - if (!this.useManualDateTime) { + protected async manualDateTimeChanged() { + this.savePreference() + + if (!this.dateTimeAndLocation.manual) { await this.refreshTab(true, true) } } - mountGoTo() { + protected locationChanged() { + this.savePreference() + return this.refreshTab(true, true) + } + + protected mountGoTo() { return this.executeMount((mount) => { - return this.api.mountGoTo(mount, this.bodyPosition.rightAscension, this.bodyPosition.declination, false) + return this.api.mountGoTo(mount, this.position.rightAscension, this.position.declination, false) }) } - mountSlew() { + protected mountSlew() { return this.executeMount((mount) => { - return this.api.mountSlew(mount, this.bodyPosition.rightAscension, this.bodyPosition.declination, false) + return this.api.mountSlew(mount, this.position.rightAscension, this.position.declination, false) }) } - mountSync() { + protected mountSync() { return this.executeMount((mount) => { - return this.api.mountSync(mount, this.bodyPosition.rightAscension, this.bodyPosition.declination, false) + return this.api.mountSync(mount, this.position.rightAscension, this.position.declination, false) }) } - frame() { - return this.browserWindow.openFraming({ - rightAscension: this.bodyPosition.rightAscensionJ2000, - declination: this.bodyPosition.declinationJ2000, + protected frame() { + return this.browserWindowService.openFraming({ + rightAscension: this.position.rightAscensionJ2000, + declination: this.position.declinationJ2000, }) } - async refreshTab(refreshTwilight: boolean = false, refreshChart: boolean = false) { - this.refreshingPosition = true - this.refreshTabCount++ + private async refreshTab(refreshTwilight: boolean = false, refreshChart: boolean = false) { + this.refresh.position = true + this.refresh.count++ - if (!this.useManualDateTime) { - this.dateTime = new Date() - this.dateTimeHour = this.dateTime.getHours() - this.dateTimeMinute = this.dateTime.getMinutes() - } else { - this.dateTime.setHours(this.dateTimeHour) - this.dateTime.setMinutes(this.dateTimeMinute) + if (!this.dateTimeAndLocation.manual) { + this.dateTimeAndLocation.dateTime = new Date() } - this.app.subTitle = `${this.location.name} · ${moment(this.dateTime).format('YYYY-MM-DD HH:mm')}` + const { dateTime, location } = this.dateTimeAndLocation + + this.app.subTitle = `${location.name} · ${extractDate(dateTime)} ${extractTime(dateTime, false)}` try { // Sun. - if (this.tab === SkyAtlasTab.SUN) { - this.name = 'Sun' - this.tags = [] - this.imageOfSun.nativeElement.src = `${this.api.baseUrl}/sky-atlas/sun/image` - const bodyPosition = await this.api.positionOfSun(this.dateTime) - Object.assign(this.bodyPosition, bodyPosition) + if (this.tab === BodyTabType.SUN) { + this.sun.image = `${this.api.baseUrl}/sky-atlas/sun/image` + const position = await this.api.positionOfSun(dateTime, location) + Object.assign(this.sun.position, position) } // Moon. - else if (this.tab === SkyAtlasTab.MOON) { - this.name = 'Moon' - this.tags = [] - const bodyPosition = await this.api.positionOfMoon(this.dateTime) - Object.assign(this.bodyPosition, bodyPosition) - this.moonIlluminated = this.bodyPosition.illuminated / 100.0 - this.moonWaning = this.bodyPosition.leading + else if (this.tab === BodyTabType.MOON) { + const position = await this.api.positionOfMoon(dateTime, location) + Object.assign(this.moon.position, position) } // Planet. - else if (this.tab === SkyAtlasTab.PLANET) { - this.tags = [] - - if (this.planet) { - this.name = this.planet.name - const bodyPosition = await this.api.positionOfPlanet(this.planet.code, this.dateTime) - Object.assign(this.bodyPosition, bodyPosition) - } else { - this.name = undefined - Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) + else if (this.tab === BodyTabType.PLANET) { + if (this.planet.selected) { + const position = await this.api.positionOfPlanet(this.planet.selected.code, dateTime, location) + Object.assign(this.planet.position, position) } } // Minor Planet. - else if (this.tab === SkyAtlasTab.MINOR_PLANET) { - this.tags = [] - - if (this.minorPlanet) { - this.name = this.minorPlanet.name - // if (this.minorPlanet.kind) this.tags.push({ title: this.minorPlanet.kind, severity: 'success' }) - if (this.minorPlanet.orbitType) this.tags.push({ title: this.minorPlanet.orbitType, severity: 'success' }) - 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(code, this.dateTime) - Object.assign(this.bodyPosition, bodyPosition) - } else { - this.name = undefined - Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) + else if (this.tab === BodyTabType.MINOR_PLANET) { + if (this.minorPlanet.search.result) { + const code = `DES=${this.minorPlanet.search.result.spkId};` + const position = await this.api.positionOfPlanet(code, dateTime, location) + Object.assign(this.minorPlanet.position, position) } } // Sky Object. - else if (this.tab === SkyAtlasTab.SKY_OBJECT) { - this.tags = [] + else if (this.tab === BodyTabType.SKY_OBJECT) { + const selected = this.skyObject.search.selected - if (this.skyObject) { - this.name = this.skyObjectPipe.transform(this.skyObject, 'name') - const bodyPosition = await this.api.positionOfSkyObject(this.skyObject, this.dateTime) - Object.assign(this.bodyPosition, bodyPosition) - } else { - this.name = undefined - Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) + if (selected) { + const position = await this.api.positionOfSkyObject(selected, dateTime, location) + Object.assign(this.skyObject.position, position) } } // Satellite. else { - this.tags = [] - - if (this.satellite) { - this.name = this.satellite.name - const bodyPosition = await this.api.positionOfSatellite(this.satellite, this.dateTime) - Object.assign(this.bodyPosition, bodyPosition) - } else { - this.name = undefined - Object.assign(this.bodyPosition, EMPTY_BODY_POSITION) + if (this.satellite.search.selected) { + const position = await this.api.positionOfSatellite(this.satellite.search.selected, dateTime, location) + Object.assign(this.satellite.position, position) } } - this.refreshingPosition = false + this.refresh.position = false - if (this.refreshTabCount === 1 || refreshTwilight) { - this.refreshingChart = true + if (this.refresh.count === 1 || refreshTwilight) { + this.refresh.chart = true - const twilight = await this.api.twilight(this.dateTime) + const twilight = await this.api.twilight(dateTime, location) this.altitudeData.datasets[0].data = [ [0.0, 90], [twilight.civilDusk[0], 90], @@ -791,101 +685,108 @@ export class AtlasComponent implements OnInit, AfterContentInit, AfterViewInit, [twilight.civilDawn[1], 90], [24.0, 90], ] + this.chart.refresh() } - if (this.refreshTabCount === 1 || refreshChart) { + if (this.refresh.count === 1 || refreshChart) { await this.refreshChart() } } finally { - this.refreshingPosition = false - this.refreshingChart = false + this.refresh.position = false + this.refresh.chart = false } } private async refreshChart() { - this.refreshingChart = true + this.refresh.chart = true + + const { dateTime, location } = this.dateTimeAndLocation try { // Sun. - if (this.tab === SkyAtlasTab.SUN) { - const points = await this.api.altitudePointsOfSun(this.dateTime) - AtlasComponent.belowZeroPoints(points) - this.altitudeData.datasets[9].data = points + if (this.tab === BodyTabType.SUN) { + const points = await this.api.altitudePointsOfSun(dateTime, location) + this.updateAltitudeDataPoints(points) } // Moon. - else if (this.tab === SkyAtlasTab.MOON) { - const points = await this.api.altitudePointsOfMoon(this.dateTime) - AtlasComponent.belowZeroPoints(points) - this.altitudeData.datasets[9].data = points + else if (this.tab === BodyTabType.MOON) { + const points = await this.api.altitudePointsOfMoon(dateTime, location) + this.updateAltitudeDataPoints(points) } // Planet. - else if (this.tab === SkyAtlasTab.PLANET && this.planet) { - const points = await this.api.altitudePointsOfPlanet(this.planet.code, this.dateTime) - AtlasComponent.belowZeroPoints(points) - this.altitudeData.datasets[9].data = points + else if (this.tab === BodyTabType.PLANET) { + if (this.planet.selected) { + const points = await this.api.altitudePointsOfPlanet(this.planet.selected.code, dateTime, location) + this.updateAltitudeDataPoints(points) + } else { + this.updateAltitudeDataPoints() + } } // Minor Planet. - else if (this.tab === SkyAtlasTab.MINOR_PLANET) { - if (this.minorPlanet) { - const code = `DES=${this.minorPlanet.spkId};` - const points = await this.api.altitudePointsOfPlanet(code, this.dateTime) - AtlasComponent.belowZeroPoints(points) - this.altitudeData.datasets[9].data = points + else if (this.tab === BodyTabType.MINOR_PLANET) { + if (this.minorPlanet.search.result) { + const code = `DES=${this.minorPlanet.search.result.spkId};` + const points = await this.api.altitudePointsOfPlanet(code, dateTime, location) + this.updateAltitudeDataPoints(points) } else { - this.altitudeData.datasets[9].data = [] + this.updateAltitudeDataPoints() } } // Sky Object. - else if (this.tab === SkyAtlasTab.SKY_OBJECT) { - if (this.skyObject) { - const points = await this.api.altitudePointsOfSkyObject(this.skyObject, this.dateTime) - AtlasComponent.belowZeroPoints(points) - this.altitudeData.datasets[9].data = points + else if (this.tab === BodyTabType.SKY_OBJECT) { + if (this.skyObject.search.selected) { + const points = await this.api.altitudePointsOfSkyObject(this.skyObject.search.selected, dateTime, location) + this.updateAltitudeDataPoints(points) } else { - this.altitudeData.datasets[9].data = [] + this.updateAltitudeDataPoints() } } // Satellite. - else if (this.tab === SkyAtlasTab.SATELLITE) { - if (this.satellite) { - const points = await this.api.altitudePointsOfSatellite(this.satellite, this.dateTime) - AtlasComponent.belowZeroPoints(points) - this.altitudeData.datasets[9].data = points + else { + if (this.satellite.search.selected) { + const points = await this.api.altitudePointsOfSatellite(this.satellite.search.selected, dateTime, location) + this.updateAltitudeDataPoints(points) } else { - this.altitudeData.datasets[9].data = [] + this.updateAltitudeDataPoints() } - } else { - return } this.chart.refresh() } finally { - this.refreshingChart = false + this.refresh.chart = false } } - private loadPreference() { - const preference = this.preference.skyAtlasPreference.get() - - 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) + private updateAltitudeDataPoints(points?: AltitudeDataPoint[]) { + if (points?.length) { + AtlasComponent.removePointsBelowZero(points) + this.altitudeData.datasets[9].data = points + } else { + this.altitudeData.datasets[9].data = [] } } - savePreference() { - const preference = this.preference.skyAtlasPreference.get() + private loadLocations() { + const settings = this.preferenceService.settings.get() + this.locations = settings.locations + this.dateTimeAndLocation.location = this.locations.find((e) => e.id === this.dateTimeAndLocation.location.id) ?? this.locations.find((e) => e.id === settings.location.id) ?? this.locations[0] + } - preference.satellites = SATELLITE_GROUPS.map((group) => { - return { group, enabled: this.satelliteSearchGroup.get(group) ?? false } - }) + private loadPreference() { + Object.assign(this.preference, this.preferenceService.skyAtlasPreference.get()) + this.satellite.search.filter.groups = this.preference.satellites + this.dateTimeAndLocation.location = this.preference.location + + this.loadLocations() + } - this.preference.skyAtlasPreference.set(preference) + protected savePreference() { + this.preference.location = this.dateTimeAndLocation.location + this.preferenceService.skyAtlasPreference.set(this.preference) } - private static belowZeroPoints(points: [number, number][]) { + private static removePointsBelowZero(points: AltitudeDataPoint[]) { for (const point of points) { if (point[1] < 0) { point[1] = NaN @@ -894,7 +795,7 @@ export class AtlasComponent implements OnInit, AfterContentInit, AfterViewInit, } private async executeMount(action: (mount: Mount) => void | Promise) { - if (await this.prime.confirm('Are you sure that you want to proceed?')) { + if (await this.angularService.confirm('Are you sure that you want to proceed?')) { return false } diff --git a/desktop/src/app/autofocus/autofocus.component.html b/desktop/src/app/autofocus/autofocus.component.html index 039328e12..d2ec84a7f 100644 --- a/desktop/src/app/autofocus/autofocus.component.html +++ b/desktop/src/app/autofocus/autofocus.component.html @@ -7,7 +7,7 @@ [(device)]="camera" (deviceChange)="cameraChanged()" /> @@ -37,7 +37,7 @@ pInputText readonly class="p-inputtext-sm border-0 max-w-full" - [value]="focuser.position" /> + [value]="focuser?.position ?? 0" />
@@ -71,28 +71,28 @@
+ spinnableNumber />
+ spinnableNumber />
@@ -105,7 +105,7 @@ [min]="1" [max]="10" (ngModelChange)="savePreference()" - scrollableNumber /> + spinnableNumber />
@@ -146,7 +146,7 @@ [step]="0.1" (ngModelChange)="savePreference()" locale="en" - scrollableNumber /> + spinnableNumber />
@@ -175,7 +175,7 @@ [min]="1" [max]="1000" (ngModelChange)="savePreference()" - scrollableNumber /> + spinnableNumber />
@@ -189,7 +189,7 @@ [min]="1" [max]="1000" (ngModelChange)="savePreference()" - scrollableNumber /> + spinnableNumber />
@@ -214,7 +214,7 @@
{ - if (event.device.id === this.camera.id) { + electronService.on('CAMERA.UPDATED', (event) => { + if (event.device.id === this.camera?.id) { ngZone.run(() => { - Object.assign(this.camera, event.device) + if (this.camera) { + Object.assign(this.camera, event.device) + } }) } }) - electron.on('CAMERA.ATTACHED', (event) => { + electronService.on('CAMERA.ATTACHED', (event) => { ngZone.run(() => { this.cameras.push(event.device) this.cameras.sort(deviceComparator) }) }) - electron.on('CAMERA.DETACHED', (event) => { + electronService.on('CAMERA.DETACHED', (event) => { ngZone.run(() => { const index = this.cameras.findIndex((e) => e.id === event.device.id) if (index >= 0) { if (this.cameras[index] === this.camera) { - Object.assign(this.camera, this.cameras[0] ?? EMPTY_CAMERA) + Object.assign(this.camera, this.cameras[0] ?? DEFAULT_CAMERA) } this.cameras.splice(index, 1) @@ -273,28 +272,30 @@ export class AutoFocusComponent implements AfterViewInit, OnDestroy, Pingable { }) }) - electron.on('FOCUSER.UPDATED', (event) => { - if (event.device.id === this.focuser.id) { + electronService.on('FOCUSER.UPDATED', (event) => { + if (event.device.id === this.focuser?.id) { ngZone.run(() => { - Object.assign(this.focuser, event.device) + if (this.focuser) { + Object.assign(this.focuser, event.device) + } }) } }) - electron.on('FOCUSER.ATTACHED', (event) => { + electronService.on('FOCUSER.ATTACHED', (event) => { ngZone.run(() => { this.focusers.push(event.device) this.focusers.sort(deviceComparator) }) }) - electron.on('FOCUSER.DETACHED', (event) => { + electronService.on('FOCUSER.DETACHED', (event) => { ngZone.run(() => { const index = this.focusers.findIndex((e) => e.id === event.device.id) if (index >= 0) { if (this.focusers[index] === this.focuser) { - Object.assign(this.focuser, this.focusers[0] ?? EMPTY_FOCUSER) + Object.assign(this.focuser, this.focusers[0] ?? DEFAULT_FOCUSER) } this.focusers.splice(index, 1) @@ -302,7 +303,7 @@ export class AutoFocusComponent implements AfterViewInit, OnDestroy, Pingable { }) }) - electron.on('AUTO_FOCUS.ELAPSED', (event) => { + electronService.on('AUTO_FOCUS.ELAPSED', (event) => { ngZone.run(() => { this.status = event.state this.running = event.state !== 'FAILED' && event.state !== 'FINISHED' @@ -328,7 +329,7 @@ export class AutoFocusComponent implements AfterViewInit, OnDestroy, Pingable { } async ngAfterViewInit() { - this.pinger.register(this, 30000) + this.ticker.register(this, 30000) this.cameras = (await this.api.cameras()).sort(deviceComparator) this.focusers = (await this.api.focusers()).sort(deviceComparator) @@ -336,18 +337,18 @@ export class AutoFocusComponent implements AfterViewInit, OnDestroy, Pingable { @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) + this.ticker.unregister(this) void this.stop() } - async ping() { - if (this.camera.id) await this.api.cameraListen(this.camera) - if (this.focuser.id) await this.api.focuserListen(this.focuser) + async tick() { + if (this.camera?.id) await this.api.cameraListen(this.camera) + if (this.focuser?.id) await this.api.focuserListen(this.focuser) } - async cameraChanged() { - if (this.camera.id) { - await this.ping() + protected async cameraChanged() { + if (this.camera?.id) { + await this.tick() const camera = await this.api.camera(this.camera.id) Object.assign(this.camera, camera) @@ -355,39 +356,45 @@ export class AutoFocusComponent implements AfterViewInit, OnDestroy, Pingable { } } - async focuserChanged() { - if (this.focuser.id) { - await this.ping() + protected async focuserChanged() { + if (this.focuser?.id) { + await this.tick() const focuser = await this.api.focuser(this.focuser.id) Object.assign(this.focuser, focuser) } } - async showCameraDialog() { - if (this.camera.id) { - if (await CameraComponent.showAsDialog(this.browserWindow, 'AUTO_FOCUS', this.camera, this.request.capture)) { + protected async showCameraDialog() { + if (this.camera?.id) { + if (await CameraComponent.showAsDialog(this.browserWindowService, 'AUTO_FOCUS', this.camera, this.request.capture)) { this.savePreference() } } } - async start() { - await this.openCameraImage() + protected async start() { + if (this.camera?.id && this.focuser?.id) { + await this.openCameraImage() - this.clearChart() - this.stepSizeForScale = this.request.stepSize + this.clearChart() + this.stepSize = this.request.stepSize + Object.assign(this.request.starDetector, this.preferenceService.settings.get().starDetector[this.request.starDetector.type]) - this.request.starDetector = this.preference.starDetectionRequest('ASTAP').get() - return this.api.autoFocusStart(this.camera, this.focuser, this.request) + await this.api.autoFocusStart(this.camera, this.focuser, this.request) + } } - stop() { - return this.api.autoFocusStop(this.camera) + protected async stop() { + if (this.camera?.id) { + await this.api.autoFocusStop(this.camera) + } } - openCameraImage() { - return this.browserWindow.openCameraImage(this.camera, 'ALIGNMENT') + protected async openCameraImage() { + if (this.camera?.id) { + await this.browserWindowService.openCameraImage(this.camera, 'ALIGNMENT') + } } private updateChart(data: AutoFocusChart) { @@ -420,8 +427,8 @@ export class AutoFocusComponent implements AfterViewInit, OnDestroy, Pingable { } const scales = this.chartOptions.scales! - scales['x']!.min = Math.max(0, data.minX - this.stepSizeForScale) - scales['x']!.max = data.maxX + this.stepSizeForScale + scales['x']!.min = Math.max(0, data.minX - this.stepSize) + scales['x']!.max = data.maxX + this.stepSize scales['y']!.max = (data.maxY || 19) + 1 const zoom = this.chartOptions.plugins!.zoom! @@ -433,7 +440,7 @@ export class AutoFocusComponent implements AfterViewInit, OnDestroy, Pingable { } private clearChart() { - this.focusPoints = [] + this.focusPoints.length = 0 for (const dataset of this.chartData.datasets) { dataset.data = [] @@ -443,20 +450,9 @@ export class AutoFocusComponent implements AfterViewInit, OnDestroy, Pingable { } private loadPreference() { - const preference: Partial = this.preference.autoFocusPreference.get() - - this.request.fittingMode = preference.fittingMode ?? 'HYPERBOLIC' - this.request.initialOffsetSteps = preference.initialOffsetSteps ?? 4 - this.request.rSquaredThreshold = preference.rSquaredThreshold ?? 0.5 - this.request.stepSize = preference.stepSize ?? 100 - this.request.totalNumberOfAttempts = preference.totalNumberOfAttempts ?? 1 - this.request.backlashCompensation.mode = preference.backlashCompensation?.mode ?? 'NONE' - this.request.backlashCompensation.backlashIn = preference.backlashCompensation?.backlashIn ?? 0 - this.request.backlashCompensation.backlashOut = preference.backlashCompensation?.backlashOut ?? 0 - - if (this.camera.id) { - const cameraPreference = this.preference.cameraPreference(this.camera).get() - Object.assign(this.request.capture, this.preference.cameraStartCaptureForAutoFocus(this.camera).get(cameraPreference)) + if (this.camera?.id) { + Object.assign(this.preference, this.preferenceService.autoFocus(this.camera).get()) + this.request = this.preference.request if (this.camera.connected) { updateCameraStartCaptureFromCamera(this.request.capture, this.camera) @@ -464,13 +460,9 @@ export class AutoFocusComponent implements AfterViewInit, OnDestroy, Pingable { } } - savePreference() { - this.preference.cameraStartCaptureForAutoFocus(this.camera).set(this.request.capture) - - const preference: AutoFocusPreference = { - ...this.request, + protected savePreference() { + if (this.camera?.id) { + this.preferenceService.autoFocus(this.camera).set(this.preference) } - - this.preference.autoFocusPreference.set(preference) } } diff --git a/desktop/src/app/calculator/calculator.component.html b/desktop/src/app/calculator/calculator.component.html index 12e22ca7e..40d22736c 100644 --- a/desktop/src/app/calculator/calculator.component.html +++ b/desktop/src/app/calculator/calculator.component.html @@ -5,7 +5,6 @@ [(ngModel)]="formula" styleClass="border-0 p-inputtext-sm" [autoDisplayFirst]="false" - (ngModelChange)="formulaChanged()" [panelStyle]="{ maxWidth: '100px' }"> ; formula: CalculatorFormula }[] = [ + protected readonly formulae: { component: Type; formula: CalculatorFormula }[] = [ { component: FormulaComponent, formula: { @@ -212,11 +211,9 @@ export class CalculatorComponent { }, ] - formula = this.formulae[0] + protected formula = this.formulae[0] constructor(app: AppComponent) { app.title = 'Calculator' } - - formulaChanged() {} } diff --git a/desktop/src/app/calculator/formula/formula.component.html b/desktop/src/app/calculator/formula/formula.component.html index 5199b89c5..a41c6d5df 100644 --- a/desktop/src/app/calculator/formula/formula.component.html +++ b/desktop/src/app/calculator/formula/formula.component.html @@ -21,7 +21,7 @@ [showButtons]="true" styleClass="border-0 p-inputtext-sm" locale="en" - scrollableNumber /> + spinnableNumber />
diff --git a/desktop/src/app/calculator/formula/formula.component.ts b/desktop/src/app/calculator/formula/formula.component.ts index e7b870b9e..2bbb69408 100644 --- a/desktop/src/app/calculator/formula/formula.component.ts +++ b/desktop/src/app/calculator/formula/formula.component.ts @@ -1,16 +1,14 @@ -import { AfterViewInit, Component, Input } from '@angular/core' +import { Component, Input } from '@angular/core' import { CalculatorFormula } from '../../../shared/types/calculator.types' @Component({ - selector: 'app-formula', + selector: 'neb-formula', templateUrl: './formula.component.html', styleUrls: ['./formula.component.scss'], }) -export class FormulaComponent implements AfterViewInit { +export class FormulaComponent { @Input({ required: true }) - readonly formula!: CalculatorFormula - - ngAfterViewInit() {} + protected readonly formula!: CalculatorFormula calculateFormula() { const result = this.formula.calculate(...this.formula.operands.map((e) => e.value)) diff --git a/desktop/src/app/calibration/calibration.component.html b/desktop/src/app/calibration/calibration.component.html index bee121072..53a446168 100644 --- a/desktop/src/app/calibration/calibration.component.html +++ b/desktop/src/app/calibration/calibration.component.html @@ -1,120 +1,124 @@ -
-
-
- -
-
- - -
- @if (node.data.type === 'NAME') { - {{ node.label }} - } @else if (node.data.type === 'GROUP') { -
- - - - - - - -
- } @else if (node.data.type === 'FRAME') { -
- - - {{ node.data.data.path }} - -
- } -
- @if (node.data.type === 'NAME') { +
+
+ + @for (key of groups; track $index) { + @let value = frames.get(key) ?? []; + +
+
+
+ (onClick)="openFilesToUpload(key)" /> + (onClick)="openDirectoryToUpload(key)" /> +
+ {{ this.frames.get(key)?.length ?? 0 }} frames +
+ - } - + size="small" + (onClick)="groupMenu.toggle($event)" /> +
+
+
+ + +
+
+
+
+ +
+ {{ item.type }} +
+ {{ item.exposureTime | exposureTime }} + {{ item.width }}x{{ item.height }} + {{ item.binX }}x{{ item.binY }} + GAIN: {{ item.gain }} + + {{ item.temperature }}°C + +
+ {{ item.path }} +
+
+
+ + + +
+
+
+
+
+
- - -
+ + } +
@@ -122,18 +126,18 @@ + [(ngModel)]="groupDialog.group" />
+ (onClick)="groupDialog.save?.()" />
diff --git a/desktop/src/app/calibration/calibration.component.scss b/desktop/src/app/calibration/calibration.component.scss deleted file mode 100644 index 3f694e53b..000000000 --- a/desktop/src/app/calibration/calibration.component.scss +++ /dev/null @@ -1,33 +0,0 @@ -:host { - ::ng-deep { - .p-treenode-label { - width: 100%; - } - - .p-tree-wrapper { - max-height: 288px; - } - - .p-tree { - .p-tree-container { - padding-right: 4px; - - .p-treenode { - padding: 0; - - .p-treenode-content { - padding: 0 0.5rem; - } - } - } - } - - .p-treenode-leaf > .p-treenode-content .p-tree-toggler { - display: none; - } - - .p-tree-empty-message { - padding: 1rem 0.5rem; - } - } -} diff --git a/desktop/src/app/calibration/calibration.component.ts b/desktop/src/app/calibration/calibration.component.ts index 89a8eae79..3a9ffa19c 100644 --- a/desktop/src/app/calibration/calibration.component.ts +++ b/desktop/src/app/calibration/calibration.component.ts @@ -1,263 +1,410 @@ -import { AfterViewInit, Component } from '@angular/core' +import { AfterViewInit, Component, HostListener, OnDestroy, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core' import { dirname } from 'path' -import { TreeDragDropService, TreeNode } from 'primeng/api' -import { TreeNodeDropEvent } from 'primeng/tree' +import { Listbox } from 'primeng/listbox' +import { MenuItem } from '../../shared/components/menu-item/menu-item.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 { CalibrationFrame, CalibrationFrameGroup } from '../../shared/types/calibration.types' +import { CalibrationFrame, DEFAULT_CALIBRATION_GROUP_DIALOG, DEFAULT_CALIBRATION_PREFERENCE } from '../../shared/types/calibration.types' +import { textComparator } from '../../shared/utils/comparators' import { AppComponent } from '../app.component' -export interface CalibrationNode extends TreeNode { - key: string - label: string - data: TreeNodeData - children: CalibrationNode[] - parent?: CalibrationNode -} - -export type TreeNodeData = { type: 'NAME'; data: string } | { type: 'GROUP'; data: CalibrationFrameGroup } | { type: 'FRAME'; data: CalibrationFrame } - @Component({ - selector: 'app-calibration', + selector: 'neb-calibration', templateUrl: './calibration.component.html', - styleUrls: ['./calibration.component.scss'], - providers: [TreeDragDropService], + encapsulation: ViewEncapsulation.None, }) -export class CalibrationComponent implements AfterViewInit { - readonly frames: CalibrationNode[] = [] +export class CalibrationComponent implements AfterViewInit, OnDestroy { + protected readonly frames = new Map() + protected readonly preference = structuredClone(DEFAULT_CALIBRATION_PREFERENCE) + protected readonly groupDialog = structuredClone(DEFAULT_CALIBRATION_GROUP_DIALOG) + protected selectedFrames: CalibrationFrame[] = [] + + protected tab = 0 + private frameId = '' + + private readonly renameSelectedFramesMenuItem: MenuItem = { + icon: 'mdi mdi-pencil', + label: 'Rename Group', + badge: '0', + visible: false, + command: () => { + this.showEditGroupDialogForSelectedFrames() + }, + } + + private readonly deleteSelectedFramesMenuItem: MenuItem = { + icon: 'mdi mdi-delete', + severity: 'danger', + label: 'Delete', + badge: '0', + visible: false, + command: () => this.deleteSelectedFrames(), + } + + protected activeGroup = '' + protected readonly groupModel: MenuItem[] = [ + { + icon: 'mdi mdi-checkbox-marked', + label: 'Select All', + command: () => { + const frames = this.activeFrames + + if (frames.length) { + const selectedFrames = new Set(this.selectedFrames) + + for (const frame of frames) { + selectedFrames.add(frame) + } + + this.selectedFrames = Array.from(selectedFrames) + this.frameSelected() + } + }, + }, + { + icon: 'mdi mdi-checkbox-blank-outline', + label: 'Unselect All', + command: () => { + const frames = this.activeFrames + + if (frames.length) { + const selectedFrames = new Set(this.selectedFrames) + + for (const frame of frames) { + selectedFrames.delete(frame) + } + + this.selectedFrames = Array.from(selectedFrames) + this.frameSelected() + } + }, + }, + SEPARATOR_MENU_ITEM, + { + icon: 'mdi mdi-checkbox-marked', + label: 'Enable All', + command: async () => { + const frames = this.activeFrames + + for (const frame of frames) { + if (!frame.enabled) { + await this.toggleFrame(frame, true) + } + } + }, + }, + { + icon: 'mdi mdi-checkbox-blank-outline', + label: 'Disable All', + command: async () => { + const frames = this.activeFrames + + for (const frame of frames) { + if (frame.enabled) { + await this.toggleFrame(frame, false) + } + } + }, + }, + SEPARATOR_MENU_ITEM, + { + icon: 'mdi mdi-pencil', + label: 'Rename Group', + command: () => { + if (this.activeGroup && this.activeFrames.length) { + this.showEditGroupDialog(this.activeGroup) + } + }, + }, + { + icon: 'mdi mdi-delete', + label: 'Delete All', + iconClass: 'text-danger', + command: async () => { + if (this.activeGroup && this.activeFrames.length) { + await this.deleteFrameGroup(this.activeGroup) + } + }, + }, + ] - showNewGroupDialog = false - newGroupName = '' - newGroupDialogSave: () => void = () => {} + @ViewChildren('frameListBox') + private readonly frameListBoxes!: QueryList + + get groups() { + return Array.from(this.frames.keys()).sort(textComparator) + } + + get activeFrames() { + return this.frames.get(this.activeGroup) ?? [] + } constructor( app: AppComponent, private readonly api: ApiService, - private readonly electron: ElectronService, - private readonly browserWindow: BrowserWindowService, - private readonly preference: PreferenceService, + private readonly electronService: ElectronService, + private readonly browserWindowService: BrowserWindowService, + private readonly preferenceService: PreferenceService, ) { app.title = 'Calibration' + + app.topMenu.push({ + icon: 'mdi mdi-plus', + label: 'New Group', + command: () => { + this.showNewGroupDialog() + }, + }) + + app.topMenu.push(this.renameSelectedFramesMenuItem) + app.topMenu.push(this.deleteSelectedFramesMenuItem) } - async ngAfterViewInit() { - await this.load() + ngAfterViewInit() { + this.loadPreference() + + return this.load() } - private makeTreeNode(key: string, label: string, data: TreeNodeData, parent?: CalibrationNode): CalibrationNode { - const draggable = data.type === 'FRAME' - const droppable = data.type === 'NAME' - return { key, label, data, children: [], parent, draggable, droppable } + @HostListener('window:unload') + ngOnDestroy() { + void this.closeFrameWindow() } - addGroup(name: string) { - const node = this.frames.find((e) => e.label === name) ?? this.makeTreeNode(`group-${name}`, name, { type: 'NAME', data: name }) + protected frameSelected() { + const count = this.selectedFrames.length + const visible = count > 0 - if (!this.frames.includes(node)) { - this.frames.push(node) - } + this.renameSelectedFramesMenuItem.visible = visible + this.deleteSelectedFramesMenuItem.visible = visible - return node + if (visible) { + this.renameSelectedFramesMenuItem.badge = `${count}` + this.deleteSelectedFramesMenuItem.badge = `${count}` + } } - addFrameGroup(name: string | CalibrationNode, group: CalibrationFrameGroup) { - const parent = typeof name === 'string' ? this.frames.find((e) => e.label === name) : name + protected async openFilesToUpload(group: string) { + const paths = await this.electronService.openImages({ defaultPath: this.preference.filePath }) - if (parent) { - const node = this.makeTreeNode(`frame-group-${group.id}`, `Frame`, { type: 'GROUP', data: group }, parent) - parent.children.push(node) - return node - } + if (paths && paths.length) { + this.preference.filePath = dirname(paths[0]) + this.savePreference() - return undefined + for (const path of paths) { + await this.upload(group, path) + } + } } - addFrame(group: string | CalibrationNode, frame: CalibrationFrame) { - const parent = typeof group === 'string' ? this.frames.find((e) => e.label === group) : group + protected async openDirectoryToUpload(group: string) { + const path = await this.electronService.openDirectory({ defaultPath: this.preference.directoryPath }) - if (parent) { - const node = this.makeTreeNode(`frame-${frame.id}`, `Frame`, { type: 'FRAME', data: frame }, parent) - parent.children.push(node) - return node + if (path) { + this.preference.directoryPath = path + this.savePreference() + await this.upload(group, path) } - - return undefined } - async openFileToUpload(node: CalibrationNode) { - if (node.data.type === 'NAME') { - const preference = this.preference.calibrationPreference.get() - const path = await this.electron.openImage({ defaultPath: preference.openPath }) + private async upload(group: string, path: string) { + const frames = await this.api.uploadCalibrationFrame(group, path) - if (path) { - preference.openPath = dirname(path) - this.preference.calibrationPreference.set(preference) - await this.upload(node, path) - } + if (frames.length > 0) { + await this.electronService.calibrationChanged() + await this.loadGroup(group) } } - async openDirectoryToUpload(node: CalibrationNode) { - if (node.data.type === 'NAME') { - const preference = this.preference.calibrationPreference.get() - const path = await this.electron.openDirectory({ defaultPath: preference.openPath }) + private async loadGroup(group: string) { + const frames = await this.api.calibrationFrames(group) - if (path) { - preference.openPath = path - this.preference.calibrationPreference.set(preference) - await this.upload(node, path) + for (let i = 0; i < this.selectedFrames.length; i++) { + for (const frame of frames) { + if (frame.id === this.selectedFrames[i].id) { + this.selectedFrames[i] = frame + } } } - } - private async upload(node: CalibrationNode, path: string) { - if (node.data.type === 'NAME') { - const frames = await this.api.uploadCalibrationFrame(node.data.data, path) + this.frames.set(group, frames) + } - if (frames.length > 0) { - await this.electron.calibrationChanged() - await this.load() - } + private loadDefaultGroupIfEmpty() { + if (!this.frames.size) { + this.frames.set('Group 1', []) } } private async load() { - this.frames.length = 0 - - const names = await this.api.calibrationGroups() + this.frames.clear() - for (const name of names) { - const nameNode = this.addGroup(name) + const groups = await this.api.calibrationGroups() - const groups = await this.api.calibrationFrames(name) + for (const group of groups) { + await this.loadGroup(group) + } - for (const group of groups) { - const frameGroupNode = this.addFrameGroup(nameNode, group) + this.loadDefaultGroupIfEmpty() + } - if (frameGroupNode) { - for (const frame of group.frames) { - this.addFrame(frameGroupNode, frame) - } - } - } - } + protected openImage(frame: CalibrationFrame) { + return this.browserWindowService.openImage({ path: frame.path, source: 'PATH' }) } - openImage(frame: CalibrationFrame) { - return this.browserWindow.openImage({ path: frame.path, source: 'PATH' }) + protected toggleFrame(frame: CalibrationFrame, enabled: boolean) { + frame.enabled = enabled + return this.api.updateCalibrationFrame(frame) } - async toggleCalibrationFrame(node: CalibrationNode, enabled: boolean) { - if (node.data.type === 'FRAME') { - await this.api.editCalibrationFrame(node.data.data) - } + protected async openFrame(frame: CalibrationFrame) { + this.frameId = await this.browserWindowService.openImage({ path: frame.path, id: 'calibration', source: 'PATH' }) } - async deleteFrame(node: CalibrationNode) { - const deleteFromParent = async () => { - if (node.parent) { - const idx = node.parent.children.indexOf(node) + protected async deleteFrame(frame: CalibrationFrame, box?: Listbox) { + await this.api.deleteCalibrationFrame(frame) - if (idx >= 0) { - node.parent.children.splice(idx, 1) - console.info('frame deleted', node) - } + let index = this.selectedFrames.indexOf(frame) - if (!node.parent.children.length) { - await this.deleteFrame(node.parent) - } - } else { - const idx = this.frames.indexOf(node) + if (index >= 0) { + this.selectedFrames.splice(index, 1) + this.frameSelected() + } - if (idx >= 0) { - this.frames.splice(idx, 1) - console.info('frame deleted', node) - await this.electron.calibrationChanged() - } + const frames = this.frames.get(frame.group) + + if (frames?.length) { + index = frames.indexOf(frame) + + if (index >= 0) { + frames.splice(index, 1) + box?.cd.markForCheck() } } + } - if (node.data.type === 'FRAME') { - await this.api.deleteCalibrationFrame(node.data.data) - await deleteFromParent() - } else { - for (const frame of Array.from(node.children)) { - await this.deleteFrame(frame) - } + private async deleteSelectedFrames() { + const groups = new Set() + const frames = Array.from(this.selectedFrames) - if (!node.children.length) { - await deleteFromParent() - } + for (const frame of frames) { + groups.add(frame.group) + await this.deleteFrame(frame) } + + this.markFrameListBoxesForCheck() } - private calibrationFrameFromNode(node: CalibrationNode) { - const frames: CalibrationFrame[] = [] + private async deleteFrameGroup(group: string) { + const frames = Array.from(this.frames.get(group) ?? []) - function recursive(node: TreeNode) { - if (node.data) { - if (node.data.type === 'NAME' || node.data.type === 'GROUP') { - if (node.children) { - for (const child of node.children) { - recursive(child) - } - } - } else { - frames.push(node.data.data) + for (const frame of frames) { + await this.deleteFrame(frame) + } + + this.markFrameListBoxesForCheck() + } + + private showEditGroupDialogForSelectedFrames() { + this.groupDialog.save = async () => { + const groups = new Set() + + groups.add(this.groupDialog.group) + + for (const frame of this.selectedFrames) { + if (this.groupDialog.group !== frame.group) { + groups.add(frame.group) + frame.group = this.groupDialog.group + await this.api.updateCalibrationFrame(frame) } } + + this.groupDialog.showDialog = false + + for (const group of groups) { + await this.loadGroup(group) + } + + await this.electronService.calibrationChanged() } - recursive(node) + this.groupDialog.group = '' + this.groupDialog.showDialog = true + } - return frames + private async closeFrameWindow() { + if (this.frameId) { + await this.electronService.closeWindow(undefined, this.frameId) + } } - showNewGroupDialogForAdd() { - this.newGroupDialogSave = () => { - this.addGroup(this.newGroupName) - this.showNewGroupDialog = false + private showNewGroupDialog() { + this.groupDialog.save = () => { + if (this.groupDialog.group) { + this.frames.set(this.groupDialog.group, []) + this.groupDialog.showDialog = false + } } - this.newGroupName = '' - this.showNewGroupDialog = true + this.groupDialog.group = '' + this.groupDialog.showDialog = true } - showNewGroupDialogForEdit(node: CalibrationNode) { - if (node.data.type === 'NAME') { - this.newGroupDialogSave = async () => { - const frames = this.calibrationFrameFromNode(node) + protected showEditGroupDialog(value: CalibrationFrame | string) { + if (typeof value === 'string') { + this.groupDialog.save = async () => { + const frames = this.frames.get(value) - for (const frame of frames) { - frame.name = this.newGroupName - await this.api.editCalibrationFrame(frame) - await this.electron.calibrationChanged() + if (frames?.length && this.groupDialog.group) { + for (const frame of frames) { + frame.group = this.groupDialog.group + await this.api.updateCalibrationFrame(frame) + } + + await this.loadGroup(value) + await this.loadGroup(this.groupDialog.group) + await this.electronService.calibrationChanged() } - this.showNewGroupDialog = false - await this.load() + this.groupDialog.showDialog = false } - this.newGroupName = node.data.data - this.showNewGroupDialog = true + this.groupDialog.group = value + } else { + this.groupDialog.save = async () => { + const prevGroup = value.group + + if (this.groupDialog.group !== prevGroup) { + value.group = this.groupDialog.group + await this.api.updateCalibrationFrame(value) + await this.loadGroup(prevGroup) + await this.loadGroup(value.group) + await this.electronService.calibrationChanged() + } + + this.groupDialog.showDialog = false + } + + this.groupDialog.group = value.group } + + this.groupDialog.showDialog = true } - editGroupName() { - this.showNewGroupDialog = false + private loadPreference() { + Object.assign(this.preference, this.preferenceService.calibrationPreference.get()) } - async frameDropped(event: TreeNodeDropEvent) { - const dragNode = event.dragNode as CalibrationNode - const dropNode = event.dropNode as CalibrationNode + protected savePreference() { + this.preferenceService.calibrationPreference.set(this.preference) + } - if (dragNode.data.type === 'FRAME' && dropNode.data.type === 'NAME' && dragNode.data.data.name !== dropNode.data.data) { - dragNode.data.data.name = dropNode.data.data - await this.api.editCalibrationFrame(dragNode.data.data) - await this.electron.calibrationChanged() - await this.load() + private markFrameListBoxesForCheck() { + for (const box of this.frameListBoxes) { + box.cd.markForCheck() } } } diff --git a/desktop/src/app/camera/camera.component.html b/desktop/src/app/camera/camera.component.html index 8f218de17..f14507963 100644 --- a/desktop/src/app/camera/camera.component.html +++ b/desktop/src/app/camera/camera.component.html @@ -1,4 +1,4 @@ -
+
@@ -36,17 +36,18 @@ [text]="true" [rounded]="true" icon="mdi mdi-image-multiple" - (click)="openCameraImage()" + (onClick)="openCameraImage()" pTooltip="View image" tooltipPosition="bottom" size="small" />
- {{ savePath || capturesPath }} + {{ preference.request.savePath || camera.capturesPath }}
+ spinnableNumber />
-
- - - - - - -
+
@@ -226,7 +210,7 @@ + spinnableNumber />
+ spinnableNumber />
@@ -273,7 +257,7 @@
+ spinnableNumber />
+ spinnableNumber />
+ spinnableNumber />
+ spinnableNumber />
@@ -348,7 +332,7 @@ Subframe
@@ -375,7 +359,7 @@ styleClass="p-inputtext-sm border-0" [allowEmpty]="false" (ngModelChange)="savePreference()" - scrollableNumber /> + spinnableNumber />
@@ -392,7 +376,7 @@ styleClass="p-inputtext-sm border-0" [allowEmpty]="false" (ngModelChange)="savePreference()" - scrollableNumber /> + spinnableNumber />
@@ -423,7 +407,7 @@ styleClass="p-inputtext-sm border-0" [allowEmpty]="false" (ngModelChange)="savePreference()" - scrollableNumber /> + spinnableNumber />
@@ -440,12 +424,63 @@ styleClass="p-inputtext-sm border-0" [allowEmpty]="false" (ngModelChange)="savePreference()" - scrollableNumber /> + spinnableNumber />
+
+ + + + + + + +
@if (pausingOrPaused) { } -
-
- Enabled - + [style]="{ width: '80vw', maxWidth: '320px' }"> + +
+ Dither +
+ Enabled + +
+
+
RA only
-
+
+ spinnableNumber />
-
+
+ spinnableNumber />
@@ -569,16 +604,16 @@ + [style]="{ width: '80vw', maxWidth: '320px' }">
Live Stacking
Enabled
@@ -587,9 +622,9 @@
32-bit (slower)
+ + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+
diff --git a/desktop/src/app/camera/camera.component.scss b/desktop/src/app/camera/camera.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/app/camera/camera.component.ts b/desktop/src/app/camera/camera.component.ts index 917255f8e..2362f69bb 100644 --- a/desktop/src/app/camera/camera.component.ts +++ b/desktop/src/app/camera/camera.component.ts @@ -1,89 +1,49 @@ import { AfterContentInit, Component, HostListener, NgZone, OnDestroy, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { CameraExposureComponent } from '../../shared/components/camera-exposure/camera-exposure.component' -import { MenuItem, MenuItemCommandEvent, SlideMenuItem } from '../../shared/components/menu-item/menu-item.component' +import { MenuItemCommandEvent, SlideMenuItem } from '../../shared/components/menu-item/menu-item.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 { Pingable, Pinger } from '../../shared/services/pinger.service' import { PreferenceService } from '../../shared/services/preference.service' -import { Camera, CameraDialogInput, CameraDialogMode, CameraPreference, CameraStartCapture, EMPTY_CAMERA, EMPTY_CAMERA_START_CAPTURE, ExposureMode, ExposureTimeUnit, updateCameraStartCaptureFromCamera } from '../../shared/types/camera.types' -import { Device } from '../../shared/types/device.types' +import { Tickable, Ticker } from '../../shared/services/ticker.service' +import { + Camera, + cameraCaptureNamingFormatWithDefault, + CameraDialogInput, + CameraDitherDialog, + CameraLiveStackingDialog, + CameraMode, + CameraNamingFormatDialog, + CameraStartCapture, + DEFAULT_CAMERA, + DEFAULT_CAMERA_PREFERENCE, + FrameType, + updateCameraStartCaptureFromCamera, +} from '../../shared/types/camera.types' +import { Device, DeviceType } from '../../shared/types/device.types' import { Focuser } from '../../shared/types/focuser.types' -import { Equipment } from '../../shared/types/home.types' import { Mount } from '../../shared/types/mount.types' import { Rotator } from '../../shared/types/rotator.types' -import { FilterWheel } from '../../shared/types/wheel.types' -import { Undefinable } from '../../shared/utils/types' +import { resetCameraCaptureNamingFormat } from '../../shared/types/settings.types' +import { Wheel } from '../../shared/types/wheel.types' import { AppComponent } from '../app.component' @Component({ - selector: 'app-camera', + selector: 'neb-camera', templateUrl: './camera.component.html', - styleUrls: ['./camera.component.scss'], }) -export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { - readonly camera = structuredClone(EMPTY_CAMERA) - readonly equipment: Equipment = {} - - startTooltip = '' - - savePath = '' - capturesPath = '' - mode: CameraDialogMode = 'CAPTURE' - - get canShowMenu() { - return this.mode === 'CAPTURE' - } - - get canShowSavePath() { - return this.mode === 'CAPTURE' - } - - get canShowInfo() { - return this.mode === 'CAPTURE' - } - - get canExposureMode() { - return this.mode === 'CAPTURE' - } - - get canExposureTime() { - return this.mode === 'CAPTURE' || this.mode === 'SEQUENCER' || this.mode === 'TPPA' || this.mode === 'AUTO_FOCUS' - } - - get canExposureTimeUnit() { - return this.mode !== 'DARV' - } - - get canExposureAmount() { - return this.mode === 'CAPTURE' || this.mode === 'SEQUENCER' || this.mode === 'AUTO_FOCUS' - } - - get canFrameType() { - return this.mode === 'CAPTURE' || this.mode === 'SEQUENCER' - } - - get canStartOrAbort() { - return this.mode === 'CAPTURE' - } - - get canSave() { - return this.mode !== 'CAPTURE' - } - - showDitherDialog = false - showLiveStackingDialog = false - - calibrationModel: SlideMenuItem[] = [] +export class CameraComponent implements AfterContentInit, OnDestroy, Tickable { + protected readonly camera = structuredClone(DEFAULT_CAMERA) + protected calibrationModel: SlideMenuItem[] = [] private readonly ditherMenuItem: SlideMenuItem = { - icon: 'icomoon random-dither', + icon: 'mdi mdi-pulse', label: 'Dither', slideMenu: [], command: () => { - this.showDitherDialog = true + this.dither.showDialog = true }, } @@ -92,7 +52,16 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { label: 'Live Stacking', slideMenu: [], command: () => { - this.showLiveStackingDialog = true + this.liveStacking.showDialog = true + }, + } + + private readonly namingFormatMenuItem: SlideMenuItem = { + icon: 'mdi mdi-rename', + label: 'Naming Format', + slideMenu: [], + command: () => { + this.namingFormat.showDialog = true }, } @@ -123,74 +92,113 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { ], } - readonly cameraModel: SlideMenuItem[] = [this.ditherMenuItem, this.liveStackingMenuItem, this.snoopDevicesMenuItem] + protected readonly cameraModel: SlideMenuItem[] = [this.ditherMenuItem, this.liveStackingMenuItem, this.namingFormatMenuItem, this.snoopDevicesMenuItem] - hasDewHeater = false - setpointTemperature = 0.0 - exposureTimeMin = 1 - exposureTimeMax = 1 - exposureTimeUnit = ExposureTimeUnit.MICROSECOND - exposureMode: ExposureMode = 'SINGLE' - subFrame = false + protected running = false + protected hasDewHeater = false + protected readonly preference = structuredClone(DEFAULT_CAMERA_PREFERENCE) + protected request = this.preference.request + protected mode: CameraMode = 'CAPTURE' - readonly request = structuredClone(EMPTY_CAMERA_START_CAPTURE) - running = false + protected readonly dither: CameraDitherDialog = { + showDialog: false, + request: this.request.dither, + } - readonly exposureTimeUnitModel: MenuItem[] = [ - { - label: 'Minute (m)', - command: () => { - this.updateExposureUnit(ExposureTimeUnit.MINUTE) - this.savePreference() - }, - }, - { - label: 'Second (s)', - command: () => { - this.updateExposureUnit(ExposureTimeUnit.SECOND) - this.savePreference() - }, - }, - { - label: 'Millisecond (ms)', - command: () => { - this.updateExposureUnit(ExposureTimeUnit.MILLISECOND) - this.savePreference() - }, - }, - { - label: 'Microsecond (µs)', - command: () => { - this.updateExposureUnit(ExposureTimeUnit.MICROSECOND) - this.savePreference() - }, - }, - ] + protected readonly liveStacking: CameraLiveStackingDialog = { + showDialog: false, + request: this.request.liveStacking, + } + + protected readonly namingFormat: CameraNamingFormatDialog = { + showDialog: false, + format: this.request.namingFormat, + } @ViewChild('cameraExposure') private readonly cameraExposure?: CameraExposureComponent get status() { - return this.cameraExposure?.state ?? 'IDLE' + return this.cameraExposure?.currentState ?? 'IDLE' } get pausingOrPaused() { return this.status === 'PAUSING' || this.status === 'PAUSED' } + get hasCalibration() { + return !this.app.modal + } + + get hasLiveStacking() { + return !this.app.modal + } + + get hasDither() { + return !this.app.modal + } + + get canSnoopDevices() { + return !this.app.modal + } + + get canShowMenu() { + return this.hasCalibration || this.hasLiveStacking || this.hasDither || this.canSnoopDevices + } + + get canShowSavePath() { + return this.mode === 'CAPTURE' + } + + get canShowInfo() { + return this.mode === 'CAPTURE' + } + + get canExposureMode() { + return this.mode === 'CAPTURE' + } + + get canExposureTime() { + return this.mode === 'CAPTURE' || this.mode === 'SEQUENCER' || this.mode === 'TPPA' || this.mode === 'AUTO_FOCUS' + } + + get canExposureTimeUnit() { + return this.mode !== 'DARV' + } + + get canExposureAmount() { + return this.mode === 'CAPTURE' || this.mode === 'SEQUENCER' || this.mode === 'AUTO_FOCUS' + } + + get canFrameType() { + return this.mode === 'CAPTURE' || this.mode === 'SEQUENCER' + } + + get canStartOrAbort() { + return this.mode === 'CAPTURE' + } + + get canSave() { + return this.mode !== 'CAPTURE' + } + + get currentWheelFilter() { + return this.preference.wheel?.names[this.preference.wheel.position - 1] + } + constructor( private readonly app: AppComponent, private readonly api: ApiService, - private readonly browserWindow: BrowserWindowService, - private readonly electron: ElectronService, - private readonly preference: PreferenceService, + private readonly browserWindowService: BrowserWindowService, + private readonly electronService: ElectronService, + private readonly preferenceService: PreferenceService, private readonly route: ActivatedRoute, - private readonly pinger: Pinger, + private readonly ticker: Ticker, ngZone: NgZone, ) { app.title = 'Camera' - electron.on('CAMERA.UPDATED', (event) => { + electronService.on('CAMERA.UPDATED', (event) => { if (event.device.id === this.camera.id) { ngZone.run(() => { Object.assign(this.camera, event.device) @@ -199,15 +207,15 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { } }) - electron.on('CAMERA.DETACHED', (event) => { + electronService.on('CAMERA.DETACHED', (event) => { if (event.device.id === this.camera.id) { ngZone.run(() => { - Object.assign(this.camera, EMPTY_CAMERA) + Object.assign(this.camera, DEFAULT_CAMERA) }) } }) - electron.on('CAMERA.CAPTURE_ELAPSED', (event) => { + electronService.on('CAMERA.CAPTURE_ELAPSED', (event) => { if (event.camera.id === this.camera.id) { ngZone.run(() => { this.running = this.cameraExposure?.handleCameraCaptureEvent(event) ?? false @@ -215,51 +223,115 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { } }) - electron.on('MOUNT.UPDATED', (event) => { - if (event.device.id === this.equipment.mount?.id) { + electronService.on('MOUNT.UPDATED', (event) => { + if (this.mode === 'CAPTURE' && event.device.id === this.preference.mount?.id) { ngZone.run(() => { - if (this.equipment.mount) { - Object.assign(this.equipment.mount, event.device) + if (this.preference.mount) { + Object.assign(this.preference.mount, event.device) } }) } }) - electron.on('WHEEL.UPDATED', (event) => { - if (event.device.id === this.equipment.wheel?.id) { + electronService.on('MOUNT.ATTACHED', () => { + if (this.mode === 'CAPTURE') { + void ngZone.run(() => { + return this.loadEquipment('MOUNT') + }) + } + }) + + electronService.on('MOUNT.DETACHED', () => { + if (this.mode === 'CAPTURE') { + void ngZone.run(() => { + return this.loadEquipment('MOUNT') + }) + } + }) + + electronService.on('WHEEL.UPDATED', (event) => { + if (this.mode === 'CAPTURE' && event.device.id === this.preference.wheel?.id) { ngZone.run(() => { - if (this.equipment.wheel) { - Object.assign(this.equipment.wheel, event.device) + if (this.preference.wheel) { + Object.assign(this.preference.wheel, event.device) } }) } }) - electron.on('FOCUSER.UPDATED', (event) => { - if (event.device.id === this.equipment.focuser?.id) { + electronService.on('WHEEL.ATTACHED', () => { + if (this.mode === 'CAPTURE') { + void ngZone.run(() => { + return this.loadEquipment('WHEEL') + }) + } + }) + + electronService.on('WHEEL.DETACHED', () => { + if (this.mode === 'CAPTURE') { + void ngZone.run(() => { + return this.loadEquipment('WHEEL') + }) + } + }) + + electronService.on('FOCUSER.UPDATED', (event) => { + if (this.mode === 'CAPTURE' && event.device.id === this.preference.focuser?.id) { ngZone.run(() => { - if (this.equipment.focuser) { - Object.assign(this.equipment.focuser, event.device) + if (this.preference.focuser) { + Object.assign(this.preference.focuser, event.device) } }) } }) - electron.on('ROTATOR.UPDATED', (event) => { - if (event.device.id === this.equipment.rotator?.id) { + electronService.on('FOCUSER.ATTACHED', () => { + if (this.mode === 'CAPTURE') { + void ngZone.run(() => { + return this.loadEquipment('FOCUSER') + }) + } + }) + + electronService.on('FOCUSER.DETACHED', () => { + if (this.mode === 'CAPTURE') { + void ngZone.run(() => { + return this.loadEquipment('FOCUSER') + }) + } + }) + + electronService.on('ROTATOR.UPDATED', (event) => { + if (this.mode === 'CAPTURE' && event.device.id === this.preference.rotator?.id) { ngZone.run(() => { - if (this.equipment.rotator) { - Object.assign(this.equipment.rotator, event.device) + if (this.preference.rotator) { + Object.assign(this.preference.rotator, event.device) } }) } }) - electron.on('CALIBRATION.CHANGED', async () => { - await ngZone.run(() => this.loadCalibrationGroups()) + electronService.on('ROTATOR.ATTACHED', () => { + if (this.mode === 'CAPTURE') { + void ngZone.run(() => { + return this.loadEquipment('ROTATOR') + }) + } + }) + + electronService.on('ROTATOR.DETACHED', () => { + if (this.mode === 'CAPTURE') { + void ngZone.run(() => { + return this.loadEquipment('ROTATOR') + }) + } + }) + + electronService.on('CALIBRATION.CHANGED', () => { + void ngZone.run(() => this.loadCalibrationGroups()) }) - electron.on('ROI.SELECTED', (event) => { + electronService.on('ROI.SELECTED', (event) => { if (event.camera.id === this.camera.id) { ngZone.run(() => { this.request.x = event.x @@ -270,39 +342,38 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { } }) - this.snoopDevicesMenuItem.visible = !app.modal + this.snoopDevicesMenuItem.visible = this.canSnoopDevices } ngAfterContentInit() { this.route.queryParams.subscribe(async (e) => { - const decodedData = JSON.parse(decodeURIComponent(e['data'] as string)) as unknown + const data = JSON.parse(decodeURIComponent(e['data'] as string)) as unknown if (this.app.modal) { - await this.loadCameraStartCaptureForDialogMode(decodedData as CameraDialogInput) + await this.loadCameraStartCaptureForDialogMode(data as CameraDialogInput) } else { - await this.cameraChanged(decodedData as Camera) + await this.cameraChanged(data as Camera) } - this.pinger.register(this, 30000) + this.ticker.register(this, 30000) - if (!this.app.modal) { + if (this.mode === 'CAPTURE') { await this.loadEquipment() + await this.loadCalibrationGroups() } - - await this.loadCalibrationGroups() }) } @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) + this.ticker.unregister(this) if (this.mode === 'CAPTURE') { void this.abortCapture() } } - async ping() { + async tick() { if (this.camera.id) { await this.api.cameraListen(this.camera) } @@ -311,28 +382,40 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { private async loadCameraStartCaptureForDialogMode(data?: CameraDialogInput) { if (data) { this.mode = data.mode - Object.assign(this.request, data.request) await this.cameraChanged(data.camera) - this.loadDefaultsForMode(data.mode) - this.normalizeExposureTimeAndUnit(this.request.exposureTime) + Object.assign(this.request, data.request) + this.loadDefaultsForMode(this.mode) } } - private loadDefaultsForMode(mode: CameraDialogMode) { + private loadDefaultsForMode(mode: CameraMode) { if (mode === 'SEQUENCER' || mode === 'AUTO_FOCUS') { - this.exposureMode = 'FIXED' + this.preference.exposureMode = 'FIXED' } else if (this.mode === 'FLAT_WIZARD') { - this.exposureMode = 'SINGLE' + this.preference.exposureMode = 'SINGLE' this.request.frameType = 'FLAT' } else if (mode === 'TPPA') { - this.exposureMode = 'FIXED' + this.preference.exposureMode = 'FIXED' this.request.exposureAmount = 1 } else if (mode === 'DARV') { - this.exposureTimeUnit = ExposureTimeUnit.SECOND + this.preference.exposureTimeUnit = 'SECOND' } + + this.ditherMenuItem.visible = this.hasDither + this.liveStackingMenuItem.visible = this.hasLiveStacking } - async cameraChanged(camera?: Camera) { + private updateSubTitle() { + let subTitle = this.camera.name + + if (this.mode !== 'CAPTURE') { + subTitle += ` · ${this.mode}` + } + + this.app.subTitle = subTitle + } + + protected async cameraChanged(camera?: Camera) { if (camera?.id) { camera = await this.api.camera(camera.id) Object.assign(this.camera, camera) @@ -341,99 +424,125 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { this.update() } - this.app.subTitle = camera?.name ?? '' - - if (this.mode !== 'CAPTURE') { - this.app.subTitle += ` · ${this.mode}` - } + this.updateSubTitle() } - private async loadEquipment() { - const buildStartTooltip = () => { - this.startTooltip = `MOUNT: ${this.equipment.mount?.name ?? 'None'} - FILTER WHEEL: ${this.equipment.wheel?.name ?? 'None'} - FOCUSER: ${this.equipment.focuser?.name ?? 'None'} - ROTATOR: ${this.equipment.rotator?.name ?? 'None'}` - } - - const makeItem = (selected: boolean, command: () => void, device?: Device) => { + private async loadEquipment(type?: DeviceType) { + const makeMenuItem = (selected: boolean, command: () => Promise | void, device?: Device) => { return { icon: device ? 'mdi mdi-connection' : 'mdi mdi-close', label: device?.name ?? 'None', selected, slideMenu: [], - command: (event: MenuItemCommandEvent) => { - command() - buildStartTooltip() - this.preference.equipmentForDevice(this.camera).set(this.equipment) + command: async (event: MenuItemCommandEvent) => { + await command() + this.savePreference() event.parentItem?.slideMenu?.forEach((item) => (item.selected = item === event.item)) }, } as SlideMenuItem } - const slideMenu = this.snoopDevicesMenuItem.slideMenu + const menu = this.snoopDevicesMenuItem.slideMenu // MOUNT - const mounts = await this.api.mounts() - this.equipment.mount = mounts.find((e) => e.name === this.equipment.mount?.name) + if (!type || type === 'MOUNT') { + menu[0].slideMenu.length = 0 - const makeMountItem = (mount?: Mount) => { - return makeItem(this.equipment.mount?.name === mount?.name, () => (this.equipment.mount = mount), mount) - } + const mounts = await this.api.mounts() + this.preference.mount = mounts.find((e) => e.name === this.preference.mount?.name) + + const makeMountItem = (mount?: Mount) => { + return makeMenuItem( + this.preference.mount?.name === mount?.name, + async () => { + this.preference.mount = mount && (await this.api.mount(mount.id)) + }, + mount, + ) + } - slideMenu[0]?.slideMenu.push(makeMountItem()) + menu[0].slideMenu.push(makeMountItem()) - for (const mount of mounts) { - slideMenu[0]?.slideMenu.push(makeMountItem(mount)) + for (const mount of mounts) { + menu[0].slideMenu.push(makeMountItem(mount)) + } } - // FILTER WHEEL + // WHEEL - const wheels = await this.api.wheels() - this.equipment.wheel = wheels.find((e) => e.name === this.equipment.wheel?.name) + if (!type || type === 'WHEEL') { + menu[1].slideMenu.length = 0 - const makeWheelItem = (wheel?: FilterWheel) => { - return makeItem(this.equipment.wheel?.name === wheel?.name, () => (this.equipment.wheel = wheel), wheel) - } + const wheels = await this.api.wheels() + this.preference.wheel = wheels.find((e) => e.name === this.preference.wheel?.name) + + const makeWheelItem = (wheel?: Wheel) => { + return makeMenuItem( + this.preference.wheel?.name === wheel?.name, + async () => { + this.preference.wheel = wheel && (await this.api.wheel(wheel.id)) + }, + wheel, + ) + } - slideMenu[1]?.slideMenu.push(makeWheelItem()) + menu[1].slideMenu.push(makeWheelItem()) - for (const wheel of wheels) { - slideMenu[1]?.slideMenu.push(makeWheelItem(wheel)) + for (const wheel of wheels) { + menu[1].slideMenu.push(makeWheelItem(wheel)) + } } // FOCUSER - const focusers = await this.api.focusers() - this.equipment.focuser = focusers.find((e) => e.name === this.equipment.focuser?.name) + if (!type || type === 'FOCUSER') { + menu[2].slideMenu.length = 0 - const makeFocuserItem = (focuser?: Focuser) => { - return makeItem(this.equipment.focuser?.name === focuser?.name, () => (this.equipment.focuser = focuser), focuser) - } + const focusers = await this.api.focusers() + this.preference.focuser = focusers.find((e) => e.name === this.preference.focuser?.name) - slideMenu[2]?.slideMenu.push(makeFocuserItem()) + const makeFocuserItem = (focuser?: Focuser) => { + return makeMenuItem( + this.preference.focuser?.name === focuser?.name, + async () => { + this.preference.focuser = focuser && (await this.api.focuser(focuser.id)) + }, + focuser, + ) + } + + menu[2].slideMenu.push(makeFocuserItem()) - for (const focuser of focusers) { - slideMenu[2]?.slideMenu.push(makeFocuserItem(focuser)) + for (const focuser of focusers) { + menu[2].slideMenu.push(makeFocuserItem(focuser)) + } } // ROTATOR - const rotators = await this.api.rotators() - this.equipment.rotator = rotators.find((e) => e.name === this.equipment.rotator?.name) + if (!type || type === 'ROTATOR') { + menu[3].slideMenu.length = 0 - const makeRotatorItem = (rotator?: Rotator) => { - return makeItem(this.equipment.rotator?.name === rotator?.name, () => (this.equipment.rotator = rotator), rotator) - } + const rotators = await this.api.rotators() + this.preference.rotator = rotators.find((e) => e.name === this.preference.rotator?.name) - slideMenu[3]?.slideMenu.push(makeRotatorItem()) + const makeRotatorItem = (rotator?: Rotator) => { + return makeMenuItem( + this.preference.rotator?.name === rotator?.name, + async () => { + this.preference.rotator = rotator && (await this.api.rotator(rotator.id)) + }, + rotator, + ) + } - for (const rotator of rotators) { - slideMenu[3]?.slideMenu.push(makeRotatorItem(rotator)) - } + menu[3].slideMenu.push(makeRotatorItem()) - buildStartTooltip() + for (const rotator of rotators) { + menu[3].slideMenu.push(makeRotatorItem(rotator)) + } + } } private async loadCalibrationGroups() { @@ -465,7 +574,7 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { label: 'Open Calibration', slideMenu: [], command: () => { - return this.browserWindow.openCalibration({ bringToFront: true }) + return this.browserWindowService.openCalibration({ bringToFront: true }) }, }) @@ -479,7 +588,7 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { this.calibrationModel = menu } - connect() { + protected connect() { if (this.camera.connected) { return this.api.cameraDisconnect(this.camera) } else { @@ -487,12 +596,12 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { } } - toggleAutoSaveAllExposures() { + protected toggleAutoSaveAllExposures() { this.request.autoSave = !this.request.autoSave this.savePreference() } - toggleAutoSubFolder() { + protected toggleAutoSubFolder() { switch (this.request.autoSubFolderMode) { case 'OFF': this.request.autoSubFolderMode = 'NOON' @@ -508,26 +617,26 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { this.savePreference() } - async chooseSavePath() { - const defaultPath = this.savePath || this.capturesPath - const path = await this.electron.openDirectory({ defaultPath }) + protected async chooseSavePath() { + const defaultPath = this.preference.request.savePath || this.camera.capturesPath + const path = await this.electronService.openDirectory({ defaultPath }) if (path) { - this.savePath = path + this.preference.request.savePath = path this.savePreference() } } - applySetpointTemperature() { + protected applySetpointTemperature() { this.savePreference() - return this.api.cameraSetpointTemperature(this.camera, this.setpointTemperature) + return this.api.cameraSetpointTemperature(this.camera, this.preference.setpointTemperature) } - toggleCooler() { + protected toggleCooler() { return this.api.cameraCooler(this.camera, this.camera.cooler) } - fullsize() { + protected fullsize() { this.request.x = this.camera.minX this.request.y = this.camera.minY this.request.width = this.camera.maxWidth @@ -535,34 +644,45 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { this.savePreference() } - openCameraImage() { - return this.browserWindow.openCameraImage(this.camera, 'CAMERA', this.request) + protected openMount(mount: Mount) { + return this.browserWindowService.openMount(mount) + } + + protected openFocuser(focuser: Focuser) { + return this.browserWindowService.openFocuser(focuser) + } + + protected openWheel(wheel: Wheel) { + return this.browserWindowService.openWheel(wheel) + } + + protected openRotator(rotator: Rotator) { + return this.browserWindowService.openRotator(rotator) + } + + protected openCameraImage() { + return this.browserWindowService.openCameraImage(this.camera, 'CAMERA', this.request) } private makeCameraStartCapture(): CameraStartCapture { - const x = this.subFrame ? this.request.x : this.camera.minX - const y = this.subFrame ? this.request.y : this.camera.minY - const width = this.subFrame ? this.request.width : this.camera.maxWidth - const height = this.subFrame ? this.request.height : this.camera.maxHeight - const exposureFactor = CameraComponent.exposureUnitFactor(this.exposureTimeUnit) - const exposureTime = Math.trunc((this.request.exposureTime * 60000000) / exposureFactor) + const subFrame = this.preference.subFrame + const x = subFrame ? this.request.x : this.camera.minX + const y = subFrame ? this.request.y : this.camera.minY + const width = subFrame ? this.request.width : this.camera.maxWidth + const height = subFrame ? this.request.height : this.camera.maxHeight const exposureAmount = - this.exposureMode === 'LOOP' ? 0 - : this.exposureMode === 'FIXED' ? this.request.exposureAmount + this.preference.exposureMode === 'LOOP' ? 0 + : this.preference.exposureMode === 'FIXED' ? this.request.exposureAmount : 1 - const savePath = this.mode !== 'CAPTURE' ? this.request.savePath : this.savePath - - const liveStackingRequest = this.preference.liveStackingRequest(this.request.liveStacking.type).get() - this.request.liveStacking.executablePath = liveStackingRequest.executablePath - this.request.liveStacking.slot = liveStackingRequest.slot || 1 - let shutterPosition: Undefinable + let shutterPosition = 0 - if (this.equipment.wheel) { - const wheelPreference = this.preference.wheelPreference(this.equipment.wheel).get() - shutterPosition = wheelPreference.shutterPosition + if (this.preference.wheel) { + shutterPosition = this.preferenceService.wheel(this.preference.wheel).get().shutterPosition } + Object.assign(this.request.liveStacking, this.preferenceService.settings.get().liveStacker[this.request.liveStacking.type]) + return { ...this.request, shutterPosition, @@ -570,169 +690,74 @@ export class CameraComponent implements AfterContentInit, OnDestroy, Pingable { y, width, height, - exposureTime, exposureAmount, - savePath, } } - async startCapture() { + protected async startCapture() { try { this.running = true await this.openCameraImage() - await this.api.cameraStartCapture(this.camera, this.makeCameraStartCapture(), this.equipment) - this.preference.equipmentForDevice(this.camera).set(this.equipment) + const { mount, wheel, focuser, rotator } = this.preference + await this.api.cameraStartCapture(this.camera, this.makeCameraStartCapture(), mount, wheel, focuser, rotator) } catch { this.running = false } } - pauseCapture() { + protected pauseCapture() { return this.api.cameraPauseCapture(this.camera) } - unpauseCapture() { + protected unpauseCapture() { return this.api.cameraUnpauseCapture(this.camera) } - abortCapture() { + protected abortCapture() { return this.api.cameraAbortCapture(this.camera) } - static exposureUnitFactor(unit: ExposureTimeUnit) { - switch (unit) { - case ExposureTimeUnit.MINUTE: - return 1 - case ExposureTimeUnit.SECOND: - return 60 - case ExposureTimeUnit.MILLISECOND: - return 60000 - case ExposureTimeUnit.MICROSECOND: - return 60000000 - default: - return 0 - } - } - - private updateExposureUnit(unit: ExposureTimeUnit, from: ExposureTimeUnit = this.exposureTimeUnit) { - const exposureMax = this.camera.exposureMax || 60000000 - - if (exposureMax) { - const a = CameraComponent.exposureUnitFactor(from) - const b = CameraComponent.exposureUnitFactor(unit) - const exposureTime = Math.trunc((this.request.exposureTime * b) / a) - const exposureTimeMin = Math.trunc((this.camera.exposureMin * b) / 60000000) - const exposureTimeMax = Math.trunc((exposureMax * b) / 60000000) - this.exposureTimeMax = Math.max(1, exposureTimeMax) - this.exposureTimeMin = Math.max(1, exposureTimeMin) - this.request.exposureTime = Math.max(this.exposureTimeMin, Math.min(exposureTime, this.exposureTimeMax)) - this.exposureTimeUnit = unit - } - } - - private normalizeExposureTimeAndUnit(exposureTime: number) { - if (this.canExposureTimeUnit) { - const factors = [ - { unit: ExposureTimeUnit.MINUTE, time: 60000000 }, - { unit: ExposureTimeUnit.SECOND, time: 1000000 }, - { unit: ExposureTimeUnit.MILLISECOND, time: 1000 }, - ] - - for (const { unit, time } of factors) { - if (exposureTime >= time) { - const k = exposureTime / time - - // exposureTime is multiple of time. - if (k === Math.floor(k)) { - this.updateExposureUnit(unit, ExposureTimeUnit.MICROSECOND) - return - } - } - } - } else { - this.updateExposureUnit(this.exposureTimeUnit, ExposureTimeUnit.MICROSECOND) - } - } - private update() { if (this.camera.id) { if (this.camera.connected) { updateCameraStartCaptureFromCamera(this.request, this.camera) - this.updateExposureUnit(this.exposureTimeUnit) } - - this.capturesPath = this.camera.capturesPath } } - clearSavePath() { - this.savePath = '' + protected clearSavePath() { + this.preference.request.savePath = '' + this.savePreference() + } + + protected resetCameraCaptureNamingFormat(type: FrameType) { + resetCameraCaptureNamingFormat(type, this.namingFormat.format, this.preferenceService.settings.get().namingFormat) this.savePreference() } - apply() { + protected apply() { return this.app.close(this.makeCameraStartCapture()) } private loadPreference() { - if (this.mode === 'CAPTURE' && this.camera.name) { - const preference: Partial = this.preference.cameraPreference(this.camera).get() - - this.request.autoSave = preference.autoSave ?? false - this.savePath = preference.savePath ?? '' - this.request.autoSubFolderMode = preference.autoSubFolderMode ?? 'OFF' - this.setpointTemperature = preference.setpointTemperature ?? 0 - this.request.exposureTime = preference.exposureTime ?? this.camera.exposureMin - this.exposureTimeUnit = preference.exposureTimeUnit ?? ExposureTimeUnit.MICROSECOND - this.exposureMode = preference.exposureMode ?? 'SINGLE' - this.request.exposureDelay = preference.exposureDelay ?? 0 - this.request.exposureAmount = preference.exposureAmount ?? 1 - this.request.x = preference.x ?? this.camera.minX - this.request.y = preference.y ?? this.camera.minY - this.request.width = preference.width ?? this.camera.maxWidth - this.request.height = preference.height ?? this.camera.maxHeight - this.subFrame = preference.subFrame ?? false - this.request.binX = preference.binX ?? 1 - this.request.binY = preference.binY ?? 1 - this.request.frameType = preference.frameType ?? 'LIGHT' - this.request.gain = preference.gain ?? 0 - this.request.offset = preference.offset ?? 0 - this.request.frameFormat = preference.frameFormat ?? (this.camera.frameFormats[0] || '') - this.request.calibrationGroup = preference.calibrationGroup - this.request.dither.enabled = preference.dither?.enabled ?? false - this.request.dither.amount = preference.dither?.amount ?? 1.5 - this.request.dither.raOnly = preference.dither?.raOnly ?? false - this.request.dither.afterExposures = preference.dither?.afterExposures ?? 1 - this.request.liveStacking.enabled = preference.liveStacking?.enabled ?? false - this.request.liveStacking.type = preference.liveStacking?.type ?? 'SIRIL' - this.request.liveStacking.executablePath = preference.liveStacking?.executablePath ?? '' - this.request.liveStacking.dark = preference.liveStacking?.dark - this.request.liveStacking.flat = preference.liveStacking?.flat - this.request.liveStacking.bias = preference.liveStacking?.bias - this.request.liveStacking.use32Bits = preference.liveStacking?.use32Bits ?? false - this.request.liveStacking.slot = preference.liveStacking?.slot ?? 1 - - Object.assign(this.equipment, this.preference.equipmentForDevice(this.camera).get()) + if (this.mode === 'CAPTURE' && this.camera.id) { + Object.assign(this.preference, this.preferenceService.camera(this.camera).get()) + this.request = this.preference.request + this.dither.request = this.request.dither + this.liveStacking.request = this.request.liveStacking + this.namingFormat.format = cameraCaptureNamingFormatWithDefault(this.request.namingFormat, this.preferenceService.settings.get().namingFormat) } } - savePreference() { + protected savePreference() { if (this.mode === 'CAPTURE' && this.camera.connected) { - const preference: CameraPreference = { - ...this.request, - setpointTemperature: this.setpointTemperature, - exposureTimeUnit: this.exposureTimeUnit, - exposureMode: this.exposureMode, - subFrame: this.subFrame, - savePath: this.request.savePath || this.savePath, - } - - this.preference.cameraPreference(this.camera).set(preference) + Object.assign(this.preference.request, this.request) + this.preferenceService.camera(this.camera).set(this.preference) } } - static async showAsDialog(window: BrowserWindowService, mode: CameraDialogMode, camera: Camera, request: CameraStartCapture) { - const result = await window.openCameraDialog({ mode, camera, request }) + static async showAsDialog(service: BrowserWindowService, mode: CameraMode, camera: Camera, request: CameraStartCapture) { + const result = await service.openCameraDialog({ mode, camera, request }) if (result) { Object.assign(request, result) diff --git a/desktop/src/app/camera/exposure-time.component.html b/desktop/src/app/camera/exposure-time.component.html new file mode 100644 index 000000000..960f08306 --- /dev/null +++ b/desktop/src/app/camera/exposure-time.component.html @@ -0,0 +1,32 @@ +
+ + + + + + +
diff --git a/desktop/src/app/camera/exposure-time.component.ts b/desktop/src/app/camera/exposure-time.component.ts new file mode 100644 index 000000000..d1ab3ecb8 --- /dev/null +++ b/desktop/src/app/camera/exposure-time.component.ts @@ -0,0 +1,223 @@ +import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, ViewEncapsulation } from '@angular/core' +import { MenuItem } from '../../shared/components/menu-item/menu-item.component' +import { ExposureTimeUnit } from '../../shared/types/camera.types' + +@Component({ + selector: 'neb-exposure-time', + templateUrl: './exposure-time.component.html', + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ExposureTimeComponent implements AfterViewInit, OnChanges { + @Input({ required: true }) + protected exposureTime: number = 0 + + @Output() + readonly exposureTimeChange = new EventEmitter() + + @Input() + protected unit: ExposureTimeUnit = 'MICROSECOND' + + @Output() + readonly unitChange = new EventEmitter() + + @Input() + protected readonly min: number = 0 + + @Input() + protected readonly max: number = 600000000 + + @Input() + protected readonly disabled: boolean = false + + @Input() + protected readonly canExposureTime: boolean = true + + @Input() + protected readonly canExposureTimeUnit: boolean = true + + @Input() + protected readonly normalized: boolean = true + + @Input() + protected readonly label?: string + + protected readonly current = { + exposureTime: this.exposureTime, + min: this.min, + max: this.max, + } + + protected readonly model: MenuItem[] = [ + { + label: 'Minute (m)', + command: () => { + this.exposureTimeUnitChanged('MINUTE') + }, + }, + { + label: 'Second (s)', + command: () => { + this.exposureTimeUnitChanged('SECOND') + }, + }, + { + label: 'Millisecond (ms)', + command: () => { + this.exposureTimeUnitChanged('MILLISECOND') + }, + }, + { + label: 'Microsecond (µs)', + command: () => { + this.exposureTimeUnitChanged('MICROSECOND') + }, + }, + ] + + private exposureTimeInMicroseconds = 0 + + ngOnChanges(changes: SimpleChanges) { + for (const key in changes) { + const change = changes[key] + + // if (change.currentValue === change.previousValue && !change.firstChange) continue + + switch (key) { + case 'unit': + this.exposureTimeUnitChanged(change.currentValue) + break + case 'exposureTime': + this.exposureTimeChanged(change.currentValue, 'MICROSECOND', this.normalized && this.exposureTimeInMicroseconds !== change.currentValue) + break + case 'min': + case 'max': + this.exposureTimeMinMaxChanged() + break + case 'normalized': + this.normalize(this.exposureTime) + break + } + } + } + + ngAfterViewInit() { + this.updateExposureTime(this.current.exposureTime, this.unit, this.unit) + } + + protected exposureTimeUnitChanged(value: ExposureTimeUnit) { + this.updateExposureTime(this.current.exposureTime, value, this.unit, false) + } + + protected exposureTimeChanged(value: number, from: ExposureTimeUnit = this.unit, normalize: boolean = false) { + this.updateExposureTime(value, this.unit, from, normalize) + } + + protected exposureTimeMinMaxChanged() { + this.updateExposureTime(this.current.exposureTime, this.unit, this.unit, false) + } + + protected exposureTimeUnitWheeled(event: WheelEvent) { + if (event.deltaY) { + const units: ExposureTimeUnit[] = ['MINUTE', 'SECOND', 'MILLISECOND', 'MICROSECOND'] + const index = units.indexOf(this.unit) + + if (index >= 0) { + if (event.deltaY > 0) { + const next = (index + 1) % units.length + this.exposureTimeUnitChanged(units[next]) + } else { + const next = (index + units.length - 1) % units.length + this.exposureTimeUnitChanged(units[next]) + } + } + } + } + + private updateExposureTime(value: number, unit: ExposureTimeUnit, from: ExposureTimeUnit, normalize: boolean = this.normalized) { + const a = ExposureTimeComponent.exposureUnitFactor(from) + const b = ExposureTimeComponent.exposureUnitFactor(unit) + + if (!a || !b) return + + this.current.min = Math.max(1, Math.trunc(((this.min || 1) * b) / 60000000)) + this.current.max = Math.max(1, Math.trunc(((this.max || 600000000) * b) / 60000000)) + this.current.exposureTime = Math.max(this.current.min, Math.min(Math.trunc((value * b) / a), this.current.max)) + + const exposureTimeInMicroseconds = Math.trunc((this.current.exposureTime * 60000000) / b) + + if (normalize) { + if (this.normalize(exposureTimeInMicroseconds)) { + return + } + } + + if (this.exposureTime !== exposureTimeInMicroseconds) { + this.exposureTime = exposureTimeInMicroseconds + this.exposureTimeInMicroseconds = exposureTimeInMicroseconds + this.exposureTimeChange.emit(exposureTimeInMicroseconds) + } + + if (this.unit !== unit) { + this.unit = unit + this.unitChange.emit(unit) + } + } + + private normalize(exposureTime: number) { + if (!this.normalized) { + return false + } + + const factors: { unit: ExposureTimeUnit; time: number }[] = [ + { unit: 'MINUTE', time: 60000000 }, + { unit: 'SECOND', time: 1000000 }, + { unit: 'MILLISECOND', time: 1000 }, + ] + + for (const { unit, time } of factors) { + if (exposureTime >= time) { + const k = exposureTime / time + + // exposureTime is multiple of time. + if (k === Math.floor(k)) { + this.updateExposureTime(exposureTime, unit, 'MICROSECOND', false) + return true + } + } + } + + return false + } + + static computeExposureTime(exposureTime: number, to: ExposureTimeUnit, from: ExposureTimeUnit = 'MICROSECOND') { + if (to === from) { + return exposureTime + } + + const a = ExposureTimeComponent.exposureUnitFactor(from) + const b = ExposureTimeComponent.exposureUnitFactor(to) + + return Math.trunc((exposureTime * b) / a) + } + + static exposureUnitFactor(unit: ExposureTimeUnit) { + switch (unit) { + case 'MINUTE': + case 'm' as ExposureTimeUnit: + return 1 + case 'SECOND': + case 's' as ExposureTimeUnit: + return 60 + case 'MILLISECOND': + case 'ms' as ExposureTimeUnit: + return 60000 + case 'MICROSECOND': + case 'us' as ExposureTimeUnit: + case 'µs' as ExposureTimeUnit: + return 60000000 + default: + return 0 + } + } +} diff --git a/desktop/src/app/filterwheel/filterwheel.component.html b/desktop/src/app/filterwheel/filterwheel.component.html index b1cf6463e..627f61436 100644 --- a/desktop/src/app/filterwheel/filterwheel.component.html +++ b/desktop/src/app/filterwheel/filterwheel.component.html @@ -144,14 +144,14 @@ + spinnableNumber />
diff --git a/desktop/src/app/filterwheel/filterwheel.component.ts b/desktop/src/app/filterwheel/filterwheel.component.ts index 4fd655939..1cc4c9bbf 100644 --- a/desktop/src/app/filterwheel/filterwheel.component.ts +++ b/desktop/src/app/filterwheel/filterwheel.component.ts @@ -6,35 +6,38 @@ import { Subject, Subscription, debounceTime } 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 { Pingable, Pinger } from '../../shared/services/pinger.service' import { PreferenceService } from '../../shared/services/preference.service' -import { CameraStartCapture, EMPTY_CAMERA_START_CAPTURE } from '../../shared/types/camera.types' +import { Tickable, Ticker } from '../../shared/services/ticker.service' +import { CameraStartCapture, DEFAULT_CAMERA_START_CAPTURE } from '../../shared/types/camera.types' import { Focuser } from '../../shared/types/focuser.types' -import { EMPTY_WHEEL, FilterSlot, FilterWheel, WheelDialogInput, WheelDialogMode, WheelPreference, makeFilterSlots } from '../../shared/types/wheel.types' -import { Undefinable } from '../../shared/utils/types' +import { DEFAULT_WHEEL, DEFAULT_WHEEL_PREFERENCE, Filter, Wheel, WheelDialogInput, WheelMode, makeFilter } from '../../shared/types/wheel.types' import { AppComponent } from '../app.component' @Component({ - selector: 'app-filterwheel', + selector: 'neb-filterwheel', templateUrl: './filterwheel.component.html', styleUrls: ['./filterwheel.component.scss'], }) -export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingable { - readonly wheel = structuredClone(EMPTY_WHEEL) - readonly request = structuredClone(EMPTY_CAMERA_START_CAPTURE) +export class FilterWheelComponent implements AfterContentInit, OnDestroy, Tickable { + protected readonly wheel = structuredClone(DEFAULT_WHEEL) + protected readonly request = structuredClone(DEFAULT_CAMERA_START_CAPTURE) + protected readonly preference = structuredClone(DEFAULT_WHEEL_PREFERENCE) - focusers: Focuser[] = [] - focuser?: Focuser - focusOffset = 0 - focusOffsetMin = 0 - focusOffsetMax = 0 + protected focusers: Focuser[] = [] + protected focuser?: Focuser + protected focuserOffset = 0 + protected focuserMinPosition = 0 + protected focuserMaxPosition = 0 - moving = false - position = 0 - filters: FilterSlot[] = [] - filter?: FilterSlot + protected moving = false + protected position = 0 + protected filters: Filter[] = [] + protected filter?: Filter - mode: WheelDialogMode = 'CAPTURE' + protected mode: WheelMode = 'CAPTURE' + + private readonly filterPublisher = new Subject() + private readonly filterSubscription?: Subscription get canShowInfo() { return this.mode === 'CAPTURE' @@ -52,25 +55,22 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab return this.mode !== 'CAPTURE' } - get currentFilter(): Undefinable { + get currentFilter(): Filter | undefined { return this.filters[this.position - 1] } - private readonly filterChangedPublisher = new Subject() - private readonly subscription?: Subscription - constructor( private readonly app: AppComponent, private readonly api: ApiService, - private readonly electron: ElectronService, - private readonly preference: PreferenceService, + private readonly electronService: ElectronService, + private readonly preferenceService: PreferenceService, private readonly route: ActivatedRoute, - private readonly pinger: Pinger, + private readonly ticker: Ticker, ngZone: NgZone, ) { app.title = 'Filter Wheel' - electron.on('WHEEL.UPDATED', (event) => { + electronService.on('WHEEL.UPDATED', (event) => { if (event.device.id === this.wheel.id) { ngZone.run(() => { Object.assign(this.wheel, event.device) @@ -79,15 +79,15 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab } }) - electron.on('WHEEL.DETACHED', (event) => { + electronService.on('WHEEL.DETACHED', (event) => { if (event.device.id === this.wheel.id) { ngZone.run(() => { - Object.assign(this.wheel, EMPTY_WHEEL) + Object.assign(this.wheel, DEFAULT_WHEEL) }) } }) - electron.on('FOCUSER.UPDATED', (event) => { + electronService.on('FOCUSER.UPDATED', (event) => { if (event.device.id === this.focuser?.id) { ngZone.run(() => { if (this.focuser) { @@ -97,7 +97,7 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab } }) - electron.on('FOCUSER.DETACHED', (event) => { + electronService.on('FOCUSER.DETACHED', (event) => { if (event.device.id === this.focuser?.id) { ngZone.run(() => { this.focuser = undefined @@ -112,12 +112,10 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab } }) - this.subscription = this.filterChangedPublisher.pipe(debounceTime(1500)).subscribe(async (filter) => { - this.savePreference() - - const names = this.filters.map(e => e.name) + this.filterSubscription = this.filterPublisher.pipe(debounceTime(1500)).subscribe(async (filter) => { + const names = this.filters.map((e) => e.name) await this.api.wheelSync(this.wheel, names) - await this.electron.send('WHEEL.RENAMED', { wheel: this.wheel, filter }) + await this.electronService.send('WHEEL.RENAMED', { wheel: this.wheel, filter }) }) hotkeys('enter', (event) => { @@ -172,18 +170,15 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab async ngAfterContentInit() { this.route.queryParams.subscribe(async (e) => { - const decodedData = JSON.parse(decodeURIComponent(e['data'] as string)) as unknown + const data = JSON.parse(decodeURIComponent(e['data'] as string)) as unknown if (this.app.modal) { - const request = decodedData as WheelDialogInput - Object.assign(this.request, request.request) - this.mode = request.mode - await this.wheelChanged(request.wheel) + await this.loadCameraStartCaptureForDialogMode(data as WheelDialogInput) } else { - await this.wheelChanged(decodedData as FilterWheel) + await this.wheelChanged(data as Wheel) } - this.pinger.register(this, 30000) + this.ticker.register(this, 30000) }) this.focusers = await this.api.focusers() @@ -196,20 +191,28 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) - this.subscription?.unsubscribe() + this.ticker.unregister(this) + this.filterSubscription?.unsubscribe() } - async ping() { + async tick() { if (this.wheel.id) await this.api.wheelListen(this.wheel) if (this.focuser?.id) await this.api.focuserListen(this.focuser) } - async wheelChanged(wheel?: FilterWheel) { + private async loadCameraStartCaptureForDialogMode(data?: WheelDialogInput) { + if (data) { + this.mode = data.mode + await this.wheelChanged(data.wheel) + Object.assign(this.request, data.request) + } + } + + protected async wheelChanged(wheel?: Wheel) { if (wheel?.id) { wheel = await this.api.wheel(wheel.id) - await this.ping() + await this.tick() Object.assign(this.wheel, wheel) @@ -220,7 +223,7 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab this.app.subTitle = wheel?.name ?? '' } - connect() { + protected connect() { if (this.wheel.connected) { return this.api.wheelDisconnect(this.wheel) } else { @@ -228,11 +231,11 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab } } - filterChanged() { + protected filterChanged() { this.updateFocusOffset() } - async moveTo(filter: FilterSlot) { + protected async moveTo(filter: Filter) { try { if (this.currentFilter) { this.moving = true @@ -245,8 +248,6 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab const offset = nextFocusOffset - currentFocusOffset if (this.focuser && offset !== 0) { - console.info('moving focuser %d steps', offset) - if (offset < 0) await this.api.focuserMoveIn(this.focuser, -offset) else await this.api.focuserMoveOut(this.focuser, offset) } @@ -257,21 +258,21 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab } } - async moveToSelectedFilter() { + protected async moveToSelectedFilter() { if (this.filter) { await this.moveTo(this.filter) } } - moveUp() { + protected moveUp() { return this.moveToPosition(this.wheel.position - 1) } - moveDown() { + protected moveDown() { return this.moveToPosition(this.wheel.position + 1) } - async moveToIndex(index: number) { + protected async moveToIndex(index: number) { if (!this.moving) { index = index >= 0 && index < this.filters.length ? index @@ -282,7 +283,7 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab } } - async moveToPosition(position: number) { + protected async moveToPosition(position: number) { if (!this.moving) { position = position >= 1 && position <= this.wheel.count ? position @@ -298,40 +299,41 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab } } - shutterToggled(filter: FilterSlot, event: CheckboxChangeEvent) { + protected shutterToggled(filter: Filter, event: CheckboxChangeEvent) { this.filters.forEach((e) => (e.dark = !!event.checked && e === filter)) - this.filterChangedPublisher.next(structuredClone(filter)) + this.preference.shutterPosition = this.filters.find((e) => e.dark)?.position ?? 0 + this.savePreference() } - filterNameChanged(filter: FilterSlot) { + protected filterNameChanged(filter: Filter) { if (filter.name) { - this.filterChangedPublisher.next(structuredClone(filter)) + this.filterPublisher.next(structuredClone(filter)) } } - async focuserChanged() { + protected async focuserChanged() { if (this.focuser) { - await this.ping() + await this.tick() - this.focusOffsetMax = this.focuser.maxPosition - this.focusOffsetMin = -this.focusOffsetMax + this.focuserMaxPosition = this.focuser.maxPosition + this.focuserMinPosition = -this.focuserMaxPosition this.updateFocusOffset() } } - focusOffsetForFilter(filter: FilterSlot) { - return this.focuser ? this.preference.focusOffsets(this.wheel, this.focuser).get()[filter.position - 1] ?? 0 : 0 + protected focusOffsetForFilter(filter: Filter) { + return this.focuser ? (this.preferenceService.focusOffsets(this.wheel, this.focuser).get()[filter.position - 1] ?? 0) : 0 } private updateFocusOffset() { - this.focusOffset = this.filter ? this.focusOffsetForFilter(this.filter) : 0 + this.focuserOffset = this.filter ? this.focusOffsetForFilter(this.filter) : 0 } - focusOffsetChanged() { + protected focusOffsetChanged() { if (this.filter && this.focuser) { - const offsets = this.preference.focusOffsets(this.wheel, this.focuser).get() - offsets[this.filter.position - 1] = this.focusOffset - this.preference.focusOffsets(this.wheel, this.focuser).set(offsets) + const offsets = this.preferenceService.focusOffsets(this.wheel, this.focuser).get() + offsets[this.filter.position - 1] = this.focuserOffset + this.preferenceService.focusOffsets(this.wheel, this.focuser).set(offsets) } } @@ -349,36 +351,21 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab if (this.moving) return - const preference = this.preference.wheelPreference(this.wheel).get() - const filters = makeFilterSlots(this.wheel, this.filters, preference.shutterPosition) - - if (filters !== this.filters) { - this.filters = filters - this.filter = filters[(this.filter?.position ?? this.position) - 1] ?? filters[0] - } + this.filters = makeFilter(this.wheel, this.filters, this.preference.shutterPosition) + this.filter = this.filters[(this.filter?.position ?? this.position) - 1] ?? this.filters[0] this.updateFocusOffset() } private loadPreference() { if (this.mode === 'CAPTURE' && this.wheel.name) { - const preference = this.preference.wheelPreference(this.wheel).get() - const shutterPosition = preference.shutterPosition ?? 0 - this.filters.forEach((e) => (e.dark = e.position === shutterPosition)) + Object.assign(this.preference, this.preferenceService.wheel(this.wheel).get()) } } private savePreference() { if (this.mode === 'CAPTURE' && this.wheel.connected) { - const dark = this.filters.find((e) => e.dark) - - const preference: WheelPreference = { - shutterPosition: dark?.position ?? 0, - } - - this.preference.wheelPreference(this.wheel).set(preference) - - // TODO: this.api.wheelSync(this.wheel, preference.names!) + this.preferenceService.wheel(this.wheel).set(this.preference) } } @@ -389,12 +376,12 @@ export class FilterWheelComponent implements AfterContentInit, OnDestroy, Pingab } } - apply() { + protected apply() { return this.app.close(this.makeCameraStartCapture()) } - static async showAsDialog(window: BrowserWindowService, mode: WheelDialogMode, wheel: FilterWheel, request: CameraStartCapture) { - const result = await window.openWheelDialog({ mode, wheel, request }) + static async showAsDialog(service: BrowserWindowService, mode: WheelMode, wheel: Wheel, request: CameraStartCapture) { + const result = await service.openWheelDialog({ mode, wheel, request }) if (result) { Object.assign(request, result) diff --git a/desktop/src/app/flat-wizard/flat-wizard.component.html b/desktop/src/app/flat-wizard/flat-wizard.component.html index ab62448c0..3539bd486 100644 --- a/desktop/src/app/flat-wizard/flat-wizard.component.html +++ b/desktop/src/app/flat-wizard/flat-wizard.component.html @@ -8,7 +8,7 @@ [(device)]="camera" (deviceChange)="cameraChanged()" /> @@ -27,19 +27,13 @@
- - - {{ savedPath }} -
+ spinnableNumber />
+ spinnableNumber />
+ spinnableNumber />
+ spinnableNumber />
@@ -139,7 +133,7 @@
{ + electronService.on('FLAT_WIZARD.ELAPSED', (event) => { ngZone.run(() => { - if (event.state === 'EXPOSURING' && event.capture && event.capture.camera.id === this.camera.id) { + if (event.state === 'EXPOSURING' && event.capture && event.capture.camera.id === this.camera?.id) { this.running = true this.cameraExposure.handleCameraCaptureEvent(event.capture, true) } else if (event.state === 'CAPTURED') { this.running = false - this.savedPath = event.savedPath - this.prime.message(`Flat frame captured`) + this.angularService.message('Flat frame captured') } else if (event.state === 'FAILED') { this.running = false - this.savedPath = undefined - this.prime.message(`Failed to find an optimal exposure time from given parameters`, 'error') + this.angularService.message('Failed to find an optimal exposure time from given parameters', 'error') } }) }) - electron.on('CAMERA.UPDATED', async (event) => { - if (event.device.id === this.camera.id) { - await ngZone.run(() => { - Object.assign(this.camera, event.device) - return this.cameraChanged() + electronService.on('CAMERA.UPDATED', (event) => { + if (event.device.id === this.camera?.id) { + ngZone.run(() => { + if (this.camera) { + Object.assign(this.camera, event.device) + void this.cameraChanged() + } }) } }) - electron.on('CAMERA.ATTACHED', (event) => { + electronService.on('CAMERA.ATTACHED', (event) => { ngZone.run(() => { this.cameras.push(event.device) this.cameras.sort(deviceComparator) }) }) - electron.on('CAMERA.DETACHED', (event) => { + electronService.on('CAMERA.DETACHED', (event) => { ngZone.run(() => { const index = this.cameras.findIndex((e) => e.id === event.device.id) if (index >= 0) { if (this.cameras[index] === this.camera) { - Object.assign(this.camera, this.cameras[0] ?? EMPTY_CAMERA) + Object.assign(this.camera, this.cameras[0] ?? DEFAULT_CAMERA) } this.cameras.splice(index, 1) @@ -109,41 +102,41 @@ export class FlatWizardComponent implements AfterViewInit, OnDestroy, Pingable { }) }) - electron.on('WHEEL.UPDATED', async (event) => { - if (event.device.id === this.wheel.id) { - await ngZone.run(() => { - Object.assign(this.wheel, event.device) - return this.wheelChanged() + electronService.on('WHEEL.UPDATED', (event) => { + if (event.device.id === this.wheel?.id) { + ngZone.run(() => { + if (this.wheel) { + Object.assign(this.wheel, event.device) + void this.wheelChanged() + } }) } }) - electron.on('WHEEL.ATTACHED', (event) => { + electronService.on('WHEEL.ATTACHED', (event) => { ngZone.run(() => { this.wheels.push(event.device) this.wheels.sort(deviceComparator) }) }) - electron.on('WHEEL.DETACHED', (event) => { + electronService.on('WHEEL.DETACHED', (event) => { ngZone.run(() => { const index = this.wheels.findIndex((e) => e.id === event.device.id) if (index >= 0) { if (this.wheels[index] === this.wheel) { - Object.assign(this.wheel, this.wheels[0] ?? EMPTY_WHEEL) + Object.assign(this.wheel, this.wheels[0] ?? DEFAULT_WHEEL) } this.wheels.splice(index, 1) } }) }) - - this.request.capture.frameType = 'FLAT' } async ngAfterViewInit() { - this.pinger.register(this, 30000) + this.ticker.register(this, 30000) this.cameras = (await this.api.cameras()).sort(deviceComparator) this.wheels = (await this.api.wheels()).sort(deviceComparator) @@ -151,27 +144,26 @@ export class FlatWizardComponent implements AfterViewInit, OnDestroy, Pingable { @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) + this.ticker.unregister(this) void this.stop() } - async ping() { - if (this.camera.id) await this.api.cameraListen(this.camera) - if (this.wheel.id) await this.api.wheelListen(this.wheel) + async tick() { + if (this.camera?.id) await this.api.cameraListen(this.camera) + if (this.wheel?.id) await this.api.wheelListen(this.wheel) } - async showCameraDialog() { - if (this.camera.id && (await CameraComponent.showAsDialog(this.browserWindow, 'FLAT_WIZARD', this.camera, this.request.capture))) { - this.preference.cameraStartCaptureForFlatWizard(this.camera).set(this.request.capture) + protected async showCameraDialog() { + if (this.camera?.id && (await CameraComponent.showAsDialog(this.browserWindowService, 'FLAT_WIZARD', this.camera, this.request.capture))) { + this.savePreference() } } - async cameraChanged() { - if (this.camera.id) { - await this.ping() + protected async cameraChanged() { + if (this.camera?.id) { + await this.tick() - const cameraPreference = this.preference.cameraPreference(this.camera).get() - this.request.capture = this.preference.cameraStartCaptureForFlatWizard(this.camera).get(cameraPreference) + this.loadPreference() this.updateEntryFromCamera(this.camera) this.request.capture.frameType = 'FLAT' } @@ -180,15 +172,16 @@ export class FlatWizardComponent implements AfterViewInit, OnDestroy, Pingable { private updateEntryFromCamera(camera?: Camera) { if (camera?.connected) { updateCameraStartCaptureFromCamera(this.request.capture, camera) + this.savePreference() } } - async wheelChanged() { - if (this.wheel.id) { - await this.ping() + protected async wheelChanged() { + if (this.wheel?.id) { + await this.tick() - const preference = this.preference.wheelPreference(this.wheel).get() - const filters = makeFilterSlots(this.wheel, this.filters, preference.shutterPosition) + const shutterPosition = this.preferenceService.wheel(this.wheel).get().shutterPosition + const filters = makeFilter(this.wheel, this.filters, shutterPosition) if (filters !== this.filters) { this.filters = filters @@ -197,20 +190,31 @@ export class FlatWizardComponent implements AfterViewInit, OnDestroy, Pingable { } } - async start() { - await this.browserWindow.openCameraImage(this.camera, 'FLAT_WIZARD') - // TODO: Iniciar para cada filtro selecionado. Usar os eventos para percorrer (se houver filtro). - // Se Falhar, interrompe todo o fluxo. - await this.api.flatWizardStart(this.camera, this.request) + protected async start() { + if (this.camera) { + await this.browserWindowService.openCameraImage(this.camera, 'FLAT_WIZARD') + // TODO: Iniciar para cada filtro selecionado. Usar os eventos para percorrer (se houver filtro). + // Se Falhar, interrompe todo o fluxo. + await this.api.flatWizardStart(this.camera, this.request) + } + } + + protected async stop() { + if (this.camera) { + await this.api.flatWizardStop(this.camera) + } } - stop() { - return this.api.flatWizardStop(this.camera) + private loadPreference() { + if (this.camera?.id) { + Object.assign(this.preference, this.preferenceService.flatWizard(this.camera).get()) + this.request = this.preference.request + } } - savePreference() { - if (this.camera.id) { - this.preference.cameraStartCaptureForFlatWizard(this.camera).set(this.request.capture) + protected savePreference() { + if (this.camera?.id) { + this.preferenceService.flatWizard(this.camera).set(this.preference) } } } diff --git a/desktop/src/app/focuser/focuser.component.html b/desktop/src/app/focuser/focuser.component.html index 4ae7750ce..42e26e757 100644 --- a/desktop/src/app/focuser/focuser.component.html +++ b/desktop/src/app/focuser/focuser.component.html @@ -11,7 +11,7 @@
- {{ moving ? 'moving' : 'idle' }} + {{ focuser.moving ? 'moving' : 'idle' }}
- + spinnableNumber /> +
+ spinnableNumber />
-
- +
+ + spinnableNumber /> { + electronService.on('FOCUSER.UPDATED', (event) => { if (event.device.id === this.focuser.id) { ngZone.run(() => { Object.assign(this.focuser, event.device) @@ -40,10 +36,10 @@ export class FocuserComponent implements AfterViewInit, OnDestroy, Pingable { } }) - electron.on('FOCUSER.DETACHED', (event) => { + electronService.on('FOCUSER.DETACHED', (event) => { if (event.device.id === this.focuser.id) { ngZone.run(() => { - Object.assign(this.focuser, EMPTY_FOCUSER) + Object.assign(this.focuser, DEFAULT_FOCUSER) }) } }) @@ -82,43 +78,47 @@ export class FocuserComponent implements AfterViewInit, OnDestroy, Pingable { }) hotkeys('up', (event) => { event.preventDefault() - this.stepsRelative = Math.min(this.focuser.maxPosition, this.stepsRelative + 1) + this.preference.stepsRelative = Math.min(this.focuser.maxPosition, this.preference.stepsRelative + 1) + this.savePreference() }) hotkeys('down', (event) => { event.preventDefault() - this.stepsRelative = Math.max(0, this.stepsRelative - 1) + this.preference.stepsRelative = Math.max(0, this.preference.stepsRelative - 1) + this.savePreference() }) hotkeys('ctrl+up', (event) => { event.preventDefault() - this.stepsAbsolute = Math.max(0, this.stepsAbsolute - 1) + this.preference.stepsAbsolute = Math.max(0, this.preference.stepsAbsolute - 1) + this.savePreference() }) hotkeys('ctrl+down', (event) => { event.preventDefault() - this.stepsAbsolute = Math.min(this.focuser.maxPosition, this.stepsAbsolute + 1) + this.preference.stepsAbsolute = Math.min(this.focuser.maxPosition, this.preference.stepsAbsolute + 1) + this.savePreference() }) } ngAfterViewInit() { this.route.queryParams.subscribe(async (e) => { - const focuser = JSON.parse(decodeURIComponent(e['data'] as string)) as Focuser - await this.focuserChanged(focuser) - this.pinger.register(this, 30000) + const data = JSON.parse(decodeURIComponent(e['data'] as string)) as Focuser + await this.focuserChanged(data) + this.ticker.register(this, 30000) }) } @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) + this.ticker.unregister(this) void this.abort() } - async ping() { + async tick() { if (this.focuser.id) { await this.api.focuserListen(this.focuser) } } - async focuserChanged(focuser?: Focuser) { + protected async focuserChanged(focuser?: Focuser) { if (focuser?.id) { focuser = await this.api.focuser(focuser.id) Object.assign(this.focuser, focuser) @@ -130,7 +130,7 @@ export class FocuserComponent implements AfterViewInit, OnDestroy, Pingable { this.app.subTitle = focuser?.name ?? '' } - connect() { + protected connect() { if (this.focuser.connected) { return this.api.focuserDisconnect(this.focuser) } else { @@ -138,61 +138,46 @@ export class FocuserComponent implements AfterViewInit, OnDestroy, Pingable { } } - async moveIn(stepSize: number = 1) { - if (!this.moving) { - this.moving = true - await this.api.focuserMoveIn(this.focuser, Math.trunc(this.stepsRelative * stepSize)) - this.savePreference() + protected async moveIn(stepSize: number = 1) { + if (!this.focuser.moving && stepSize) { + await this.api.focuserMoveIn(this.focuser, Math.trunc(this.preference.stepsRelative * stepSize)) } } - async moveOut(stepSize: number = 1) { - if (!this.moving) { - this.moving = true - await this.api.focuserMoveOut(this.focuser, Math.trunc(this.stepsRelative * stepSize)) - this.savePreference() + protected async moveOut(stepSize: number = 1) { + if (!this.focuser.moving && stepSize) { + await this.api.focuserMoveOut(this.focuser, Math.trunc(this.preference.stepsRelative * stepSize)) } } - async moveTo() { - if (!this.moving && this.stepsAbsolute !== this.focuser.position) { - this.moving = true - await this.api.focuserMoveTo(this.focuser, this.stepsAbsolute) - this.savePreference() + protected async moveTo() { + if (!this.focuser.moving && this.preference.stepsAbsolute !== this.focuser.position) { + await this.api.focuserMoveTo(this.focuser, this.preference.stepsAbsolute) } } - async sync() { - if (!this.moving) { - await this.api.focuserSync(this.focuser, this.stepsAbsolute) - this.savePreference() + protected async sync() { + if (!this.focuser.moving) { + await this.api.focuserSync(this.focuser, this.preference.stepsAbsolute) } } - abort() { + protected abort() { return this.api.focuserAbort(this.focuser) } - private update() { - if (this.focuser.id) { - this.moving = this.focuser.moving - } - } + private update() {} private loadPreference() { if (this.focuser.id) { - const preference = this.preference.focuserPreference(this.focuser).get() - this.stepsRelative = preference.stepsRelative ?? 100 - this.stepsAbsolute = preference.stepsAbsolute ?? this.focuser.position + Object.assign(this.preference, this.preferenceService.focuser(this.focuser).get()) + this.preference.stepsAbsolute = this.focuser.position } } - private savePreference() { + protected savePreference() { if (this.focuser.connected) { - const preference = this.preference.focuserPreference(this.focuser).get() - preference.stepsAbsolute = this.stepsAbsolute - preference.stepsRelative = this.stepsRelative - this.preference.focuserPreference(this.focuser).set(preference) + this.preferenceService.focuser(this.focuser).set(this.preference) } } } diff --git a/desktop/src/app/framing/framing.component.html b/desktop/src/app/framing/framing.component.html index eb480fb86..8186c7931 100644 --- a/desktop/src/app/framing/framing.component.html +++ b/desktop/src/app/framing/framing.component.html @@ -4,7 +4,8 @@ + [(ngModel)]="preference.rightAscension" + (ngModelChange)="savePreference()" />
@@ -13,7 +14,8 @@ + [(ngModel)]="preference.declination" + (ngModelChange)="savePreference()" />
@@ -25,9 +27,10 @@ class="w-full" [showButtons]="true" styleClass="p-inputtext-sm border-0 w-full" - [(ngModel)]="width" + [(ngModel)]="preference.width" + (ngModelChange)="savePreference()" locale="en" - scrollableNumber /> + spinnableNumber />
@@ -39,9 +42,10 @@ class="w-full" [showButtons]="true" styleClass="p-inputtext-sm border-0 w-full" - [(ngModel)]="height" + [(ngModel)]="preference.height" + (ngModelChange)="savePreference()" locale="en" - scrollableNumber /> + spinnableNumber />
@@ -54,10 +58,11 @@ class="w-full" [showButtons]="true" styleClass="p-inputtext-sm border-0 w-full" - [(ngModel)]="fov" + [(ngModel)]="preference.fov" + (ngModelChange)="savePreference()" locale="en" [minFractionDigits]="1" - scrollableNumber /> + spinnableNumber />
@@ -70,10 +75,11 @@ class="w-full" [showButtons]="true" styleClass="p-inputtext-sm border-0 w-full" - [(ngModel)]="rotation" + [(ngModel)]="preference.rotation" + (ngModelChange)="savePreference()" locale="en" [minFractionDigits]="1" - scrollableNumber /> + spinnableNumber />
@@ -81,7 +87,8 @@ @@ -107,7 +114,7 @@
{ + electronService.on('DATA.CHANGED', (event: LoadFraming) => { return ngZone.run(() => this.frameFromData(event)) }) - - this.loadPreference() } async ngAfterViewInit() { @@ -74,93 +41,69 @@ export class FramingComponent implements AfterViewInit, OnDestroy { try { this.hipsSurveys = await this.api.hipsSurveys() - this.hipsSurvey = this.hipsSurveys.find((e) => e.id === this.hipsSurvey?.id) ?? this.hipsSurveys[0] + this.loadPreference() } finally { this.loading = false } this.route.queryParams.subscribe((e) => { - const data = JSON.parse(decodeURIComponent(e['data'] as string)) as FramingData + const data = JSON.parse(decodeURIComponent(e['data'] as string)) as LoadFraming return this.frameFromData(data) }) } @HostListener('window:unload') ngOnDestroy() { - void this.closeFrameImage() - void this.electron.closeWindow(undefined, this.frameId) + void this.closeImageWindow() } - private async 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 + private async frameFromData(data: LoadFraming) { + this.preference.rightAscension = data.rightAscension || this.preference.rightAscension + this.preference.declination = data.declination || this.preference.declination + this.preference.width = data.width || this.preference.width + this.preference.height = data.height || this.preference.height + this.preference.fov = data.fov || this.preference.fov + if (data.rotation === 0 || data.rotation) this.preference.rotation = data.rotation + + this.savePreference() if (data.rightAscension && data.declination) { await this.frame() } } - async frame() { - if (!this.hipsSurvey) return - - await this.closeFrameImage() + protected async frame() { + if (!this.preference.hipsSurvey) return this.loading = true try { - const path = await this.api.frame(this.rightAscension, this.declination, this.width, this.height, this.fov, this.rotation, this.hipsSurvey) - const title = `Framing ・ ${this.rightAscension} ・ ${this.declination}` - - this.framePath = path - this.frameId = await this.browserWindow.openImage({ path, source: 'FRAMING', id: 'framing', title }) + const { rightAscension, declination, width, height, fov, rotation, hipsSurvey } = this.preference + const path = await this.api.frame(rightAscension, declination, width, height, fov, rotation, hipsSurvey) + const title = `Framing ・ ${rightAscension} ・ ${declination}` - this.savePreference() + this.frameId = await this.browserWindowService.openImage({ path, source: 'FRAMING', id: 'framing', title }) } catch (e) { console.error(e) - this.prime.message('Failed to retrieve the image', 'error') + this.angularService.message('Failed to retrieve the image', 'error') } finally { this.loading = false } } private loadPreference() { - const preference = this.storage.get(FRAMING_KEY, {}) - - 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 - - if (preference.hipsSurvey) { - this.hipsSurveys = [preference.hipsSurvey] - this.hipsSurvey = this.hipsSurveys[0] - } + Object.assign(this.preference, this.preferenceService.framing.get()) + this.preference.hipsSurvey = this.hipsSurveys.find((e) => e.id === this.preference.hipsSurvey?.id) ?? this.hipsSurveys[0] } - private savePreference() { - 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_KEY, preference) + protected savePreference() { + this.preferenceService.framing.set(this.preference) } - private async closeFrameImage() { - if (this.framePath) { - await this.api.closeImage(this.framePath) + private async closeImageWindow() { + if (this.frameId) { + await this.electronService.closeWindow(undefined, this.frameId) } } } diff --git a/desktop/src/app/guider/guider.component.html b/desktop/src/app/guider/guider.component.html index c602ba772..38e80746d 100644 --- a/desktop/src/app/guider/guider.component.html +++ b/desktop/src/app/guider/guider.component.html @@ -9,41 +9,47 @@ + [(ngModel)]="preference.host" + (ngModelChange)="savePreference()" />
+ spinnableNumber />
+ [text]="true" + pTooltip="Disconnect" + tooltipPosition="bottom" /> + [text]="true" + pTooltip="Connect" + tooltipPosition="bottom" />
- {{ guideState | enum | lowercase }} + {{ guider.state | enum | lowercase }} - {{ message }} + {{ guider.message }}
-
+
+ value="{{ chartInfo.rmsRA.toFixed(2) + ' (' + (chartInfo.rmsRA * chartInfo.pixelScale).toFixed(2) + '" )' }}" />
@@ -78,7 +84,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - value="{{ rmsDEC.toFixed(2) + ' (' + (rmsDEC * pixelScale).toFixed(2) + '" )' }}" /> + value="{{ chartInfo.rmsDEC.toFixed(2) + ' (' + (chartInfo.rmsDEC * chartInfo.pixelScale).toFixed(2) + '" )' }}" />
@@ -88,7 +94,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - value="{{ rmsTotal.toFixed(2) + ' (' + (rmsTotal * pixelScale).toFixed(2) + '" )' }}" /> + value="{{ chartInfo.rmsTotal.toFixed(2) + ' (' + (chartInfo.rmsTotal * chartInfo.pixelScale).toFixed(2) + '" )' }}" />
@@ -98,7 +104,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="guideStep?.starMass ?? 0" /> + [value]="guider.step?.starMass ?? 0" />
@@ -108,7 +114,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="guideStep?.hfd ?? 0" /> + [value]="guider.step?.hfd ?? 0" />
@@ -118,7 +124,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="guideStep?.snr ?? 0" /> + [value]="guider.step?.snr ?? 0" />
@@ -126,7 +132,7 @@
-
- +
+ + spinnableNumber />
-
- +
+ + spinnableNumber />
-
- +
+ + spinnableNumber />
@@ -231,7 +237,7 @@ + spinnableNumber />
@@ -284,15 +291,16 @@
+ spinnableNumber />
@@ -300,7 +308,7 @@
+ spinnableNumber />
@@ -393,16 +402,17 @@
+ spinnableNumber />
diff --git a/desktop/src/app/guider/guider.component.scss b/desktop/src/app/guider/guider.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/app/guider/guider.component.ts b/desktop/src/app/guider/guider.component.ts index 96e9d7ebd..f0f13b9bc 100644 --- a/desktop/src/app/guider/guider.component.ts +++ b/desktop/src/app/guider/guider.component.ts @@ -1,71 +1,45 @@ -import { AfterViewInit, Component, HostListener, NgZone, OnDestroy, ViewChild } from '@angular/core' -import { Title } from '@angular/platform-browser' -import { ChartData, ChartOptions } from 'chart.js' +import { AfterViewInit, Component, HostListener, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core' +import { Chart, ChartData, ChartOptions } from 'chart.js' +import zoomPlugin from 'chartjs-plugin-zoom' import { UIChart } from 'primeng/chart' import { ApiService } from '../../shared/services/api.service' import { ElectronService } from '../../shared/services/electron.service' -import { Pingable, Pinger } from '../../shared/services/pinger.service' -import { GuideDirection, GuideOutput, GuideState, GuideStep, Guider, GuiderHistoryStep, GuiderPlotMode, GuiderYAxisUnit } from '../../shared/types/guider.types' - -export interface GuiderPreference { - settleAmount?: number - settleTime?: number - settleTimeout?: number -} +import { PreferenceService } from '../../shared/services/preference.service' +import { Tickable, Ticker } from '../../shared/services/ticker.service' +import { DEFAULT_GUIDER_CHART_INFO, DEFAULT_GUIDER_PHD2, DEFAULT_GUIDER_PREFERENCE, DEFAULT_GUIDER_PULSE, GuideDirection, GuideOutput, Guider, GuiderHistoryStep } from '../../shared/types/guider.types' +import { AppComponent } from '../app.component' @Component({ - selector: 'app-guider', + selector: 'neb-guider', templateUrl: './guider.component.html', - styleUrls: ['./guider.component.scss'], }) -export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { - guideOutputs: GuideOutput[] = [] - guideOutput?: GuideOutput - guideOutputConnected = false - pulseGuiding = false - - guideNorthDuration = 1000 - guideSouthDuration = 1000 - guideWestDuration = 1000 - guideEastDuration = 1000 - - connected = false - host = 'localhost' - port = 4400 - guideState: GuideState = 'STOPPED' - guideStep?: GuideStep - message = '' - - settleAmount = 1.5 - settleTime = 10 - settleTimeout = 30 - readonly phdGuideHistory: GuiderHistoryStep[] = [] - private phdDurationScale = 1.0 - - pixelScale = 1.0 - rmsRA = 0.0 - rmsDEC = 0.0 - rmsTotal = 0.0 - - plotMode: GuiderPlotMode = 'RA/DEC' - yAxisUnit: GuiderYAxisUnit = 'ARCSEC' +export class GuiderComponent implements OnInit, AfterViewInit, OnDestroy, Tickable { + protected guideOutputs: GuideOutput[] = [] + protected guideOutput?: GuideOutput + + protected readonly preference = structuredClone(DEFAULT_GUIDER_PREFERENCE) + protected readonly guider = structuredClone(DEFAULT_GUIDER_PHD2) + protected readonly pulse = structuredClone(DEFAULT_GUIDER_PULSE) + protected readonly chartInfo = structuredClone(DEFAULT_GUIDER_CHART_INFO) + + private readonly guideHistory: GuiderHistoryStep[] = [] @ViewChild('chart') private readonly chart!: UIChart get stopped() { - return this.guideState === 'STOPPED' + return this.guider.state === 'STOPPED' } get looping() { - return this.guideState === 'LOOPING' + return this.guider.state === 'LOOPING' } get guiding() { - return this.guideState === 'GUIDING' + return this.guider.state === 'GUIDING' } - readonly chartData: ChartData = { + protected readonly chartData: ChartData = { labels: Array.from({ length: 100 }, (_, i) => `${i}`), datasets: [ // RA. @@ -103,7 +77,7 @@ export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { ], } - readonly chartOptions: ChartOptions = { + protected readonly chartOptions: ChartOptions = { responsive: true, plugins: { legend: { @@ -123,10 +97,10 @@ export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition const barType = context.dataset.type === 'bar' const raType = context.datasetIndex === 0 || context.datasetIndex === 2 - const scale = barType ? this.phdDurationScale : 1.0 + const scale = barType ? this.chartInfo.durationScale : 1.0 const y = context.parsed.y * scale const prefix = raType ? 'RA: ' : 'DEC: ' - const lineSuffix = this.yAxisUnit === 'ARCSEC' ? '"' : 'px' + const lineSuffix = this.preference.yAxisUnit === 'ARCSEC' ? '"' : 'px' const formattedY = prefix + (barType ? y.toFixed(0) + ' ms' : y.toFixed(2) + lineSuffix) return formattedY }, @@ -221,15 +195,16 @@ export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { } constructor( - title: Title, + app: AppComponent, private readonly api: ApiService, - private readonly pinger: Pinger, - electron: ElectronService, + private readonly ticker: Ticker, + private readonly preferenceService: PreferenceService, + electronService: ElectronService, ngZone: NgZone, ) { - title.setTitle('Guider') + app.title = 'Guider' - electron.on('GUIDE_OUTPUT.UPDATED', (event) => { + electronService.on('GUIDE_OUTPUT.UPDATED', (event) => { if (event.device.id === this.guideOutput?.id) { ngZone.run(() => { if (this.guideOutput) { @@ -240,69 +215,67 @@ export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { } }) - electron.on('GUIDE_OUTPUT.ATTACHED', (event) => { + electronService.on('GUIDE_OUTPUT.ATTACHED', (event) => { ngZone.run(() => { this.guideOutputs.push(event.device) }) }) - electron.on('GUIDE_OUTPUT.DETACHED', (event) => { + electronService.on('GUIDE_OUTPUT.DETACHED', (event) => { ngZone.run(() => { const index = this.guideOutputs.findIndex((e) => e.id === event.device.id) if (index >= 0) this.guideOutputs.splice(index, 1) }) }) - electron.on('GUIDER.CONNECTED', () => { + electronService.on('GUIDER.CONNECTED', () => { ngZone.run(() => { - this.connected = true + this.guider.connected = true }) }) - electron.on('GUIDER.DISCONNECTED', () => { + electronService.on('GUIDER.DISCONNECTED', () => { ngZone.run(() => { - this.connected = false + this.guider.connected = false }) }) - electron.on('GUIDER.UPDATED', (event) => { + electronService.on('GUIDER.UPDATED', (event) => { ngZone.run(() => { this.processGuiderStatus(event.data) }) }) - electron.on('GUIDER.STEPPED', (event) => { + electronService.on('GUIDER.STEPPED', (event) => { ngZone.run(() => { - if (this.phdGuideHistory.length >= 100) { - this.phdGuideHistory.splice(0, this.phdGuideHistory.length - 99) + if (this.guideHistory.length >= 100) { + this.guideHistory.splice(0, this.guideHistory.length - 99) } - this.phdGuideHistory.push(event.data) + this.guideHistory.push(event.data) this.updateGuideHistoryChart() if (event.data.guideStep) { - this.guideStep = event.data.guideStep + this.guider.step = event.data.guideStep } else { // Dithering. } }) }) - electron.on('GUIDER.MESSAGE_RECEIVED', (event) => { + electronService.on('GUIDER.MESSAGE_RECEIVED', (event) => { ngZone.run(() => { - this.message = event.data + this.guider.message = event.data }) }) } - async ngAfterViewInit() { - this.pinger.register(this, 30000) - - const settle = await this.api.getGuidingSettle() + ngOnInit() { + Chart.register(zoomPlugin) + } - this.settleAmount = settle.amount - this.settleTime = settle.time - this.settleTimeout = settle.timeout + async ngAfterViewInit() { + this.ticker.register(this, 30000) this.guideOutputs = await this.api.guideOutputs() @@ -310,46 +283,50 @@ export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { this.processGuiderStatus(status) const history = await this.api.guidingHistory() - this.phdGuideHistory.push(...history) + this.guideHistory.push(...history) this.updateGuideHistoryChart() + + this.loadPreference() } @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) + this.ticker.unregister(this) } - async ping() { + async tick() { if (this.guideOutput?.id) await this.api.guideOutputListen(this.guideOutput) } private processGuiderStatus(event: Guider) { - this.connected = event.connected - this.guideState = event.state - this.pixelScale = event.pixelScale + this.guider.connected = event.connected + this.guider.state = event.state + this.chartInfo.pixelScale = event.pixelScale } - plotModeChanged() { + protected plotModeChanged() { this.updateGuideHistoryChart() + this.savePreference() } - yAxisUnitChanged() { + protected yAxisUnitChanged() { this.updateGuideHistoryChart() + this.savePreference() } private updateGuideHistoryChart() { - if (this.phdGuideHistory.length > 0) { - const history = this.phdGuideHistory[this.phdGuideHistory.length - 1] - this.rmsTotal = history.rmsTotal - this.rmsDEC = history.rmsDEC - this.rmsRA = history.rmsRA + if (this.guideHistory.length > 0) { + const history = this.guideHistory[this.guideHistory.length - 1] + this.chartInfo.rmsTotal = history.rmsTotal + this.chartInfo.rmsDEC = history.rmsDEC + this.chartInfo.rmsRA = history.rmsRA } else { return } - const startId = this.phdGuideHistory[0].id - const guideSteps = this.phdGuideHistory.filter((e) => e.guideStep !== undefined) - const scale = this.yAxisUnit === 'ARCSEC' ? this.pixelScale : 1.0 + const startId = this.guideHistory[0].id + const guideSteps = this.guideHistory.filter((e) => e.guideStep !== undefined) + const scale = this.preference.yAxisUnit === 'ARCSEC' ? this.chartInfo.pixelScale : 1.0 let maxDuration = 0 @@ -358,9 +335,9 @@ export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { maxDuration = Math.max(maxDuration, Math.abs(step.guideStep!.decDuration)) } - this.phdDurationScale = maxDuration / 16.0 + this.chartInfo.durationScale = maxDuration / 16.0 - if (this.plotMode === 'RA/DEC') { + if (this.preference.plotMode === 'RA/DEC') { this.chartData.datasets[0].data = guideSteps.map((e) => [e.id - startId, -e.guideStep!.raDistance * scale]) this.chartData.datasets[1].data = guideSteps.map((e) => [e.id - startId, e.guideStep!.decDistance * scale]) } else { @@ -369,18 +346,18 @@ export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { } const durationScale = (direction?: GuideDirection) => { - return !direction || direction === 'NORTH' || direction === 'WEST' ? this.phdDurationScale : -this.phdDurationScale + return !direction || direction === 'NORTH' || direction === 'WEST' ? this.chartInfo.durationScale : -this.chartInfo.durationScale } - this.chartData.datasets[2].data = this.phdGuideHistory.map((e) => (e.guideStep?.raDuration ?? 0) / durationScale(e.guideStep?.raDirection)) - this.chartData.datasets[3].data = this.phdGuideHistory.map((e) => (e.guideStep?.decDuration ?? 0) / durationScale(e.guideStep?.decDirection)) + this.chartData.datasets[2].data = this.guideHistory.map((e) => (e.guideStep?.raDuration ?? 0) / durationScale(e.guideStep?.raDirection)) + this.chartData.datasets[3].data = this.guideHistory.map((e) => (e.guideStep?.decDuration ?? 0) / durationScale(e.guideStep?.decDirection)) this.chart.refresh() } - async guideOutputChanged() { + protected async guideOutputChanged() { if (this.guideOutput?.id) { - await this.ping() + await this.tick() const guideOutput = await this.api.guideOutput(this.guideOutput.id) Object.assign(this.guideOutput, guideOutput) @@ -389,28 +366,28 @@ export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { } } - async guidePulseStart(...directions: GuideDirection[]) { + protected async guidePulseStart(...directions: GuideDirection[]) { if (this.guideOutput) { for (const direction of directions) { switch (direction) { case 'NORTH': - await this.api.guideOutputPulse(this.guideOutput, direction, this.guideNorthDuration * 1000) + await this.api.guideOutputPulse(this.guideOutput, direction, this.preference.pulseDuration.north * 1000) break case 'SOUTH': - await this.api.guideOutputPulse(this.guideOutput, direction, this.guideSouthDuration * 1000) + await this.api.guideOutputPulse(this.guideOutput, direction, this.preference.pulseDuration.south * 1000) break case 'WEST': - await this.api.guideOutputPulse(this.guideOutput, direction, this.guideWestDuration * 1000) + await this.api.guideOutputPulse(this.guideOutput, direction, this.preference.pulseDuration.west * 1000) break case 'EAST': - await this.api.guideOutputPulse(this.guideOutput, direction, this.guideEastDuration * 1000) + await this.api.guideOutputPulse(this.guideOutput, direction, this.preference.pulseDuration.east * 1000) break } } } } - async guidePulseStop() { + protected async guidePulseStop() { if (this.guideOutput) { await this.api.guideOutputPulse(this.guideOutput, 'NORTH', 0) await this.api.guideOutputPulse(this.guideOutput, 'SOUTH', 0) @@ -419,40 +396,41 @@ export class GuiderComponent implements AfterViewInit, OnDestroy, Pingable { } } - guidingConnect() { - if (this.connected) { + protected guidingConnect() { + if (this.guider.connected) { return this.api.guidingDisconnect() } else { - return this.api.guidingConnect(this.host, this.port) + return this.api.guidingConnect(this.preference.host, this.preference.port) } } - async guidingStart(event: MouseEvent) { + protected async guidingStart(event: MouseEvent) { await this.api.guidingLoop(true) + await this.api.guidingSettle(this.preference.settle) await this.api.guidingStart(event.shiftKey) } - async settleChanged() { - await this.api.setGuidingSettle({ - amount: this.settleAmount, - time: this.settleTime, - timeout: this.settleTimeout, - }) - } - - guidingClearHistory() { - this.phdGuideHistory.length = 0 + protected guidingClearHistory() { + this.guideHistory.length = 0 return this.api.guidingClearHistory() } - guidingStop() { + protected guidingStop() { return this.api.guidingStop() } + private loadPreference() { + Object.assign(this.preference, this.preferenceService.guider.get()) + } + + protected savePreference() { + this.preferenceService.guider.set(this.preference) + } + private update() { - if (this.guideOutput) { - this.guideOutputConnected = this.guideOutput.connected - this.pulseGuiding = this.guideOutput.pulseGuiding + if (this.guideOutput?.id) { + this.pulse.connected = this.guideOutput.connected + this.pulse.pulsing = this.guideOutput.pulseGuiding } } } diff --git a/desktop/src/app/home/home.component.html b/desktop/src/app/home/home.component.html index 3d918cf3f..960215797 100644 --- a/desktop/src/app/home/home.component.html +++ b/desktop/src/app/home/home.component.html @@ -13,7 +13,7 @@
- {{ item?.name }} + {{ connection?.name }}
{{ item.name }} - {{ item.host }}:{{ item.port }} + {{ item.type }} | {{ item.host }}:{{ item.port }}
{{ (item.connectedAt | date: 'yyyy-MM-dd HH:mm:ss') ?? 'never' }} @@ -217,6 +217,15 @@
Auto Focus
+
+ + +
Stacker
+
+
+ +
Calibration
+
+
+
+ @@ -269,11 +287,11 @@
@@ -283,18 +301,16 @@ header="Connection" [modal]="true" [draggable]="false" - [(visible)]="showConnectionDialog" + [(visible)]="connectionDialog.showDialog" [style]="{ width: '90vw' }"> -
+
+ [(ngModel)]="connectionDialog.connection.name" />
@@ -303,7 +319,7 @@ + [(ngModel)]="connectionDialog.connection.host" />
@@ -313,25 +329,24 @@ [showButtons]="true" styleClass="border-0 p-inputtext-sm w-full" placeholder="7624" - [(ngModel)]="newConnection[0].port" + [(ngModel)]="connectionDialog.connection.port" [min]="80" [max]="65535" [format]="false" - scrollableNumber /> + spinnableNumber />
- + (deviceDisconnect)="deviceDisconnected($event)" + [toolbarBuilder]="deviceMenuToolbarBuilder" /> diff --git a/desktop/src/app/home/home.component.scss b/desktop/src/app/home/home.component.scss index f50b9ce83..fac79d445 100644 --- a/desktop/src/app/home/home.component.scss +++ b/desktop/src/app/home/home.component.scss @@ -1,27 +1,22 @@ -:host { - p-splitbutton ::ng-deep { - .p-splitbutton-menubutton { - width: 0rem; - padding: 4px; - } - } - - .buttons p-button ::ng-deep { - display: contents; - - > button { - min-height: 56px; - max-height: 56px; - display: flex; - - img { - height: 32px; +neb-home { + .buttons { + p-button { + display: contents; + + > button { + min-height: 56px; + max-height: 56px; + display: flex; + + img { + height: 32px; + } } - } - &.p-disabled { - img { - filter: grayscale(1); + &.p-disabled { + img { + filter: grayscale(1); + } } } } diff --git a/desktop/src/app/home/home.component.ts b/desktop/src/app/home/home.component.ts index 2b655bac2..5bb19a589 100644 --- a/desktop/src/app/home/home.component.ts +++ b/desktop/src/app/home/home.component.ts @@ -1,62 +1,78 @@ -import { AfterContentInit, Component, NgZone, ViewChild } from '@angular/core' +import { AfterContentInit, Component, NgZone, ViewChild, ViewEncapsulation } from '@angular/core' import { dirname } from 'path' import { DeviceChooserComponent } from '../../shared/components/device-chooser/device-chooser.component' import { DeviceConnectionCommandEvent, DeviceListMenuComponent } from '../../shared/components/device-list-menu/device-list-menu.component' import { MenuItem, SlideMenuItem } from '../../shared/components/menu-item/menu-item.component' +import { AngularService } from '../../shared/services/angular.service' 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 { PrimeService } from '../../shared/services/prime.service' -import { Camera } from '../../shared/types/camera.types' -import { Device } from '../../shared/types/device.types' -import { Focuser } from '../../shared/types/focuser.types' -import { CONNECTION_TYPES, ConnectionDetails, EMPTY_CONNECTION_DETAILS, HomeWindowType } from '../../shared/types/home.types' -import { Mount } from '../../shared/types/mount.types' -import { Rotator } from '../../shared/types/rotator.types' -import { FilterWheel } from '../../shared/types/wheel.types' -import { Undefinable } from '../../shared/utils/types' +import { Camera, isCamera } from '../../shared/types/camera.types' +import { Device, DeviceType } from '../../shared/types/device.types' +import { Focuser, isFocuser } from '../../shared/types/focuser.types' +import { ConnectionDetails, DEFAULT_CONNECTION_DETAILS, DEFAULT_HOME_CONNECTION_DIALOG, DEFAULT_HOME_PREFERENCE, HomeWindowType } from '../../shared/types/home.types' +import { isMount, Mount } from '../../shared/types/mount.types' +import { isRotator, Rotator } from '../../shared/types/rotator.types' +import { isWheel, Wheel } from '../../shared/types/wheel.types' import { AppComponent } from '../app.component' -interface MappedDevice { - CAMERA: Camera - MOUNT: Mount - FOCUSER: Focuser - WHEEL: FilterWheel - ROTATOR: Rotator -} - function scrollPageOf(element: Element) { return parseInt(element.getAttribute('scroll-page') ?? '0') } @Component({ - selector: 'app-home', + selector: 'neb-home', templateUrl: './home.component.html', styleUrls: ['./home.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class HomeComponent implements AfterContentInit { - @ViewChild('deviceMenu') - private readonly deviceMenu!: DeviceListMenuComponent + protected readonly preference = structuredClone(DEFAULT_HOME_PREFERENCE) + protected connection?: ConnectionDetails + protected readonly connectionDialog = structuredClone(DEFAULT_HOME_CONNECTION_DIALOG) - @ViewChild('imageMenu') - private readonly imageMenu!: DeviceListMenuComponent + protected cameras: Camera[] = [] + protected mounts: Mount[] = [] + protected focusers: Focuser[] = [] + protected wheels: Wheel[] = [] + protected rotators: Rotator[] = [] + protected domes: Camera[] = [] + protected switches: Camera[] = [] - readonly connectionTypes = Array.from(CONNECTION_TYPES) - showConnectionDialog = false - connections: ConnectionDetails[] = [] - connection?: ConnectionDetails - newConnection?: [ConnectionDetails, Undefinable] + protected page = 0 - cameras: Camera[] = [] - mounts: Mount[] = [] - focusers: Focuser[] = [] - wheels: FilterWheel[] = [] - rotators: Rotator[] = [] - domes: Camera[] = [] - switches: Camera[] = [] + protected readonly deviceModel: MenuItem[] = [] - currentPage = 0 + protected readonly imageModel: SlideMenuItem[] = [ + { + icon: 'mdi mdi-image-plus', + label: 'Open new image', + slideMenu: [], + command: () => { + return this.openImage() + }, + }, + ] + + protected readonly deviceMenuToolbarBuilder = (device: Device): MenuItem[] => { + if (isCamera(device)) { + return [ + { + icon: 'mdi mdi-image', + label: 'View Image', + command: () => { + return this.browserWindowService.openCameraImage(device) + }, + }, + ] + } else { + return [] + } + } + + @ViewChild('deviceMenu') + private readonly deviceMenu!: DeviceListMenuComponent get connected() { return !!this.connection && this.connection.connected @@ -122,152 +138,109 @@ export class HomeComponent implements AfterContentInit { return this.connection?.type === 'ALPACA' && this.hasDevices } - readonly deviceModel: MenuItem[] = [] - - readonly imageModel: SlideMenuItem[] = [ - { - icon: 'mdi mdi-image-plus', - label: 'Open new image', - slideMenu: [], - command: () => { - return this.openImage(true) - }, - }, - ] + constructor( + app: AppComponent, + private readonly electronService: ElectronService, + private readonly browserWindowService: BrowserWindowService, + private readonly api: ApiService, + private readonly angularService: AngularService, + private readonly preferenceService: PreferenceService, + ngZone: NgZone, + ) { + app.title = 'Nebulosa' - private startListening(type: K, onAdd: (device: MappedDevice[K]) => number, onRemove: (device: MappedDevice[K]) => number, onUpdate: (device: MappedDevice[K]) => void) { - this.electron.on(`${type}.ATTACHED`, (event) => { - this.ngZone.run(() => { - onAdd(event.device as never) + electronService.on('CAMERA.ATTACHED', (event) => { + ngZone.run(() => { + this.deviceAdded(event.device) }) }) - - this.electron.on(`${type}.DETACHED`, (event) => { - this.ngZone.run(() => { - onRemove(event.device as never) + electronService.on(`CAMERA.DETACHED`, (event) => { + ngZone.run(() => { + this.deviceRemoved(event.device) }) }) - - this.electron.on(`${type}.UPDATED`, (event) => { - this.ngZone.run(() => { - onUpdate(event.device as never) + electronService.on(`CAMERA.UPDATED`, (event) => { + ngZone.run(() => { + this.deviceUpdated(event.device) }) }) - } - constructor( - app: AppComponent, - private readonly electron: ElectronService, - private readonly browserWindow: BrowserWindowService, - private readonly api: ApiService, - private readonly prime: PrimeService, - private readonly preference: PreferenceService, - private readonly ngZone: NgZone, - ) { - app.title = 'Nebulosa' - - this.startListening( - 'CAMERA', - (device) => { - return this.cameras.push(device) - }, - (device) => { - const found = this.cameras.findIndex((e) => e.id === device.id) - this.cameras.splice(found, 1) - return this.cameras.length - }, - (device) => { - const found = this.cameras.find((e) => e.id === device.id) - if (!found) return - Object.assign(found, device) - }, - ) - - this.startListening( - 'MOUNT', - (device) => { - return this.mounts.push(device) - }, - (device) => { - const found = this.mounts.findIndex((e) => e.id === device.id) - this.mounts.splice(found, 1) - return this.mounts.length - }, - (device) => { - const found = this.mounts.find((e) => e.id === device.id) - if (!found) return - Object.assign(found, device) - }, - ) + electronService.on('MOUNT.ATTACHED', (event) => { + ngZone.run(() => { + this.deviceAdded(event.device) + }) + }) + electronService.on(`MOUNT.DETACHED`, (event) => { + ngZone.run(() => { + this.deviceRemoved(event.device) + }) + }) + electronService.on(`MOUNT.UPDATED`, (event) => { + ngZone.run(() => { + this.deviceUpdated(event.device) + }) + }) - this.startListening( - 'FOCUSER', - (device) => { - return this.focusers.push(device) - }, - (device) => { - const found = this.focusers.findIndex((e) => e.id === device.id) - this.focusers.splice(found, 1) - return this.focusers.length - }, - (device) => { - const found = this.focusers.find((e) => e.id === device.id) - if (!found) return - Object.assign(found, device) - }, - ) + electronService.on('FOCUSER.ATTACHED', (event) => { + ngZone.run(() => { + this.deviceAdded(event.device) + }) + }) + electronService.on(`FOCUSER.DETACHED`, (event) => { + ngZone.run(() => { + this.deviceRemoved(event.device) + }) + }) + electronService.on(`FOCUSER.UPDATED`, (event) => { + ngZone.run(() => { + this.deviceUpdated(event.device) + }) + }) - this.startListening( - 'WHEEL', - (device) => { - return this.wheels.push(device) - }, - (device) => { - const found = this.wheels.findIndex((e) => e.id === device.id) - this.wheels.splice(found, 1) - return this.wheels.length - }, - (device) => { - const found = this.wheels.find((e) => e.id === device.id) - if (!found) return - Object.assign(found, device) - }, - ) + electronService.on('WHEEL.ATTACHED', (event) => { + ngZone.run(() => { + this.deviceAdded(event.device) + }) + }) + electronService.on(`WHEEL.DETACHED`, (event) => { + ngZone.run(() => { + this.deviceRemoved(event.device) + }) + }) + electronService.on(`WHEEL.UPDATED`, (event) => { + ngZone.run(() => { + this.deviceUpdated(event.device) + }) + }) - this.startListening( - 'ROTATOR', - (device) => { - return this.rotators.push(device) - }, - (device) => { - const found = this.rotators.findIndex((e) => e.id === device.id) - this.rotators.splice(found, 1) - return this.rotators.length - }, - (device) => { - const found = this.rotators.find((e) => e.id === device.id) - if (!found) return - Object.assign(found, device) - }, - ) + electronService.on('ROTATOR.ATTACHED', (event) => { + ngZone.run(() => { + this.deviceAdded(event.device) + }) + }) + electronService.on(`ROTATOR.DETACHED`, (event) => { + ngZone.run(() => { + this.deviceRemoved(event.device) + }) + }) + electronService.on(`ROTATOR.UPDATED`, (event) => { + ngZone.run(() => { + this.deviceUpdated(event.device) + }) + }) - electron.on('CONNECTION.CLOSED', async (event) => { + electronService.on('CONNECTION.CLOSED', async (event) => { if (this.connection?.id === event.id) { await ngZone.run(() => { return this.updateConnection() }) } }) - - this.connections = preference.connections.get().sort((a, b) => (b.connectedAt ?? 0) - (a.connectedAt ?? 0)) - this.connections.forEach((e) => { - e.id = undefined - e.connected = false - }) - this.connection = this.connections[0] } async ngAfterContentInit() { + this.loadPreference() + await this.updateConnection() if (this.connected) { @@ -279,54 +252,103 @@ export class HomeComponent implements AfterContentInit { } } - addConnection() { - this.newConnection = [structuredClone(EMPTY_CONNECTION_DETAILS), undefined] - this.showConnectionDialog = true + private deviceAdded(device: Device) { + if (isCamera(device)) { + this.cameras.push(device) + } else if (isMount(device)) { + this.mounts.push(device) + } else if (isFocuser(device)) { + this.focusers.push(device) + } else if (isWheel(device)) { + this.wheels.push(device) + } else if (isRotator(device)) { + this.rotators.push(device) + } } - editConnection(connection: ConnectionDetails, event: MouseEvent) { - this.newConnection = [structuredClone(connection), connection] - this.showConnectionDialog = true + private deviceRemoved(device: Device) { + if (isCamera(device)) { + const found = this.cameras.findIndex((e) => e.id === device.id) + this.cameras.splice(found, 1) + } else if (isMount(device)) { + const found = this.mounts.findIndex((e) => e.id === device.id) + this.mounts.splice(found, 1) + } else if (isFocuser(device)) { + const found = this.focusers.findIndex((e) => e.id === device.id) + this.focusers.splice(found, 1) + } else if (isWheel(device)) { + const found = this.wheels.findIndex((e) => e.id === device.id) + this.wheels.splice(found, 1) + } else if (isRotator(device)) { + const found = this.rotators.findIndex((e) => e.id === device.id) + this.rotators.splice(found, 1) + } + } + + private deviceUpdated(device: Device) { + if (isCamera(device)) { + const found = this.cameras.find((e) => e.id === device.id) + found && Object.assign(found, device) + } else if (isMount(device)) { + const found = this.mounts.find((e) => e.id === device.id) + found && Object.assign(found, device) + } else if (isFocuser(device)) { + const found = this.focusers.find((e) => e.id === device.id) + found && Object.assign(found, device) + } else if (isWheel(device)) { + const found = this.wheels.find((e) => e.id === device.id) + found && Object.assign(found, device) + } else if (isRotator(device)) { + const found = this.rotators.find((e) => e.id === device.id) + found && Object.assign(found, device) + } + } + + protected addConnection() { + this.connectionDialog.edited = false + this.connectionDialog.connection = structuredClone(DEFAULT_CONNECTION_DETAILS) + this.connectionDialog.showDialog = true + } + + protected editConnection(connection: ConnectionDetails, event: MouseEvent) { + this.connectionDialog.edited = true + this.connectionDialog.connection = connection + this.connectionDialog.showDialog = true event.stopImmediatePropagation() } - deleteConnection(connection: ConnectionDetails, event: MouseEvent) { - const index = this.connections.findIndex((e) => e === connection) + protected deleteConnection(connection: ConnectionDetails, event: MouseEvent) { + const index = this.preference.connections.findIndex((e) => e === connection) if (index >= 0 && !connection.connected) { - this.connections.splice(index, 1) + this.preference.connections.splice(index, 1) + + if (!this.preference.connections.length) { + this.preference.connections.push(structuredClone(DEFAULT_CONNECTION_DETAILS)) + } if (connection === this.connection) { - this.connection = this.connections[0] + this.connection = this.preference.connections[0] } - this.preference.connections.set(this.connections) + this.savePreference() } event.stopImmediatePropagation() } - saveConnection() { - if (this.newConnection) { - // Edit. - if (this.newConnection[1]) { - Object.assign(this.newConnection[1], this.newConnection[0]) - } - // New. - else { - const newConnection = structuredClone(this.newConnection[0]) - this.connections = [...this.connections, newConnection] - this.connection = newConnection - } + protected saveConnection() { + if (!this.connectionDialog.edited) { + this.connection = this.connectionDialog.connection + this.preference.connections.push(this.connection) } - this.preference.connections.set(this.connections) + this.savePreference() - this.newConnection = undefined - this.showConnectionDialog = false + this.connectionDialog.showDialog = false } - async connect() { + protected async connect() { try { if (this.connection && !this.connection.connected) { this.connection.id = await this.api.connect(this.connection.host, this.connection.port, this.connection.type) @@ -334,13 +356,13 @@ export class HomeComponent implements AfterContentInit { } catch (e) { console.error(e) - this.prime.message('Connection failed', 'error') + this.angularService.message('Connection failed', 'error') } finally { await this.updateConnection() } } - async disconnect() { + protected async disconnect() { try { if (this.connection?.id && this.connection.connected) { await this.api.disconnect(this.connection.id) @@ -364,7 +386,7 @@ export class HomeComponent implements AfterContentInit { return DeviceChooserComponent.handleDisconnectDevice(this.api, event.device, event.item) } - private async openDevice(type: K) { + private async openDevice(type: DeviceType) { this.deviceModel.length = 0 const devices: Device[] = @@ -377,54 +399,45 @@ export class HomeComponent implements AfterContentInit { if (devices.length === 0) return - this.deviceMenu.header = type - const device = await this.deviceMenu.show(devices) + const device = await this.deviceMenu.show(devices, undefined, type) if (device && device !== 'NONE') { - await this.openDeviceWindow(type, device as never) + await this.openDeviceWindow(device) } } - private async openDeviceWindow(type: K, device: MappedDevice[K]) { - switch (type) { + private async openDeviceWindow(device: Device) { + switch (device.type) { case 'MOUNT': - await this.browserWindow.openMount(device as Mount, { bringToFront: true }) + await this.browserWindowService.openMount(device as Mount, { bringToFront: true }) break case 'CAMERA': - await this.browserWindow.openCamera(device as Camera, { bringToFront: true }) + await this.browserWindowService.openCamera(device as Camera, { bringToFront: true }) break case 'FOCUSER': - await this.browserWindow.openFocuser(device as Focuser, { bringToFront: true }) + await this.browserWindowService.openFocuser(device as Focuser, { bringToFront: true }) break case 'WHEEL': - await this.browserWindow.openWheel(device as FilterWheel, { bringToFront: true }) + await this.browserWindowService.openWheel(device as Wheel, { bringToFront: true }) break case 'ROTATOR': - await this.browserWindow.openRotator(device as Rotator, { bringToFront: true }) + await this.browserWindowService.openRotator(device as Rotator, { bringToFront: true }) break } } - private async openImage(force: boolean = false) { - if (force || this.cameras.length === 0) { - const preference = this.preference.homePreference.get() - const path = await this.electron.openImage({ defaultPath: preference.imagePath }) + private async openImage() { + const path = await this.electronService.openImage({ defaultPath: this.preference.imagePath }) - if (path) { - preference.imagePath = dirname(path) - this.preference.homePreference.set(preference) - await this.browserWindow.openImage({ path, source: 'PATH' }) - } - } else { - const camera = await this.imageMenu.show(this.cameras) + if (path) { + this.preference.imagePath = dirname(path) + this.savePreference() - if (camera && camera !== 'NONE') { - await this.browserWindow.openCameraImage(camera) - } + await this.browserWindowService.openImage({ path, source: 'PATH' }) } } - async open(type: HomeWindowType) { + protected async open(type: HomeWindowType) { switch (type) { case 'MOUNT': case 'CAMERA': @@ -434,40 +447,46 @@ export class HomeComponent implements AfterContentInit { await this.openDevice(type) break case 'GUIDER': - await this.browserWindow.openGuider({ bringToFront: true }) + await this.browserWindowService.openGuider({ bringToFront: true }) break case 'SKY_ATLAS': - await this.browserWindow.openSkyAtlas(undefined, { bringToFront: true }) + await this.browserWindowService.openSkyAtlas(undefined, { bringToFront: true }) break case 'FRAMING': - await this.browserWindow.openFraming(undefined, { bringToFront: true }) + await this.browserWindowService.openFraming(undefined, { bringToFront: true }) break case 'ALIGNMENT': - await this.browserWindow.openAlignment({ bringToFront: true }) + await this.browserWindowService.openAlignment({ bringToFront: true }) break case 'SEQUENCER': - await this.browserWindow.openSequencer({ bringToFront: true }) + await this.browserWindowService.openSequencer({ bringToFront: true }) break case 'AUTO_FOCUS': - await this.browserWindow.openAutoFocus({ bringToFront: true }) + await this.browserWindowService.openAutoFocus({ bringToFront: true }) break case 'FLAT_WIZARD': - await this.browserWindow.openFlatWizard({ bringToFront: true }) + await this.browserWindowService.openFlatWizard({ bringToFront: true }) + break + case 'STACKER': + await this.browserWindowService.openStacker({ bringToFront: true }) break case 'INDI': - await this.browserWindow.openINDI(undefined, { bringToFront: true }) + await this.browserWindowService.openINDI(undefined, { bringToFront: true }) break case 'IMAGE': await this.openImage() break case 'SETTINGS': - await this.browserWindow.openSettings() + await this.browserWindowService.openSettings() break case 'CALCULATOR': - await this.browserWindow.openCalculator() + await this.browserWindowService.openCalculator() + break + case 'CALIBRATION': + await this.browserWindowService.openCalibration() break case 'ABOUT': - await this.browserWindow.openAbout() + await this.browserWindowService.openAbout() break } } @@ -479,7 +498,7 @@ export class HomeComponent implements AfterContentInit { if (status && !this.connection.connected) { this.connection.connectedAt = Date.now() - this.preference.connections.set(this.connections) + this.savePreference() this.connection.connected = true } else if (!status) { this.connection.connected = false @@ -491,7 +510,7 @@ export class HomeComponent implements AfterContentInit { const statuses = await this.api.connectionStatuses() for (const status of statuses) { - for (const connection of this.connections) { + for (const connection of this.preference.connections) { if (!connection.connected && (status.host === connection.host || status.ip === connection.host) && status.port === connection.port) { connection.id = status.id connection.type = status.type @@ -518,7 +537,7 @@ export class HomeComponent implements AfterContentInit { } } - scrolled(event: Event) { + protected scrolled(event: Event) { function isVisible(element: Element) { const bound = element.getBoundingClientRect() @@ -536,16 +555,18 @@ export class HomeComponent implements AfterContentInit { } } - this.currentPage = page + this.page = page + + event.stopImmediatePropagation() } - scrollTo(event: Event, page: number) { - this.currentPage = page + protected scrollTo(event: Event, page: number) { + this.page = page this.scrollToPage(page) event.stopImmediatePropagation() } - scrollToPage(page: number) { + protected scrollToPage(page: number) { const scrollChidren = document.getElementsByClassName('scroll-child') for (let i = 0; i < scrollChidren.length; i++) { @@ -557,4 +578,25 @@ export class HomeComponent implements AfterContentInit { } } } + + private loadPreference() { + Object.assign(this.preference, this.preferenceService.home.get()) + + this.preference.connections + .sort((a, b) => (b.connectedAt ?? 0) - (a.connectedAt ?? 0)) + .forEach((e) => { + e.id = undefined + e.connected = false + }) + + if (!this.preference.connections.length) { + this.preference.connections.push(structuredClone(DEFAULT_CONNECTION_DETAILS)) + } + + this.connection = this.preference.connections[0] + } + + protected savePreference() { + this.preferenceService.home.set(this.preference) + } } diff --git a/desktop/src/app/image/crosshair.component.ts b/desktop/src/app/image/crosshair.component.ts new file mode 100644 index 000000000..22f9e44cf --- /dev/null +++ b/desktop/src/app/image/crosshair.component.ts @@ -0,0 +1,53 @@ +import { Component, ViewEncapsulation } from '@angular/core' + +@Component({ + selector: 'neb-crosshair', + template: ` + + + + + + + + `, + styles: ` + :host { + width: 100%; + height: 100%; + pointer-events: none; + } + `, + encapsulation: ViewEncapsulation.None, +}) +export class CrossHairComponent {} diff --git a/desktop/src/app/image/image.component.html b/desktop/src/app/image/image.component.html index 9142038e3..3bec82bfe 100644 --- a/desktop/src/app/image/image.component.html +++ b/desktop/src/app/image/image.component.html @@ -1,52 +1,25 @@ -
+ + {{ zoom.scale.toFixed(1) }}x + + +
- - - - - - - + + (click)="drawDetectedStar(s)">
+
- X: {{ imageROI.x }} Y: {{ imageROI.y }} W: {{ imageROI.width }} H: {{ imageROI.height }} + X: {{ imageROI.x.toFixed(0) }} Y: {{ imageROI.y.toFixed(0) }} W: {{ imageROI.width.toFixed(0) }} H: {{ imageROI.height.toFixed(0) }}
+ [model]="contextMenuModel"> @@ -159,7 +142,9 @@
Stars & DSOs
@@ -167,8 +152,9 @@
@@ -190,7 +176,9 @@
Minor Planets
@@ -199,27 +187,35 @@
+ spinnableNumber />
+ @if (annotation.request.minorPlanetsMagLimit >= 20 || annotation.request.includeMinorPlanetsWithoutMagnitude) { +
+ + Can take a long time +
+ }

Minor planets Annotation uses the @@ -247,17 +243,17 @@

- +
@@ -267,7 +263,7 @@ @@ -277,55 +273,55 @@
+ *ngIf="astronomicalObject.info.constellation">
+ *ngIf="astronomicalObject.info.magnitude">
+ *ngIf="astronomicalObject.info.type">
+ *ngIf="astronomicalObject.info.distance"> @@ -333,10 +329,10 @@ @@ -344,32 +340,32 @@
- + + [(ngModel)]="solver.request.blind" />
@@ -410,9 +406,9 @@ + [(ngModel)]="solver.request.centerRA" />
@@ -420,9 +416,9 @@ + [(ngModel)]="solver.request.centerDEC" />
@@ -431,17 +427,17 @@
+ [(ngModel)]="solver.request.radius" + spinnableNumber />
- @if (solver.type === 'SIRIL') { + @if (solver.request.type === 'SIRIL' || solver.request.type === 'PIXINSIGHT') {
+ spinnableNumber />
@@ -464,10 +460,10 @@ [step]="0.01" styleClass="p-inputtext-sm border-0 w-full" [showButtons]="true" - [(ngModel)]="solver.pixelSize" + [(ngModel)]="solver.request.pixelSize" [allowEmpty]="false" locale="en" - scrollableNumber /> + spinnableNumber />
@@ -572,11 +568,19 @@
+ @@ -598,9 +602,9 @@ [max]="65536" [showButtons]="true" styleClass="p-inputtext-sm border-0 w-full" - [(ngModel)]="stretchShadow" + [(ngModel)]="stretch.transformation.shadow" locale="en" - scrollableNumber /> + spinnableNumber /> @@ -609,9 +613,9 @@ [max]="65536" [showButtons]="true" styleClass="p-inputtext-sm border-0 w-full" - [(ngModel)]="stretchHighlight" + [(ngModel)]="stretch.transformation.highlight" locale="en" - scrollableNumber /> + spinnableNumber />
@@ -620,8 +624,8 @@ class="mt-3 px-2" [min]="0" [max]="65536" - [ngModel]="stretchShadowAndHighlight()" - (ngModelChange)="stretchShadow.set($event[0]); stretchHighlight.set($event[1])" + [ngModel]="[stretch.transformation.shadow, stretch.transformation.highlight]" + (ngModelChange)="stretch.transformation.shadow = $event[0]; stretch.transformation.highlight = $event[1]" [range]="true" />
@@ -632,9 +636,9 @@ [max]="65536" [showButtons]="true" styleClass="p-inputtext-sm border-0 w-full" - [(ngModel)]="stretchMidtone" + [(ngModel)]="stretch.transformation.midtone" locale="en" - scrollableNumber /> + spinnableNumber />
@@ -643,7 +647,7 @@ class="mt-3 px-2" [min]="0" [max]="65536" - [(ngModel)]="stretchMidtone" /> + [(ngModel)]="stretch.transformation.midtone" />
@@ -682,18 +686,19 @@
+ spinnableNumber />
@@ -734,13 +739,13 @@
@@ -765,7 +770,7 @@ @@ -778,7 +783,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="imageInfo.statistics.count" /> + [value]="statistics.statistics.count" />
@@ -788,7 +793,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="(imageInfo.statistics.mean * statisticsBitLength.rangeMax).toFixed(8)" /> + [value]="(statistics.statistics.mean * statistics.bitOption.rangeMax).toFixed(8)" />
@@ -798,7 +803,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="(imageInfo.statistics.median * statisticsBitLength.rangeMax).toFixed(8)" /> + [value]="(statistics.statistics.median * statistics.bitOption.rangeMax).toFixed(8)" />
@@ -808,7 +813,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="(imageInfo.statistics.variance * statisticsBitLength.rangeMax * statisticsBitLength.rangeMax).toFixed(8)" /> + [value]="(statistics.statistics.variance * statistics.bitOption.rangeMax * statistics.bitOption.rangeMax).toFixed(8)" />
@@ -818,7 +823,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="(imageInfo.statistics.avgDev * statisticsBitLength.rangeMax).toFixed(8)" /> + [value]="(statistics.statistics.avgDev * statistics.bitOption.rangeMax).toFixed(8)" />
@@ -828,7 +833,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="(imageInfo.statistics.stdDev * statisticsBitLength.rangeMax).toFixed(8)" /> + [value]="(statistics.statistics.stdDev * statistics.bitOption.rangeMax).toFixed(8)" />
@@ -838,7 +843,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="(imageInfo.statistics.minimum * statisticsBitLength.rangeMax).toFixed(8)" /> + [value]="(statistics.statistics.minimum * statistics.bitOption.rangeMax).toFixed(8)" />
@@ -848,20 +853,20 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="(imageInfo.statistics.maximum * statisticsBitLength.rangeMax).toFixed(8)" /> + [value]="(statistics.statistics.maximum * statistics.bitOption.rangeMax).toFixed(8)" />
+ (ngModelChange)="savePreference()" />
@@ -877,7 +882,7 @@ @@ -886,7 +891,7 @@
+ *ngIf="starDetector.request.type !== 'SIRIL'"> + spinnableNumber />
+ *ngIf="starDetector.request.type === 'SIRIL'"> + spinnableNumber />
@@ -940,7 +945,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="starDetection.stars.length" /> + [value]="starDetector.stars.length" />
@@ -950,7 +955,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - value="{{ starDetection.computed.hfd.toFixed(2) }} | {{ starDetection.computed.stdDev.toFixed(4) }}" /> + value="{{ starDetector.computed.hfd.toFixed(2) }} | {{ starDetector.computed.stdDev.toFixed(4) }}" />
@@ -960,7 +965,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="starDetection.computed.snr.toFixed(1)" /> + [value]="starDetector.computed.snr.toFixed(1)" />
@@ -970,7 +975,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - value="{{ starDetection.computed.fluxMin.toFixed(0) }} | {{ starDetection.computed.fluxMax.toFixed(0) }}" /> + value="{{ starDetector.computed.fluxMin.toFixed(0) }} | {{ starDetector.computed.fluxMax.toFixed(0) }}" />
@@ -990,7 +995,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - value="{{ starDetection.selected.x.toFixed(0) }} | {{ starDetection.selected.y.toFixed(0) }}" /> + value="{{ starDetector.selected.x.toFixed(0) }} | {{ starDetector.selected.y.toFixed(0) }}" />
@@ -1000,7 +1005,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="starDetection.selected.flux.toFixed(0)" /> + [value]="starDetector.selected.flux.toFixed(0)" />
@@ -1010,7 +1015,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="starDetection.selected.hfd.toFixed(2)" /> + [value]="starDetector.selected.hfd.toFixed(2)" />
@@ -1020,7 +1025,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="starDetection.selected.snr.toFixed(1)" /> + [value]="starDetector.selected.snr.toFixed(1)" /> @@ -1029,7 +1034,7 @@
+ [(ngModel)]="fov.selected.focalLength" + (ngModelChange)="saveFOV()" + spinnableNumber />
@@ -1073,8 +1081,9 @@ + [(ngModel)]="fov.selected.aperture" + (ngModelChange)="saveFOV()" + spinnableNumber /> @@ -1083,7 +1092,7 @@ @@ -1095,8 +1104,9 @@ [showButtons]="true" [min]="1" [max]="9999" - [(ngModel)]="fov.cameraSize.width" - scrollableNumber /> + [(ngModel)]="fov.selected.cameraSize.width" + (ngModelChange)="saveFOV()" + spinnableNumber /> @@ -1107,8 +1117,9 @@ [showButtons]="true" [min]="1" [max]="9999" - [(ngModel)]="fov.cameraSize.height" - scrollableNumber /> + [(ngModel)]="fov.selected.cameraSize.height" + (ngModelChange)="saveFOV()" + spinnableNumber /> @@ -1122,9 +1133,10 @@ [step]="0.01" [minFractionDigits]="0" [maxFractionDigits]="2" - [(ngModel)]="fov.pixelSize.width" + [(ngModel)]="fov.selected.pixelSize.width" + (ngModelChange)="saveFOV()" locale="en" - scrollableNumber /> + spinnableNumber /> @@ -1138,13 +1150,14 @@ [step]="0.01" [minFractionDigits]="0" [maxFractionDigits]="2" - [(ngModel)]="fov.pixelSize.height" + [(ngModel)]="fov.selected.pixelSize.height" + (ngModelChange)="saveFOV()" locale="en" - scrollableNumber /> + spinnableNumber /> -
+
+ spinnableNumber />
@@ -1167,8 +1181,9 @@ [showButtons]="true" [min]="1" [max]="5" - [(ngModel)]="fov.bin" - scrollableNumber /> + [(ngModel)]="fov.selected.bin" + (ngModelChange)="saveFOV()" + spinnableNumber />
@@ -1183,97 +1198,81 @@ [minFractionDigits]="0" [maxFractionDigits]="2" [(ngModel)]="fov.rotation" + (ngModelChange)="saveFOV(false)" locale="en" - scrollableNumber /> + spinnableNumber /> --> -
- - +
+ style="max-height: 112px"> @for (item of fov.fovs; track $index) {
+ class="flex align-items-center gap-2 border-left-3 p-2 border-round cursor-pointer" + [class.bg-blue-900]="fov.selected === item" + [style.border-color]="item.color" + (click)="selectFOV(item)">
+ [(ngModel)]="item.enabled" + (onChange)="$event.originalEvent?.stopImmediatePropagation()" />
-
+
+ value="FL: {{ item.focalLength }} mm" /> + value="AP: {{ item.aperture }} mm" /> + value="RES: {{ item.cameraSize.width }}x{{ item.cameraSize.height }}" /> + value="PS: {{ item.pixelSize.width }}x{{ item.pixelSize.height }} µm" /> + value="MULT: {{ item.barlowReducer.toFixed(2) }}x" /> + value="BIN: {{ item.bin }}" /> + value="ANGLE: {{ item.rotation }}°" /> @if (item.computed) { + value="F/{{ item.computed.focalRatio.toFixed(1) }}" /> + value="SCALE: {{ item.computed.cameraResolution.width.toFixed(2) }}"x{{ item.computed.cameraResolution.height.toFixed(2) }}"" /> + value="FOV: {{ item.computed.fieldSize.width.toFixed(2) }}°x{{ item.computed.fieldSize.height.toFixed(2) }}°" /> }
- + pTooltip="Remove" + tooltipPosition="bottom" + (onClick)="deleteFOV(item); $event.stopImmediatePropagation()" />
} @@ -1425,6 +1424,28 @@ + +
+
+ + +
+
+
+
diff --git a/desktop/src/app/image/image.component.scss b/desktop/src/app/image/image.component.scss index 43a03b000..affaac825 100644 --- a/desktop/src/app/image/image.component.scss +++ b/desktop/src/app/image/image.component.scss @@ -15,35 +15,33 @@ image-rendering: pixelated; border-radius: 4px; } -} -.roi { - width: 128px; - height: 128px; - border: 1px dashed rgba(255, 255, 0, 0.9); - background-color: transparent; - box-sizing: border-box; -} + .roi { + width: 128px; + height: 128px; + box-sizing: border-box; + } -.roi-coordinates { - background: rgba(0, 0, 0, 0.5); - padding: 4px 8px; - border-radius: 2px; - font-size: 12px !important; - min-width: 71px; - top: 46px; - left: 50%; - white-space: nowrap; - transform: translate(-50%, 0%); -} + .roi-coordinates { + background: rgba(0, 0, 0, 0.5); + padding: 4px 8px; + border-radius: 2px; + font-size: 12px !important; + min-width: 71px; + top: 46px; + left: 50%; + white-space: nowrap; + transform: translate(-50%, 0%); + } -.coordinates { - bottom: 8px; - left: 50%; - padding: 8px; - border-radius: 2px; - background: rgba(0, 0, 0, 0.65); - border: 1px solid rgba(255, 255, 255, 0.15); - width: 260px; - transform: translate(-50%, 0px); + .coordinates { + bottom: 8px; + left: 50%; + padding: 8px; + border-radius: 2px; + background: rgba(0, 0, 0, 0.65); + border: 1px solid rgba(255, 255, 255, 0.15); + width: 260px; + transform: translate(-50%, 0px); + } } diff --git a/desktop/src/app/image/image.component.ts b/desktop/src/app/image/image.component.ts index 4ab3031b9..09004ae16 100644 --- a/desktop/src/app/image/image.component.ts +++ b/desktop/src/app/image/image.component.ts @@ -1,240 +1,119 @@ -import { AfterViewInit, Component, ElementRef, HostListener, NgZone, OnDestroy, ViewChild, computed, model } from '@angular/core' +import { AfterViewInit, Component, ElementRef, HostListener, NgZone, OnDestroy, ViewChild } from '@angular/core' import { ActivatedRoute } from '@angular/router' -import { DragEvent, Interactable, ResizeEvent } from '@interactjs/types/index' import hotkeys from 'hotkeys-js' -import interact from 'interactjs' -import createPanZoom, { PanZoom } from 'panzoom' +import { NgxLegacyMoveableComponent, OnDrag, OnResize, OnRotate } from 'ngx-moveable' +import createPanZoom from 'panzoom' import { basename, dirname, extname } from 'path' import { ContextMenu } from 'primeng/contextmenu' import { DeviceListMenuComponent } from '../../shared/components/device-list-menu/device-list-menu.component' import { HistogramComponent } from '../../shared/components/histogram/histogram.component' import { MenuItem } from '../../shared/components/menu-item/menu-item.component' import { SEPARATOR_MENU_ITEM } from '../../shared/constants' +import { AngularService } from '../../shared/services/angular.service' 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 { PrimeService } from '../../shared/services/prime.service' -import { Angle, EquatorialCoordinateJ2000 } from '../../shared/types/atlas.types' +import { EquatorialCoordinateJ2000 } from '../../shared/types/atlas.types' import { Camera } from '../../shared/types/camera.types' import { - AnnotationInfoDialog, + AstronomicalObjectDialog, DEFAULT_FOV, + DEFAULT_IMAGE_ANNOTATION_DIALOG, + DEFAULT_IMAGE_CALIBRATION, + DEFAULT_IMAGE_DATA, + DEFAULT_IMAGE_FOV_DIALOG, + DEFAULT_IMAGE_LIVE_STACKING, + DEFAULT_IMAGE_MOUSE_COORDINATES, + DEFAULT_IMAGE_MOUSE_POSITION, + DEFAULT_IMAGE_PREFERENCE, + DEFAULT_IMAGE_ROI, + DEFAULT_IMAGE_SAVE_DIALOG, + DEFAULT_IMAGE_SETTINGS_DIALOG, + DEFAULT_IMAGE_SOLVED, + DEFAULT_IMAGE_SOLVER_DIALOG, + DEFAULT_IMAGE_STATISTICS_DIALOG, + DEFAULT_IMAGE_ZOOM, + DEFAULT_STAR_DETECTOR_DIALOG, DetectedStar, - EMPTY_IMAGE_SOLVED, - FITSHeaderItem, FOV, - IMAGE_STATISTICS_BIT_OPTIONS, ImageAnnotation, - ImageAnnotationDialog, - ImageChannel, - ImageData, - ImageFITSHeadersDialog, - ImageFOVDialog, + imageFormatFromExtension, + ImageHeaderItem, + ImageHeadersDialog, ImageInfo, - ImageROI, ImageSCNRDialog, - ImageSaveDialog, ImageSolved, - ImageSolverDialog, - ImageStatisticsBitOption, ImageStretchDialog, - ImageTransformation, LiveStackingMode, OpenImage, - StarDetectionDialog, } from '../../shared/types/image.types' import { Mount } from '../../shared/types/mount.types' -import { CoordinateInterpolator, InterpolatedCoordinate } from '../../shared/utils/coordinate-interpolation' +import { PlateSolverRequest } from '../../shared/types/platesolver.types' +import { StarDetectionRequest } from '../../shared/types/stardetector.types' +import { CoordinateInterpolator } from '../../shared/utils/coordinate-interpolation' import { AppComponent } from '../app.component' @Component({ - selector: 'app-image', + selector: 'neb-image', templateUrl: './image.component.html', styleUrls: ['./image.component.scss'], }) export class ImageComponent implements AfterViewInit, OnDestroy { - @ViewChild('image') - private readonly image!: ElementRef - - @ViewChild('roi') - private readonly roi!: ElementRef - - @ViewChild('menu') - private readonly menu!: ContextMenu - - @ViewChild('deviceMenu') - private readonly deviceMenu!: DeviceListMenuComponent - - @ViewChild('histogram') - private readonly histogram?: HistogramComponent - - @ViewChild('detectedStarCanvas') - private readonly detectedStarCanvas!: ElementRef - - imageInfo?: ImageInfo - private imageURL!: string - imageData: ImageData = {} - liveStackingMode: LiveStackingMode = 'NONE' - - readonly scnrChannels: { name: string; value?: ImageChannel }[] = [ - { name: 'None', value: undefined }, - { name: 'Red', value: 'RED' }, - { name: 'Green', value: 'GREEN' }, - { name: 'Blue', value: 'BLUE' }, - ] - readonly scnr: ImageSCNRDialog = { - showDialog: false, - amount: 0.5, - method: 'AVERAGE_NEUTRAL', - } - - readonly stretch: ImageStretchDialog = { + protected readonly preference = structuredClone(DEFAULT_IMAGE_PREFERENCE) + protected readonly solver = structuredClone(DEFAULT_IMAGE_SOLVER_DIALOG) + protected readonly starDetector = structuredClone(DEFAULT_STAR_DETECTOR_DIALOG) + protected transformation = this.preference.transformation + protected readonly fov = structuredClone(DEFAULT_IMAGE_FOV_DIALOG) + protected readonly annotation = structuredClone(DEFAULT_IMAGE_ANNOTATION_DIALOG) + protected readonly imageROI = structuredClone(DEFAULT_IMAGE_ROI) + protected readonly saveAs = structuredClone(DEFAULT_IMAGE_SAVE_DIALOG) + protected readonly statistics = structuredClone(DEFAULT_IMAGE_STATISTICS_DIALOG) + protected readonly mouseCoordinate = structuredClone(DEFAULT_IMAGE_MOUSE_COORDINATES) + protected readonly liveStacking = structuredClone(DEFAULT_IMAGE_LIVE_STACKING) + protected readonly zoom = structuredClone(DEFAULT_IMAGE_ZOOM) + protected readonly settings = structuredClone(DEFAULT_IMAGE_SETTINGS_DIALOG) + private readonly calibration = structuredClone(DEFAULT_IMAGE_CALIBRATION) + private readonly mouseMountCoordinate = structuredClone(DEFAULT_IMAGE_MOUSE_POSITION) + private readonly imageData = structuredClone(DEFAULT_IMAGE_DATA) + + protected readonly stretch: ImageStretchDialog = { showDialog: false, - auto: true, - shadow: 0, - highlight: 1, - midtone: 0.5, - } - - readonly stretchShadow = model(0) - readonly stretchHighlight = model(65536) - readonly stretchMidtone = model(32768) - readonly stretchShadowAndHighlight = computed(() => [this.stretchShadow(), this.stretchHighlight()]) - - readonly transformation: ImageTransformation = { - force: false, - debayer: true, - stretch: this.stretch, - mirrorHorizontal: false, - mirrorVertical: false, - invert: false, - scnr: this.scnr, + transformation: this.transformation.stretch, } - calibrationViaCamera = true - - readonly annotation: ImageAnnotationDialog = { + protected readonly scnr: ImageSCNRDialog = { showDialog: false, - running: false, - visible: false, - useStarsAndDSOs: true, - useMinorPlanets: false, - minorPlanetsMagLimit: 18.0, - includeMinorPlanetsWithoutMagnitude: true, - useSimbad: false, - data: [], + transformation: this.transformation.scnr, } - readonly annotationInfo: AnnotationInfoDialog = { + protected readonly astronomicalObject: AstronomicalObjectDialog = { showDialog: false, } - readonly starDetection: StarDetectionDialog = { - showDialog: false, - running: false, - type: 'ASTAP', - minSNR: 0, - maxStars: 0, - visible: false, - stars: [], - computed: { - hfd: 0, - snr: 0, - stdDev: 0, - fluxMax: 0, - fluxMin: 0, - }, - selected: { - x: 0, - y: 0, - snr: 0, - hfd: 0, - flux: 0, - }, - } - - readonly solver: ImageSolverDialog = { - showDialog: false, - running: false, - type: 'ASTAP', - blind: true, - centerRA: '', - centerDEC: '', - radius: 4, - focalLength: 0, - pixelSize: 0, - solved: structuredClone(EMPTY_IMAGE_SOLVED), - } - - crossHair = false - - readonly fitsHeaders: ImageFITSHeadersDialog = { + protected readonly headers: ImageHeadersDialog = { showDialog: false, headers: [], } - showStatisticsDialog = false - - readonly statisticsBitOptions: ImageStatisticsBitOption[] = IMAGE_STATISTICS_BIT_OPTIONS - statisticsBitLength = this.statisticsBitOptions[0] - - readonly fov: ImageFOVDialog = { - ...structuredClone(DEFAULT_FOV), - showDialog: false, - fovs: [], - showCameraDialog: false, - cameras: [], - showTelescopeDialog: false, - telescopes: [], - } - - get canAddFOV() { - return this.fov.aperture && this.fov.focalLength && this.fov.cameraSize.width && this.fov.cameraSize.height && this.fov.pixelSize.width && this.fov.pixelSize.height && this.fov.bin - } - - private panZoom?: PanZoom - private imageMouseX = 0 - private imageMouseY = 0 - - roiInteractable?: Interactable - readonly imageROI: ImageROI = { - x: 0, - y: 0, - width: 128, - height: 128, - } - - readonly saveAs: ImageSaveDialog = { - showDialog: false, - format: 'FITS', - bitpix: 'BYTE', - path: '', - shouldBeTransformed: true, - transformation: this.transformation, - } + protected imageInfo?: ImageInfo private readonly saveAsMenuItem: MenuItem = { label: 'Save as...', icon: 'mdi mdi-content-save', command: async () => { - const preference = this.preference.imagePreference.get() - - const path = await this.electron.saveImage({ defaultPath: preference.savePath }) + const path = await this.electronService.saveImage({ defaultPath: this.preference.savePath }) if (path) { const extension = extname(path).toLowerCase() - this.saveAs.format = - extension === '.xisf' ? 'XISF' - : extension === '.png' ? 'PNG' - : extension === '.jpg' ? 'JPG' - : 'FITS' + this.saveAs.format = imageFormatFromExtension(extension) this.saveAs.bitpix = this.imageInfo?.bitpix ?? 'BYTE' this.saveAs.path = path - this.saveAs.showDialog = true - preference.savePath = dirname(path) - this.preference.imagePreference.set(preference) + this.preference.savePath = dirname(path) + this.savePreference() + + this.saveAs.showDialog = true } }, } @@ -280,7 +159,8 @@ export class ImageComponent implements AfterViewInit, OnDestroy { command: () => { this.transformation.mirrorHorizontal = !this.transformation.mirrorHorizontal this.horizontalMirrorMenuItem.selected = this.transformation.mirrorHorizontal - void this.loadImage() + this.savePreference() + return this.loadImage() }, } @@ -291,7 +171,8 @@ export class ImageComponent implements AfterViewInit, OnDestroy { command: () => { this.transformation.mirrorVertical = !this.transformation.mirrorVertical this.verticalMirrorMenuItem.selected = this.transformation.mirrorVertical - void this.loadImage() + this.savePreference() + return this.loadImage() }, } @@ -314,7 +195,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { icon: 'mdi mdi-chart-histogram', label: 'Statistics', command: () => { - this.showStatisticsDialog = true + this.statistics.showDialog = true return this.computeHistogram() }, } @@ -323,7 +204,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { icon: 'mdi mdi-list-box', label: 'FITS Header', command: () => { - this.fitsHeaders.showDialog = true + this.headers.showDialog = true }, } @@ -336,7 +217,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { if (path) { void this.executeMount((mount) => { - return this.api.pointMountHere(mount, path, this.imageMouseX, this.imageMouseY) + return this.api.pointMountHere(mount, path, this.mouseMountCoordinate) }) } }, @@ -347,7 +228,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { icon: 'mdi mdi-image', disabled: true, command: () => { - const coordinate = this.mouseCoordinateInterpolation?.interpolate(this.imageMouseX, this.imageMouseY, false, false) + const coordinate = this.mouseCoordinate.interpolator?.interpolate(this.mouseMountCoordinate.x, this.mouseMountCoordinate.y, false, false) if (coordinate) { void this.frame(coordinate) @@ -374,7 +255,6 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.annotation.showDialog = true }, check: (event) => { - event.originalEvent?.stopImmediatePropagation() this.annotation.visible = !!event.checked }, } @@ -386,11 +266,10 @@ export class ImageComponent implements AfterViewInit, OnDestroy { checkable: false, selected: false, command: () => { - this.starDetection.showDialog = true + this.starDetector.showDialog = true }, check: (event) => { - this.starDetection.visible = !!event.checked - event.originalEvent?.stopImmediatePropagation() + this.starDetector.visible = !!event.checked }, } @@ -399,49 +278,8 @@ export class ImageComponent implements AfterViewInit, OnDestroy { icon: 'mdi mdi-select', selected: false, command: () => { - if (this.roiInteractable) { - this.roiInteractable.unset() - this.roiInteractable = undefined - } else { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call - this.roiInteractable = interact(this.roi.nativeElement) - .origin({ x: 0, y: 0 }) - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - .resizable({ - edges: { left: true, right: true, bottom: true, top: true }, - inertia: true, - listeners: { - move: (event: ResizeEvent) => { - this.roiResizableMove(event) - }, - }, - modifiers: [ - interact.modifiers.restrictEdges({ - outer: 'parent', - }), - interact.modifiers.restrictSize({ - min: { width: 8, height: 8 }, - }), - ], - }) - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - .draggable({ - listeners: { - move: (event: DragEvent) => { - this.roiDraggableMove(event) - }, - }, - inertia: true, - modifiers: [ - interact.modifiers.restrictRect({ - restriction: 'parent', - endOnly: true, - }), - ], - }) - } - - this.roiMenuItem.selected = !!this.roiInteractable + this.imageROI.show = !this.imageROI.show + this.roiMenuItem.selected = this.imageROI.show }, } @@ -463,7 +301,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { items: [this.crosshairMenuItem, this.annotationMenuItem, this.detectStarsMenuItem, this.roiMenuItem, this.fovMenuItem], } - readonly contextMenuItems = [ + protected readonly contextMenuModel = [ this.saveAsMenuItem, SEPARATOR_MENU_ITEM, this.plateSolveMenuItem, @@ -484,25 +322,6 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.frameAtThisCoordinateMenuItem, ] - mouseCoordinate?: InterpolatedCoordinate & Partial<{ x: number; y: number }> - private mouseCoordinateInterpolation?: CoordinateInterpolator - - get isMouseCoordinateVisible() { - return !!this.mouseCoordinate && !this.transformation.mirrorHorizontal && !this.transformation.mirrorVertical - } - - get imagePath() { - if (this.liveStackingMode === 'NONE' || this.liveStackingMode === 'RAW' || !this.imageData.liveStackedPath) { - return this.imageData.path - } else { - return this.imageData.liveStackedPath - } - } - - get canPlateSolve() { - return this.solver.type !== 'SIRIL' || (this.solver.focalLength > 0 && this.solver.pixelSize > 0) - } - private readonly liveStackingMenuItem: MenuItem = { label: 'RAW', icon: 'mdi mdi-image-multiple', @@ -524,15 +343,57 @@ export class ImageComponent implements AfterViewInit, OnDestroy { ], } + @ViewChild('image') + private readonly image!: ElementRef + + @ViewChild('roi') + private readonly roi!: ElementRef + + @ViewChild('menu') + private readonly menu!: ContextMenu + + @ViewChild('deviceMenu') + private readonly deviceMenu!: DeviceListMenuComponent + + @ViewChild('histogram') + private readonly histogram?: HistogramComponent + + @ViewChild('detectedStarCanvas') + private readonly detectedStarCanvas!: ElementRef + + @ViewChild('moveable') + private readonly moveable!: NgxLegacyMoveableComponent + + get isMouseCoordinateVisible() { + return this.mouseCoordinate.show && !!this.mouseCoordinate.interpolator && !this.transformation.mirrorHorizontal && !this.transformation.mirrorVertical + } + + get imagePath() { + if (this.liveStacking.mode === 'NONE' || this.liveStacking.mode === 'RAW' || !this.liveStacking.path) { + return this.imageData.path + } else { + return this.liveStacking.path + } + } + + get canPlateSolve() { + return (this.solver.request.type !== 'SIRIL' && this.solver.request.type !== 'PIXINSIGHT') || (this.solver.request.focalLength > 0 && this.solver.request.pixelSize > 0) + } + + get canAddFOV() { + const fov = this.fov.selected + return fov.aperture && fov.focalLength && fov.cameraSize.width && fov.cameraSize.height && fov.pixelSize.width && fov.pixelSize.height && fov.bin + } + constructor( private readonly app: AppComponent, private readonly route: ActivatedRoute, private readonly api: ApiService, - private readonly electron: ElectronService, - private readonly browserWindow: BrowserWindowService, - private readonly preference: PreferenceService, - private readonly prime: PrimeService, - private readonly ngZone: NgZone, + private readonly electronService: ElectronService, + private readonly browserWindowService: BrowserWindowService, + private readonly preferenceService: PreferenceService, + private readonly angularService: AngularService, + ngZone: NgZone, ) { app.title = 'Image' @@ -576,22 +437,18 @@ export class ImageComponent implements AfterViewInit, OnDestroy { }, }) - this.stretchShadow.subscribe((value) => { - this.stretch.shadow = value / 65536 - }) - - this.stretchHighlight.subscribe((value) => { - this.stretch.highlight = value / 65536 - }) - - this.stretchMidtone.subscribe((value) => { - this.stretch.midtone = value / 65536 + app.topMenu.push({ + icon: 'mdi mdi-cog', + label: 'Settings', + command: () => { + this.settings.showDialog = true + }, }) - electron.on('CAMERA.CAPTURE_ELAPSED', async (event) => { + electronService.on('CAMERA.CAPTURE_ELAPSED', async (event) => { if (event.state === 'EXPOSURE_FINISHED' && event.camera.id === this.imageData.camera?.id) { await ngZone.run(async () => { - if (this.liveStackingMode === 'NONE') { + if (this.liveStacking.mode === 'NONE') { if (event.liveStackedPath) { await this.changeLiveStackingMode('STACKED') } @@ -600,9 +457,9 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } this.imageData.path = event.savedPath - this.imageData.liveStackedPath = event.liveStackedPath this.imageData.capture = event.capture this.imageData.exposureCount = event.exposureCount + this.liveStacking.path = event.liveStackedPath this.clearOverlay() @@ -611,13 +468,13 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } }) - electron.on('DATA.CHANGED', (event: OpenImage) => { + electronService.on('DATA.CHANGED', (event: OpenImage) => { return ngZone.run(() => { return this.loadImageFromOpenImage(event) }) }) - electron.on('CALIBRATION.CHANGED', async () => { + electronService.on('CALIBRATION.CHANGED', async () => { return ngZone.run(() => { return this.loadCalibrationGroups() }) @@ -677,22 +534,22 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } @HostListener('window:unload') - async ngOnDestroy() { - await this.closeImage(true) - - this.roiInteractable?.unset() + ngOnDestroy() { + this.zoom.panZoom?.dispose() + void this.closeImage() } - private markCalibrationGroupItem(name?: string) { + private markCalibrationGroupItem(name: string | undefined = this.transformation.calibrationGroup) { const items = this.calibrationMenuItem.items + const calibrationViaCamera = this.calibration.source === 'CAMERA' if (items) { items[2].disabled = !this.imageInfo?.camera?.id - items[2].selected = this.calibrationViaCamera + items[2].selected = calibrationViaCamera for (let i = 3; i < items.length; i++) { const item = items[i] - item.selected = !this.calibrationViaCamera && item.data === name + item.selected = !calibrationViaCamera && item.data === name } } } @@ -705,7 +562,8 @@ export class ImageComponent implements AfterViewInit, OnDestroy { if (!found) { reloadImage = !!this.transformation.calibrationGroup this.transformation.calibrationGroup = undefined - this.calibrationViaCamera = true + this.savePreference() + this.calibration.source = 'CAMERA' } const makeItem = (name?: string) => { @@ -715,11 +573,12 @@ export class ImageComponent implements AfterViewInit, OnDestroy { return { label, icon, - selected: !this.calibrationViaCamera && this.transformation.calibrationGroup === name, + selected: this.calibration.source === 'MENU' && this.transformation.calibrationGroup === name, data: name, command: () => { - this.calibrationViaCamera = false + this.calibration.source = 'MENU' this.transformation.calibrationGroup = name + this.savePreference() this.markCalibrationGroupItem(name) void this.loadImage() }, @@ -732,7 +591,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { label: 'Open', icon: 'mdi mdi-wrench', command: () => { - return this.browserWindow.openCalibration() + return this.browserWindowService.openCalibration() }, }) @@ -741,13 +600,13 @@ export class ImageComponent implements AfterViewInit, OnDestroy { menu.push({ label: 'Camera', icon: 'mdi mdi-camera-iris', - selected: this.calibrationViaCamera, + selected: this.calibration.source === 'CAMERA', disabled: !this.imageInfo?.camera?.id, data: 0, command: () => { if (this.imageInfo?.camera?.id) { - this.calibrationViaCamera = !this.calibrationViaCamera - this.markCalibrationGroupItem(this.transformation.calibrationGroup) + this.calibration.source = this.calibration.source === 'CAMERA' ? 'MENU' : 'CAMERA' + this.markCalibrationGroupItem() void this.loadImage() } }, @@ -760,7 +619,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } this.calibrationMenuItem.items = menu - this.menu.model = this.contextMenuItems + this.menu.model = this.contextMenuModel this.menu.cd.markForCheck() if (reloadImage) { @@ -768,96 +627,79 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } } - private async closeImage(force: boolean = false) { - if (this.imageData.path && force) { - await this.api.closeImage(this.imageData.path) - } - if (this.imageData.liveStackedPath && force) { - await this.api.closeImage(this.imageData.liveStackedPath) + private async closeImage() { + const path = this.imagePath + + if (path) { + await this.api.closeImage(path) } } - private async changeLiveStackingMode(mode: LiveStackingMode) { - this.liveStackingMode = mode + private changeLiveStackingMode(mode: LiveStackingMode) { + this.liveStacking.mode = mode - if (this.liveStackingMode !== 'NONE') { + if (this.liveStacking.mode !== 'NONE') { this.disableCalibration(true) } - this.liveStackingMenuItem.visible = this.liveStackingMode !== 'NONE' + this.liveStackingMenuItem.visible = this.liveStacking.mode !== 'NONE' this.liveStackingMenuItem.label = mode - await this.loadImage(true) + return this.loadImage(true) } - private roiResizableMove(event: ResizeEvent) { - const target = event.target - - if (this.panZoom) { - const { scale } = this.panZoom.getTransform() - - let x = parseFloat(target.getAttribute('data-x') ?? '0') || 0 - let y = parseFloat(target.getAttribute('data-y') ?? '0') || 0 - - target.style.width = `${event.rect.width / scale}px` - target.style.height = `${event.rect.height / scale}px` - - x += event.deltaRect!.left / scale - y += event.deltaRect!.top / scale - - target.style.transform = `translate(${x}px, ${y}px)` - - target.setAttribute('data-x', `${x}`) - target.setAttribute('data-y', `${y}`) + protected roiDrag(event: OnDrag) { + const { target, transform } = event + target.style.transform = transform - this.ngZone.run(() => { - this.imageROI.x = Math.round(x) - this.imageROI.y = Math.round(y) - this.imageROI.width = Math.round(event.rect.width / scale) - this.imageROI.height = Math.round(event.rect.height / scale) - }) - } + const rect = this.moveable.getRect() + this.imageROI.x = Math.trunc(rect.left) + this.imageROI.y = Math.trunc(rect.top) } - private roiDraggableMove(event: DragEvent) { - const target = event.target - - if (this.panZoom) { - const { scale } = this.panZoom.getTransform() + protected roiResize(event: OnResize) { + const { target, width, height, transform } = event + target.style.transform = transform - const x = (parseFloat(target.getAttribute('data-x') ?? '0') || 0) + event.dx / scale - const y = (parseFloat(target.getAttribute('data-y') ?? '0') || 0) + event.dy / scale + const rect = this.moveable.getRect() - target.style.transform = `translate(${x}px, ${y}px)` + target.style.width = `${width}px` + this.imageROI.x = Math.trunc(rect.left) + this.imageROI.width = Math.trunc(width) - target.setAttribute('data-x', `${x}`) - target.setAttribute('data-y', `${y}`) + target.style.height = `${height}px` + this.imageROI.y = Math.trunc(rect.top) + this.imageROI.height = Math.trunc(height) + } - this.ngZone.run(() => { - this.imageROI.x = Math.round(x) - this.imageROI.y = Math.round(y) - }) - } + protected roiRotate(event: OnRotate) { + const { target, transform } = event + target.style.transform = transform } - roiForCamera() { + protected roiForCamera() { return this.executeCamera((camera) => { - const x = camera.x + this.imageROI.x - const y = camera.y + this.imageROI.y - const width = camera.binX * this.imageROI.width - const height = camera.binY * this.imageROI.height + const x = Math.max(0, Math.min(camera.x + this.imageROI.x, camera.maxX)) + const y = Math.max(0, Math.min(camera.y + this.imageROI.y, camera.maxY)) + const width = Math.max(0, Math.min(camera.binX * this.imageROI.width, camera.maxWidth)) + const height = Math.max(0, Math.min(camera.binY * this.imageROI.height, camera.maxHeight)) - return this.electron.send('ROI.SELECTED', { camera, x, y, width, height }) + return this.electronService.send('ROI.SELECTED', { camera, x, y, width, height }) }, false) } private async loadImageFromOpenImage(data: OpenImage) { - Object.assign(this.imageData, data) + if (data.camera) this.imageData.camera = data.camera + if (data.path) this.imageData.path = data.path + this.imageData.source = data.source + if (data.title) this.imageData.title = data.title + if (data.capture) this.imageData.capture = data.capture // Not clicked on menu item. - if (this.calibrationViaCamera && this.transformation.calibrationGroup !== data.capture?.calibrationGroup) { + if (this.calibration.source === 'CAMERA' && this.transformation.calibrationGroup !== data.capture?.calibrationGroup) { this.transformation.calibrationGroup = data.capture?.calibrationGroup - this.markCalibrationGroupItem(this.transformation.calibrationGroup) + this.savePreference() + this.markCalibrationGroupItem() } if (data.source === 'FRAMING') { @@ -872,7 +714,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { if (data.path) { this.clearOverlay() - await this.loadImage(true) + await this.loadImage(!!this.imageInfo) } } @@ -881,41 +723,38 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.annotation.visible = false this.annotationMenuItem.checkable = false - this.starDetection.stars = [] - this.starDetection.visible = false + this.starDetector.stars = [] + this.starDetector.visible = false this.detectStarsMenuItem.checkable = false - Object.assign(this.solver.solved, EMPTY_IMAGE_SOLVED) + Object.assign(this.solver.solved, DEFAULT_IMAGE_SOLVED) this.histogram?.update([]) } - private async computeHistogram() { + protected async computeHistogram() { const path = this.imagePath if (path) { - const data = await this.api.imageHistogram(path, this.statisticsBitLength.bitLength) + const data = await this.api.imageHistogram(path, this.statistics.bitOption.bitLength) this.histogram?.update(data) } } - statisticsBitLengthChanged() { - return this.computeHistogram() - } - - async detectStars() { + protected async detectStars() { const path = this.imagePath if (path) { - const options = this.preference.starDetectionRequest(this.starDetection.type).get() - options.minSNR = this.starDetection.minSNR - options.maxStars = this.starDetection.maxStars + const request: StarDetectionRequest = { + ...this.starDetector.request, + ...this.preferenceService.settings.get().starDetector[this.starDetector.request.type], + } try { - this.starDetection.running = true - this.starDetection.stars = await this.api.detectStars(path, options) + this.starDetector.running = true + this.starDetector.stars = await this.api.detectStars(path, request) } finally { - this.starDetection.running = false + this.starDetector.running = false } let hfd = 0 @@ -924,12 +763,12 @@ export class ImageComponent implements AfterViewInit, OnDestroy { let fluxMin = 0 let fluxMax = 0 - const starCount = this.starDetection.stars.length + const starCount = this.starDetector.stars.length if (starCount) { - fluxMax = this.starDetection.stars[0].flux + fluxMax = this.starDetector.stars[0].flux - for (const star of this.starDetection.stars) { + for (const star of this.starDetector.stars) { hfd += star.hfd snr += star.snr fluxMax = Math.min(fluxMax, star.flux) @@ -941,29 +780,29 @@ export class ImageComponent implements AfterViewInit, OnDestroy { let squared = 0 - for (const star of this.starDetection.stars) { + for (const star of this.starDetector.stars) { squared += Math.pow(star.hfd - hfd, 2) } stdDev = Math.sqrt(squared / starCount) } - this.starDetection.computed.hfd = hfd - this.starDetection.computed.stdDev = stdDev - this.starDetection.computed.snr = snr - this.starDetection.computed.fluxMax = fluxMin - this.starDetection.computed.fluxMin = fluxMax + this.starDetector.computed.hfd = hfd + this.starDetector.computed.stdDev = stdDev + this.starDetector.computed.snr = snr + this.starDetector.computed.fluxMax = fluxMin + this.starDetector.computed.fluxMin = fluxMax this.savePreference() - this.starDetection.visible = this.starDetection.stars.length > 0 - this.detectStarsMenuItem.checkable = this.starDetection.visible - this.detectStarsMenuItem.checked = this.starDetection.visible + this.starDetector.visible = this.starDetector.stars.length > 0 + this.detectStarsMenuItem.checkable = this.starDetector.visible + this.detectStarsMenuItem.checked = this.starDetector.visible } } - selectDetectedStar(star: DetectedStar) { - Object.assign(this.starDetection.selected, star) + protected drawDetectedStar(star: DetectedStar) { + Object.assign(this.starDetector.selected, star) const canvas = this.detectedStarCanvas.nativeElement const ctx = canvas.getContext('2d') @@ -971,7 +810,9 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } private async loadImage(force: boolean = false) { - await this.closeImage(force) + if (force) { + await this.closeImage() + } const path = this.imagePath @@ -1002,7 +843,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { const image = this.image.nativeElement const transformation = structuredClone(this.transformation) - if (this.calibrationViaCamera && this.liveStackingMode !== 'NONE') transformation.calibrationGroup = this.imageData.capture?.calibrationGroup + if (this.calibration.source === 'CAMERA' && this.liveStacking.mode !== 'NONE') transformation.calibrationGroup = this.imageData.capture?.calibrationGroup const { info, blob } = await this.api.openImage(path, transformation, this.imageData.camera) if (!blob || !info) return @@ -1010,29 +851,26 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.imageInfo = info this.scnrMenuItem.disabled = info.mono - if (info.rightAscension) this.solver.centerRA = info.rightAscension - if (info.declination) this.solver.centerDEC = info.declination - this.solver.blind = !this.solver.centerRA || !this.solver.centerDEC + if (info.rightAscension) this.solver.request.centerRA = info.rightAscension + if (info.declination) this.solver.request.centerDEC = info.declination + this.solver.request.blind = !this.solver.request.centerRA || !this.solver.request.centerDEC - if (this.stretch.auto) { - this.stretchShadow.set(Math.trunc(info.stretchShadow * 65536)) - this.stretchHighlight.set(Math.trunc(info.stretchHighlight * 65536)) - this.stretchMidtone.set(Math.trunc(info.stretchMidtone * 65536)) + if (this.stretch.transformation.auto) { + Object.assign(this.stretch.transformation, info.stretch) } this.updateImageSolved(info.solved) - this.fitsHeaders.headers = info.headers + this.headers.headers = info.headers + this.statistics.statistics = info.statistics this.retrieveInfoFromImageHeaders(info.headers) - if (this.imageURL) window.URL.revokeObjectURL(this.imageURL) - this.imageURL = window.URL.createObjectURL(blob) - image.src = this.imageURL + image.src = URL.createObjectURL(blob) if (!info.camera?.id) { - this.calibrationViaCamera = false - this.markCalibrationGroupItem(this.transformation.calibrationGroup) + this.calibration.source = 'MENU' + this.markCalibrationGroupItem() } else if (this.calibrationMenuItem.items) { this.calibrationMenuItem.items[2].disabled = false } @@ -1040,46 +878,38 @@ export class ImageComponent implements AfterViewInit, OnDestroy { return this.retrieveCoordinateInterpolation() } - private retrieveInfoFromImageHeaders(headers: FITSHeaderItem[]) { - const imagePreference = this.preference.imagePreference.get() - + private retrieveInfoFromImageHeaders(headers: ImageHeaderItem[]) { for (const item of headers) { if (item.name === 'FOCALLEN') { - this.solver.focalLength = parseFloat(item.value) + this.solver.request.focalLength = parseFloat(item.value) } else if (item.name === 'XPIXSZ') { - this.solver.pixelSize = parseFloat(item.value) + this.solver.request.pixelSize = parseFloat(item.value) } } - - this.solver.focalLength ||= imagePreference.solver?.focalLength ?? 0 - this.solver.pixelSize ||= imagePreference.solver?.pixelSize ?? 0 } - imageClicked(event: MouseEvent, contextMenu: boolean) { - this.imageMouseX = event.offsetX - this.imageMouseY = event.offsetY + protected imageClicked(event: MouseEvent, contextMenu: boolean) { + this.mouseMountCoordinate.x = event.offsetX + this.mouseMountCoordinate.y = event.offsetY if (contextMenu) { this.menu.show(event) } } - imageMouseMoved(event: MouseEvent) { + protected imageMouseMoved(event: MouseEvent) { this.imageMouseMovedWithCoordinates(event.offsetX, event.offsetY) } - imageMouseMovedWithCoordinates(x: number, y: number) { - if (!this.menu.visible()) { - this.mouseCoordinate = this.mouseCoordinateInterpolation?.interpolateAsText(x, y, true, true, false) - - if (this.mouseCoordinate) { - this.mouseCoordinate.x = x - this.mouseCoordinate.y = y - } + private imageMouseMovedWithCoordinates(x: number, y: number) { + if (!this.menu.visible() && this.mouseCoordinate.interpolator) { + Object.assign(this.mouseCoordinate, this.mouseCoordinate.interpolator.interpolateAsText(x, y, true, true, false)) + this.mouseCoordinate.x = x + this.mouseCoordinate.y = y } } - async saveImageAs() { + protected async saveImageAs() { const path = this.imagePath if (path) { @@ -1088,13 +918,13 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } } - async annotateImage() { + protected async annotateImage() { const path = this.imagePath if (path) { try { this.annotation.running = true - this.annotation.data = await this.api.annotationsOfImage(path, this.annotation.useStarsAndDSOs, this.annotation.useMinorPlanets, this.annotation.minorPlanetsMagLimit, this.annotation.includeMinorPlanetsWithoutMagnitude, this.annotation.useSimbad) + this.annotation.data = await this.api.annotationsOfImage(path, this.annotation.request) this.annotation.visible = this.annotation.data.length > 0 this.annotationMenuItem.checkable = this.annotation.visible this.annotationMenuItem.checked = this.annotation.visible @@ -1105,94 +935,101 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } } - showAnnotationInfo(annotation: ImageAnnotation) { - this.annotationInfo.info = annotation.star ?? annotation.dso ?? annotation.minorPlanet - this.annotationInfo.showDialog = true + protected showAnnotationInfo(annotation: ImageAnnotation) { + this.astronomicalObject.info = annotation.star ?? annotation.dso ?? annotation.minorPlanet + this.astronomicalObject.showDialog = true } private disableAutoStretch() { - this.stretch.auto = false + this.stretch.transformation.auto = false + this.savePreference() this.autoStretchMenuItem.selected = false } private disableCalibration(canEnable: boolean = true) { this.transformation.calibrationGroup = undefined + this.savePreference() this.markCalibrationGroupItem(undefined) this.calibrationMenuItem.disabled = !canEnable } - autoStretch() { - this.stretch.auto = true + protected autoStretch() { + this.stretch.transformation.auto = true + this.savePreference() this.autoStretchMenuItem.selected = true return this.loadImage() } - async resetStretch(load: boolean = true) { - this.stretchShadow.set(0) - this.stretchHighlight.set(65536) - this.stretchMidtone.set(32768) + protected async resetStretch(load: boolean = true) { + this.stretch.transformation.shadow = 0 + this.stretch.transformation.highlight = 65536 + this.stretch.transformation.midtone = 32768 + this.savePreference() if (load) { await this.stretchImage() } } - async toggleStretch() { - this.stretch.auto = !this.stretch.auto - this.autoStretchMenuItem.selected = this.stretch.auto + private async toggleStretch() { + this.stretch.transformation.auto = !this.stretch.transformation.auto + this.savePreference() + this.autoStretchMenuItem.selected = this.stretch.transformation.auto - if (!this.stretch.auto) { - await this.resetStretch() + if (this.stretch.transformation.auto) { + return this.loadImage() } else { - await this.loadImage() + return this.resetStretch() } } - stretchImage() { + protected stretchImage() { this.disableAutoStretch() return this.loadImage() } - invertImage() { + private invertImage() { this.transformation.invert = !this.transformation.invert this.invertMenuItem.selected = this.transformation.invert + this.savePreference() return this.loadImage() } - scnrImage() { + protected scnrImage() { return this.loadImage() } - toggleCrosshair() { - this.crossHair = !this.crossHair - this.crosshairMenuItem.selected = this.crossHair + private toggleCrosshair() { + this.preference.crossHair = !this.preference.crossHair + this.savePreference() + this.crosshairMenuItem.selected = this.preference.crossHair } - zoomIn() { - if (!this.panZoom) return - const { scale } = this.panZoom.getTransform() - this.panZoom.smoothZoomAbs(window.innerWidth / 2, window.innerHeight / 2, scale * 1.1) + private zoomIn() { + if (!this.zoom.panZoom) return + const { scale } = this.zoom.panZoom.getTransform() + this.zoom.panZoom.smoothZoomAbs(window.innerWidth / 2, window.innerHeight / 2, scale * 1.1) } - zoomOut() { - if (!this.panZoom) return - const { scale } = this.panZoom.getTransform() - this.panZoom.smoothZoomAbs(window.innerWidth / 2, window.innerHeight / 2, scale * 0.9) + private zoomOut() { + if (!this.zoom.panZoom) return + const { scale } = this.zoom.panZoom.getTransform() + this.zoom.panZoom.smoothZoomAbs(window.innerWidth / 2, window.innerHeight / 2, scale * 0.9) } - center() { + private center() { const { width, height } = this.image.nativeElement.getBoundingClientRect() - this.panZoom?.moveTo(window.innerWidth / 2 - width / 2, (window.innerHeight - 42) / 2 - height / 2) + this.zoom.panZoom?.moveTo(window.innerWidth / 2 - width / 2, (window.innerHeight - 42) / 2 - height / 2) } - resetZoom(fitToScreen: boolean = false, center: boolean = true) { + private resetZoom(fitToScreen: boolean = false, center: boolean = true) { if (fitToScreen) { const { width, height } = this.image.nativeElement const factor = Math.min(window.innerWidth, window.innerHeight - 42) / Math.min(width, height) - this.panZoom?.smoothZoomAbs(window.innerWidth / 2, window.innerHeight / 2, factor) + this.zoom.panZoom?.smoothZoomAbs(window.innerWidth / 2, window.innerHeight / 2, factor) } else { - this.panZoom?.smoothZoomAbs(window.innerWidth / 2, window.innerHeight / 2, 1.0) + this.zoom.panZoom?.smoothZoomAbs(window.innerWidth / 2, window.innerHeight / 2, 1.0) } if (center) { @@ -1200,44 +1037,49 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } } - async enterFullscreen() { - this.app.showTopBar = !(await this.electron.fullscreenWindow(true)) + private async enterFullscreen() { + this.app.showTopBar = !(await this.electronService.fullscreenWindow(true)) } - async exitFullscreen() { - this.app.showTopBar = !(await this.electron.fullscreenWindow(false)) + private async exitFullscreen() { + this.app.showTopBar = !(await this.electronService.fullscreenWindow(false)) } private async retrieveCoordinateInterpolation() { const path = this.imagePath if (path) { - const coordinate = await this.api.coordinateInterpolation(this.imagePath) + const coordinate = await this.api.coordinateInterpolation(path) if (coordinate && this.imageInfo) { 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) + const x = Math.max(0, Math.min(this.mouseCoordinate.x, this.imageInfo.width)) + const y = Math.max(0, Math.min(this.mouseCoordinate.y, this.imageInfo.height)) + this.mouseCoordinate.interpolator = new CoordinateInterpolator(ma, md, x0, y0, x1, y1, delta) + this.mouseCoordinate.show = true this.imageMouseMovedWithCoordinates(x, y) - } else { - this.mouseCoordinateInterpolation = undefined - this.mouseCoordinate = undefined + return } } + + this.mouseCoordinate.interpolator = undefined + this.mouseCoordinate.show = false } - async solveImage() { + protected async solverStart() { const path = this.imagePath if (path) { this.solver.running = true try { - const solver = this.preference.plateSolverRequest(this.solver.type).get() - const solved = await this.api.solveImage(solver, path, this.solver.blind, this.solver.centerRA, this.solver.centerDEC, this.solver.radius) + const request: PlateSolverRequest = { + ...this.solver.request, + ...this.preferenceService.settings.get().plateSolver[this.solver.request.type], + } + + const solved = await this.api.solverStart(request, path) - this.savePreference() this.updateImageSolved(solved) } catch { this.updateImageSolved(this.imageInfo?.solved) @@ -1251,8 +1093,12 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } } + protected solverStop() { + return this.api.solverStop() + } + private updateImageSolved(solved?: ImageSolved) { - Object.assign(this.solver.solved, solved ?? EMPTY_IMAGE_SOLVED) + Object.assign(this.solver.solved, solved ?? DEFAULT_IMAGE_SOLVED) this.annotationMenuItem.disabled = !this.solver.solved.solved this.fovMenuItem.disabled = !this.solver.solved.solved this.pointMountHereMenuItem.disabled = !this.solver.solved.solved @@ -1262,27 +1108,27 @@ export class ImageComponent implements AfterViewInit, OnDestroy { else this.fov.fovs.forEach((e) => (e.computed = undefined)) } - mountSync(coordinate: EquatorialCoordinateJ2000) { + protected mountSync(coordinate: EquatorialCoordinateJ2000) { return this.executeMount((mount) => { return this.api.mountSync(mount, coordinate.rightAscensionJ2000, coordinate.declinationJ2000, true) }) } - mountGoTo(coordinate: EquatorialCoordinateJ2000) { + protected mountGoTo(coordinate: EquatorialCoordinateJ2000) { return this.executeMount((mount) => { return this.api.mountGoTo(mount, coordinate.rightAscensionJ2000, coordinate.declinationJ2000, true) }) } - mountSlew(coordinate: EquatorialCoordinateJ2000) { + protected mountSlew(coordinate: EquatorialCoordinateJ2000) { return this.executeMount((mount) => { return this.api.mountSlew(mount, coordinate.rightAscensionJ2000, coordinate.declinationJ2000, true) }) } - async frame(coordinate: EquatorialCoordinateJ2000) { + protected async frame(coordinate: EquatorialCoordinateJ2000) { if (this.solver.solved.solved) { - await this.browserWindow.openFraming({ + await this.browserWindowService.openFraming({ rightAscension: coordinate.rightAscensionJ2000, declination: coordinate.declinationJ2000, fov: this.solver.solved.width / 60, @@ -1291,29 +1137,40 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } } - imageLoaded() { - const imageWrapperElement = this.image.nativeElement.parentElement + protected imageLoaded() { + const image = this.image.nativeElement + const imageWrapper = image.parentElement + + URL.revokeObjectURL(image.src) - if (!this.panZoom && imageWrapperElement) { - this.panZoom = createPanZoom(imageWrapperElement, { + if (!this.zoom.panZoom && imageWrapper) { + const panZoom = createPanZoom(imageWrapper, { minZoom: 0.1, maxZoom: 500.0, autocenter: true, zoomDoubleClickSpeed: 1, + zoomSpeed: 1, filterKey: () => { return true }, beforeWheel: (e) => { - return e.target !== this.image.nativeElement && e.target !== this.roi.nativeElement + return e.target !== this.image.nativeElement && e.target !== this.roi.nativeElement && (e.target as HTMLElement).tagName !== 'circle' }, beforeMouseDown: (e) => { - return e.target !== this.image.nativeElement + return e.target !== this.image.nativeElement && (e.target as HTMLElement).tagName !== 'circle' }, }) + + panZoom.on('transform', () => { + const { scale } = panZoom.getTransform() + this.zoom.scale = scale + }) + + this.zoom.panZoom = panZoom } } - async showFOVCameras() { + protected async showFOVCameraDialog() { if (!this.fov.cameras.length) { this.fov.cameras = await this.api.fovCameras() } @@ -1322,7 +1179,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.fov.showCameraDialog = true } - async showFOVTelescopes() { + protected async showFOVTelescopeDialog() { if (!this.fov.telescopes.length) { this.fov.telescopes = await this.api.fovTelescopes() } @@ -1331,47 +1188,49 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.fov.showTelescopeDialog = true } - chooseCamera() { + protected chooseCamera() { if (this.fov.camera) { - this.fov.cameraSize.width = this.fov.camera.width - this.fov.cameraSize.height = this.fov.camera.height - this.fov.pixelSize.width = this.fov.camera.pixelSize - this.fov.pixelSize.height = this.fov.camera.pixelSize + this.fov.selected.cameraSize.width = this.fov.camera.width + this.fov.selected.cameraSize.height = this.fov.camera.height + this.fov.selected.pixelSize.width = this.fov.camera.pixelSize + this.fov.selected.pixelSize.height = this.fov.camera.pixelSize this.fov.camera = undefined this.fov.showCameraDialog = false } } - chooseTelescope() { + protected chooseTelescope() { if (this.fov.telescope) { - this.fov.aperture = this.fov.telescope.aperture - this.fov.focalLength = this.fov.telescope.focalLength + this.fov.selected.aperture = this.fov.telescope.aperture + this.fov.selected.focalLength = this.fov.telescope.focalLength this.fov.telescope = undefined this.fov.showTelescopeDialog = false } } - addFOV() { - if (this.computeFOV(this.fov)) { - this.fov.fovs.push(structuredClone(this.fov)) - this.preference.imageFOVs.set(this.fov.fovs) + protected addFOV() { + if (this.computeFOV(this.fov.selected)) { + this.fov.fovs.push(structuredClone(this.fov.selected)) + this.savePreference() } } - editFOV(fov: FOV) { - Object.assign(this.fov, structuredClone(fov)) - this.fov.edited = fov + private removeSelectedFOV() { + this.fov.selected = structuredClone(DEFAULT_FOV) } - cancelEditFOV() { - this.fov.edited = undefined + protected selectFOV(fov: FOV) { + if (this.fov.selected === fov) { + this.removeSelectedFOV() + } else { + this.fov.selected = fov + } } - saveFOV() { - if (this.fov.edited && this.computeFOV(this.fov)) { - Object.assign(this.fov.edited, structuredClone(this.fov)) - this.preference.imageFOVs.set(this.fov.fovs) - this.fov.edited = undefined + protected saveFOV(compute: boolean = true) { + // Edited. + if (this.fov.fovs.includes(this.fov.selected) && (!compute || this.computeFOV(this.fov.selected))) { + this.savePreference() } } @@ -1413,56 +1272,44 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } } - deleteFOV(fov: FOV) { + protected deleteFOV(fov: FOV) { const index = this.fov.fovs.indexOf(fov) if (index >= 0) { - if (this.fov.fovs[index] === this.fov.edited) { - this.fov.edited = undefined - } - this.fov.fovs.splice(index, 1) - this.preference.imageFOVs.set(this.fov.fovs) + this.savePreference() + + if (this.fov.selected === this.fov.fovs[index]) { + this.removeSelectedFOV() + } } } private loadPreference() { - const preference = this.preference.imagePreference.get() - this.solver.radius = preference.solver?.radius ?? this.solver.radius - this.solver.type = preference.solver?.type ?? 'ASTAP' - this.solver.focalLength = preference.solver?.focalLength ?? 0 - this.solver.pixelSize = preference.solver?.pixelSize ?? 0 - this.starDetection.type = preference.starDetection?.type ?? this.starDetection.type - this.starDetection.minSNR = preference.starDetection?.minSNR ?? this.preference.starDetectionRequest(this.starDetection.type).get().minSNR ?? this.starDetection.minSNR - this.starDetection.maxStars = preference.starDetection?.maxStars ?? this.preference.starDetectionRequest(this.starDetection.type).get().maxStars ?? this.starDetection.maxStars - - this.fov.fovs = this.preference.imageFOVs.get() - this.fov.fovs.forEach((e) => { - e.enabled = false - e.computed = undefined - }) + Object.assign(this.preference, this.preferenceService.imagePreference.get()) + this.solver.request = this.preference.solver + this.starDetector.request = this.preference.starDetector + this.settings.preference = this.preference + this.transformation = this.preference.transformation + this.saveAs.transformation = this.transformation + this.stretch.transformation = this.transformation.stretch + this.scnr.transformation = this.transformation.scnr + this.annotation.request = this.preference.annotation + this.fov.fovs = this.preference.fovs + + this.autoStretchMenuItem.selected = this.transformation.stretch.auto + this.invertMenuItem.selected = this.transformation.invert + this.horizontalMirrorMenuItem.selected = this.transformation.mirrorHorizontal + this.verticalMirrorMenuItem.selected = this.transformation.mirrorVertical + this.crosshairMenuItem.selected = this.preference.crossHair } - private savePreference() { - const preference = this.preference.imagePreference.get() - - preference.solver = { - type: this.solver.type, - focalLength: this.solver.focalLength, - pixelSize: this.solver.pixelSize, - radius: this.solver.radius, - } - preference.starDetection = { - type: this.starDetection.type, - maxStars: this.starDetection.maxStars, - minSNR: this.starDetection.minSNR, - } - - this.preference.imagePreference.set(preference) + protected savePreference() { + this.preferenceService.imagePreference.set(this.preference) } private async executeCamera(action: (camera: Camera) => void | Promise, showConfirmation: boolean = true) { - if (showConfirmation && (await this.prime.confirm('Are you sure that you want to proceed?'))) { + if (showConfirmation && (await this.angularService.confirm('Are you sure that you want to proceed?'))) { return false } @@ -1472,8 +1319,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { await action(cameras[0]) return true } else { - this.deviceMenu.header = 'CAMERA' - const camera = await this.deviceMenu.show(cameras) + const camera = await this.deviceMenu.show(cameras, undefined, 'CAMERA') if (camera && camera !== 'NONE' && camera.connected) { await action(camera) @@ -1485,7 +1331,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } private async executeMount(action: (mount: Mount) => void | Promise, showConfirmation: boolean = true) { - if (showConfirmation && (await this.prime.confirm('Are you sure that you want to proceed?'))) { + if (showConfirmation && (await this.angularService.confirm('Are you sure that you want to proceed?'))) { return false } @@ -1495,8 +1341,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { await action(mounts[0]) return true } else { - this.deviceMenu.header = 'MOUNT' - const mount = await this.deviceMenu.show(mounts) + const mount = await this.deviceMenu.show(mounts, undefined, 'MOUNT') if (mount && mount !== 'NONE' && mount.connected) { await action(mount) diff --git a/desktop/src/app/indi/indi.component.html b/desktop/src/app/indi/indi.component.html index cf3d4fe4c..ed92efcae 100644 --- a/desktop/src/app/indi/indi.component.html +++ b/desktop/src/app/indi/indi.component.html @@ -33,7 +33,7 @@
- diff --git a/desktop/src/app/indi/indi.component.scss b/desktop/src/app/indi/indi.component.scss index 6c27e4f87..465bcc5e8 100644 --- a/desktop/src/app/indi/indi.component.scss +++ b/desktop/src/app/indi/indi.component.scss @@ -1,16 +1,16 @@ -:host { - ::ng-deep { - .p-listbox-list-wrapper { - max-height: calc(100vh - 175px) !important; - } +neb-indi { + .properties { + height: calc(100vh - 100px); + overflow-y: auto; } -} -.properties { - height: calc(100vh - 100px); - overflow-y: auto; -} + .properties::-webkit-scrollbar { + display: none; + } -.properties::-webkit-scrollbar { - display: none; + .p-listbox-filter.p-inputtext { + border: 0px; + border-radius: 4px; + background: #1a1a1a; + } } diff --git a/desktop/src/app/indi/indi.component.ts b/desktop/src/app/indi/indi.component.ts index 776bd110d..f528be883 100644 --- a/desktop/src/app/indi/indi.component.ts +++ b/desktop/src/app/indi/indi.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, HostListener, NgZone, OnDestroy, ViewChild } from '@angular/core' +import { AfterViewInit, Component, HostListener, NgZone, OnDestroy, ViewChild, ViewEncapsulation } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { MenuItem } from 'primeng/api' import { Listbox } from 'primeng/listbox' @@ -9,34 +9,35 @@ import { deviceComparator, textComparator } from '../../shared/utils/comparators import { AppComponent } from '../app.component' @Component({ - selector: 'app-indi', + selector: 'neb-indi', templateUrl: './indi.component.html', styleUrls: ['./indi.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class INDIComponent implements AfterViewInit, OnDestroy { - devices: Device[] = [] - properties: INDIProperty[] = [] - groups: MenuItem[] = [] + protected devices: Device[] = [] + protected properties: INDIProperty[] = [] + protected groups: MenuItem[] = [] - device?: Device - group = '' - showLog = false - messages: string[] = [] + protected device?: Device + protected group = '' + protected showLog = false + protected messages: string[] = [] @ViewChild('listbox') - readonly messageListbox!: Listbox + protected readonly messageBox!: Listbox constructor( app: AppComponent, private readonly route: ActivatedRoute, private readonly api: ApiService, - electron: ElectronService, + electronService: ElectronService, ngZone: NgZone, ) { app.title = 'INDI' - electron.on('DEVICE.PROPERTY_CHANGED', (event) => { - if (this.device?.id === event.device.id) { + electronService.on('DEVICE.PROPERTY_CHANGED', (event) => { + if (event.device.id === this.device?.id) { ngZone.run(() => { if (event.property) { this.addOrUpdateProperty(event.property) @@ -46,8 +47,8 @@ export class INDIComponent implements AfterViewInit, OnDestroy { } }) - electron.on('DEVICE.PROPERTY_DELETED', (event) => { - if (this.device?.id === event.device.id) { + electronService.on('DEVICE.PROPERTY_DELETED', (event) => { + if (event.device.id === this.device?.id) { const index = this.properties.findIndex((e) => e.name === event.property?.name) if (index >= 0) { @@ -59,12 +60,12 @@ export class INDIComponent implements AfterViewInit, OnDestroy { } }) - electron.on('DEVICE.MESSAGE_RECEIVED', (event) => { - if (this.device && event.device.id === this.device.id) { + electronService.on('DEVICE.MESSAGE_RECEIVED', (event) => { + if (event.device.id === this.device?.id) { ngZone.run(() => { if (event.message) { this.messages.splice(0, 0, event.message) - this.messageListbox.cd.markForCheck() + this.messageBox.cd.markForCheck() } }) } @@ -80,7 +81,7 @@ export class INDIComponent implements AfterViewInit, OnDestroy { } }) - this.devices = [...(await this.api.cameras()), ...(await this.api.mounts()), ...(await this.api.focusers()), ...(await this.api.wheels())].sort(deviceComparator) + this.devices = [...(await this.api.cameras()), ...(await this.api.mounts()), ...(await this.api.focusers()), ...(await this.api.wheels()), ...(await this.api.rotators())].sort(deviceComparator) if (this.devices.length) { this.device = this.devices[0] @@ -95,7 +96,7 @@ export class INDIComponent implements AfterViewInit, OnDestroy { } } - async deviceChanged(device: Device) { + protected async deviceChanged(device: Device) { if (this.device) { await this.api.indiUnlisten(this.device) } @@ -107,7 +108,7 @@ export class INDIComponent implements AfterViewInit, OnDestroy { this.messages = await this.api.indiLog(device) } - changeGroup(group: string) { + protected changeGroup(group: string) { this.showLog = false this.group = group } diff --git a/desktop/src/app/indi/property/indi-property.component.scss b/desktop/src/app/indi/property/indi-property.component.scss index fc6120b85..3c6597153 100644 --- a/desktop/src/app/indi/property/indi-property.component.scss +++ b/desktop/src/app/indi/property/indi-property.component.scss @@ -1,21 +1,21 @@ -:host { +neb-indi-property { background: rgba(0, 0, 0, 0.1); border-radius: 8px; display: block; margin-bottom: 4px; -} -.mdi.mdi-circle { - &.IDLE { - color: #039be5; - } - &.OK { - color: #43a047; - } - &.BUSY { - color: #f57c00; - } - &.ALERT { - color: #e53935; + .mdi.mdi-circle { + &.IDLE { + color: #039be5; + } + &.OK { + color: #43a047; + } + &.BUSY { + color: #f57c00; + } + &.ALERT { + color: #e53935; + } } } diff --git a/desktop/src/app/indi/property/indi-property.component.ts b/desktop/src/app/indi/property/indi-property.component.ts index 52d9b6747..7ccde2aa4 100644 --- a/desktop/src/app/indi/property/indi-property.component.ts +++ b/desktop/src/app/indi/property/indi-property.component.ts @@ -1,17 +1,18 @@ -import { AfterContentInit, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core' +import { AfterContentInit, Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core' import { INDIProperty, INDIPropertyItem, INDISendProperty, INDISendPropertyItem } from '../../../shared/types/device.types' @Component({ - selector: 'app-indi-property', + selector: 'neb-indi-property', templateUrl: './indi-property.component.html', styleUrls: ['./indi-property.component.scss'], + encapsulation: ViewEncapsulation.None, }) -export class INDIPropertyComponent implements AfterContentInit, OnDestroy { +export class INDIPropertyComponent implements AfterContentInit { @Input({ required: true }) - property!: INDIProperty + protected property!: INDIProperty @Input() - disabled = false + protected disabled = false @Output() readonly onSend = new EventEmitter() @@ -24,8 +25,6 @@ export class INDIPropertyComponent implements AfterContentInit, OnDestroy { } } - ngOnDestroy() {} - sendSwitch(item: INDIPropertyItem) { const property: INDISendProperty = { name: this.property.name, diff --git a/desktop/src/app/mount/mount.component.html b/desktop/src/app/mount/mount.component.html index e8acceb0a..153f63b0a 100644 --- a/desktop/src/app/mount/mount.component.html +++ b/desktop/src/app/mount/mount.component.html @@ -11,7 +11,7 @@ - {{ parking ? 'parking' : parked ? 'parked' : slewing ? 'slewing' : tracking ? 'tracking' : 'idle' }} + {{ mount.parking ? 'parking' : mount.parked ? 'parked' : mount.slewing ? 'slewing' : tracking ? 'tracking' : 'idle' }}
@@ -55,7 +55,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="rightAscensionJ2000" /> + [value]="currentComputedLocation.rightAscensionJ2000" />
@@ -65,7 +65,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="declinationJ2000" /> + [value]="currentComputedLocation.declinationJ2000" />
@@ -75,7 +75,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="rightAscension" /> + [value]="mount.rightAscension" />
@@ -85,7 +85,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="declination" /> + [value]="mount.declination" />
@@ -95,7 +95,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="azimuth" /> + [value]="currentComputedLocation.azimuth" />
@@ -105,7 +105,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="altitude" /> + [value]="currentComputedLocation.altitude" /> @@ -115,7 +115,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="lst" /> + [value]="currentComputedLocation.lst" /> @@ -125,7 +125,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="constellation ?? '-'" /> + [value]="currentComputedLocation.constellation" /> @@ -135,7 +135,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - value="{{ meridianAt }} (-{{ timeLeftToMeridianFlip }})" /> + value="{{ currentComputedLocation.meridianAt }} (-{{ currentComputedLocation.timeLeftToMeridianFlip }})" /> @@ -145,7 +145,7 @@ pInputText readonly class="p-inputtext-sm border-0 w-full" - [value]="pierSide" /> + [value]="currentComputedLocation.pierSide" /> @@ -162,9 +162,9 @@
@@ -197,9 +197,9 @@ @@ -209,9 +209,9 @@ @@ -219,7 +219,7 @@
- +
@@ -430,12 +430,12 @@ [min]="1024" [max]="65535" placeholder="10001" - scrollableNumber /> + spinnableNumber />
- + Use together with the - + Use together with the
() private readonly computeTargetCoordinatePublisher = new Subject() private readonly computeCoordinateSubscriptions: Subscription[] = [] private readonly moveToDirection = [false, false] - readonly ephemerisModel: SlideMenuItem[] = [ + protected tracking = false + protected trackMode: TrackMode = 'SIDEREAL' + protected slewRate?: SlewRate + protected slewingDirection?: MountSlewDirection + + protected readonly ephemerisModel: SlideMenuItem[] = [ { icon: 'mdi mdi-image', label: 'Frame', slideMenu: [], command: () => { - const data: FramingData = { rightAscension: this.rightAscensionJ2000, declination: this.declinationJ2000 } - return this.browserWindow.openFraming(data) + return this.browserWindowService.openFraming({ rightAscension: this.currentComputedLocation.rightAscensionJ2000, declination: this.currentComputedLocation.declinationJ2000 }) }, }, SEPARATOR_MENU_ITEM, @@ -72,24 +50,25 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { label: 'Find sky objects around the coordinates', slideMenu: [], command: () => { - const data: SkyAtlasInput = { - tab: SkyAtlasTab.SKY_OBJECT, - filter: { rightAscension: this.rightAscensionJ2000, declination: this.declinationJ2000 }, - } - - return this.browserWindow.openSkyAtlas(data, { bringToFront: true }) + return this.browserWindowService.openSkyAtlas( + { + tab: BodyTabType.SKY_OBJECT, + filter: { rightAscension: this.currentComputedLocation.rightAscensionJ2000, declination: this.currentComputedLocation.declinationJ2000 }, + }, + { bringToFront: true }, + ) }, }, ] - readonly targetCoordinateModel: SlideMenuItem[] = [ + protected readonly targetCoordinateModel: SlideMenuItem[] = [ { icon: 'mdi mdi-telescope', label: 'Go To', slideMenu: [], command: () => { this.targetCoordinateCommand = this.targetCoordinateModel[0] - return this.goTo() + this.savePreference() }, }, { @@ -98,7 +77,7 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { slideMenu: [], command: () => { this.targetCoordinateCommand = this.targetCoordinateModel[1] - return this.slewTo() + this.savePreference() }, }, { @@ -107,7 +86,7 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { slideMenu: [], command: () => { this.targetCoordinateCommand = this.targetCoordinateModel[2] - return this.sync() + this.savePreference() }, }, { @@ -115,8 +94,13 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { label: 'Frame', slideMenu: [], command: () => { - const data: FramingData = { rightAscension: this.targetRightAscension, declination: this.targetDeclination } - return this.browserWindow.openFraming(data) + const { targetRightAscension, targetDeclination, targetCoordinateType } = this.preference + + if (targetCoordinateType === 'J2000') { + return this.browserWindowService.openFraming({ rightAscension: targetRightAscension, declination: targetDeclination }) + } else { + return this.browserWindowService.openFraming({ rightAscension: this.targetComputedLocation.rightAscensionJ2000, declination: this.targetComputedLocation.declinationJ2000 }) + } }, }, SEPARATOR_MENU_ITEM, @@ -129,9 +113,9 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { label: 'Current location', slideMenu: [], command: () => { - this.targetRightAscension = this.rightAscension - this.targetDeclination = this.declination - this.targetCoordinateType = 'JNOW' + this.preference.targetRightAscension = this.mount.rightAscension + this.preference.targetDeclination = this.mount.declination + this.preference.targetCoordinateType = 'JNOW' }, }, { @@ -139,9 +123,9 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { label: 'Current location (J2000)', slideMenu: [], command: () => { - this.targetRightAscension = this.rightAscensionJ2000 - this.targetDeclination = this.declinationJ2000 - this.targetCoordinateType = 'J2000' + this.preference.targetRightAscension = this.currentComputedLocation.rightAscensionJ2000 + this.preference.targetDeclination = this.currentComputedLocation.declinationJ2000 + this.preference.targetCoordinateType = 'J2000' }, }, { @@ -217,30 +201,22 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { }, ] - targetCoordinateCommand = this.targetCoordinateModel[0] - - readonly remoteControl: MountRemoteControlDialog = { - showDialog: false, - type: 'LX200', - host: '0.0.0.0', - port: 10001, - data: [], - } + protected targetCoordinateCommand = this.targetCoordinateModel[0] constructor( private readonly app: AppComponent, private readonly api: ApiService, - private readonly browserWindow: BrowserWindowService, - electron: ElectronService, - private readonly preference: PreferenceService, + private readonly browserWindowService: BrowserWindowService, + electronService: ElectronService, + private readonly preferenceService: PreferenceService, private readonly route: ActivatedRoute, - private readonly prime: PrimeService, - private readonly pinger: Pinger, + private readonly angularService: AngularService, + private readonly ticker: Ticker, ngZone: NgZone, ) { app.title = 'Mount' - electron.on('MOUNT.UPDATED', async (event) => { + electronService.on('MOUNT.UPDATED', async (event) => { if (event.device.id === this.mount.id) { await ngZone.run(async () => { const wasConnected = this.mount.connected @@ -254,10 +230,10 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { } }) - electron.on('MOUNT.DETACHED', (event) => { + electronService.on('MOUNT.DETACHED', (event) => { if (event.device.id === this.mount.id) { ngZone.run(() => { - Object.assign(this.mount, EMPTY_MOUNT) + Object.assign(this.mount, DEFAULT_MOUNT) }) } }) @@ -315,15 +291,15 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { ngAfterContentInit() { this.route.queryParams.subscribe(async (e) => { - const mount = JSON.parse(decodeURIComponent(e['data'] as string)) as Mount - await this.mountChanged(mount) - this.pinger.register(this, 30000) + const data = JSON.parse(decodeURIComponent(e['data'] as string)) as Mount + await this.mountChanged(data) + this.ticker.register(this, 30000) }) } @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) + this.ticker.unregister(this) this.computeCoordinateSubscriptions.forEach((e) => { e.unsubscribe() @@ -332,13 +308,13 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { void this.abort() } - async ping() { + async tick() { if (this.mount.id) { await this.api.mountListen(this.mount) } } - async mountChanged(mount?: Mount) { + protected async mountChanged(mount?: Mount) { if (mount?.id) { mount = await this.api.mount(mount.id) Object.assign(this.mount, mount) @@ -350,7 +326,7 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { this.app.subTitle = mount?.name ?? '' } - connect() { + protected connect() { if (this.mount.connected) { return this.api.mountDisconnect(this.mount) } else { @@ -358,41 +334,44 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { } } - async showRemoteControlDialog() { - this.remoteControl.data = await this.api.mountRemoteControlList(this.mount) + protected async showRemoteControlDialog() { + this.remoteControl.controls = await this.api.mountRemoteControlList(this.mount) this.remoteControl.showDialog = true } - async startRemoteControl() { + protected async startRemoteControl() { try { - await this.api.mountRemoteControlStart(this.mount, this.remoteControl.type, this.remoteControl.host, this.remoteControl.port) - this.remoteControl.data = await this.api.mountRemoteControlList(this.mount) + await this.api.mountRemoteControlStart(this.mount, this.remoteControl.protocol, this.remoteControl.host, this.remoteControl.port) + this.remoteControl.controls = await this.api.mountRemoteControlList(this.mount) } catch { - this.prime.message('Failed to start remote control', 'error') + this.angularService.message('Failed to start remote control', 'error') } } - async stopRemoteControl(type: MountRemoteControlType) { - await this.api.mountRemoteControlStop(this.mount, type) - this.remoteControl.data = await this.api.mountRemoteControlList(this.mount) + protected async stopRemoteControl(protocol: MountRemoteControlProtocol) { + await this.api.mountRemoteControlStop(this.mount, protocol) + this.remoteControl.controls = await this.api.mountRemoteControlList(this.mount) } - async goTo() { - await this.api.mountGoTo(this.mount, this.targetRightAscension, this.targetDeclination, this.targetCoordinateType === 'J2000') + protected async goTo() { + const { targetRightAscension, targetDeclination, targetCoordinateType } = this.preference + await this.api.mountGoTo(this.mount, targetRightAscension, targetDeclination, targetCoordinateType === 'J2000') this.savePreference() } - async slewTo() { - await this.api.mountSlew(this.mount, this.targetRightAscension, this.targetDeclination, this.targetCoordinateType === 'J2000') + protected async slewTo() { + const { targetRightAscension, targetDeclination, targetCoordinateType } = this.preference + await this.api.mountSlew(this.mount, targetRightAscension, targetDeclination, targetCoordinateType === 'J2000') this.savePreference() } - async sync() { - await this.api.mountSync(this.mount, this.targetRightAscension, this.targetDeclination, this.targetCoordinateType === 'J2000') + protected async sync() { + const { targetRightAscension, targetDeclination, targetCoordinateType } = this.preference + await this.api.mountSync(this.mount, targetRightAscension, targetDeclination, targetCoordinateType === 'J2000') this.savePreference() } - async targetCoordinateCommandClicked() { + protected async targetCoordinateCommandClicked() { if (this.targetCoordinateCommand === this.targetCoordinateModel[0]) { await this.goTo() } else if (this.targetCoordinateCommand === this.targetCoordinateModel[1]) { @@ -402,7 +381,7 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { } } - moveTo(direction: MoveDirectionType, pressed: boolean, event?: MouseEvent) { + protected moveTo(direction: MountSlewDirection, pressed: boolean, event?: MouseEvent) { if (!event || event.button === 0) { this.slewingDirection = pressed ? direction : undefined @@ -442,50 +421,40 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { } } - abort() { + protected abort() { return this.api.mountAbort(this.mount) } - trackingToggled() { + protected trackingToggled() { return this.api.mountTracking(this.mount, this.tracking) } - trackModeChanged() { + protected trackModeChanged() { return this.api.mountTrackMode(this.mount, this.trackMode) } - async slewRateChanged() { + protected async slewRateChanged() { if (this.slewRate) { await this.api.mountSlewRate(this.mount, this.slewRate) } } - park() { + protected park() { return this.api.mountPark(this.mount) } - unpark() { + protected unpark() { return this.api.mountUnpark(this.mount) } - home() { + protected home() { return this.api.mountHome(this.mount) } private update() { if (this.mount.id) { - this.slewing = this.mount.slewing - this.parking = this.mount.parking - this.parked = this.mount.parked - this.canPark = this.mount.canPark - this.canHome = this.mount.canHome - this.trackModes = this.mount.trackModes this.trackMode = this.mount.trackMode - this.slewRates = this.mount.slewRates this.slewRate = this.mount.slewRate - this.rightAscension = this.mount.rightAscension - this.declination = this.mount.declination - this.pierSide = this.mount.pierSide this.tracking = this.mount.tracking this.computeCoordinatePublisher.next() @@ -494,56 +463,43 @@ export class MountComponent implements AfterContentInit, OnDestroy, Pingable { private async computeCoordinates() { if (this.mount.connected) { - const computedCoordinates = await this.api.mountComputeLocation(this.mount, false, this.mount.rightAscension, this.mount.declination, true, true, true) - this.rightAscensionJ2000 = computedCoordinates.rightAscensionJ2000 - this.declinationJ2000 = computedCoordinates.declinationJ2000 - this.azimuth = computedCoordinates.azimuth - this.altitude = computedCoordinates.altitude - this.constellation = computedCoordinates.constellation - this.meridianAt = computedCoordinates.meridianAt - this.timeLeftToMeridianFlip = computedCoordinates.timeLeftToMeridianFlip - this.lst = computedCoordinates.lst + Object.assign(this.currentComputedLocation, await this.api.mountComputeLocation(this.mount, false, this.mount.rightAscension, this.mount.declination, true, true, true)) } } - async computeTargetCoordinates() { + protected async computeTargetCoordinates() { if (this.mount.connected) { - const computedLocation = await this.api.mountComputeLocation(this.mount, this.targetCoordinateType === 'J2000', this.targetRightAscension, this.targetDeclination, true, true, true) - this.targetComputedLocation = computedLocation + const { targetRightAscension, targetDeclination, targetCoordinateType } = this.preference + Object.assign(this.targetComputedLocation, await this.api.mountComputeLocation(this.mount, targetCoordinateType === 'J2000', targetRightAscension, targetDeclination, true, true, true)) } } private updateTargetCoordinate(coordinates: ComputedLocation) { - if (this.targetCoordinateType === 'J2000') { - this.targetRightAscension = coordinates.rightAscensionJ2000 - this.targetDeclination = coordinates.declinationJ2000 + if (this.preference.targetCoordinateType === 'J2000') { + this.preference.targetRightAscension = coordinates.rightAscensionJ2000 + this.preference.targetDeclination = coordinates.declinationJ2000 } else { - this.targetRightAscension = coordinates.rightAscension - this.targetDeclination = coordinates.declination + this.preference.targetRightAscension = coordinates.rightAscension + this.preference.targetDeclination = coordinates.declination } + this.savePreference() + this.computeTargetCoordinatePublisher.next() } private loadPreference() { if (this.mount.id) { - const mountPreference: Partial = this.preference.mountPreference(this.mount).get() - this.targetCoordinateType = mountPreference.targetCoordinateType ?? 'JNOW' - this.targetRightAscension = mountPreference.targetRightAscension ?? '00h00m00s' - this.targetDeclination = mountPreference.targetDeclination ?? `00°00'00"` + Object.assign(this.preference, this.preferenceService.mount(this.mount).get()) + this.targetCoordinateCommand = this.targetCoordinateModel[this.preference.targetCoordinateCommand] ?? this.targetCoordinateModel[0] this.computeTargetCoordinatePublisher.next() } } private savePreference() { if (this.mount.connected) { - const preference: MountPreference = { - targetCoordinateType: this.targetCoordinateType, - targetRightAscension: this.targetRightAscension, - targetDeclination: this.targetDeclination, - } - - this.preference.mountPreference(this.mount).set(preference) + this.preference.targetCoordinateCommand = this.targetCoordinateModel.indexOf(this.targetCoordinateCommand) + this.preferenceService.mount(this.mount).set(this.preference) } } } diff --git a/desktop/src/app/rotator/rotator.component.html b/desktop/src/app/rotator/rotator.component.html index 7e9cc5dfd..1e415be90 100644 --- a/desktop/src/app/rotator/rotator.component.html +++ b/desktop/src/app/rotator/rotator.component.html @@ -11,7 +11,7 @@
- {{ moving ? 'moving' : 'idle' }} + {{ rotator.moving ? 'moving' : 'idle' }}
@@ -45,13 +45,13 @@ styleClass="p-inputtext-sm border-0 max-w-full" [(ngModel)]="rotator.angle" locale="en" - scrollableNumber /> + spinnableNumber />
Reversed + [(ngModel)]="rotator.reversed" />
@@ -85,14 +85,15 @@ [max]="rotator.maxAngle" [showButtons]="true" styleClass="p-inputtext-sm border-0 max-w-full" - [(ngModel)]="angle" + [(ngModel)]="preference.angle" + (ngModelChange)="savePreference()" [allowEmpty]="false" locale="en" - scrollableNumber /> + spinnableNumber /> { + electronService.on('ROTATOR.UPDATED', (event) => { if (event.device.id === this.rotator.id) { ngZone.run(() => { Object.assign(this.rotator, event.device) @@ -39,10 +35,10 @@ export class RotatorComponent implements AfterViewInit, OnDestroy, Pingable { } }) - electron.on('ROTATOR.DETACHED', (event) => { + electronService.on('ROTATOR.DETACHED', (event) => { if (event.device.id === this.rotator.id) { ngZone.run(() => { - Object.assign(this.rotator, EMPTY_ROTATOR) + Object.assign(this.rotator, DEFAULT_ROTATOR) }) } }) @@ -50,25 +46,25 @@ export class RotatorComponent implements AfterViewInit, OnDestroy, Pingable { ngAfterViewInit() { this.route.queryParams.subscribe(async (e) => { - const rotator = JSON.parse(decodeURIComponent(e['data'] as string)) as Rotator - await this.rotatorChanged(rotator) - this.pinger.register(this, 30000) + const data = JSON.parse(decodeURIComponent(e['data'] as string)) as Rotator + await this.rotatorChanged(data) + this.ticker.register(this, 30000) }) } @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) + this.ticker.unregister(this) void this.abort() } - async ping() { + async tick() { if (this.rotator.id) { await this.api.rotatorListen(this.rotator) } } - async rotatorChanged(rotator?: Rotator) { + protected async rotatorChanged(rotator?: Rotator) { if (rotator?.id) { rotator = await this.api.rotator(rotator.id) Object.assign(this.rotator, rotator) @@ -80,7 +76,7 @@ export class RotatorComponent implements AfterViewInit, OnDestroy, Pingable { this.app.subTitle = rotator?.name ?? '' } - connect() { + protected connect() { if (this.rotator.connected) { return this.api.rotatorDisconnect(this.rotator) } else { @@ -88,52 +84,37 @@ export class RotatorComponent implements AfterViewInit, OnDestroy, Pingable { } } - reverse(enabled: boolean) { + protected reverse(enabled: boolean) { return this.api.rotatorReverse(this.rotator, enabled) } - async move() { - if (!this.moving) { - this.moving = true - await this.api.rotatorMove(this.rotator, this.angle) - this.savePreference() - } + protected move() { + return this.api.rotatorMove(this.rotator, this.preference.angle) } - async sync() { - if (!this.moving) { - await this.api.rotatorSync(this.rotator, this.angle) - this.savePreference() - } + protected sync() { + return this.api.rotatorSync(this.rotator, this.preference.angle) } - abort() { + protected abort() { return this.api.rotatorAbort(this.rotator) } - home() { + protected home() { return this.api.rotatorHome(this.rotator) } - private update() { - if (this.rotator.id) { - this.moving = this.rotator.moving - this.reversed = this.rotator.reversed - } - } + private update() {} private loadPreference() { if (this.rotator.id) { - const preference = this.preference.rotatorPreference(this.rotator).get() - this.angle = preference.angle ?? 0 + Object.assign(this.preference, this.preferenceService.rotator(this.rotator).get()) } } - private savePreference() { + protected savePreference() { if (this.rotator.connected) { - const preference = this.preference.rotatorPreference(this.rotator).get() - preference.angle = this.angle - this.preference.rotatorPreference(this.rotator).set(preference) + this.preferenceService.rotator(this.rotator).set(this.preference) } } } diff --git a/desktop/src/app/sequencer/sequencer.component.html b/desktop/src/app/sequencer/sequencer.component.html index 815df01fb..13ddc495a 100644 --- a/desktop/src/app/sequencer/sequencer.component.html +++ b/desktop/src/app/sequencer/sequencer.component.html @@ -18,8 +18,8 @@ locale="en" styleClass="p-inputtext-sm border-0" [allowEmpty]="false" - (ngModelChange)="savePlan()" - scrollableNumber /> + (ngModelChange)="savePreference()" + spinnableNumber />
@@ -27,12 +27,12 @@ + (ngModelChange)="savePreference()" /> @@ -40,7 +40,7 @@ @@ -59,10 +59,10 @@ tooltipPosition="bottom" [positionTop]="8"> @@ -70,13 +70,13 @@
- + Dither + (ngModelChange)="savePreference()" />
@@ -86,7 +86,7 @@ [disabled]="running || !plan.dither.enabled" label="RA only" [(ngModel)]="plan.dither.raOnly" - (ngModelChange)="savePlan()" /> + (ngModelChange)="savePreference()" />
@@ -100,8 +100,8 @@ [step]="0.1" locale="en" [minFractionDigits]="1" - (ngModelChange)="savePlan()" - scrollableNumber /> + (ngModelChange)="savePreference()" + spinnableNumber />
@@ -115,8 +115,8 @@ [max]="1000" [(ngModel)]="plan.dither.afterExposures" [step]="1" - (ngModelChange)="savePlan()" - scrollableNumber /> + (ngModelChange)="savePreference()" + spinnableNumber /> @@ -131,7 +131,7 @@ [binary]="true" [disabled]="running" [(ngModel)]="plan.autoFocus.enabled" - (ngModelChange)="savePlan()" /> + (ngModelChange)="savePreference()" />
@@ -140,19 +140,19 @@ [disabled]="running || !plan.autoFocus.enabled" label="On start" [(ngModel)]="plan.autoFocus.onStart" - (ngModelChange)="savePlan()" /> + (ngModelChange)="savePreference()" /> + (ngModelChange)="savePreference()" />
+ (ngModelChange)="savePreference()" /> + (ngModelChange)="savePreference()" + spinnableNumber />
@@ -172,7 +172,7 @@ [binary]="true" [disabled]="running || !plan.autoFocus.enabled" [(ngModel)]="plan.autoFocus.afterExposuresEnabled" - (ngModelChange)="savePlan()" /> + (ngModelChange)="savePreference()" /> + (ngModelChange)="savePreference()" + spinnableNumber />
@@ -192,7 +192,7 @@ [binary]="true" [disabled]="running || !plan.autoFocus.enabled" [(ngModel)]="plan.autoFocus.afterTemperatureChangeEnabled" - (ngModelChange)="savePlan()" /> + (ngModelChange)="savePreference()" /> + (ngModelChange)="savePreference()" + spinnableNumber /> @@ -212,7 +212,7 @@ [binary]="true" [disabled]="running || !plan.autoFocus.enabled" [(ngModel)]="plan.autoFocus.afterHFDIncreaseEnabled" - (ngModelChange)="savePlan()" /> + (ngModelChange)="savePreference()" /> + (ngModelChange)="savePreference()" + spinnableNumber />
+ +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+
-
+
-
+
+ {{ event.state | enum }} @@ -311,7 +382,7 @@ cdkDropList (cdkDropListDropped)="drop($event)">
@@ -322,36 +393,36 @@
- + (onClick)="showCameraDialog(sequence)" size="small" /> -->
+ (click)="showSequenceMenu(sequence, entryMenu)" /> + (onClick)="deleteSequence(sequence, i)" /> + (onClick)="duplicateSequence(sequence, i)" /> + [(ngModel)]="sequence.enabled" + (ngModelChange)="savePreference()" />
+ [info]="sequence" + [wheel]="plan.wheel" + [canRemoveFilter]="sequence.enabled" + (filterRemoved)="filterRemoved(sequence)" />
@@ -394,22 +467,40 @@
-
+ @if (pausingOrPaused) { + + } @else if (!running) { + + }
-
@@ -442,96 +532,84 @@ [text]="true" icon="mdi mdi-checkbox-marked" label="Select All" - (onClick)="updateAllAvailableEntryPropertiesToApply(true)" /> + (onClick)="selectSequenceProperty(true)" /> + (onClick)="selectSequenceProperty(false)" />
+ [(ngModel)]="property.properties.EXPOSURE_TIME" />
+ [(ngModel)]="property.properties.EXPOSURE_AMOUNT" />
+ [(ngModel)]="property.properties.EXPOSURE_DELAY" />
+ [(ngModel)]="property.properties.FRAME_TYPE" />
+ [(ngModel)]="property.properties.X" />
+ [(ngModel)]="property.properties.Y" />
+ [(ngModel)]="property.properties.WIDTH" />
+ [(ngModel)]="property.properties.HEIGHT" />
+ [(ngModel)]="property.properties.BIN" />
+ [(ngModel)]="property.properties.FRAME_FORMAT" />
+ [(ngModel)]="property.properties.GAIN" />
+ [(ngModel)]="property.properties.OFFSET" />
@@ -540,11 +618,11 @@ icon="mdi mdi-check" label="Apply" size="small" - (onClick)="applyCameraStartCaptureToEntries()" /> + (onClick)="copySequencePropertyToSequencies()" />
diff --git a/desktop/src/app/sequencer/sequencer.component.scss b/desktop/src/app/sequencer/sequencer.component.scss index bf78461c8..bbe2b5908 100644 --- a/desktop/src/app/sequencer/sequencer.component.scss +++ b/desktop/src/app/sequencer/sequencer.component.scss @@ -1,24 +1,22 @@ -:host { - ::ng-deep { - .p-card { - .p-card-body { - padding: 0px; - padding-left: 1rem !important; - padding-right: 1rem !important; - } - - .p-card-content { - padding: 0px; - padding-top: 0.5rem !important; - padding-bottom: 0.5rem !important; - } +neb-sequencer { + .p-card { + .p-card-body { + padding: 0px; + padding-left: 1rem !important; + padding-right: 1rem !important; } - .p-orderlist-controls { - display: none; + .p-card-content { + padding: 0px; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; } } + .p-orderlist-controls { + display: none; + } + .mdi.mdi-progress-indicator:before { font-size: 11px; } diff --git a/desktop/src/app/sequencer/sequencer.component.ts b/desktop/src/app/sequencer/sequencer.component.ts index 48ee6940f..c1a92261b 100644 --- a/desktop/src/app/sequencer/sequencer.component.ts +++ b/desktop/src/app/sequencer/sequencer.component.ts @@ -1,63 +1,56 @@ import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop' -import { AfterContentInit, Component, HostListener, NgZone, OnDestroy, QueryList, ViewChildren } from '@angular/core' +import { AfterContentInit, Component, HostListener, NgZone, OnDestroy, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core' +import { dirname } from 'path' import { CameraExposureComponent } from '../../shared/components/camera-exposure/camera-exposure.component' import { DialogMenuComponent } from '../../shared/components/dialog-menu/dialog-menu.component' -import { SlideMenuItem } from '../../shared/components/menu-item/menu-item.component' +import { MenuItem, SlideMenuItem } from '../../shared/components/menu-item/menu-item.component' +import { AngularService } from '../../shared/services/angular.service' import { ApiService } from '../../shared/services/api.service' import { BrowserWindowService } from '../../shared/services/browser-window.service' import { ElectronService } from '../../shared/services/electron.service' -import { LocalStorageService } from '../../shared/services/local-storage.service' -import { Pingable, Pinger } from '../../shared/services/pinger.service' -import { PrimeService } from '../../shared/services/prime.service' +import { PreferenceService } from '../../shared/services/preference.service' +import { Tickable, Ticker } from '../../shared/services/ticker.service' import { JsonFile } from '../../shared/types/app.types' -import { Camera, CameraCaptureEvent, CameraStartCapture, updateCameraStartCaptureFromCamera } from '../../shared/types/camera.types' +import { Camera, cameraCaptureNamingFormatWithDefault, FrameType, updateCameraStartCaptureFromCamera } from '../../shared/types/camera.types' import { Focuser } from '../../shared/types/focuser.types' import { Mount } from '../../shared/types/mount.types' import { Rotator } from '../../shared/types/rotator.types' -import { EMPTY_SEQUENCE_PLAN, SEQUENCE_ENTRY_PROPERTIES, SequenceCaptureMode, SequenceEntryProperty, SequencePlan, SequencerEvent } from '../../shared/types/sequencer.types' -import { FilterWheel } from '../../shared/types/wheel.types' +import { DEFAULT_SEQUENCE, DEFAULT_SEQUENCE_PROPERTY_DIALOG, DEFAULT_SEQUENCER_PLAN, DEFAULT_SEQUENCER_PREFERENCE, Sequence, SequenceProperty, SequencerEvent, SequencerPlan } from '../../shared/types/sequencer.types' +import { resetCameraCaptureNamingFormat } from '../../shared/types/settings.types' +import { Wheel } from '../../shared/types/wheel.types' import { deviceComparator } from '../../shared/utils/comparators' -import { Undefinable } from '../../shared/utils/types' import { AppComponent } from '../app.component' import { CameraComponent } from '../camera/camera.component' import { FilterWheelComponent } from '../filterwheel/filterwheel.component' -export const SEQUENCER_SAVED_PATH_KEY = 'sequencer.savedPath' -export const SEQUENCER_PLAN_KEY = 'sequencer.plan' - @Component({ - selector: 'app-sequencer', + selector: 'neb-sequencer', templateUrl: './sequencer.component.html', styleUrls: ['./sequencer.component.scss'], + encapsulation: ViewEncapsulation.None, }) -export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable { - cameras: Camera[] = [] - mounts: Mount[] = [] - wheels: FilterWheel[] = [] - focusers: Focuser[] = [] - rotators: Rotator[] = [] - - camera?: Camera - mount?: Mount - wheel?: FilterWheel - focuser?: Focuser - rotator?: Rotator - - readonly captureModes: SequenceCaptureMode[] = ['FULLY', 'INTERLEAVED'] - readonly plan = structuredClone(EMPTY_SEQUENCE_PLAN) - - private entryToApply?: CameraStartCapture - private entryToApplyCount: [number, number] = [0, 0] - readonly availableEntryPropertiesToApply = new Map() - showEntryPropertiesToApplyDialog = false - readonly entryMenuModel: SlideMenuItem[] = [ +export class SequencerComponent implements AfterContentInit, OnDestroy, Tickable { + protected cameras: Camera[] = [] + protected mounts: Mount[] = [] + protected wheels: Wheel[] = [] + protected focusers: Focuser[] = [] + protected rotators: Rotator[] = [] + + protected readonly property = structuredClone(DEFAULT_SEQUENCE_PROPERTY_DIALOG) + protected readonly preference = structuredClone(DEFAULT_SEQUENCER_PREFERENCE) + protected plan = this.preference.plan + protected event?: SequencerEvent + protected running = false + + // NOTE: Remove the "plan.sequences.length <= 1" on layout if add more options + protected readonly sequenceModel: SlideMenuItem[] = [ { icon: 'mdi mdi-content-copy', label: 'Apply to all', slideMenu: [], command: () => { - this.entryToApplyCount = [-1000, 1000] - this.showEntryPropertiesToApplyDialog = true + this.property.count = [-1000, 1000] + this.property.showDialog = true }, }, { @@ -65,8 +58,8 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable label: 'Apply to all above', slideMenu: [], command: () => { - this.entryToApplyCount = [-1000, 0] - this.showEntryPropertiesToApplyDialog = true + this.property.count = [-1000, 0] + this.property.showDialog = true }, }, { @@ -74,8 +67,8 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable label: 'Apply to above', slideMenu: [], command: () => { - this.entryToApplyCount = [-1, 0] - this.showEntryPropertiesToApplyDialog = true + this.property.count = [-1, 0] + this.property.showDialog = true }, }, { @@ -83,8 +76,8 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable label: 'Apply to below', slideMenu: [], command: () => { - this.entryToApplyCount = [1, 0] - this.showEntryPropertiesToApplyDialog = true + this.property.count = [1, 0] + this.property.showDialog = true }, }, { @@ -92,109 +85,103 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable label: 'Apply to all below', slideMenu: [], command: () => { - this.entryToApplyCount = [1000, 0] - this.showEntryPropertiesToApplyDialog = true + this.property.count = [1000, 0] + this.property.showDialog = true }, }, ] - readonly sequenceEvents: CameraCaptureEvent[] = [] + private readonly createNewMenuItem: MenuItem = { + icon: 'mdi mdi-plus', + label: 'Create new', + command: () => { + this.preference.loadPath = undefined + this.savePreference() - event?: SequencerEvent - running = false + this.app.subTitle = undefined + this.saveMenuItem.visible = false + this.saveMenuItem.disabled = true - @ViewChildren('cameraExposure') - private readonly cameraExposures!: QueryList + if (!this.loadPlan(structuredClone(DEFAULT_SEQUENCER_PLAN))) { + this.add() + } + }, + } - get canStart() { - return !!this.camera && this.camera.connected && !!this.plan.entries.find((e) => e.enabled) + private readonly saveMenuItem: MenuItem = { + icon: 'mdi mdi-content-save', + label: 'Save', + visible: false, + command: () => this.savePlanToJson(false), } - get savedPath() { - return this.app.subTitle + private readonly saveAsMenuItem: MenuItem = { + icon: 'mdi mdi-content-save-edit', + label: 'Save as', + command: () => this.savePlanToJson(true), } - set savedPath(value: Undefinable) { - this.app.subTitle = value + private readonly loadMenuItem: MenuItem = { + icon: 'mdi mdi-folder-open', + label: 'Load', + command: async () => { + const defaultPath = this.preference.loadPath ? dirname(this.preference.loadPath) : undefined + const file = await this.electronService.openJson({ defaultPath }) + + if (file !== false) { + this.loadPlanFromJson(file) + } + }, } - get savedPathWasModified() { - return !!this.app.topMenu[1].badge + @ViewChildren('cameraExposure') + private readonly cameraExposures!: QueryList + + get canStart() { + return !!this.plan.camera?.connected && !!this.plan.sequences.find((e) => e.enabled) } - set savedPathWasModified(value: boolean) { - this.app.topMenu[1].badge = value ? '1' : undefined + get pausingOrPaused() { + return this.event?.state === 'PAUSING' || this.event?.state === 'PAUSED' } constructor( private readonly app: AppComponent, private readonly api: ApiService, - private readonly browserWindow: BrowserWindowService, - private readonly electron: ElectronService, - private readonly storage: LocalStorageService, - private readonly prime: PrimeService, - private readonly pinger: Pinger, + private readonly browserWindowService: BrowserWindowService, + private readonly electronService: ElectronService, + private readonly preferenceService: PreferenceService, + private readonly angularService: AngularService, + private readonly ticker: Ticker, ngZone: NgZone, ) { app.title = 'Sequencer' - app.topMenu.push({ - icon: 'mdi mdi-plus', - label: 'Create new', - command: () => { - this.savedPath = undefined - this.savedPathWasModified = false - this.storage.delete(SEQUENCER_SAVED_PATH_KEY) + app.topMenu.push(this.createNewMenuItem) + app.topMenu.push(this.saveMenuItem) + app.topMenu.push(this.saveAsMenuItem) + app.topMenu.push(this.loadMenuItem) - Object.assign(this.plan, structuredClone(EMPTY_SEQUENCE_PLAN)) - this.add() - }, - }) - app.topMenu.push({ - icon: 'mdi mdi-content-save', - label: 'Save', - command: async () => { - const file = await electron.saveJson({ path: this.savedPath, json: this.plan }) - - if (file !== false) { - this.afterSavedJsonFile(file) - } - }, - }) - app.topMenu.push({ - icon: 'mdi mdi-content-save-edit', - label: 'Save as', - command: async () => { - const file = await electron.saveJson({ json: this.plan }) - - if (file !== false) { - this.afterSavedJsonFile(file) - } - }, - }) - app.topMenu.push({ - icon: 'mdi mdi-folder-open', - label: 'Load', - command: async () => { - const file = await electron.openJson() - - if (file !== false) { - this.loadSavedJsonFile(file) - } - }, - }) + app.beforeClose = async () => { + if (!this.saveMenuItem.disabled) { + return !(await angularService.confirm('Are you sure you want to close the window? Please make sure to save before exiting to avoid losing any important changes.')) + } else { + return true + } + } - electron.on('CAMERA.UPDATED', (event) => { + electronService.on('CAMERA.UPDATED', (event) => { const camera = this.cameras.find((e) => e.id === event.device.id) if (camera) { ngZone.run(() => { Object.assign(camera, event.device) + this.updateSequencesFromCamera(camera) }) } }) - electron.on('MOUNT.UPDATED', (event) => { + electronService.on('MOUNT.UPDATED', (event) => { const mount = this.mounts.find((e) => e.id === event.device.id) if (mount) { @@ -204,7 +191,7 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable } }) - electron.on('WHEEL.UPDATED', (event) => { + electronService.on('WHEEL.UPDATED', (event) => { const wheel = this.wheels.find((e) => e.id === event.device.id) if (wheel) { @@ -214,7 +201,7 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable } }) - electron.on('FOCUSER.UPDATED', (event) => { + electronService.on('FOCUSER.UPDATED', (event) => { const focuser = this.focusers.find((e) => e.id === event.device.id) if (focuser) { @@ -224,7 +211,7 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable } }) - electron.on('ROTATOR.UPDATED', (event) => { + electronService.on('ROTATOR.UPDATED', (event) => { const rotator = this.rotators.find((e) => e.id === event.device.id) if (rotator) { @@ -234,7 +221,7 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable } }) - electron.on('SEQUENCER.ELAPSED', (event) => { + electronService.on('SEQUENCER.ELAPSED', (event) => { ngZone.run(() => { if (this.running !== event.remainingTime > 0) { this.enableOrDisableTopbarMenu(event.remainingTime <= 0) @@ -251,14 +238,10 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable } }) }) - - for (const p of SEQUENCE_ENTRY_PROPERTIES) { - this.availableEntryPropertiesToApply.set(p, true) - } } async ngAfterContentInit() { - this.pinger.register(this, 30000) + this.ticker.register(this, 30000) this.cameras = (await this.api.cameras()).sort(deviceComparator) this.mounts = (await this.api.mounts()).sort(deviceComparator) @@ -266,127 +249,140 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable this.focusers = (await this.api.focusers()).sort(deviceComparator) this.rotators = (await this.api.rotators()).sort(deviceComparator) - await this.loadSavedJsonFileFromPathOrAddDefault() + this.loadPreference() - // this.route.queryParams.subscribe(e => { }) + await this.loadPlanFromPath() } @HostListener('window:unload') ngOnDestroy() { - this.pinger.unregister(this) + this.ticker.unregister(this) } - async ping() { - if (this.camera?.id) await this.api.cameraListen(this.camera) - if (this.mount?.id) await this.api.mountListen(this.mount) - if (this.focuser?.id) await this.api.focuserListen(this.focuser) - if (this.wheel?.id) await this.api.wheelListen(this.wheel) - if (this.rotator?.id) await this.api.rotatorListen(this.rotator) + async tick() { + if (this.plan.camera?.id) await this.api.cameraListen(this.plan.camera) + if (this.plan.mount?.id) await this.api.mountListen(this.plan.mount) + if (this.plan.focuser?.id) await this.api.focuserListen(this.plan.focuser) + if (this.plan.wheel?.id) await this.api.wheelListen(this.plan.wheel) + if (this.plan.rotator?.id) await this.api.rotatorListen(this.plan.rotator) } - private enableOrDisableTopbarMenu(enable: boolean) { - this.app.topMenu.forEach((e) => (e.disabled = !enable)) + private enableOrDisableTopbarMenu(enabled: boolean) { + this.createNewMenuItem.disabled = !enabled + this.loadMenuItem.disabled = !enabled } - add() { - const camera = this.camera ?? (this.cameras[0] as Undefinable) - // const wheel = this.wheel ?? this.wheels[0] - // const focuser = this.focuser ?? this.focusers[0] - // const rotator = this.rotator ?? this.rotators[0] + protected add() { + const camera = this.plan.camera - this.plan.entries.push({ - enabled: true, - exposureTime: 1000000, - exposureAmount: 1, - exposureDelay: 0, + const sequence: Sequence = { + ...structuredClone(DEFAULT_SEQUENCE), x: camera?.minX ?? 0, y: camera?.minY ?? 0, width: camera?.maxWidth ?? 0, height: camera?.maxHeight ?? 0, - frameType: 'LIGHT', - binX: 1, - binY: 1, - gain: 0, - offset: 0, frameFormat: camera?.frameFormats[0], - autoSave: true, - autoSubFolderMode: 'OFF', - dither: { - enabled: false, - amount: 0, - raOnly: false, - afterExposures: 0, - }, - liveStacking: { - enabled: false, - type: 'SIRIL', - executablePath: '', - use32Bits: false, - slot: 1, - }, - }) + } - this.savePlan() - } + if (camera?.connected) { + updateCameraStartCaptureFromCamera(sequence, camera) + } + + this.plan.sequences.push(sequence) - drop(event: CdkDragDrop) { - moveItemInArray(this.plan.entries, event.previousIndex, event.currentIndex) + this.savePreference() } - private afterSavedJsonFile(file: JsonFile) { - if (file.path) { - this.savedPath = file.path - this.storage.set(SEQUENCER_SAVED_PATH_KEY, this.savedPath) - this.savedPathWasModified = false + protected drop(event: CdkDragDrop) { + if (event.previousIndex !== event.currentIndex) { + moveItemInArray(this.plan.sequences, event.previousIndex, event.currentIndex) + this.savePreference() } } - private loadSavedJsonFile(file: JsonFile) { - if (this.loadPlan(file.json)) { - this.afterSavedJsonFile(file) - } else { - this.prime.message(`No entry found for the saved Sequence at: ${file.path}`, 'warn') - + private loadPlanFromJson(file: JsonFile) { + if (!this.loadPlan(file.json)) { + this.angularService.message('No sequence found', 'warn') this.add() } - } - private async loadSavedJsonFileFromPathOrAddDefault() { - const savedPath = this.storage.get>(SEQUENCER_SAVED_PATH_KEY, undefined) + this.preference.loadPath = file.path + this.savePreference() - if (savedPath) { - const file = await this.electron.readJson(savedPath) + this.app.subTitle = file.path + this.saveMenuItem.visible = !!file.path + this.saveMenuItem.disabled = true + } - if (file !== false) { - this.loadSavedJsonFile(file) + private async loadPlanFromPath() { + if (this.preference.loadPath) { + const file = await this.electronService.readJson(this.preference.loadPath) + + if (file !== false && file.path) { + this.loadPlanFromJson(file) return } - this.prime.message(`Failed to load the saved Sequence at: ${savedPath}`, 'error') + this.angularService.message('Failed to load the file', 'error') - this.storage.delete(SEQUENCER_SAVED_PATH_KEY) + this.preference.loadPath = undefined + this.savePreference() } - if (!this.loadPlan()) { + this.saveMenuItem.visible = false + + if (!this.loadPlan(this.plan)) { this.add() } } - private loadPlan(plan?: SequencePlan) { - plan ??= this.storage.get(SEQUENCER_PLAN_KEY, this.plan) + private loadPlan(plan: SequencerPlan) { + if (this.plan !== plan) { + Object.assign(this.plan, plan) + } + + this.plan.camera = this.cameras.find((e) => e.id === plan.camera?.id) + this.plan.mount = this.mounts.find((e) => e.id === plan.mount?.id) + this.plan.wheel = this.wheels.find((e) => e.id === plan.wheel?.id) + this.plan.focuser = this.focusers.find((e) => e.id === plan.focuser?.id) + this.plan.rotator = this.rotators.find((e) => e.id === plan.rotator?.id) + + const settings = this.preferenceService.settings.get() + cameraCaptureNamingFormatWithDefault(this.plan.namingFormat, settings.namingFormat) + + return this.plan.sequences.length + } - Object.assign(this.plan, structuredClone(plan)) + private async savePlanToJson(createNew: boolean) { + const path = createNew ? undefined : this.preference.loadPath + const file = await this.electronService.saveJson({ json: this.plan, path }) - this.camera = this.cameras.find((e) => e.name === this.plan.camera?.name) ?? this.cameras[0] - this.mount = this.mounts.find((e) => e.name === this.plan.mount?.name) ?? this.mounts[0] - this.wheel = this.wheels.find((e) => e.name === this.plan.wheel?.name) ?? this.wheels[0] - this.focuser = this.focusers.find((e) => e.name === this.plan.focuser?.name) ?? this.focusers[0] - this.rotator = this.rotators.find((e) => e.name === this.plan.rotator?.name) ?? this.rotators[0] + if (file !== false) { + this.preference.loadPath = file.path + this.savePreference() - return plan.entries.length + this.app.subTitle = file.path + this.saveMenuItem.disabled = true + } } - toggleAutoSubFolder() { + protected resetCameraCaptureNamingFormat(type?: FrameType) { + const settings = this.preferenceService.settings.get() + const cameraNamingFormat = this.plan.camera?.id ? this.preferenceService.camera(this.plan.camera).get().request.namingFormat : settings.namingFormat + + if (type) { + resetCameraCaptureNamingFormat(type, this.plan.namingFormat, cameraNamingFormat) + } else { + resetCameraCaptureNamingFormat('LIGHT', this.plan.namingFormat, cameraNamingFormat) + resetCameraCaptureNamingFormat('DARK', this.plan.namingFormat, cameraNamingFormat) + resetCameraCaptureNamingFormat('FLAT', this.plan.namingFormat, cameraNamingFormat) + resetCameraCaptureNamingFormat('BIAS', this.plan.namingFormat, cameraNamingFormat) + } + + this.savePreference() + } + + protected toggleAutoSubFolder() { if (!this.running) { switch (this.plan.autoSubFolderMode) { case 'OFF': @@ -400,167 +396,197 @@ export class SequencerComponent implements AfterContentInit, OnDestroy, Pingable break } - this.savePlan() + this.savePreference() } } - async showCameraDialog(entry: CameraStartCapture) { - if (this.camera && (await CameraComponent.showAsDialog(this.browserWindow, 'SEQUENCER', this.camera, entry))) { - this.savePlan() + protected async showCameraDialog(sequence: Sequence) { + if (this.plan.camera && (await CameraComponent.showAsDialog(this.browserWindowService, 'SEQUENCER', this.plan.camera, sequence))) { + this.savePreference() } } - async showWheelDialog(entry: CameraStartCapture) { - if (this.wheel && (await FilterWheelComponent.showAsDialog(this.browserWindow, 'SEQUENCER', this.wheel, entry))) { - this.savePlan() + protected async showWheelDialog(sequence: Sequence) { + if (this.plan.wheel && (await FilterWheelComponent.showAsDialog(this.browserWindowService, 'SEQUENCER', this.plan.wheel, sequence))) { + this.savePreference() } } - async cameraChanged() { - await this.ping() - - this.updateEntriesFromCamera(this.camera) - } - - private updateEntriesFromCamera(camera?: Camera) { + private updateSequencesFromCamera(camera?: Camera) { if (camera?.connected) { - for (const entry of this.plan.entries) { - updateCameraStartCaptureFromCamera(entry, camera) + for (const sequence of this.plan.sequences) { + updateCameraStartCaptureFromCamera(sequence, camera) } } } - mountChanged() { - return this.ping() + protected async cameraChanged() { + if (this.plan.camera) { + await this.api.cameraListen(this.plan.camera) + this.updateSequencesFromCamera(this.plan.camera) + } + + this.savePreference() } - focuserChanged() { - return this.ping() + protected async mountChanged() { + if (this.plan.mount) { + await this.api.mountListen(this.plan.mount) + } + + this.savePreference() } - wheelChanged() { - return this.ping() + protected async focuserChanged() { + if (this.plan.focuser) { + await this.api.focuserListen(this.plan.focuser) + } + + this.savePreference() } - rotatorChanged() { - return this.ping() + protected async wheelChanged() { + if (this.plan.wheel) { + await this.api.wheelListen(this.plan.wheel) + } + + this.savePreference() } - savePlan() { - this.plan.camera = this.camera - this.plan.mount = this.mount - this.plan.wheel = this.wheel - this.plan.focuser = this.focuser - this.plan.rotator = this.rotator - this.storage.set(SEQUENCER_PLAN_KEY, this.plan) - this.savedPathWasModified = !!this.savedPath + protected async rotatorChanged() { + if (this.plan.rotator) { + await this.api.rotatorListen(this.plan.rotator) + } + + this.savePreference() } - showEntryMenu(entry: CameraStartCapture, dialogMenu: DialogMenuComponent) { - this.entryToApply = entry - const index = this.plan.entries.indexOf(entry) + protected showSequenceMenu(sequence: Sequence, dialogMenu: DialogMenuComponent) { + this.property.sequence = sequence - this.entryMenuModel.forEach((e) => (e.visible = true)) + const index = this.plan.sequences.indexOf(sequence) + const lastIndex = this.plan.sequences.length - 1 - if (index === 0 || this.plan.entries.length === 1) { - // Hides all above and above. - this.entryMenuModel[1].visible = false - this.entryMenuModel[2].visible = false - } else if (index === 1) { - // Hides all above. - this.entryMenuModel[1].visible = false - } + this.sequenceModel[1].visible = index >= 2 // ALL ABOBE + this.sequenceModel[2].visible = index >= 1 // ABOBE + this.sequenceModel[3].visible = index < lastIndex // BELOW + this.sequenceModel[4].visible = index < lastIndex - 1 // ALL BELOW + this.sequenceModel[0].visible = this.sequenceModel[2].visible && this.sequenceModel[3].visible - if (index === this.plan.entries.length - 1 || this.plan.entries.length === 1) { - // Hides below and all below. - this.entryMenuModel[3].visible = false - this.entryMenuModel[4].visible = false - } else if (index === this.plan.entries.length - 2) { - // Hides all below. - this.entryMenuModel[4].visible = false + if (this.sequenceModel.find((e) => e.visible)) { + dialogMenu.show() } - - dialogMenu.show() } - updateAllAvailableEntryPropertiesToApply(selected: boolean) { - for (const p of SEQUENCE_ENTRY_PROPERTIES) { - this.availableEntryPropertiesToApply.set(p, selected) + protected selectSequenceProperty(selected: boolean) { + for (const [key] of Object.entries(this.property.properties)) { + this.property.properties[key as SequenceProperty] = selected } } - applyCameraStartCaptureToEntries() { - const source = this.entryToApply + protected copySequencePropertyToSequencies() { + const source = this.property.sequence + if (!source) return - const index = this.plan.entries.indexOf(source) - for (let count of this.entryToApplyCount) { + const index = this.plan.sequences.indexOf(source) + + for (const count of this.property.count) { if (index < 0 || count === 0) continue const below = Math.sign(count) - count = Math.abs(count) - - for (let i = 1; i <= count; i++) { + for (let i = 1; i <= Math.abs(count); i++) { const pos = index + i * below - if (pos >= 0 && pos < this.plan.entries.length) { - const dest = this.plan.entries[pos] - - if (!dest.enabled) continue - - if (this.availableEntryPropertiesToApply.get('EXPOSURE_TIME')) dest.exposureTime = source.exposureTime - if (this.availableEntryPropertiesToApply.get('EXPOSURE_AMOUNT')) dest.exposureAmount = source.exposureAmount - if (this.availableEntryPropertiesToApply.get('EXPOSURE_DELAY')) dest.exposureDelay = source.exposureDelay - if (this.availableEntryPropertiesToApply.get('FRAME_TYPE')) dest.frameType = source.frameType - if (this.availableEntryPropertiesToApply.get('X')) dest.x = source.x - if (this.availableEntryPropertiesToApply.get('Y')) dest.y = source.y - if (this.availableEntryPropertiesToApply.get('WIDTH')) dest.width = source.width - if (this.availableEntryPropertiesToApply.get('HEIGHT')) dest.height = source.height - if (this.availableEntryPropertiesToApply.get('BIN')) dest.binX = source.binX - if (this.availableEntryPropertiesToApply.get('BIN')) dest.binY = source.binY - if (this.availableEntryPropertiesToApply.get('FRAME_FORMAT')) dest.frameFormat = source.frameFormat - if (this.availableEntryPropertiesToApply.get('GAIN')) dest.gain = source.gain - if (this.availableEntryPropertiesToApply.get('OFFSET')) dest.offset = source.offset + if (pos >= 0 && pos < this.plan.sequences.length) { + const dest = this.plan.sequences[pos] + + if (!dest.enabled || dest === source) continue + + if (this.property.properties.EXPOSURE_TIME) dest.exposureTime = source.exposureTime + if (this.property.properties.EXPOSURE_AMOUNT) dest.exposureAmount = source.exposureAmount + if (this.property.properties.EXPOSURE_DELAY) dest.exposureDelay = source.exposureDelay + if (this.property.properties.FRAME_TYPE) dest.frameType = source.frameType + if (this.property.properties.X) dest.x = source.x + if (this.property.properties.Y) dest.y = source.y + if (this.property.properties.WIDTH) dest.width = source.width + if (this.property.properties.HEIGHT) dest.height = source.height + if (this.property.properties.BIN) dest.binX = source.binX + if (this.property.properties.BIN) dest.binY = source.binY + if (this.property.properties.FRAME_FORMAT) dest.frameFormat = source.frameFormat + if (this.property.properties.GAIN) dest.gain = source.gain + if (this.property.properties.OFFSET) dest.offset = source.offset } else { break } } } - this.savePlan() + this.savePreference() - this.showEntryPropertiesToApplyDialog = false + this.property.showDialog = false } - deleteEntry(entry: CameraStartCapture, index: number) { - if (entry === this.plan.entries[index]) { - this.plan.entries.splice(index, 1) - this.savePlan() + protected deleteSequence(sequence: Sequence, index: number) { + if (sequence === this.plan.sequences[index]) { + this.plan.sequences.splice(index, 1) + this.savePreference() } } - duplicateEntry(entry: CameraStartCapture, index: number) { - this.plan.entries.splice(index + 1, 0, structuredClone(entry)) - this.savePlan() + protected duplicateSequence(sequence: Sequence, index: number) { + this.plan.sequences.splice(index + 1, 0, structuredClone(sequence)) + this.savePreference() } - async start() { - if (this.camera) { + protected filterRemoved(sequence: Sequence) { + sequence.filterPosition = 0 + this.savePreference() + } + + protected async start() { + if (this.plan.camera) { for (let i = 0; i < this.cameraExposures.length; i++) { this.cameraExposures.get(i)?.reset() } - this.savePlan() + await this.browserWindowService.openCameraImage(this.plan.camera, 'SEQUENCER') + await this.api.sequencerStart(this.plan.camera, this.plan) + } + } + + protected async pause() { + if (this.plan.camera) { + await this.api.sequencerPause(this.plan.camera) + } + } - await this.browserWindow.openCameraImage(this.camera, 'SEQUENCER') - await this.api.sequencerStart(this.camera, this.plan) + protected async unpause() { + if (this.plan.camera) { + await this.api.sequencerUnpause(this.plan.camera) } } - async stop() { - if (this.camera) { - await this.api.sequencerStop(this.camera) + protected async stop() { + if (this.plan.camera) { + await this.api.sequencerStop(this.plan.camera) + } + } + + private loadPreference() { + Object.assign(this.preference, this.preferenceService.sequencerPreference.get()) + this.plan = this.preference.plan + this.property.properties = this.preference.properties + + this.loadPlan(this.plan) + } + + protected savePreference() { + this.preferenceService.sequencerPreference.set(this.preference) + + if (this.preference.loadPath) { + this.saveMenuItem.disabled = false } } } diff --git a/desktop/src/app/settings/settings.component.html b/desktop/src/app/settings/settings.component.html index 29212fcab..999c36db7 100644 --- a/desktop/src/app/settings/settings.component.html +++ b/desktop/src/app/settings/settings.component.html @@ -2,11 +2,10 @@
@@ -14,19 +13,25 @@
+ *ngIf="tab === 'LOCATION'">
+ [autoDisplayFirst]="false"> + +
+ {{ preference.location.name || '?' }} +
+
+
@@ -39,15 +44,7 @@ tooltipPosition="bottom" (onClick)="addLocation()" /> -
+
+ +
+ *ngIf="tab === 'PLATE_SOLVER'">
@@ -71,7 +73,6 @@ optionsValue="value" [(ngModel)]="plateSolverType" styleClass="p-inputtext-sm border-0" - (ngModelChange)="plateSolvers.get(plateSolverType)!.type = $event; save()" [autoDisplayFirst]="false" /> @@ -80,12 +81,12 @@ class="col-12" *ngIf="plateSolverType !== 'ASTROMETRY_NET_ONLINE'"> + (pathChange)="savePreference()" />
@if (plateSolverType === 'ASTROMETRY_NET_ONLINE') {
@@ -93,8 +94,8 @@ + [(ngModel)]="plateSolver.apiUrl" + (ngModelChange)="savePreference()" />
@@ -103,43 +104,63 @@ + [(ngModel)]="plateSolver.apiKey" + (ngModelChange)="savePreference()" />
} -
- - - - -
-
- - - - -
+ @if (plateSolverType !== 'PIXINSIGHT') { +
+ + + + +
+
+ + + + +
+ } + @if (plateSolverType === 'PIXINSIGHT') { +
+ + + + +
+ }
+ *ngIf="tab === 'STAR_DETECTOR'">
@@ -149,86 +170,92 @@ optionsValue="value" [(ngModel)]="starDetectorType" styleClass="p-inputtext-sm border-0" - (ngModelChange)="starDetectors.get(starDetectorType)!.type = $event; save()" [autoDisplayFirst]="false" />
+ (pathChange)="savePreference()" />
-
+
- + [(ngModel)]="starDetector.timeout" + (ngModelChange)="savePreference()" + [showButtons]="true" + [min]="0" + [max]="300" + spinnableNumber /> +
+ *ngIf="starDetectorType === 'PIXINSIGHT'"> - + spinnableNumber /> +
-
- - - +
+
+
+
+
+ + +
+
+ +
+ *ngIf="liveStackerType === 'PIXINSIGHT'"> + spinnableNumber />
@@ -236,48 +263,117 @@
+ *ngIf="tab === 'STACKER'">
+ (pathChange)="savePreference()" />
+ *ngIf="stackerType === 'PIXINSIGHT'"> + spinnableNumber />
+
+
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+
diff --git a/desktop/src/app/settings/settings.component.scss b/desktop/src/app/settings/settings.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/app/settings/settings.component.ts b/desktop/src/app/settings/settings.component.ts index f815edd99..4cf58566a 100644 --- a/desktop/src/app/settings/settings.component.ts +++ b/desktop/src/app/settings/settings.component.ts @@ -1,138 +1,106 @@ -import { Component } from '@angular/core' -import { LocationDialog } from '../../shared/dialogs/location/location.dialog' -import { DropdownOptionsPipe } from '../../shared/pipes/dropdown-options.pipe' +import { AfterViewInit, Component, HostListener, OnDestroy } from '@angular/core' +import { debounceTime, Subject, Subscription } from 'rxjs' import { ElectronService } from '../../shared/services/electron.service' import { PreferenceService } from '../../shared/services/preference.service' -import { PrimeService } from '../../shared/services/prime.service' -import { EMPTY_LOCATION, Location } from '../../shared/types/atlas.types' -import { LiveStackerType, LiveStackingRequest } from '../../shared/types/camera.types' -import { PlateSolverRequest, PlateSolverType, StarDetectionRequest, StarDetectorType } from '../../shared/types/settings.types' +import { DEFAULT_LOCATION, Location } from '../../shared/types/atlas.types' +import { DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT, FrameType, LiveStackerType } from '../../shared/types/camera.types' +import { PlateSolverType } from '../../shared/types/platesolver.types' +import { DEFAULT_SETTINGS_PREFERENCE, resetCameraCaptureNamingFormat, SettingsTabKey } from '../../shared/types/settings.types' +import { StackerType } from '../../shared/types/stacker.types' +import { StarDetectorType } from '../../shared/types/stardetector.types' import { AppComponent } from '../app.component' @Component({ - selector: 'app-settings', + selector: 'neb-settings', templateUrl: './settings.component.html', - styleUrls: ['./settings.component.scss'], }) -export class SettingsComponent { - tab = 0 - readonly tabs: { id: number; name: string }[] = [ - { - id: 0, - name: 'Location', - }, - { - id: 1, - name: 'Plate Solver', - }, - { - id: 2, - name: 'Star Detection', - }, - { - id: 3, - name: 'Live Stacking', - }, - ] - - readonly locations: Location[] - location: Location - - plateSolverType: PlateSolverType = 'ASTAP' - readonly plateSolvers = new Map() - - starDetectorType: StarDetectorType = 'ASTAP' - readonly starDetectors = new Map() - - liveStackerType: LiveStackerType = 'SIRIL' - readonly liveStackers = new Map() +export class SettingsComponent implements AfterViewInit, OnDestroy { + protected tab: SettingsTabKey = 'LOCATION' + protected readonly tabs: SettingsTabKey[] = ['LOCATION', 'PLATE_SOLVER', 'STAR_DETECTOR', 'LIVE_STACKER', 'STACKER', 'CAPTURE_NAMING_FORMAT'] + protected readonly preference = structuredClone(DEFAULT_SETTINGS_PREFERENCE) - constructor( - app: AppComponent, - private readonly preference: PreferenceService, - private readonly electron: ElectronService, - private readonly prime: PrimeService, - private readonly dropdownOptions: DropdownOptionsPipe, - ) { - app.title = 'Settings' + protected plateSolverType: PlateSolverType = 'ASTAP' + protected starDetectorType: StarDetectorType = 'ASTAP' + protected liveStackerType: LiveStackerType = 'SIRIL' + protected stackerType: StackerType = 'PIXINSIGHT' - this.locations = preference.locations.get() - this.location = preference.selectedLocation.get(this.locations[0]) + private readonly locationChangePublisher = new Subject() + private readonly locationChangeSubscription?: Subscription - for (const type of dropdownOptions.transform('PLATE_SOLVER')) { - this.plateSolvers.set(type, preference.plateSolverRequest(type).get()) - } - for (const type of dropdownOptions.transform('STAR_DETECTOR')) { - this.starDetectors.set(type, preference.starDetectionRequest(type).get()) - } - for (const type of dropdownOptions.transform('LIVE_STACKER')) { - this.liveStackers.set(type, preference.liveStackingRequest(type).get()) - } + get plateSolver() { + return this.preference.plateSolver[this.plateSolverType] } - addLocation() { - return this.showLocation(structuredClone(EMPTY_LOCATION)) + get starDetector() { + return this.preference.starDetector[this.starDetectorType] } - editLocation() { - return this.showLocation(this.location) + get liveStacker() { + return this.preference.liveStacker[this.liveStackerType] } - private async showLocation(location: Location) { - const result = await this.prime.open(LocationDialog, { header: 'Location', data: location }) + get stacker() { + return this.preference.stacker[this.stackerType] + } - if (result) { - const index = this.locations.findIndex((e) => e.id === result.id) + constructor( + app: AppComponent, + private readonly preferenceService: PreferenceService, + private readonly electronService: ElectronService, + ) { + app.title = 'Settings' - if (result.id === 0) { - result.id = Date.now() - } + this.locationChangeSubscription = this.locationChangePublisher.pipe(debounceTime(2000)).subscribe((location) => { + return this.electronService.locationChanged(location) + }) + } - if (index >= 0) { - Object.assign(this.locations[index], result) - this.location = this.locations[index] - } else { - this.locations.push(result) - this.location = result - } + ngAfterViewInit() { + this.loadPreference() + } - this.preference.locations.set(this.locations) - this.preference.selectedLocation.set(this.location) + @HostListener('window:unload') + ngOnDestroy() { + this.locationChangeSubscription?.unsubscribe() + } - await this.electron.send('LOCATION.CHANGED', this.location) - } + protected addLocation() { + const location = structuredClone(DEFAULT_LOCATION) + location.id = +new Date() + this.preference.locations.push(location) + this.locationChanged(location) } - async deleteLocation() { - if (this.locations.length > 1) { - const index = this.locations.findIndex((e) => e.id === this.location.id) + protected deleteLocation() { + if (this.preference.locations.length > 1) { + const index = this.preference.locations.findIndex((e) => e.id === this.preference.location.id) if (index >= 0) { - this.locations.splice(index, 1) - this.location = this.locations[0]! - - this.preference.locations.set(this.locations) - this.preference.selectedLocation.set(this.location) - - await this.electron.send('LOCATION.CHANGED', this.location) + this.preference.locations.splice(index, 1) + this.locationChanged(this.preference.locations[0]) } } } - locationChanged() { - this.preference.selectedLocation.set(this.location) - return this.electron.send('LOCATION.CHANGED', this.location) + protected locationChanged(location?: Location) { + if (location) { + this.preference.location = location + this.savePreference() + this.locationChangePublisher.next(location) + } + } + + protected resetCameraCaptureNamingFormat(type: FrameType) { + resetCameraCaptureNamingFormat(type, this.preference.namingFormat, DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT) + this.savePreference() } - save() { - for (const type of this.dropdownOptions.transform('PLATE_SOLVER')) { - this.preference.plateSolverRequest(type).set(this.plateSolvers.get(type)) - } - for (const type of this.dropdownOptions.transform('STAR_DETECTOR')) { - this.preference.starDetectionRequest(type).set(this.starDetectors.get(type)) - } - for (const type of this.dropdownOptions.transform('LIVE_STACKER')) { - this.preference.liveStackingRequest(type).set(this.liveStackers.get(type)) - } + private loadPreference() { + Object.assign(this.preference, this.preferenceService.settings.get()) + this.preference.location = this.preference.locations.find((e) => e.id === this.preference.location.id) ?? this.preference.locations[0] + } + + protected savePreference() { + this.preferenceService.settings.set(this.preference) } } diff --git a/desktop/src/app/stacker/stacker.component.html b/desktop/src/app/stacker/stacker.component.html new file mode 100644 index 000000000..4cc290230 --- /dev/null +++ b/desktop/src/app/stacker/stacker.component.html @@ -0,0 +1,183 @@ +
+
+ +
+
+ +
+
+ + + + +
+
+
+ {{ item.type }} + +
+ Reference + +
+
+ Enabled + +
+
+ @if (item.analyzed) { +
+ EXP: {{ item.analyzed.exposureTime | exposureTime }} + WIDTH: {{ item.analyzed.width }} + HEIGHT: {{ item.analyzed.height }} + BIN: {{ item.analyzed.binX }}x{{ item.analyzed.binY }} + GAIN: {{ item.analyzed.gain }} +
+ } +
+ {{ item.path }} +
+
+
+ + +
+
+
+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + + +
+
+
+
+
+ + +
+
diff --git a/desktop/src/app/stacker/stacker.component.ts b/desktop/src/app/stacker/stacker.component.ts new file mode 100644 index 000000000..35ed8c021 --- /dev/null +++ b/desktop/src/app/stacker/stacker.component.ts @@ -0,0 +1,152 @@ +import { AfterViewInit, Component, HostListener, OnDestroy } from '@angular/core' +import { dirname } from 'path' +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 { DEFAULT_STACKER_PREFERENCE, StackingRequest, StackingTarget } from '../../shared/types/stacker.types' +import { AppComponent } from '../app.component' + +@Component({ + selector: 'neb-stacker', + templateUrl: './stacker.component.html', +}) +export class StackerComponent implements AfterViewInit, OnDestroy { + protected running = false + protected readonly preference = structuredClone(DEFAULT_STACKER_PREFERENCE) + protected request = this.preference.request + + private frameId = '' + + get referenceTarget() { + return this.request.targets.find((e) => e.enabled && e.reference && e.type === 'LIGHT') + } + + get hasReference() { + return !!this.referenceTarget + } + + get canStart() { + return !!this.request.outputDirectory && this.hasReference + } + + constructor( + app: AppComponent, + private readonly electronService: ElectronService, + private readonly api: ApiService, + private readonly preferenceService: PreferenceService, + private readonly browserWindowService: BrowserWindowService, + ) { + app.title = 'Stacker' + } + + async ngAfterViewInit() { + this.loadPreference() + + this.running = await this.api.stackerIsRunning() + } + + @HostListener('window:unload') + ngOnDestroy() { + void this.closeFrameWindow() + } + + protected async openImages() { + try { + this.running = true + + const images = await this.electronService.openImages({ defaultPath: this.preference.defaultPath }) + + if (images && images.length) { + const targets: StackingTarget[] = [...this.request.targets] + + for (const path of images) { + const analyzed = await this.api.stackerAnalyze(path) + + if (analyzed && analyzed.type === 'LIGHT') { + targets.push({ + enabled: true, + path, + analyzed, + type: analyzed.type, + group: analyzed.group, + reference: !targets.length && !this.referenceTarget, + }) + } + } + + this.request.targets = targets + + this.preference.defaultPath = dirname(images[0]) + this.savePreference() + } + } finally { + this.running = false + } + } + + protected referenceChanged(target: StackingTarget, enabled: boolean) { + if (enabled) { + for (const item of this.request.targets) { + if (item.reference && item !== target) { + item.reference = false + } + } + } + } + + protected async openTargetImage(target: StackingTarget) { + this.frameId = await this.browserWindowService.openImage({ path: target.path, id: 'stacker', source: 'PATH' }) + } + + protected deleteTarget(target: StackingTarget) { + const index = this.request.targets.findIndex((e) => e === target) + + if (index >= 0) { + this.request.targets.splice(index, 1) + } + } + + private async closeFrameWindow() { + if (this.frameId) { + await this.electronService.closeWindow(undefined, this.frameId) + } + } + + protected async startStacking() { + const settings = this.preferenceService.settings.get() + + const request: StackingRequest = { + ...this.request, + ...settings.stacker[this.request.type], + referencePath: this.referenceTarget!.path, + targets: this.request.targets.filter((e) => e.enabled), + } + + this.savePreference() + + try { + this.running = true + const path = await this.api.stackerStart(request) + + if (path) { + await this.browserWindowService.openImage({ path, source: 'STACKER' }) + } + } finally { + this.running = false + } + } + + protected stopStacking() { + return this.api.stackerStop() + } + + private loadPreference() { + Object.assign(this.preference, this.preferenceService.stacker.get()) + this.request = this.preference.request + } + + protected savePreference() { + this.preferenceService.stacker.set(this.preference) + } +} diff --git a/desktop/src/assets/data/.gitignore b/desktop/src/assets/data/.gitignore new file mode 100644 index 000000000..7850aa44f --- /dev/null +++ b/desktop/src/assets/data/.gitignore @@ -0,0 +1 @@ +nebulosa.json diff --git a/desktop/src/assets/fonts/icomoon.ttf b/desktop/src/assets/fonts/icomoon.ttf deleted file mode 100644 index 796fd516a..000000000 Binary files a/desktop/src/assets/fonts/icomoon.ttf and /dev/null differ diff --git a/desktop/src/assets/icons/blackhole.png b/desktop/src/assets/icons/blackhole.png new file mode 100644 index 000000000..f104e1abe Binary files /dev/null and b/desktop/src/assets/icons/blackhole.png differ diff --git a/desktop/src/assets/icons/calibration.png b/desktop/src/assets/icons/calibration.png new file mode 100644 index 000000000..77f50c6ae Binary files /dev/null and b/desktop/src/assets/icons/calibration.png differ diff --git a/desktop/src/assets/icons/magnifier.png b/desktop/src/assets/icons/magnifier.png deleted file mode 100644 index d4e62b51d..000000000 Binary files a/desktop/src/assets/icons/magnifier.png and /dev/null differ diff --git a/desktop/src/index.html b/desktop/src/index.html index 2c5d8fa92..7a090f0c7 100644 --- a/desktop/src/index.html +++ b/desktop/src/index.html @@ -13,7 +13,7 @@ href="assets/icons/favicon.ico" /> - - + + diff --git a/desktop/src/shared/components/camera-exposure/camera-exposure.component.ts b/desktop/src/shared/components/camera-exposure/camera-exposure.component.ts index e01882614..1c51b1a50 100644 --- a/desktop/src/shared/components/camera-exposure/camera-exposure.component.ts +++ b/desktop/src/shared/components/camera-exposure/camera-exposure.component.ts @@ -1,5 +1,5 @@ import { Component, Input } from '@angular/core' -import { CameraCaptureEvent, CameraCaptureState, EMPTY_CAMERA_CAPTURE_INFO, EMPTY_CAMERA_STEP_INFO } from '../../types/camera.types' +import { CameraCaptureEvent, CameraCaptureState, DEFAULT_CAMERA_CAPTURE_INFO, DEFAULT_CAMERA_STEP_INFO } from '../../types/camera.types' @Component({ selector: 'neb-camera-exposure', @@ -8,18 +8,22 @@ import { CameraCaptureEvent, CameraCaptureState, EMPTY_CAMERA_CAPTURE_INFO, EMPT }) export class CameraExposureComponent { @Input() - info?: string + protected info?: string @Input() - showRemainingTime: boolean = true + protected showRemainingTime: boolean = true @Input() - readonly step = structuredClone(EMPTY_CAMERA_STEP_INFO) + protected readonly step = structuredClone(DEFAULT_CAMERA_STEP_INFO) @Input() - readonly capture = structuredClone(EMPTY_CAMERA_CAPTURE_INFO) + protected readonly capture = structuredClone(DEFAULT_CAMERA_CAPTURE_INFO) - state?: CameraCaptureState = 'IDLE' + protected state: CameraCaptureState = 'IDLE' + + get currentState() { + return this.state + } handleCameraCaptureEvent(event: CameraCaptureEvent, looping: boolean = false) { this.capture.elapsedTime = event.captureElapsedTime @@ -50,13 +54,13 @@ export class CameraExposureComponent { this.state = event.state } - return this.state !== undefined && this.state !== 'CAPTURE_FINISHED' && this.state !== 'IDLE' + return this.state !== 'CAPTURE_FINISHED' && this.state !== 'IDLE' } reset() { this.state = 'IDLE' - Object.assign(this.step, EMPTY_CAMERA_STEP_INFO) - Object.assign(this.capture, EMPTY_CAMERA_CAPTURE_INFO) + Object.assign(this.step, DEFAULT_CAMERA_STEP_INFO) + Object.assign(this.capture, DEFAULT_CAMERA_CAPTURE_INFO) } } diff --git a/desktop/src/shared/components/camera-info/camera-info.component.html b/desktop/src/shared/components/camera-info/camera-info.component.html index bd1ffe2ed..3444ae02c 100644 --- a/desktop/src/shared/components/camera-info/camera-info.component.html +++ b/desktop/src/shared/components/camera-info/camera-info.component.html @@ -1,56 +1,64 @@
- {{ info.frameType }} - + class="flex flex-column align-items-center"> + + {{ info.frameType }}
- {{ info.exposureAmount || '∞' }} / {{ info.exposureTime | exposureTime }} - + class="flex flex-column align-items-center"> + + {{ info.exposureAmount || '∞' }} / {{ info.exposureTime | exposureTime }}
- {{ info.exposureDelay * 1000000 | exposureTime }} - + class="flex flex-column align-items-center"> + + {{ info.exposureDelay * 1000000 | exposureTime }}
- {{ info.x }} {{ info.y }} {{ info.width }} {{ info.height }} - + class="flex flex-column align-items-center"> + + {{ info.x }} {{ info.y }} {{ info.width }} {{ info.height }}
- {{ info.binX }}x{{ info.binY }} - + class="flex flex-column align-items-center"> + + {{ info.binX }}x{{ info.binY }}
- {{ info.gain }} - + class="flex flex-column align-items-center"> + + {{ info.gain }}
- {{ info.offset }} - + class="flex flex-column align-items-center"> + + {{ info.offset }}
- {{ info.frameFormat }} - + class="flex flex-column align-items-center"> + + {{ info.frameFormat }}
- {{ filter }} - + class="flex flex-row gap-1 align-items-center"> +
+ + {{ filter }} +
+
diff --git a/desktop/src/shared/components/camera-info/camera-info.component.scss b/desktop/src/shared/components/camera-info/camera-info.component.scss deleted file mode 100644 index a608020ad..000000000 --- a/desktop/src/shared/components/camera-info/camera-info.component.scss +++ /dev/null @@ -1,28 +0,0 @@ -.tag { - position: relative; - display: flex; - align-items: end; - border-radius: 4px; - - span { - display: block; - text-align: center; - margin-top: 9px; - font-size: 0.875rem !important; - } - - label { - border-radius: 2px; - font-size: 9px !important; - font-weight: bold; - top: -0.5rem !important; - background-color: #151515d0; - padding: 2px 4px; - position: absolute; - pointer-events: none; - left: 50%; - transform: translate(-50%, 0%); - display: block; - width: max-content; - } -} diff --git a/desktop/src/shared/components/camera-info/camera-info.component.ts b/desktop/src/shared/components/camera-info/camera-info.component.ts index dd180f422..c3472e027 100644 --- a/desktop/src/shared/components/camera-info/camera-info.component.ts +++ b/desktop/src/shared/components/camera-info/camera-info.component.ts @@ -1,24 +1,30 @@ -import { Component, Input } from '@angular/core' +import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core' import { CameraStartCapture } from '../../types/camera.types' -import { FilterWheel } from '../../types/wheel.types' +import { Wheel } from '../../types/wheel.types' @Component({ selector: 'neb-camera-info', templateUrl: './camera-info.component.html', - styleUrls: ['./camera-info.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class CameraInfoComponent { @Input({ required: true }) - readonly info!: CameraStartCapture + protected readonly info!: CameraStartCapture @Input() - readonly wheel?: FilterWheel + protected readonly wheel?: Wheel @Input() - readonly hasType: boolean = true + protected readonly hasType: boolean = true @Input() - readonly hasExposure: boolean = true + protected readonly hasExposure: boolean = true + + @Input() + protected readonly canRemoveFilter = false + + @Output() + protected readonly filterRemoved = new EventEmitter() get hasFilter() { return !!this.wheel && !!this.info.filterPosition && this.wheel.connected diff --git a/desktop/src/shared/components/device-chooser/device-chooser.component.scss b/desktop/src/shared/components/device-chooser/device-chooser.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/shared/components/device-chooser/device-chooser.component.ts b/desktop/src/shared/components/device-chooser/device-chooser.component.ts index 3af1562c6..c77fa36f4 100644 --- a/desktop/src/shared/components/device-chooser/device-chooser.component.ts +++ b/desktop/src/shared/components/device-chooser/device-chooser.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core' +import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core' import { ApiService } from '../../services/api.service' import { Device } from '../../types/device.types' import { Undefinable } from '../../utils/types' @@ -8,26 +8,26 @@ import { MenuItem } from '../menu-item/menu-item.component' @Component({ selector: 'neb-device-chooser', templateUrl: './device-chooser.component.html', - styleUrls: ['./device-chooser.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class DeviceChooserComponent { @Input({ required: true }) - readonly title!: string + protected readonly title!: string @Input() - readonly noDeviceMessage?: string + protected readonly noDeviceMessage?: string @Input({ required: true }) - readonly icon!: string + protected readonly icon!: string @Input({ required: true }) - readonly devices!: T[] + protected readonly devices!: T[] @Input() - readonly hasNone: boolean = false + protected readonly hasNone: boolean = false @Input() - device?: T + protected device?: T @Output() readonly deviceChange = new EventEmitter() @@ -67,48 +67,66 @@ export class DeviceChooserComponent { } static async handleConnectDevice(api: ApiService, device: Device, item: MenuItem) { + if (device.connected) return undefined + await api.indiDeviceConnect(device) item.disabled = true - return new Promise>((resolve) => { - setTimeout(async () => { + return new Promise((resolve) => { + let counter = 0 + + const timer = setInterval(async () => { Object.assign(device, await api.indiDevice(device)) if (device.connected) { item.icon = 'mdi mdi-close' item.severity = 'danger' item.label = 'Disconnect' + clearInterval(timer) resolve({ device, item }) - } else { + } else if (counter >= 10) { + clearInterval(timer) resolve(undefined) + } else { + counter++ + return } item.disabled = false - }, 1000) + }, 1500) }) } static async handleDisconnectDevice(api: ApiService, device: Device, item: MenuItem) { + if (!device.connected) return undefined + await api.indiDeviceDisconnect(device) item.disabled = true return new Promise>((resolve) => { - setTimeout(async () => { + let counter = 0 + + const timer = setTimeout(async () => { Object.assign(device, await api.indiDevice(device)) if (!device.connected) { item.icon = 'mdi mdi-connection' item.severity = 'info' item.label = 'Connect' + clearInterval(timer) resolve({ device, item }) - } else { + } else if (counter >= 10) { + clearInterval(timer) resolve(undefined) + } else { + counter++ + return } item.disabled = false - }, 1000) + }, 1500) }) } } diff --git a/desktop/src/shared/components/device-list-menu/device-list-menu.component.scss b/desktop/src/shared/components/device-list-menu/device-list-menu.component.scss index 5581d9d1b..ef8a048d3 100644 --- a/desktop/src/shared/components/device-list-menu/device-list-menu.component.scss +++ b/desktop/src/shared/components/device-list-menu/device-list-menu.component.scss @@ -1,5 +1,5 @@ -:host { - ::ng-deep .p-menuitem-link { +neb-device-list-menu { + .p-menuitem-link { padding: 0.5rem 0.75rem; min-height: 43px; } diff --git a/desktop/src/shared/components/device-list-menu/device-list-menu.component.ts b/desktop/src/shared/components/device-list-menu/device-list-menu.component.ts index 5af2dc2b5..5d879ee0e 100644 --- a/desktop/src/shared/components/device-list-menu/device-list-menu.component.ts +++ b/desktop/src/shared/components/device-list-menu/device-list-menu.component.ts @@ -1,6 +1,6 @@ -import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core' +import { Component, EventEmitter, Input, Output, ViewChild, ViewEncapsulation } from '@angular/core' import { SEPARATOR_MENU_ITEM } from '../../constants' -import { PrimeService } from '../../services/prime.service' +import { AngularService } from '../../services/angular.service' import { isGuideHead } from '../../types/camera.types' import { Device } from '../../types/device.types' import { deviceComparator } from '../../utils/comparators' @@ -17,22 +17,26 @@ export interface DeviceConnectionCommandEvent { selector: 'neb-device-list-menu', templateUrl: './device-list-menu.component.html', styleUrls: ['./device-list-menu.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class DeviceListMenuComponent { @Input() - readonly model: SlideMenuItem[] = [] + protected readonly model: SlideMenuItem[] = [] @Input() - readonly modelAtFirst: boolean = true + protected readonly modelAtFirst: boolean = true @Input() - readonly disableIfDeviceIsNotConnected: boolean = true + protected readonly disableIfDeviceIsNotConnected: boolean = true @Input() - header?: string + protected header?: string @Input() - readonly hasNone: boolean = false + protected readonly hasNone: boolean = false + + @Input() + protected readonly toolbarBuilder?: (device: Device) => MenuItem[] @Output() readonly deviceConnect = new EventEmitter() @@ -43,15 +47,17 @@ export class DeviceListMenuComponent { @ViewChild('menu') private readonly menu!: DialogMenuComponent - constructor(private readonly prime: PrimeService) {} + constructor(private readonly angularService: AngularService) {} - show(devices: T[], selected?: NoInfer) { + show(devices: T[], selected?: NoInfer, header?: string) { const model: SlideMenuItem[] = [] + if (header) this.header = header + return new Promise>((resolve) => { if (devices.length <= 0) { resolve(undefined) - this.prime.message('Please connect your equipment first!', 'warn') + this.angularService.message('Please connect your equipment first!', 'warn') return } @@ -92,12 +98,15 @@ export class DeviceListMenuComponent { } for (const device of devices.sort(deviceComparator)) { + const toolbarMenu = this.toolbarBuilder?.(device) ?? [] + model.push({ label: device.name, selected: selected === device, disabled: this.disableIfDeviceIsNotConnected && !device.connected, slideMenu: [], toolbarMenu: [ + ...toolbarMenu, { icon: 'mdi ' + (device.connected ? 'mdi-close' : 'mdi-connection'), severity: device.connected ? 'danger' : 'info', @@ -122,8 +131,7 @@ export class DeviceListMenuComponent { populateWithModel() } - this.menu.model = model - this.menu.show() + this.menu.show(model) }) } diff --git a/desktop/src/shared/components/dialog-menu/dialog-menu.component.html b/desktop/src/shared/components/dialog-menu/dialog-menu.component.html index 1bd0f9b85..453f951ab 100644 --- a/desktop/src/shared/components/dialog-menu/dialog-menu.component.html +++ b/desktop/src/shared/components/dialog-menu/dialog-menu.component.html @@ -10,9 +10,9 @@ (onHide)="hide()" [style]="{ width: 'auto' }"> - {{ header }} + {{ currentHeader }} () @Input() - model: SlideMenuItem[] = [] + protected model: SlideMenuItem[] = [] @Input() - header?: string + protected header?: string @Input() - updateHeaderWithMenuLabel: boolean = true + protected updateHeaderWithMenuLabel: boolean = true + protected currentHeader = this.header private readonly navigationHeader: Undefinable[] = [] - show() { + show(model?: SlideMenuItem[]) { + if (model?.length) this.model = model + this.currentHeader = this.header this.visible = true this.visibleChange.emit(true) } @@ -36,14 +40,14 @@ export class DialogMenuComponent { this.visibleChange.emit(false) } - next(event: MenuItemCommandEvent) { + protected next(event: MenuItemCommandEvent) { if (!event.item?.slideMenu?.length) { this.hide() } else { - this.navigationHeader.push(this.header) + this.navigationHeader.push(this.currentHeader) if (this.updateHeaderWithMenuLabel) { - this.header = event.item.label + this.currentHeader = event.item.label } } } @@ -53,7 +57,7 @@ export class DialogMenuComponent { const header = this.navigationHeader.splice(this.navigationHeader.length - 1, 1)[0] if (this.updateHeaderWithMenuLabel) { - this.header = header + this.currentHeader = header } } } diff --git a/desktop/src/shared/components/histogram/histogram.component.scss b/desktop/src/shared/components/histogram/histogram.component.scss deleted file mode 100644 index a5051dfe6..000000000 --- a/desktop/src/shared/components/histogram/histogram.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -:host { - position: relative; -} - -.minX, -.maxX { - bottom: -12px; -} diff --git a/desktop/src/shared/components/histogram/histogram.component.ts b/desktop/src/shared/components/histogram/histogram.component.ts index 1b78ff066..742910ff7 100644 --- a/desktop/src/shared/components/histogram/histogram.component.ts +++ b/desktop/src/shared/components/histogram/histogram.component.ts @@ -1,9 +1,9 @@ -import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core' +import { AfterViewInit, Component, ElementRef, ViewChild, ViewEncapsulation } from '@angular/core' @Component({ selector: 'neb-histogram', templateUrl: './histogram.component.html', - styleUrls: ['./histogram.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class HistogramComponent implements AfterViewInit { @ViewChild('canvas') diff --git a/desktop/src/shared/dialogs/location/location.dialog.html b/desktop/src/shared/components/location/location.dialog.html similarity index 76% rename from desktop/src/shared/dialogs/location/location.dialog.html rename to desktop/src/shared/components/location/location.dialog.html index 1b554e2bf..8bb727601 100644 --- a/desktop/src/shared/dialogs/location/location.dialog.html +++ b/desktop/src/shared/components/location/location.dialog.html @@ -4,7 +4,8 @@ + [(ngModel)]="location.name" + (ngModelChange)="locationChanged()" />
@@ -17,9 +18,10 @@ [min]="-720" [max]="720" [(ngModel)]="location.offsetInMinutes" + (ngModelChange)="locationChanged()" [showButtons]="true" [allowEmpty]="false" - scrollableNumber /> + spinnableNumber />
@@ -32,9 +34,10 @@ [min]="-1000" [max]="10000" [(ngModel)]="location.elevation" + (ngModelChange)="locationChanged()" [showButtons]="true" [allowEmpty]="false" - scrollableNumber /> + spinnableNumber />
@@ -47,8 +50,9 @@ [max]="90" [maxFractionDigits]="5" [(ngModel)]="location.latitude" + (ngModelChange)="locationChanged()" [allowEmpty]="false" - scrollableNumber /> + spinnableNumber />
@@ -61,8 +65,9 @@ [max]="180" [maxFractionDigits]="5" [(ngModel)]="location.longitude" + (ngModelChange)="locationChanged()" [allowEmpty]="false" - scrollableNumber /> + spinnableNumber />
@@ -70,10 +75,14 @@ + (latitudeChange)="locationChanged()" + [(longitude)]="location.longitude" + (longitudeChange)="locationChanged()" /> - } @else if (item.label && item.splitButtonMenu?.length) { + [severity]="item.badgeSeverity ?? 'danger'" + [value]="item.badge" + styleClass="absolute flex justify-content-center align-items-center top-0" + [style]="{ width: '14px', minWidth: '14px', height: '14px', minHeight: '14px', right: '-2px' }" /> () diff --git a/desktop/src/shared/components/menu-item/menu-item.component.html b/desktop/src/shared/components/menu-item/menu-item.component.html index 42f945147..b65c070a2 100644 --- a/desktop/src/shared/components/menu-item/menu-item.component.html +++ b/desktop/src/shared/components/menu-item/menu-item.component.html @@ -12,7 +12,7 @@ + (onChange)="item.check?.($event); $event.originalEvent?.stopImmediatePropagation()" /> } @if (item.items?.length || item.slideMenu?.length) { diff --git a/desktop/src/shared/components/menu-item/menu-item.component.scss b/desktop/src/shared/components/menu-item/menu-item.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/shared/components/menu-item/menu-item.component.ts b/desktop/src/shared/components/menu-item/menu-item.component.ts index 85f0ff880..d980aff44 100644 --- a/desktop/src/shared/components/menu-item/menu-item.component.ts +++ b/desktop/src/shared/components/menu-item/menu-item.component.ts @@ -1,7 +1,7 @@ -import { Component, Input } from '@angular/core' +import { Component, Input, ViewEncapsulation } from '@angular/core' import { CheckboxChangeEvent } from 'primeng/checkbox' import { InputSwitchChangeEvent } from 'primeng/inputswitch' -import { Severity, TooltipPosition } from '../../types/app.types' +import { Severity, TooltipPosition } from '../../types/angular.types' export interface MenuItemCommandEvent { originalEvent?: Event @@ -44,6 +44,9 @@ export interface MenuItem { command?: (event: MenuItemCommandEvent) => void check?: (event: CheckboxChangeEvent) => void toggle?: (event: InputSwitchChangeEvent) => void + + styleClass?: string + iconClass?: string } export interface SlideMenuItem extends MenuItem { @@ -53,9 +56,9 @@ export interface SlideMenuItem extends MenuItem { @Component({ selector: 'neb-menu-item', templateUrl: './menu-item.component.html', - styleUrls: ['./menu-item.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class MenuItemComponent { @Input({ required: true }) - readonly item!: MenuItem + protected readonly item!: MenuItem } diff --git a/desktop/src/shared/components/moon/moon.component.html b/desktop/src/shared/components/moon/moon.component.html index e8086e306..960765765 100644 --- a/desktop/src/shared/components/moon/moon.component.html +++ b/desktop/src/shared/components/moon/moon.component.html @@ -1,4 +1,5 @@ + [width]="width" + style="filter: brightness(1.5); background-repeat: no-repeat; background-position: center"> diff --git a/desktop/src/shared/components/moon/moon.component.scss b/desktop/src/shared/components/moon/moon.component.scss deleted file mode 100644 index 184d65ea9..000000000 --- a/desktop/src/shared/components/moon/moon.component.scss +++ /dev/null @@ -1,4 +0,0 @@ -canvas { - background-repeat: no-repeat; - background-position: center; -} diff --git a/desktop/src/shared/components/moon/moon.component.ts b/desktop/src/shared/components/moon/moon.component.ts index 77a77e97c..16f13b385 100644 --- a/desktop/src/shared/components/moon/moon.component.ts +++ b/desktop/src/shared/components/moon/moon.component.ts @@ -1,25 +1,25 @@ -import { AfterViewInit, Component, ElementRef, Input, OnChanges, ViewChild } from '@angular/core' +import { AfterViewInit, Component, ElementRef, Input, OnChanges, ViewChild, ViewEncapsulation } from '@angular/core' @Component({ selector: 'neb-moon', templateUrl: './moon.component.html', - styleUrls: ['./moon.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class MoonComponent implements AfterViewInit, OnChanges { - @ViewChild('moon') - private readonly moon?: ElementRef - @Input() - height = 256 + protected height = 256 @Input() - width = 256 + protected width = 256 @Input() - illuminationRatio = 0 + protected illuminationRatio = 0 @Input() - waning = false + protected waning = false + + @ViewChild('moon') + private readonly moon?: ElementRef ngAfterViewInit() { this.draw() @@ -37,17 +37,14 @@ export class MoonComponent implements AfterViewInit, OnChanges { ctx.clearRect(0, 0, canvas.width, canvas.height) - const offset = 32 - const offset4 = offset / 4 - - const height = canvas.height - offset - const width = canvas.width - offset + const height = canvas.height + const width = canvas.width canvas.style.backgroundImage = `url('assets/images/moon.png')` - canvas.style.backgroundSize = `${height + offset4 * 2 - 2}px` + canvas.style.backgroundSize = `${height - 2}px` - const cx = width / 2 + offset4 - const cy = height / 2 + offset4 + const cx = width / 2 + const cy = height / 2 const pointsA: [number, number][] = [] const pointsB: [number, number][] = [] @@ -56,20 +53,20 @@ export class MoonComponent implements AfterViewInit, OnChanges { const angle = ((a - 90) * Math.PI) / 180 let x1 = Math.ceil(Math.cos(angle) * cx) const y1 = Math.ceil(Math.sin(angle) * cy) - const moonWidth = x1 * 2 - let x2 = Math.floor(moonWidth * this.illuminationRatio) + const w = x1 * 2 + let x2 = Math.floor(w * this.illuminationRatio) if (this.waning) { x1 = cx + x1 - x2 = x1 - (moonWidth - x2) + x2 = x1 - (w - x2) } else { x1 = cx - x1 - x2 = x1 + (moonWidth - x2) + x2 = x1 + (w - x2) } const y2 = cy + y1 - const p1: [number, number] = [x1 + offset4, y2 + offset4] - const p2: [number, number] = [x2 + offset4, y2 + offset4] + const p1: [number, number] = [x1, y2] + const p2: [number, number] = [x2, y2] pointsA.push(p1) pointsB.push(p2) @@ -78,7 +75,7 @@ export class MoonComponent implements AfterViewInit, OnChanges { const newPoints = pointsA.concat(pointsB.reverse()) ctx.beginPath() - ctx.fillStyle = '#121212D8' + ctx.fillStyle = '#121212E8' ctx.filter = 'blur(1px)' let first = true diff --git a/desktop/src/shared/components/path-chooser/path-chooser.component.scss b/desktop/src/shared/components/path-chooser/path-chooser.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/shared/components/path-chooser/path-chooser.component.ts b/desktop/src/shared/components/path-chooser/path-chooser.component.ts index 090302db1..92b525181 100644 --- a/desktop/src/shared/components/path-chooser/path-chooser.component.ts +++ b/desktop/src/shared/components/path-chooser/path-chooser.component.ts @@ -1,54 +1,44 @@ -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChange, SimpleChanges } from '@angular/core' +import { Component, EventEmitter, Input, Output } from '@angular/core' import { dirname } from 'path' import { ElectronService } from '../../services/electron.service' -import { Undefinable } from '../../utils/types' @Component({ selector: 'neb-path-chooser', templateUrl: './path-chooser.component.html', - styleUrls: ['./path-chooser.component.scss'], }) -export class PathChooserComponent implements OnChanges { +export class PathChooserComponent { @Input({ required: true }) - readonly key!: string + protected readonly key!: string @Input() - readonly label?: string + protected readonly label?: string @Input() - readonly placeholder?: string + protected readonly placeholder?: string @Input() - readonly disabled: boolean = false + protected readonly disabled: boolean = false @Input() - readonly readonly: boolean = false + protected readonly readonly: boolean = false @Input({ required: true }) - readonly directory!: boolean + protected readonly directory!: boolean @Input() - path?: string + protected path?: string @Output() readonly pathChange = new EventEmitter() - constructor(private readonly electron: ElectronService) {} + constructor(private readonly electronService: ElectronService) {} - ngOnChanges(changes: SimpleChanges) { - const pathChanged = changes['path'] as Undefinable - - if (pathChanged?.currentValue) { - this.path = pathChanged.currentValue as string - } - } - - async choosePath() { + protected async choosePath() { const key = `pathChooser.${this.key}.defaultPath` - const storedPath = localStorage.getItem(key) - const defaultPath = storedPath && !this.directory ? dirname(storedPath) : this.path + const lastPath = localStorage.getItem(key) || undefined + const defaultPath = lastPath && !this.directory ? dirname(lastPath) : lastPath - const path = await (this.directory ? this.electron.openDirectory({ defaultPath }) : this.electron.openFile({ defaultPath })) + const path = await (this.directory ? this.electronService.openDirectory({ defaultPath }) : this.electronService.openFile({ defaultPath })) if (path) { this.path = path diff --git a/desktop/src/shared/components/slide-menu/slide-menu.component.scss b/desktop/src/shared/components/slide-menu/slide-menu.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/shared/components/slide-menu/slide-menu.component.ts b/desktop/src/shared/components/slide-menu/slide-menu.component.ts index 0b1df0c79..838e60a04 100644 --- a/desktop/src/shared/components/slide-menu/slide-menu.component.ts +++ b/desktop/src/shared/components/slide-menu/slide-menu.component.ts @@ -1,11 +1,11 @@ -import { Component, ElementRef, EventEmitter, Input, OnInit, Output, TemplateRef } from '@angular/core' +import { Component, ElementRef, EventEmitter, Input, OnInit, Output, TemplateRef, ViewEncapsulation } from '@angular/core' import { Nullable } from '../../utils/types' import { MenuItemCommandEvent, SlideMenuItem } from '../menu-item/menu-item.component' @Component({ selector: 'neb-slide-menu', templateUrl: './slide-menu.component.html', - styleUrls: ['./slide-menu.component.scss'], + encapsulation: ViewEncapsulation.None, }) export class SlideMenuComponent implements OnInit { @Input({ required: true }) @@ -20,7 +20,7 @@ export class SlideMenuComponent implements OnInit { @Output() readonly onBack = new EventEmitter() - currentMenu!: SlideMenuItem[] + protected currentMenu!: SlideMenuItem[] private readonly navigation: SlideMenuItem[][] = [] diff --git a/desktop/src/shared/constants.ts b/desktop/src/shared/constants.ts index 744d74377..cf7016eb0 100644 --- a/desktop/src/shared/constants.ts +++ b/desktop/src/shared/constants.ts @@ -7,11 +7,13 @@ export const TWO_DIGITS_FORMATTER = new Intl.NumberFormat('en-US', { 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, diff --git a/desktop/src/shared/dialogs/confirm/confirm.dialog.scss b/desktop/src/shared/dialogs/confirm/confirm.dialog.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/shared/dialogs/confirm/confirm.dialog.ts b/desktop/src/shared/dialogs/confirm/confirm.dialog.ts index 802fb7a07..0f65686bd 100644 --- a/desktop/src/shared/dialogs/confirm/confirm.dialog.ts +++ b/desktop/src/shared/dialogs/confirm/confirm.dialog.ts @@ -1,11 +1,10 @@ import { Component } from '@angular/core' import { ConfirmEventType, Confirmation } from 'primeng/api' import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog' -import { PrimeService } from '../../services/prime.service' +import { AngularService } from '../../services/angular.service' @Component({ templateUrl: './confirm.dialog.html', - styleUrls: ['./confirm.dialog.scss'], }) export class ConfirmDialog { readonly header: string @@ -27,8 +26,8 @@ export class ConfirmDialog { this.dialogRef.close(ConfirmEventType.ACCEPT) } - static async open(prime: PrimeService, message: string) { + static async open(service: AngularService, message: string) { const data: Confirmation = { message } - return (await prime.open(ConfirmDialog, { header: 'Confirmation', data, style: { maxWidth: '320px' } })) ?? ConfirmEventType.CANCEL + return (await service.open(ConfirmDialog, { header: 'Confirmation', data, style: { maxWidth: '320px' } })) ?? ConfirmEventType.CANCEL } } diff --git a/desktop/src/shared/dialogs/location/location.dialog.scss b/desktop/src/shared/dialogs/location/location.dialog.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/desktop/src/shared/dialogs/location/location.dialog.ts b/desktop/src/shared/dialogs/location/location.dialog.ts deleted file mode 100644 index 05666f1a1..000000000 --- a/desktop/src/shared/dialogs/location/location.dialog.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { AfterViewInit, Component, ViewChild } from '@angular/core' -import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog' -import { MapComponent } from '../../components/map/map.component' -import { EMPTY_LOCATION, Location } from '../../types/atlas.types' - -@Component({ - templateUrl: './location.dialog.html', - styleUrls: ['./location.dialog.scss'], -}) -export class LocationDialog implements AfterViewInit { - @ViewChild('map') - private readonly map!: MapComponent - - readonly location: Location - - constructor( - private readonly dialogRef: DynamicDialogRef, - config: DynamicDialogConfig, - ) { - this.location = config.data ?? structuredClone(EMPTY_LOCATION) - } - - ngAfterViewInit() { - this.map.refresh() - } - - save() { - this.dialogRef.close(this.location) - } -} diff --git a/desktop/src/shared/directives/input-number-scrollable.ts b/desktop/src/shared/directives/spinnable-number.directive.ts similarity index 83% rename from desktop/src/shared/directives/input-number-scrollable.ts rename to desktop/src/shared/directives/spinnable-number.directive.ts index a7797ddd4..a3b588cd2 100644 --- a/desktop/src/shared/directives/input-number-scrollable.ts +++ b/desktop/src/shared/directives/spinnable-number.directive.ts @@ -1,8 +1,8 @@ import { Directive, Host, HostListener } from '@angular/core' import { InputNumber } from 'primeng/inputnumber' -@Directive({ selector: '[scrollableNumber]' }) -export class ScrollableNumberDirective { +@Directive({ selector: '[spinnableNumber]' }) +export class SpinnableNumberDirective { constructor(@Host() private readonly inputNumber: InputNumber) {} @HostListener('wheel', ['$event']) diff --git a/desktop/src/shared/directives/stop-propagation.directive.ts b/desktop/src/shared/directives/stop-propagation.directive.ts index 29726b68b..5de9883da 100644 --- a/desktop/src/shared/directives/stop-propagation.directive.ts +++ b/desktop/src/shared/directives/stop-propagation.directive.ts @@ -3,13 +3,13 @@ import { Directive, HostListener, Input } from '@angular/core' @Directive({ selector: '[stopPropagation]' }) export class StopPropagationDirective { @Input('spEnabled') - readonly enabled: boolean = true + protected readonly enabled: boolean = true @Input('spImmediate') - readonly immediate: boolean = true + protected readonly immediate: boolean = true @Input('spPreventDefault') - readonly preventDefault: boolean = false + protected readonly preventDefault: boolean = false @HostListener('click', ['$event']) @HostListener('contextmenu', ['$event']) diff --git a/desktop/src/shared/interceptors/confirmation.interceptor.ts b/desktop/src/shared/interceptors/confirmation.interceptor.ts index 3a29b4d5e..ec5be3093 100644 --- a/desktop/src/shared/interceptors/confirmation.interceptor.ts +++ b/desktop/src/shared/interceptors/confirmation.interceptor.ts @@ -6,7 +6,7 @@ import { IdempotencyKeyInterceptor } from './idempotency-key.interceptor' @Injectable({ providedIn: 'root' }) export class ConfirmationInterceptor implements HttpInterceptor { - constructor(private readonly confirmation: ConfirmationService) {} + constructor(private readonly confirmationService: ConfirmationService) {} intercept(req: HttpRequest, next: HttpHandler): Observable> { const hasConfirmation = req.urlWithParams.includes('hasConfirmation') @@ -15,8 +15,7 @@ export class ConfirmationInterceptor implements HttpInterceptor { const idempotencyKey = req.headers.get(IdempotencyKeyInterceptor.HEADER_KEY) if (idempotencyKey) { - console.info('registered confirmation:', req.method, req.urlWithParams, idempotencyKey) - this.confirmation.register(idempotencyKey) + this.confirmationService.register(idempotencyKey) } const res = next.handle(req) @@ -24,8 +23,7 @@ export class ConfirmationInterceptor implements HttpInterceptor { if (idempotencyKey) { return res.pipe( finalize(() => { - console.info('unregistered confirmation:', req.method, req.urlWithParams, idempotencyKey) - this.confirmation.unregister(idempotencyKey) + this.confirmationService.unregister(idempotencyKey) }), ) } diff --git a/desktop/src/shared/interceptors/location.interceptor.ts b/desktop/src/shared/interceptors/location.interceptor.ts index 3efc4f402..8e2af3dbc 100644 --- a/desktop/src/shared/interceptors/location.interceptor.ts +++ b/desktop/src/shared/interceptors/location.interceptor.ts @@ -7,15 +7,33 @@ import { PreferenceService } from '../services/preference.service' export class LocationInterceptor implements HttpInterceptor { static readonly HEADER_KEY = 'X-Location' - constructor(private readonly preference: PreferenceService) {} + constructor(private readonly preferenceService: PreferenceService) {} intercept(req: HttpRequest, next: HttpHandler): Observable> { if (req.urlWithParams.includes('hasLocation')) { - const location = this.preference.selectedLocation.get() + const params = new URLSearchParams(req.urlWithParams) + const hasLocation = params.get('hasLocation') - req = req.clone({ - headers: req.headers.set(LocationInterceptor.HEADER_KEY, JSON.stringify(location)), - }) + if (!hasLocation || hasLocation === 'true') { + const location = this.preferenceService.settings.get().location + + req = req.clone({ + headers: req.headers.set(LocationInterceptor.HEADER_KEY, JSON.stringify(location)), + }) + } else { + const id = parseInt(hasLocation) + + if (id) { + const locations = this.preferenceService.settings.get().locations + const location = locations.find((e) => e.id === id) + + if (location) { + req = req.clone({ + headers: req.headers.set(LocationInterceptor.HEADER_KEY, JSON.stringify(location)), + }) + } + } + } } return next.handle(req) diff --git a/desktop/src/shared/pipes/dropdown-options.pipe.ts b/desktop/src/shared/pipes/dropdown-options.pipe.ts index a0fed50c1..4055016d2 100644 --- a/desktop/src/shared/pipes/dropdown-options.pipe.ts +++ b/desktop/src/shared/pipes/dropdown-options.pipe.ts @@ -1,12 +1,17 @@ import { Pipe, PipeTransform } from '@angular/core' import { Hemisphere } from '../types/alignment.types' +import { Constellation, CONSTELLATIONS, SATELLITE_GROUPS, SatelliteGroupType, SKY_OBJECT_TYPES, SkyObjectType } from '../types/atlas.types' import { AutoFocusFittingMode, BacklashCompensationMode } from '../types/autofocus.type' import { ExposureMode, FrameType, LiveStackerType } from '../types/camera.types' import { GuideDirection, GuiderPlotMode, GuiderYAxisUnit } from '../types/guider.types' -import { Bitpix, ImageChannel, ImageFormat, SCNRProtectionMethod } from '../types/image.types' -import { MountRemoteControlType } from '../types/mount.types' -import { SequenceCaptureMode } from '../types/sequencer.types' -import { PlateSolverType, StarDetectorType } from '../types/settings.types' +import { ConnectionType } from '../types/home.types' +import { Bitpix, IMAGE_STATISTICS_BIT_OPTIONS, ImageChannel, ImageFormat, ImageStatisticsBitOption, SCNRProtectionMethod } from '../types/image.types' +import { MountRemoteControlProtocol } from '../types/mount.types' +import { PlateSolverType } from '../types/platesolver.types' +import { SequencerCaptureMode } from '../types/sequencer.types' +import { SettingsTabKey } from '../types/settings.types' +import { StackerGroupType, StackerType } from '../types/stacker.types' +import { StarDetectorType } from '../types/stardetector.types' export interface DropdownOptions { STAR_DETECTOR: StarDetectorType[] @@ -18,7 +23,7 @@ export interface DropdownOptions { IMAGE_FORMAT: ImageFormat[] IMAGE_BITPIX: Bitpix[] IMAGE_CHANNEL: ImageChannel[] - MOUNT_REMOTE_CONTROL_TYPE: MountRemoteControlType[] + MOUNT_REMOTE_CONTROL_PROTOCOL: MountRemoteControlProtocol[] FRAME_TYPE: FrameType[] EXPOSURE_MODE: ExposureMode[] GUIDE_DIRECTION: GuideDirection[] @@ -27,7 +32,16 @@ export interface DropdownOptions { HEMISPHERE: Hemisphere[] GUIDER_PLOT_MODE: GuiderPlotMode[] GUIDER_Y_AXIS_UNIT: GuiderYAxisUnit[] - SEQUENCE_CAPTURE_MODE: SequenceCaptureMode[] + SEQUENCE_CAPTURE_MODE: SequencerCaptureMode[] + STACKER: StackerType[] + SETTINGS_TAB: SettingsTabKey[] + STACKER_GROUP_TYPE: StackerGroupType[] + CONNECTION_TYPE: ConnectionType[] + IMAGE_STATISTICS_BIT_OPTION: ImageStatisticsBitOption[] + SATELLITE_GROUP_TYPE: SatelliteGroupType[] + CONSTELLATION: Constellation[] + SKY_OBJECT_TYPE: SkyObjectType[] + SEQUENCER_CAPTURE_MODE: SequencerCaptureMode[] } @Pipe({ name: 'dropdownOptions' }) @@ -37,7 +51,7 @@ export class DropdownOptionsPipe implements PipeTransform { case 'STAR_DETECTOR': return ['ASTAP', 'PIXINSIGHT', 'SIRIL'] as DropdownOptions[K] case 'PLATE_SOLVER': - return ['ASTAP', 'ASTROMETRY_NET_ONLINE', 'SIRIL'] as DropdownOptions[K] + return ['ASTAP', 'ASTROMETRY_NET_ONLINE', 'SIRIL', 'PIXINSIGHT'] as DropdownOptions[K] case 'CURVE_FITTING_MODE': return ['TRENDLINES', 'PARABOLIC', 'TREND_PARABOLIC', 'HYPERBOLIC', 'TREND_HYPERBOLIC'] as DropdownOptions[K] case 'BACKLASH_COMPENSATION_MODE': @@ -52,7 +66,7 @@ export class DropdownOptionsPipe implements PipeTransform { return ['BYTE', 'SHORT', 'INTEGER', 'FLOAT', 'DOUBLE'] as DropdownOptions[K] case 'IMAGE_CHANNEL': return ['RED', 'GREEN', 'BLUE', 'GRAY'] as DropdownOptions[K] - case 'MOUNT_REMOTE_CONTROL_TYPE': + case 'MOUNT_REMOTE_CONTROL_PROTOCOL': return ['LX200', 'STELLARIUM'] as DropdownOptions[K] case 'FRAME_TYPE': return ['LIGHT', 'DARK', 'FLAT', 'BIAS'] as DropdownOptions[K] @@ -72,6 +86,24 @@ export class DropdownOptionsPipe implements PipeTransform { return ['ARCSEC', 'PIXEL'] as DropdownOptions[K] case 'SEQUENCE_CAPTURE_MODE': return ['FULLY', 'INTERLEAVED'] as DropdownOptions[K] + case 'STACKER': + return ['PIXINSIGHT'] as DropdownOptions[K] + case 'SETTINGS_TAB': + return ['LOCATION', 'PLATE_SOLVER', 'STAR_DETECTOR', 'LIVE_STACKER', 'STACKER', 'CAPTURE_NAMING_FORMAT'] as DropdownOptions[K] + case 'STACKER_GROUP_TYPE': + return ['LUMINANCE', 'RED', 'GREEN', 'BLUE', 'MONO', 'RGB'] as DropdownOptions[K] + case 'CONNECTION_TYPE': + return ['INDI', 'ALPACA'] as DropdownOptions[K] + case 'IMAGE_STATISTICS_BIT_OPTION': + return IMAGE_STATISTICS_BIT_OPTIONS as DropdownOptions[K] + case 'SATELLITE_GROUP_TYPE': + return SATELLITE_GROUPS as unknown as DropdownOptions[K] + case 'CONSTELLATION': + return CONSTELLATIONS as unknown as DropdownOptions[K] + case 'SKY_OBJECT_TYPE': + return SKY_OBJECT_TYPES as unknown as DropdownOptions[K] + case 'SEQUENCER_CAPTURE_MODE': + return ['FULLY', 'INTERLEAVED'] as DropdownOptions[K] } return [] diff --git a/desktop/src/shared/pipes/enum-dropdown.pipe.ts b/desktop/src/shared/pipes/enum-dropdown.pipe.ts index f544b2972..1e02fa89e 100644 --- a/desktop/src/shared/pipes/enum-dropdown.pipe.ts +++ b/desktop/src/shared/pipes/enum-dropdown.pipe.ts @@ -1,16 +1,12 @@ import { Pipe, PipeTransform } from '@angular/core' -import { EnumPipe, EnumPipeKey } from './enum.pipe' - -export interface EnumDropdownItem { - label: string - value: EnumPipeKey -} +import { DropdownItem } from '../types/angular.types' +import { EnumPipe } from './enum.pipe' @Pipe({ name: 'enumDropdown' }) export class EnumDropdownPipe implements PipeTransform { constructor(private readonly enumPipe: EnumPipe) {} - transform(value: EnumPipeKey[]): EnumDropdownItem[] { + transform(value: T[]): DropdownItem[] { return value.map((value) => { return { label: this.enumPipe.transform(value), value } }) diff --git a/desktop/src/shared/pipes/enum.pipe.ts b/desktop/src/shared/pipes/enum.pipe.ts index 4a09f52ec..a2d2822b8 100644 --- a/desktop/src/shared/pipes/enum.pipe.ts +++ b/desktop/src/shared/pipes/enum.pipe.ts @@ -2,13 +2,16 @@ import { Pipe, PipeTransform } from '@angular/core' import { DARVState, Hemisphere, TPPAState } from '../types/alignment.types' import { Constellation, SatelliteGroupType, SkyObjectType } from '../types/atlas.types' import { AutoFocusFittingMode, AutoFocusState, BacklashCompensationMode } from '../types/autofocus.type' -import { CameraCaptureState, ExposureMode, FrameType, LiveStackerType } from '../types/camera.types' +import { CameraCaptureState, ExposureMode, ExposureTimeUnit, FrameType, LiveStackerType } from '../types/camera.types' import { FlatWizardState } from '../types/flat-wizard.types' import { GuideDirection, GuideState, GuiderPlotMode, GuiderYAxisUnit } from '../types/guider.types' -import { Bitpix, SCNRProtectionMethod } from '../types/image.types' -import { MountRemoteControlType } from '../types/mount.types' -import { SequenceCaptureMode } from '../types/sequencer.types' -import { PlateSolverType, StarDetectorType } from '../types/settings.types' +import { Bitpix, ImageChannel, SCNRProtectionMethod } from '../types/image.types' +import { MountRemoteControlProtocol } from '../types/mount.types' +import { PlateSolverType } from '../types/platesolver.types' +import { SequencerCaptureMode, SequencerState } from '../types/sequencer.types' +import { SettingsTabKey } from '../types/settings.types' +import { StackerGroupType, StackerType } from '../types/stacker.types' +import { StarDetectorType } from '../types/stardetector.types' import { Undefinable } from '../utils/types' export type EnumPipeKey = @@ -33,14 +36,22 @@ export type EnumPipeKey = | LiveStackerType | GuiderPlotMode | GuiderYAxisUnit - | MountRemoteControlType - | SequenceCaptureMode + | MountRemoteControlProtocol + | SequencerCaptureMode | Bitpix + | StackerType + | StackerGroupType + | SettingsTabKey + | SequencerState + | ExposureTimeUnit + | ImageChannel | 'ALL' @Pipe({ name: 'enum' }) export class EnumPipe implements PipeTransform { readonly enums: Record> = { + 'DX/DY': 'dx/dy', + 'RA/DEC': 'RA/DEC', ABSOLUTE: 'Absolute', ACTIVE_GALAXY_NUCLEUS: 'Active Galaxy Nucleus', ACTIVE: 'Active', @@ -57,10 +68,13 @@ export class EnumPipe implements PipeTransform { AQL: 'Aquila', AQR: 'Aquarius', ARA: 'Ara', + ARCSEC: 'Arcsec', ARGOS: 'ARGOS Data Collection System', ARI: 'Aries', ASSOCIATION_OF_STARS: 'Association of Stars', ASTAP: 'Astap', + ASTROMETRY_NET_ONLINE: 'Astrometry.net (Online)', + ASTROMETRY_NET: 'Astrometry.net', ASYMPTOTIC_GIANT_BRANCH_STAR: 'Asymptotic Giant Branch Star', AUR: 'Auriga', AVERAGE_NEUTRAL: 'Average Neutral', @@ -76,16 +90,19 @@ export class EnumPipe implements PipeTransform { BLUE_OBJECT: 'Blue Object', BLUE_STRAGGLER: 'Blue Straggler', BLUE_SUPERGIANT: 'Blue Supergiant', + BLUE: 'Blue', BOO: 'Boötes', BRIGHTEST_GALAXY_IN_A_CLUSTER_BCG: 'Brightest Galaxy in a Cluster (BCG)', BROWN_DWARF: 'Brown Dwarf', BUBBLE: 'Bubble', BY_DRA_VARIABLE: 'BY Dra Variable', + BYTE: 'Byte', CAE: 'Caelum', CALIBRATING: 'Calibrating', CAM: 'Camelopardalis', CAP: 'Capricornus', CAPTURE_FINISHED: undefined, + CAPTURE_NAMING_FORMAT: 'Capture Naming Format', CAPTURE_STARTED: undefined, CAPTURED: 'Captured', CAR: 'Carina', @@ -134,7 +151,9 @@ export class EnumPipe implements PipeTransform { DMC: 'Disaster Monitoring', DOR: 'Dorado', DOUBLE_OR_MULTIPLE_STAR: 'Double or Multiple Star', + DOUBLE: 'Double', DRA: 'Draco', + EAST: 'East', ECLIPSING_BINARY: 'Eclipsing Binary', EDUCATION: 'Education', ELLIPSOIDAL_VARIABLE: 'Ellipsoidal Variable', @@ -158,8 +177,10 @@ export class EnumPipe implements PipeTransform { FINISHED: 'Finished', FIXED: 'Fixed', FLAT: 'Flat', + FLOAT: 'Float', FOR: 'Fornax', FORWARD: 'Forward', + FULLY: 'Fully', GALAXY_IN_PAIR_OF_GALAXIES: 'Galaxy in Pair of Galaxies', GALAXY_TOWARDS_A_CLUSTER_OF_GALAXIES: 'Galaxy towards a Cluster of Galaxies', GALAXY_TOWARDS_A_GROUP_OF_GALAXIES: 'Galaxy towards a Group of Galaxies', @@ -186,6 +207,8 @@ export class EnumPipe implements PipeTransform { GRAVITATIONALLY_LENSED_IMAGE_OF_A_GALAXY: 'Gravitationally Lensed Image of a Galaxy', GRAVITATIONALLY_LENSED_IMAGE_OF_A_QUASAR: 'Gravitationally Lensed Image of a Quasar', GRAVITATIONALLY_LENSED_IMAGE: 'Gravitationally Lensed Image', + GRAY: 'Gray', + GREEN: 'Green', GROUP_OF_GALAXIES: 'Group of Galaxies', GRU: 'Grus', GUIDING: 'Guiding', @@ -209,8 +232,10 @@ export class EnumPipe implements PipeTransform { IND: 'Indus', INFRA_RED_SOURCE: 'Infra-Red Source', INITIAL_PAUSE: 'Initial Pause', + INTEGER: 'Integer', INTELSAT: 'Intelsat', INTERACTING_GALAXIES: 'Interacting Galaxies', + INTERLEAVED: 'Interleaved', INTERSTELLAR_FILAMENT: 'Interstellar Filament', INTERSTELLAR_MEDIUM_OBJECT: 'Interstellar Medium Object', INTERSTELLAR_SHELL: 'Interstellar Shell', @@ -225,15 +250,20 @@ export class EnumPipe implements PipeTransform { LIB: 'Libra', LIGHT: 'Light', LINER_TYPE_ACTIVE_GALAXY_NUCLEUS: 'LINER-type Active Galaxy Nucleus', + LIVE_STACKER: 'Live Stacker', LMI: 'Leo Minor', + LOCATION: 'Location', LONG_PERIOD_VARIABLE: 'Long-Period Variable', + LONG: 'Long', LOOP: 'Loop', LOOPING: 'Looping', LOST_LOCK: 'Lost Lock', LOW_MASS_STAR: 'Low-mass Star', LOW_MASS_X_RAY_BINARY: 'Low Mass X-ray Binary', LOW_SURFACE_BRIGHTNESS_GALAXY: 'Low Surface Brightness Galaxy', + LUMINANCE: 'Luminance', LUP: 'Lupus', + LX200: 'LX200', LYN: 'Lynx', LYR: 'Lyra', MAIN_SEQUENCE_STAR: 'Main Sequence Star', @@ -244,15 +274,19 @@ export class EnumPipe implements PipeTransform { MEN: 'Mensa', METRIC_RADIO_SOURCE: 'Metric Radio Source', MIC: 'Microscopium', + MICROSECOND: 'µs', MICRO_LENSING_EVENT: '(Micro)Lensing Event', MID_IR_SOURCE_3_TO_30_M: 'Mid-IR Source (3 to 30 µm)', MILITARY: 'Miscellaneous Military', + MILLISECOND: 'ms', MILLIMETRIC_RADIO_SOURCE: 'Millimetric Radio Source', MINIMUM_NEUTRAL: 'Minimum Neutral', + MINUTE: 'm', MIRA_VARIABLE: 'Mira Variable', MOLECULAR_CLOUD: 'Molecular Cloud', MOLNIYA: 'Molniya', MON: 'Monoceros', + MONO: 'Mono', MOVING_GROUP: 'Moving Group', MOVING: 'Moving', MUS: 'Musca', @@ -264,6 +298,8 @@ export class EnumPipe implements PipeTransform { NOAA: 'NOAA', NONE: 'None', NOR: 'Norma', + NORTH: 'North', + NORTHERN: 'Northern', NOT_AN_OBJECT_ERROR_ARTEFACT: 'Not an Object (Error, Artefact, ...)', OBJECT_OF_UNKNOWN_NATURE: 'Object of Unknown Nature', OCT: 'Octans', @@ -290,9 +326,11 @@ export class EnumPipe implements PipeTransform { PER: 'Perseus', PHE: 'Phoenix', PIC: 'Pictor', + PIXEL: 'Pixel', PIXINSIGHT: 'PixInsight', PLANET: 'Planet', PLANETARY_NEBULA: 'Planetary Nebula', + PLATE_SOLVER: 'Plate Solver', POST_AGB_STAR: 'Post-AGB Star', PROTO_CLUSTER_OF_GALAXIES: 'Proto Cluster of Galaxies', PSA: 'Piscis Austrinus', @@ -310,13 +348,16 @@ export class EnumPipe implements PipeTransform { RADUGA: 'Raduga', RED_GIANT_BRANCH_STAR: 'Red Giant Branch star', RED_SUPERGIANT: 'Red Supergiant', + RED: 'Red', REFLECTION_NEBULA: 'Reflection Nebula', REGION_DEFINED_IN_THE_SKY: 'Region defined in the Sky', RESOURCE: 'Earth Resources', RET: 'Reticulum', + RGB: 'RGB', ROTATING_VARIABLE: 'Rotating Variable', RR_LYRAE_VARIABLE: 'RR Lyrae Variable', RS_CVN_VARIABLE: 'RS CVn Variable', + RUNNING: 'Running', RV_TAURI_VARIABLE: 'RV Tauri Variable', S_STAR: 'S Star', SARSAT: 'Search & Rescue (SARSAT)', @@ -327,6 +368,7 @@ export class EnumPipe implements PipeTransform { SCO: 'Scorpius', SCT: 'Scutum', SELECTED: 'Selected', + SECOND: 's', SER: 'Serpens', SES: 'SES', SETTLING: 'Settling', @@ -336,21 +378,27 @@ export class EnumPipe implements PipeTransform { SEYFERT_GALAXY: 'Seyfert Galaxy', SGE: 'Sagitta', SGR: 'Sagittarius', + SHORT: 'Short', SINGLE: 'Single', SIRIL: 'Siril', SLEWED: 'Slewed', SLEWING: 'Slewing', SOLVED: 'Solved', SOLVING: 'Solving', + SOUTH: 'South', + SOUTHERN: 'Southern', SPECTROSCOPIC_BINARY: 'Spectroscopic Binary', SPIRE: 'Spire', + STACKER: 'Stacker', STACKING: 'Stacking', + STAR_DETECTOR: 'Star Detector', STAR_FORMING_REGION: 'Star Forming Region', STAR: 'Star', STARBURST_GALAXY: 'Starburst Galaxy', STARLINK: 'Starlink', STATIONS: 'Space Stations', STELLAR_STREAM: 'Stellar Stream', + STELLARIUM: 'Stellarium', STOPPED: 'Stopped', SUB_MILLIMETRIC_SOURCE: 'Sub-Millimetric Source', SUPERCLUSTER_OF_GALAXIES: 'Supercluster of Galaxies', @@ -384,6 +432,7 @@ export class EnumPipe implements PipeTransform { VUL: 'Vulpecula', WAITING: 'Waiting', WEATHER: 'Weather', + WEST: 'West', WHITE_DWARF: 'White Dwarf', WOLF_RAYET: 'Wolf-Rayet', X_COMM: 'Experimental Comm', @@ -391,28 +440,6 @@ export class EnumPipe implements PipeTransform { X_RAY_SOURCE: 'X-ray Source', YELLOW_SUPERGIANT: 'Yellow Supergiant', YOUNG_STELLAR_OBJECT: 'Young Stellar Object', - ASTROMETRY_NET: 'Astrometry.net', - ASTROMETRY_NET_ONLINE: 'Astrometry.net (Online)', - NORTH: 'North', - NORTHERN: 'Northern', - SOUTH: 'South', - SOUTHERN: 'Southern', - WEST: 'West', - EAST: 'East', - 'RA/DEC': 'RA/DEC', - 'DX/DY': 'dx/dy', - ARCSEC: 'Arcsec', - PIXEL: 'Pixel', - LX200: 'LX200', - STELLARIUM: 'Stellarium', - FULLY: 'Fully', - INTERLEAVED: 'Interleaved', - BYTE: 'Byte', - SHORT: 'Short', - INTEGER: 'Integer', - LONG: 'Long', - FLOAT: 'Float', - DOUBLE: 'Double', } // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents diff --git a/desktop/src/shared/pipes/exposureTime.pipe.ts b/desktop/src/shared/pipes/exposureTime.pipe.ts index 210175fae..a97fdfcb6 100644 --- a/desktop/src/shared/pipes/exposureTime.pipe.ts +++ b/desktop/src/shared/pipes/exposureTime.pipe.ts @@ -38,7 +38,7 @@ const secondFormatter = formatter(TWO_DIGITS_FORMATTER, 's') function format(value: number, factors: [number, number], formatters: [UnitFormatter, UnitFormatter]) { const a = value / factors[0] const b = (a - Math.trunc(a)) * factors[1] - return `${formatters[0](Math.trunc(a))}${formatters[1](Math.trunc(b))}` + return `${formatters[0](a)}${formatters[1](b)}` } function hours(value: number) { diff --git a/desktop/src/shared/pipes/path.pipe.ts b/desktop/src/shared/pipes/path.pipe.ts new file mode 100644 index 000000000..dd61d2ea5 --- /dev/null +++ b/desktop/src/shared/pipes/path.pipe.ts @@ -0,0 +1,26 @@ +import { Pipe, PipeTransform } from '@angular/core' +import * as path from 'path' + +export type PathCommand = 'normalize' | 'basename' | 'dirname' | 'extname' | 'namespaced' + +@Pipe({ name: 'path' }) +export class PathPipe implements PipeTransform { + transform(value: string | undefined, command: PathCommand) { + if (!value) return value + + switch (command) { + case 'normalize': + return path.normalize(value) + case 'basename': + return path.basename(value) + case 'dirname': + return path.dirname(value) + case 'extname': + return path.extname(value) + case 'namespaced': + return path.toNamespacedPath(value) + default: + return value + } + } +} diff --git a/desktop/src/shared/pipes/skyObject.pipe.ts b/desktop/src/shared/pipes/skyObject.pipe.ts index a73df5489..c8b414f03 100644 --- a/desktop/src/shared/pipes/skyObject.pipe.ts +++ b/desktop/src/shared/pipes/skyObject.pipe.ts @@ -2,9 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core' import { AstronomicalObject } from '../types/atlas.types' import { Undefinable } from '../utils/types' -const SKY_OBJECT_PARTS = ['name', 'firstName'] as const - -export type SkyObjectPart = (typeof SKY_OBJECT_PARTS)[number] +export type SkyObjectPart = 'name' | 'firstName' @Pipe({ name: 'skyObject' }) export class SkyObjectPipe implements PipeTransform { @@ -13,7 +11,7 @@ export class SkyObjectPipe implements PipeTransform { case 'name': return value?.name.replaceAll('|', ' · ') case 'firstName': - return value?.name.split(/\[([^\]]+)\]/g).filter(Boolean)[0] + return value?.name.split(/\[([^\]]+)\]/g).find(Boolean) default: return `${value}` } diff --git a/desktop/src/shared/services/prime.service.ts b/desktop/src/shared/services/angular.service.ts similarity index 80% rename from desktop/src/shared/services/prime.service.ts rename to desktop/src/shared/services/angular.service.ts index 3d450f9e7..43e5e5ab1 100644 --- a/desktop/src/shared/services/prime.service.ts +++ b/desktop/src/shared/services/angular.service.ts @@ -5,14 +5,14 @@ import { ConfirmDialog } from '../dialogs/confirm/confirm.dialog' import { Undefinable } from '../utils/types' @Injectable({ providedIn: 'root' }) -export class PrimeService { +export class AngularService { constructor( - private readonly dialog: DialogService, - private readonly messager: MessageService, + private readonly dialogService: DialogService, + private readonly messageService: MessageService, ) {} open(componentType: Type, config: DynamicDialogConfig) { - const ref = this.dialog.open(componentType, { + const ref = this.dialogService.open(componentType, { ...config, duplicate: true, draggable: config.draggable ?? true, @@ -41,6 +41,6 @@ export class PrimeService { } message(text: string, severity: 'info' | 'warn' | 'error' | 'success' = 'success') { - this.messager.add({ severity, detail: text, life: 8500 }) + this.messageService.add({ severity, detail: text, life: 8500 }) } } diff --git a/desktop/src/shared/services/api.service.ts b/desktop/src/shared/services/api.service.ts index 74e32b42f..1278d9cab 100644 --- a/desktop/src/shared/services/api.service.ts +++ b/desktop/src/shared/services/api.service.ts @@ -1,33 +1,35 @@ import { Injectable } from '@angular/core' -import moment from 'moment' import { DARVStart, TPPAStart } from '../types/alignment.types' -import { Angle, BodyPosition, CloseApproach, ComputedLocation, Constellation, DeepSkyObject, MinorPlanet, Satellite, SatelliteGroupType, SkyObjectType, Twilight } from '../types/atlas.types' +import { extractDate, extractDateTime } from '../types/angular.types' +import { Angle, BodyPosition, CloseApproach, ComputedLocation, Constellation, DeepSkyObject, Location, MinorPlanet, Satellite, SatelliteGroupType, SkyObjectType, Twilight } from '../types/atlas.types' import { AutoFocusRequest } from '../types/autofocus.type' -import { CalibrationFrame, CalibrationFrameGroup } from '../types/calibration.types' +import { CalibrationFrame } from '../types/calibration.types' import { Camera, CameraStartCapture } from '../types/camera.types' import { Device, INDIProperty, INDISendProperty } from '../types/device.types' import { FlatWizardRequest } from '../types/flat-wizard.types' import { Focuser } from '../types/focuser.types' import { HipsSurvey } from '../types/framing.types' import { GuideDirection, GuideOutput, Guider, GuiderHistoryStep, SettleInfo } from '../types/guider.types' -import { ConnectionStatus, ConnectionType, Equipment } from '../types/home.types' -import { CoordinateInterpolation, DetectedStar, FOVCamera, FOVTelescope, ImageAnnotation, ImageInfo, ImageSaveDialog, ImageSolved, ImageTransformation } from '../types/image.types' -import { CelestialLocationType, Mount, MountRemoteControl, MountRemoteControlType, SlewRate, TrackMode } from '../types/mount.types' +import { ConnectionStatus, ConnectionType } from '../types/home.types' +import { AnnotateImageRequest, CoordinateInterpolation, DetectedStar, FOVCamera, FOVTelescope, ImageAnnotation, ImageInfo, ImageMousePosition, ImageSaveDialog, ImageSolved, ImageTransformation } from '../types/image.types' +import { CelestialLocationType, Mount, MountRemoteControl, MountRemoteControlProtocol, SlewRate, TrackMode } from '../types/mount.types' +import { PlateSolverRequest } from '../types/platesolver.types' import { Rotator } from '../types/rotator.types' -import { SequencePlan } from '../types/sequencer.types' -import { PlateSolverRequest, StarDetectionRequest } from '../types/settings.types' -import { FilterWheel } from '../types/wheel.types' +import { SequencerPlan } from '../types/sequencer.types' +import { AnalyzedTarget, StackingRequest } from '../types/stacker.types' +import { StarDetectionRequest } from '../types/stardetector.types' +import { Wheel } from '../types/wheel.types' import { Undefinable } from '../utils/types' import { HttpService } from './http.service' @Injectable({ providedIn: 'root' }) export class ApiService { - constructor(private readonly http: HttpService) {} - get baseUrl() { return this.http.baseUrl } + constructor(private readonly http: HttpService) {} + // CONNECTION connect(host: string, port: number, type: ConnectionType) { @@ -69,12 +71,6 @@ export class ApiService { return this.http.get(`cameras/${camera.id}/capturing`) } - cameraSnoop(camera: Camera, equipment: Equipment) { - const { mount, wheel, focuser, rotator } = equipment - const query = this.http.query({ mount: mount?.id, wheel: wheel?.id, focuser: focuser?.id, rotator: rotator?.id }) - return this.http.put(`cameras/${camera.id}/snoop?${query}`) - } - cameraCooler(camera: Camera, enabled: boolean) { return this.http.put(`cameras/${camera.id}/cooler?enabled=${enabled}`) } @@ -83,8 +79,7 @@ export class ApiService { return this.http.put(`cameras/${camera.id}/temperature/setpoint?temperature=${temperature}`) } - cameraStartCapture(camera: Camera, data: CameraStartCapture, equipment: Equipment) { - const { mount, wheel, focuser, rotator } = equipment + cameraStartCapture(camera: Camera, data: CameraStartCapture, mount?: Mount, wheel?: Wheel, focuser?: Focuser, rotator?: Rotator) { const query = this.http.query({ mount: mount?.id, wheel: wheel?.id, focuser: focuser?.id, rotator: rotator?.id }) return this.http.put(`cameras/${camera.id}/capture/start?${query}`, data) } @@ -179,13 +174,13 @@ export class ApiService { return this.http.get(`mounts/${mount.id}/location/${type}`) } - pointMountHere(mount: Mount, path: string, x: number, y: number) { - const query = this.http.query({ path, x, y }) + pointMountHere(mount: Mount, path: string, point: ImageMousePosition) { + const query = this.http.query({ path, ...point }) return this.http.put(`mounts/${mount.id}/point-here?${query}`) } - mountRemoteControlStart(mount: Mount, type: MountRemoteControlType, host: string, port: number) { - const query = this.http.query({ type, host, port }) + mountRemoteControlStart(mount: Mount, protocol: MountRemoteControlProtocol, host: string, port: number) { + const query = this.http.query({ protocol, host, port }) return this.http.put(`mounts/${mount.id}/remote-control/start?${query}`) } @@ -193,8 +188,8 @@ export class ApiService { return this.http.get(`mounts/${mount.id}/remote-control`) } - mountRemoteControlStop(mount: Mount, type: MountRemoteControlType) { - const query = this.http.query({ type }) + mountRemoteControlStop(mount: Mount, protocol: MountRemoteControlProtocol) { + const query = this.http.query({ protocol }) return this.http.put(`mounts/${mount.id}/remote-control/stop?${query}`) } @@ -247,30 +242,30 @@ export class ApiService { // FILTER WHEEL wheels() { - return this.http.get(`wheels`) + return this.http.get(`wheels`) } wheel(id: string) { - return this.http.get(`wheels/${id}`) + return this.http.get(`wheels/${id}`) } - wheelConnect(wheel: FilterWheel) { + wheelConnect(wheel: Wheel) { return this.http.put(`wheels/${wheel.id}/connect`) } - wheelDisconnect(wheel: FilterWheel) { + wheelDisconnect(wheel: Wheel) { return this.http.put(`wheels/${wheel.id}/disconnect`) } - wheelMoveTo(wheel: FilterWheel, position: number) { + wheelMoveTo(wheel: Wheel, position: number) { return this.http.put(`wheels/${wheel.id}/move-to?position=${position}`) } - wheelSync(wheel: FilterWheel, names: string[]) { + wheelSync(wheel: Wheel, names: string[]) { return this.http.put(`wheels/${wheel.id}/sync?names=${names.join(',')}`) } - wheelListen(wheel: FilterWheel) { + wheelListen(wheel: Wheel) { return this.http.put(`wheels/${wheel.id}/listen`) } @@ -386,14 +381,10 @@ export class ApiService { return this.http.put(`guiding/dither?${query}`) } - setGuidingSettle(settle: SettleInfo) { + guidingSettle(settle: SettleInfo) { return this.http.put(`guiding/settle`, settle) } - getGuidingSettle() { - return this.http.get(`guiding/settle`) - } - guidingStop() { return this.http.put(`guiding/stop`) } @@ -454,51 +445,51 @@ export class ApiService { // SKY ATLAS - positionOfSun(dateTime: Date, fast: boolean = false) { - const [date, time] = moment(dateTime).format('YYYY-MM-DD HH:mm').split(' ') - const query = this.http.query({ date, time, fast, hasLocation: true }) + positionOfSun(dateTime: Date, location?: Location, fast: boolean = false) { + const [date, time] = extractDateTime(dateTime) + const query = this.http.query({ date, time, fast, hasLocation: location?.id || true }) return this.http.get(`sky-atlas/sun/position?${query}`) } - altitudePointsOfSun(dateTime: Date, fast: boolean = false) { - const date = moment(dateTime).format('YYYY-MM-DD') - const query = this.http.query({ date, fast, hasLocation: true }) + altitudePointsOfSun(dateTime: Date, location?: Location, fast: boolean = false) { + const date = extractDate(dateTime) + const query = this.http.query({ date, fast, hasLocation: location?.id || true }) return this.http.get<[number, number][]>(`sky-atlas/sun/altitude-points?${query}`) } - positionOfMoon(dateTime: Date, fast: boolean = false) { - const [date, time] = moment(dateTime).format('YYYY-MM-DD HH:mm').split(' ') - const query = this.http.query({ date, time, fast, hasLocation: true }) + positionOfMoon(dateTime: Date, location?: Location, fast: boolean = false) { + const [date, time] = extractDateTime(dateTime) + const query = this.http.query({ date, time, fast, hasLocation: location?.id || true }) return this.http.get(`sky-atlas/moon/position?${query}`) } - altitudePointsOfMoon(dateTime: Date, fast: boolean = false) { - const date = moment(dateTime).format('YYYY-MM-DD') - const query = this.http.query({ date, fast, hasLocation: true }) + altitudePointsOfMoon(dateTime: Date, location?: Location, fast: boolean = false) { + const date = extractDate(dateTime) + const query = this.http.query({ date, fast, hasLocation: location?.id || true }) return this.http.get<[number, number][]>(`sky-atlas/moon/altitude-points?${query}`) } - positionOfPlanet(code: string, dateTime: Date, fast: boolean = false) { - const [date, time] = moment(dateTime).format('YYYY-MM-DD HH:mm').split(' ') - const query = this.http.query({ date, time, fast, hasLocation: true }) + positionOfPlanet(code: string, dateTime: Date, location?: Location, fast: boolean = false) { + const [date, time] = extractDateTime(dateTime) + const query = this.http.query({ date, time, fast, hasLocation: location?.id || true }) return this.http.get(`sky-atlas/planets/${encodeURIComponent(code)}/position?${query}`) } - altitudePointsOfPlanet(code: string, dateTime: Date, fast: boolean = false) { - const date = moment(dateTime).format('YYYY-MM-DD') - const query = this.http.query({ date, fast, hasLocation: true }) + altitudePointsOfPlanet(code: string, dateTime: Date, location?: Location, fast: boolean = false) { + const date = extractDate(dateTime) + const query = this.http.query({ date, fast, hasLocation: location?.id || true }) return this.http.get<[number, number][]>(`sky-atlas/planets/${encodeURIComponent(code)}/altitude-points?${query}`) } - positionOfSkyObject(simbad: DeepSkyObject, dateTime: Date) { - const [date, time] = moment(dateTime).format('YYYY-MM-DD HH:mm').split(' ') - const query = this.http.query({ date, time, hasLocation: true }) + positionOfSkyObject(simbad: DeepSkyObject, dateTime: Date, location?: Location) { + const [date, time] = extractDateTime(dateTime) + const query = this.http.query({ date, time, hasLocation: location?.id || true }) return this.http.get(`sky-atlas/sky-objects/${simbad.id}/position?${query}`) } - altitudePointsOfSkyObject(simbad: DeepSkyObject, dateTime: Date) { - const date = moment(dateTime).format('YYYY-MM-DD') - const query = this.http.query({ date, hasLocation: true }) + altitudePointsOfSkyObject(simbad: DeepSkyObject, dateTime: Date, location?: Location) { + const date = extractDate(dateTime) + const query = this.http.query({ date, hasLocation: location?.id || true }) return this.http.get<[number, number][]>(`sky-atlas/sky-objects/${simbad.id}/altitude-points?${query}`) } @@ -511,15 +502,15 @@ export class ApiService { return this.http.get(`sky-atlas/sky-objects/types`) } - positionOfSatellite(satellite: Satellite, dateTime: Date) { - const [date, time] = moment(dateTime).format('YYYY-MM-DD HH:mm').split(' ') - const query = this.http.query({ date, time, hasLocation: true }) + positionOfSatellite(satellite: Satellite, dateTime: Date, location?: Location) { + const [date, time] = extractDateTime(dateTime) + const query = this.http.query({ date, time, hasLocation: location?.id || true }) return this.http.get(`sky-atlas/satellites/${satellite.id}/position?${query}`) } - altitudePointsOfSatellite(satellite: Satellite, dateTime: Date) { - const date = moment(dateTime).format('YYYY-MM-DD') - const query = this.http.query({ date, hasLocation: true }) + altitudePointsOfSatellite(satellite: Satellite, dateTime: Date, location?: Location) { + const date = extractDate(dateTime) + const query = this.http.query({ date, hasLocation: location?.id || true }) return this.http.get<[number, number][]>(`sky-atlas/satellites/${satellite.id}/altitude-points?${query}`) } @@ -528,9 +519,9 @@ export class ApiService { return this.http.get(`sky-atlas/satellites?${query}`) } - twilight(dateTime: Date, fast: boolean = false) { - const date = moment(dateTime).format('YYYY-MM-DD') - const query = this.http.query({ date, fast, hasLocation: true }) + twilight(dateTime: Date, location?: Location, fast: boolean = false) { + const date = extractDate(dateTime) + const query = this.http.query({ date, fast, hasLocation: location?.id || true }) return this.http.get(`sky-atlas/twilight?${query}`) } @@ -539,15 +530,15 @@ export class ApiService { return this.http.get(`sky-atlas/minor-planets?${query}`) } - closeApproachesForMinorPlanets(days: number = 7, distance: number = 10, dateTime?: Date | string) { - const date = !dateTime || typeof dateTime === 'string' ? dateTime : moment(dateTime).format('YYYY-MM-DD') + closeApproachesOfMinorPlanets(days: number = 7, distance: number = 10, dateTime?: Date | string) { + const date = !dateTime || typeof dateTime === 'string' ? dateTime : extractDate(dateTime) const query = this.http.query({ days, distance, date }) return this.http.get(`sky-atlas/minor-planets/close-approaches?${query}`) } - annotationsOfImage(path: string, starsAndDSOs: boolean = true, minorPlanets: boolean = false, minorPlanetMagLimit: number = 12.0, includeMinorPlanetsWithoutMagnitude: boolean = false, useSimbad: boolean = false) { - const query = this.http.query({ path, starsAndDSOs, minorPlanets, minorPlanetMagLimit, includeMinorPlanetsWithoutMagnitude, useSimbad, hasLocation: true }) - return this.http.get(`image/annotations?${query}`) + annotationsOfImage(path: string, request: AnnotateImageRequest, location?: Location) { + const query = this.http.query({ path, hasLocation: location?.id || true }) + return this.http.put(`image/annotations?${query}`, request) } saveImageAs(path: string, save: ImageSaveDialog, camera?: Camera) { @@ -585,7 +576,7 @@ export class ApiService { } calibrationFrames(name: string) { - return this.http.get(`calibration-frames/${name}`) + return this.http.get(`calibration-frames/${name}`) } uploadCalibrationFrame(name: string, path: string) { @@ -593,9 +584,8 @@ export class ApiService { return this.http.put(`calibration-frames/${name}?${query}`) } - editCalibrationFrame(frame: CalibrationFrame) { - const query = this.http.query({ name: frame.name, enabled: frame.enabled }) - return this.http.patch(`calibration-frames/${frame.id}?${query}`) + updateCalibrationFrame(frame: CalibrationFrame) { + return this.http.post('calibration-frames', frame) } deleteCalibrationFrame(frame: CalibrationFrame) { @@ -643,12 +633,20 @@ export class ApiService { // SEQUENCER - sequencerStart(camera: Camera, plan: SequencePlan) { - const body: SequencePlan = { ...plan, mount: undefined, camera: undefined, wheel: undefined, focuser: undefined } - const query = this.http.query({ mount: plan.mount?.id, focuser: plan.focuser?.id, wheel: plan.wheel?.id }) + sequencerStart(camera: Camera, plan: SequencerPlan) { + const body: SequencerPlan = { ...plan, mount: undefined, camera: undefined, wheel: undefined, focuser: undefined, rotator: undefined } + const query = this.http.query({ mount: plan.mount?.id, focuser: plan.focuser?.id, wheel: plan.wheel?.id, rotator: plan.rotator?.id }) return this.http.put(`sequencer/${camera.id}/start?${query}`, body) } + sequencerPause(camera: Camera) { + return this.http.put(`sequencer/${camera.id}/pause`) + } + + sequencerUnpause(camera: Camera) { + return this.http.put(`sequencer/${camera.id}/unpause`) + } + sequencerStop(camera: Camera) { return this.http.put(`sequencer/${camera.id}/stop`) } @@ -665,9 +663,13 @@ export class ApiService { // SOLVER - solveImage(solver: PlateSolverRequest, path: string, blind: boolean, centerRA: Angle, centerDEC: Angle, radius: Angle) { - const query = this.http.query({ path, blind, centerRA, centerDEC, radius }) - return this.http.put(`plate-solver?${query}`, solver) + solverStart(solver: PlateSolverRequest, path: string) { + const query = this.http.query({ path }) + return this.http.put(`plate-solver/start?${query}`, solver) + } + + solverStop() { + return this.http.put('plate-solver/stop') } // AUTO FOCUS @@ -680,26 +682,22 @@ export class ApiService { return this.http.put(`auto-focus/${camera.id}/stop`) } - // PREFERENCE - - clearPreferences() { - return this.http.put('preferences/clear') - } + // STACKER - deletePreference(key: string) { - return this.http.delete(`preferences/${key}`) + stackerStart(request: StackingRequest) { + return this.http.put('stacker/start', request) } - getPreference(key: string) { - return this.http.get(`preferences/${key}`) + stackerIsRunning() { + return this.http.get('stacker/running') } - setPreference(key: string, data: unknown) { - return this.http.put(`preferences/${key}`, { data }) + stackerStop() { + return this.http.put('stacker/stop') } - hasPreference(key: string) { - return this.http.get(`preferences/${key}/exists`) + stackerAnalyze(path: string) { + return this.http.put(`stacker/analyze?path=${path}`) } // CONFIRMATION diff --git a/desktop/src/shared/services/browser-window.service.ts b/desktop/src/shared/services/browser-window.service.ts index c96e3e34b..2ff5fa757 100644 --- a/desktop/src/shared/services/browser-window.service.ts +++ b/desktop/src/shared/services/browser-window.service.ts @@ -1,29 +1,29 @@ import { Injectable } from '@angular/core' -import { FramingData } from '../../app/framing/framing.component' import { OpenWindow, WindowPreference } from '../types/app.types' import { SkyAtlasInput } from '../types/atlas.types' import { Camera, CameraDialogInput, CameraStartCapture } from '../types/camera.types' import { Device } from '../types/device.types' import { Focuser } from '../types/focuser.types' +import { LoadFraming } from '../types/framing.types' import { ImageSource, OpenImage } from '../types/image.types' import { Mount } from '../types/mount.types' import { Rotator } from '../types/rotator.types' -import { FilterWheel, WheelDialogInput } from '../types/wheel.types' +import { Wheel, WheelDialogInput } from '../types/wheel.types' import { Undefinable } from '../utils/types' import { ElectronService } from './electron.service' @Injectable({ providedIn: 'root' }) export class BrowserWindowService { - constructor(private readonly electron: ElectronService) {} + constructor(private readonly electronService: ElectronService) {} openWindow(open: OpenWindow): Promise { open.preference.modal = false - return this.electron.ipcRenderer.invoke('WINDOW.OPEN', { ...open, windowId: window.id }) + return this.electronService.ipcRenderer.invoke('WINDOW.OPEN', { ...open, windowId: window.id }) } openModal(open: OpenWindow): Promise> { open.preference.modal = true - return this.electron.ipcRenderer.invoke('WINDOW.OPEN', { ...open, windowId: window.id }) + return this.electronService.ipcRenderer.invoke('WINDOW.OPEN', { ...open, windowId: window.id }) } openMount(data: Mount, preference: WindowPreference = {}) { @@ -32,7 +32,7 @@ export class BrowserWindowService { } openCamera(data: Camera, preference: WindowPreference = {}) { - Object.assign(preference, { icon: 'camera', width: 400, height: 467 }) + Object.assign(preference, { icon: 'camera', width: 400, height: 477 }) return this.openWindow({ preference, data, id: `camera.${data.name}`, path: 'camera' }) } @@ -51,7 +51,7 @@ export class BrowserWindowService { return this.openWindow({ preference, data, id: `focuser.${data.name}`, path: 'focuser' }) } - openWheel(data: FilterWheel, preference: WindowPreference = {}) { + openWheel(data: Wheel, preference: WindowPreference = {}) { Object.assign(preference, { icon: 'filter-wheel', width: 280, height: 195 }) return this.openWindow({ preference, data, id: `wheel.${data.name}`, path: 'wheel' }) } @@ -72,7 +72,7 @@ export class BrowserWindowService { } openGuider(preference: WindowPreference = {}) { - Object.assign(preference, { icon: 'guider', width: 440, height: 455 }) + Object.assign(preference, { icon: 'guider', width: 380, height: 444 }) return this.openWindow({ preference, id: 'guider', path: 'guider' }) } @@ -103,7 +103,7 @@ export class BrowserWindowService { return this.openWindow({ preference, data, id: 'atlas', path: 'atlas' }) } - openFraming(data?: FramingData, preference: WindowPreference = {}) { + openFraming(data?: LoadFraming, preference: WindowPreference = {}) { Object.assign(preference, { icon: 'framing', width: 280, height: 303 }) return this.openWindow({ preference, data, id: 'framing', path: 'framing' }) } @@ -129,7 +129,7 @@ export class BrowserWindowService { } openSettings(preference: WindowPreference = {}) { - Object.assign(preference, { icon: 'settings', width: 350, height: 450, autoResizable: false }) + Object.assign(preference, { icon: 'settings', width: 320, height: 450, minHeight: 350 }) return this.openWindow({ preference, id: 'settings', path: 'settings' }) } @@ -139,12 +139,17 @@ export class BrowserWindowService { } openCalibration(preference: WindowPreference = {}) { - Object.assign(preference, { icon: 'stack', width: 420, height: 400, minHeight: 400 }) + Object.assign(preference, { icon: 'calibration', width: 370, height: 442, minHeight: 400 }) return this.openWindow({ preference, id: 'calibration', path: 'calibration' }) } + openStacker(preference: WindowPreference = {}) { + Object.assign(preference, { icon: 'stack', width: 370, height: 460 }) + return this.openWindow({ preference, id: 'stacker', path: 'stacker' }) + } + openAbout() { - const preference: WindowPreference = { icon: 'about', width: 430, height: 307, bringToFront: true } + const preference: WindowPreference = { icon: 'about', width: 430, height: 340, bringToFront: true } return this.openWindow({ preference, id: 'about', path: 'about' }) } } diff --git a/desktop/src/shared/services/confirmation.service.ts b/desktop/src/shared/services/confirmation.service.ts index 3f996eb4c..55b4c0b12 100644 --- a/desktop/src/shared/services/confirmation.service.ts +++ b/desktop/src/shared/services/confirmation.service.ts @@ -1,15 +1,15 @@ import { Injectable } from '@angular/core' import { ConfirmEventType } from 'primeng/api' import { ConfirmationEvent } from '../types/app.types' +import { AngularService } from './angular.service' import { ApiService } from './api.service' -import { PrimeService } from './prime.service' @Injectable({ providedIn: 'root' }) export class ConfirmationService { private readonly keys = new Map() constructor( - private readonly prime: PrimeService, + private readonly angularService: AngularService, private readonly api: ApiService, ) {} @@ -26,8 +26,7 @@ export class ConfirmationService { } async processConfirmationEvent(event: ConfirmationEvent) { - console.info('processing confirmation event', event) - const response = await this.prime.confirm(event.message) + const response = await this.angularService.confirm(event.message) await this.api.confirm(event.idempotencyKey, response === ConfirmEventType.ACCEPT) this.unregister(event.idempotencyKey) } diff --git a/desktop/src/shared/services/electron.service.ts b/desktop/src/shared/services/electron.service.ts index 8804deec1..bf88acc10 100644 --- a/desktop/src/shared/services/electron.service.ts +++ b/desktop/src/shared/services/electron.service.ts @@ -4,9 +4,9 @@ import { Injectable } from '@angular/core' // other than as TypeScript types, the resulting javascript file will // look as if you never imported the module at all. -import * as childProcess from 'child_process' -import { ipcRenderer, webFrame } from 'electron' -import * as fs from 'fs' +import type * as childProcess from 'child_process' +import type { ipcRenderer, webFrame } from 'electron' +import type * as fs from 'fs' import { DARVEvent, TPPAEvent } from '../types/alignment.types' import { DeviceMessageEvent } from '../types/api.types' import { CloseWindow, ConfirmationEvent, FullscreenWindow, JsonFile, NotificationEvent, OpenDirectory, OpenFile, ResizeWindow, SaveJson, WindowCommand } from '../types/app.types' @@ -22,10 +22,22 @@ import { ROISelected } from '../types/image.types' import { Mount } from '../types/mount.types' import { Rotator } from '../types/rotator.types' import { SequencerEvent } from '../types/sequencer.types' -import { FilterWheel, WheelRenamed } from '../types/wheel.types' -import { Undefinable } from '../utils/types' - -interface EventMappedType { +import { Wheel, WheelRenamed } from '../types/wheel.types' + +export const OPEN_IMAGE_FILE_FILTER: Electron.FileFilter[] = [ + { name: 'All', extensions: ['fits', 'fit', 'xisf'] }, + { name: 'FITS', extensions: ['fits', 'fit'] }, + { name: 'XISF', extensions: ['xisf'] }, +] + +export const SAVE_IMAGE_FILE_FILTER: Electron.FileFilter[] = [ + { name: 'All', extensions: ['fits', 'fit', 'xisf', 'png', 'jpg', 'jpeg'] }, + { name: 'FITS', extensions: ['fits', 'fit'] }, + { name: 'XISF', extensions: ['xisf'] }, + { name: 'Image', extensions: ['png', 'jpg', 'jpeg'] }, +] + +export interface EventTypes { NOTIFICATION: NotificationEvent CONFIRMATION: ConfirmationEvent 'DEVICE.PROPERTY_CHANGED': INDIMessageEvent @@ -44,9 +56,9 @@ interface EventMappedType { 'ROTATOR.UPDATED': DeviceMessageEvent 'ROTATOR.ATTACHED': DeviceMessageEvent 'ROTATOR.DETACHED': DeviceMessageEvent - 'WHEEL.UPDATED': DeviceMessageEvent - 'WHEEL.ATTACHED': DeviceMessageEvent - 'WHEEL.DETACHED': DeviceMessageEvent + 'WHEEL.UPDATED': DeviceMessageEvent + 'WHEEL.ATTACHED': DeviceMessageEvent + 'WHEEL.DETACHED': DeviceMessageEvent 'GUIDE_OUTPUT.UPDATED': DeviceMessageEvent 'GUIDE_OUTPUT.ATTACHED': DeviceMessageEvent 'GUIDE_OUTPUT.DETACHED': DeviceMessageEvent @@ -82,10 +94,10 @@ interface EventMappedType { @Injectable({ providedIn: 'root' }) export class ElectronService { - ipcRenderer!: typeof ipcRenderer - webFrame!: typeof webFrame - childProcess!: typeof childProcess - fs!: typeof fs + readonly ipcRenderer!: typeof ipcRenderer + private readonly webFrame!: typeof webFrame + private readonly childProcess!: typeof childProcess + private readonly fs!: typeof fs constructor() { if (this.isElectron) { @@ -116,35 +128,41 @@ export class ElectronService { return !!(window && window.process?.type) } - send(channel: K, data?: EventMappedType[K]) { + send(channel: K, data?: EventTypes[K]) { return this.ipcRenderer.invoke(channel, data) } - on(channel: K, listener: (arg: EventMappedType[K]) => void) { - console.info('listening to channel: %s', channel) - + on(channel: K, listener: (arg: EventTypes[K]) => void) { this.ipcRenderer.on(channel, (_, arg) => { listener(arg) }) } - openFile(data?: OpenFile): Promise> { - return this.send('FILE.OPEN', { ...data, windowId: data?.windowId ?? window.id }) + openFile(data?: OpenFile): Promise { + return this.send('FILE.OPEN', { ...data, windowId: data?.windowId ?? window.id, multiple: false }) } - saveFile(data?: OpenFile): Promise> { + openFiles(data?: OpenFile): Promise { + return this.send('FILE.OPEN', { ...data, windowId: data?.windowId ?? window.id, multiple: true }) + } + + saveFile(data?: OpenFile): Promise { return this.send('FILE.SAVE', { ...data, windowId: data?.windowId ?? window.id }) } - openImage(data?: OpenFile): Promise> { + openImage(data?: OpenFile) { return this.openFile({ ...data, windowId: data?.windowId ?? window.id, - filters: [ - { name: 'All', extensions: ['fits', 'fit', 'xisf'] }, - { name: 'FITS', extensions: ['fits', 'fit'] }, - { name: 'XISF', extensions: ['xisf'] }, - ], + filters: OPEN_IMAGE_FILE_FILTER, + }) + } + + openImages(data?: OpenFile) { + return this.openFiles({ + ...data, + windowId: data?.windowId ?? window.id, + filters: OPEN_IMAGE_FILE_FILTER, }) } @@ -152,12 +170,7 @@ export class ElectronService { return this.saveFile({ ...data, windowId: data?.windowId ?? window.id, - filters: [ - { name: 'All', extensions: ['fits', 'fit', 'xisf', 'png', 'jpg', 'jpeg'] }, - { name: 'FITS', extensions: ['fits', 'fit'] }, - { name: 'XISF', extensions: ['xisf'] }, - { name: 'Image', extensions: ['png', 'jpg', 'jpeg'] }, - ], + filters: SAVE_IMAGE_FILE_FILTER, }) } @@ -166,7 +179,7 @@ export class ElectronService { } async saveJson(data: SaveJson): Promise | false> { - data.path = data.path || (await this.saveFile({ ...data, windowId: data.windowId ?? window.id, filters: [{ name: 'JSON files', extensions: ['json'] }] })) + data.path = data.path || (await this.saveFile({ ...data, windowId: data.windowId ?? window.id, filters: [{ name: 'JSON files', extensions: ['json'] }] })) || undefined if (data.path) { if (await this.writeJson(data)) { @@ -199,20 +212,6 @@ export class ElectronService { return this.send('WINDOW.RESIZE', { height: Math.floor(size), windowId: window.id }) } - async autoResizeWindow(timeout: number = 500): Promise> { - if (timeout <= 0) { - const size = document.getElementsByTagName('app-root')[0].getBoundingClientRect().height - - if (size) { - await this.resizeWindow(size) - } - } else { - return setTimeout(() => this.autoResizeWindow(0), timeout) as unknown as number - } - - return undefined - } - pinWindow() { return this.send('WINDOW.PIN', { windowId: window.id }) } @@ -240,4 +239,8 @@ export class ElectronService { calibrationChanged() { return this.send('CALIBRATION.CHANGED') } + + locationChanged(location: Location) { + return this.send('LOCATION.CHANGED', location) + } } diff --git a/desktop/src/shared/services/http.service.ts b/desktop/src/shared/services/http.service.ts index d6310aaa7..e3bcfc3ec 100644 --- a/desktop/src/shared/services/http.service.ts +++ b/desktop/src/shared/services/http.service.ts @@ -7,11 +7,9 @@ export type QueryParamType = Nullable | QueryParamTyp @Injectable({ providedIn: 'root' }) export class HttpService { - constructor(private readonly http: HttpClient) {} + readonly baseUrl = `http://${window.apiHost}:${window.apiPort}` - get baseUrl() { - return `http://${window.apiHost}:${window.apiPort}` - } + constructor(private readonly http: HttpClient) {} get(path: string) { return firstValueFrom(this.http.get(`${this.baseUrl}/${path}`)) diff --git a/desktop/src/shared/services/pinger.service.ts b/desktop/src/shared/services/pinger.service.ts deleted file mode 100644 index dd4123a81..000000000 --- a/desktop/src/shared/services/pinger.service.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Injectable } from '@angular/core' - -export interface Pingable { - ping(): void -} - -@Injectable({ providedIn: 'root' }) -export class Pinger { - private readonly pingables = new Map() - - isRegistered(pingable: Pingable) { - return this.pingables.has(pingable) - } - - register(pingable: Pingable, interval: number, initialDelay: number = 1000) { - this.unregister(pingable) - - if (interval > 0) { - if (initialDelay > 0 && initialDelay < interval - 1000) { - setTimeout(() => { - pingable.ping() - }, initialDelay) - } - - const ping = setInterval(() => { - pingable.ping() - }, interval) as unknown as number - - this.pingables.set(pingable, ping) - } - } - - unregister(pingable: Pingable) { - clearInterval(this.pingables.get(pingable)) - this.pingables.delete(pingable) - } -} diff --git a/desktop/src/shared/services/preference.service.ts b/desktop/src/shared/services/preference.service.ts index 320a7c3fb..739bf6b3b 100644 --- a/desktop/src/shared/services/preference.service.ts +++ b/desktop/src/shared/services/preference.service.ts @@ -1,17 +1,21 @@ import { Injectable } from '@angular/core' -import { AlignmentPreference, EMPTY_ALIGNMENT_PREFERENCE } from '../types/alignment.types' -import { EMPTY_LOCATION, EMPTY_SKY_ATLAS_PREFERENCE, Location, SkyAtlasPreference } from '../types/atlas.types' -import { AutoFocusPreference, EMPTY_AUTO_FOCUS_PREFERENCE } from '../types/autofocus.type' -import { CalibrationPreference } from '../types/calibration.types' -import { Camera, CameraPreference, CameraStartCapture, EMPTY_CAMERA_PREFERENCE, EMPTY_LIVE_STACKING_REQUEST, LiveStackerType, LiveStackingRequest } from '../types/camera.types' -import { Device } from '../types/device.types' -import { Focuser, FocuserPreference } from '../types/focuser.types' -import { ConnectionDetails, Equipment, HomePreference } from '../types/home.types' -import { EMPTY_IMAGE_PREFERENCE, FOV, ImagePreference } from '../types/image.types' -import { EMPTY_MOUNT_PREFERENCE, Mount, MountPreference } from '../types/mount.types' -import { Rotator, RotatorPreference } from '../types/rotator.types' -import { EMPTY_PLATE_SOLVER_REQUEST, EMPTY_STAR_DETECTION_REQUEST, PlateSolverRequest, PlateSolverType, StarDetectionRequest, StarDetectorType } from '../types/settings.types' -import { FilterWheel, WheelPreference } from '../types/wheel.types' +import { AlignmentPreference, alignmentPreferenceWithDefault, DEFAULT_ALIGNMENT_PREFERENCE } from '../types/alignment.types' +import { DEFAULT_SKY_ATLAS_PREFERENCE, SkyAtlasPreference, skyAtlasPreferenceWithDefault } from '../types/atlas.types' +import { AutoFocusPreference, autoFocusPreferenceWithDefault, DEFAULT_AUTO_FOCUS_PREFERENCE } from '../types/autofocus.type' +import { CalibrationPreference, calibrationPreferenceWithDefault, DEFAULT_CALIBRATION_PREFERENCE } from '../types/calibration.types' +import { Camera, CameraPreference, cameraPreferenceWithDefault, DEFAULT_CAMERA_PREFERENCE } from '../types/camera.types' +import { DEFAULT_FLAT_WIZARD_PREFERENCE, FlatWizardPreference, flatWizardPreferenceWithDefault } from '../types/flat-wizard.types' +import { DEFAULT_FOCUSER_PREFERENCE, Focuser, FocuserPreference, focuserPreferenceWithDefault } from '../types/focuser.types' +import { DEFAULT_FRAMING_PREFERENCE, FramingPreference, framingPreferenceWithDefault } from '../types/framing.types' +import { DEFAULT_GUIDER_PREFERENCE, GuiderPreference, guiderPreferenceWithDefault } from '../types/guider.types' +import { DEFAULT_HOME_PREFERENCE, HomePreference, homePreferenceWithDefault } from '../types/home.types' +import { DEFAULT_IMAGE_PREFERENCE, ImagePreference, imagePreferenceWithDefault } from '../types/image.types' +import { DEFAULT_MOUNT_PREFERENCE, Mount, MountPreference, mountPreferenceWithDefault } from '../types/mount.types' +import { DEFAULT_ROTATOR_PREFERENCE, Rotator, RotatorPreference, rotatorPreferenceWithDefault } from '../types/rotator.types' +import { DEFAULT_SEQUENCER_PREFERENCE, SequencerPreference, sequencerPreferenceWithDefault } from '../types/sequencer.types' +import { DEFAULT_SETTINGS_PREFERENCE, SettingsPreference, settingsPreferenceWithDefault } from '../types/settings.types' +import { DEFAULT_STACKER_PREFERENCE, StackerPreference, stackerPreferenceWithDefault } from '../types/stacker.types' +import { DEFAULT_WHEEL_PREFERENCE, Wheel, WheelPreference, wheelPreferenceWithDefault } from '../types/wheel.types' import { Undefinable } from '../utils/types' import { LocalStorageService } from './local-storage.service' @@ -20,6 +24,7 @@ export class PreferenceData { private readonly storage: LocalStorageService, private readonly key: string, private readonly defaultValue: T | (() => T), + private readonly withDefault?: (value: T) => T, ) {} has() { @@ -27,7 +32,8 @@ export class PreferenceData { } get(defaultValue?: T | (() => T)): T { - return this.storage.get(this.key, defaultValue ?? this.defaultValue) + const value = this.storage.get(this.key, defaultValue ?? this.defaultValue) + return this.withDefault?.(value) ?? value } set(value: Undefinable) { @@ -43,70 +49,46 @@ export class PreferenceData { export class PreferenceService { constructor(private readonly storage: LocalStorageService) {} - wheelPreference(wheel: FilterWheel) { - return new PreferenceData(this.storage, `wheel.${wheel.name}`, {}) + wheel(wheel: Wheel) { + return new PreferenceData(this.storage, `wheel.${wheel.name}`, () => structuredClone(DEFAULT_WHEEL_PREFERENCE), wheelPreferenceWithDefault) } - cameraPreference(camera: Camera) { - return new PreferenceData(this.storage, `camera.${camera.name}`, () => structuredClone(EMPTY_CAMERA_PREFERENCE)) + camera(camera: Camera) { + return new PreferenceData(this.storage, `camera.${camera.name}`, () => structuredClone(DEFAULT_CAMERA_PREFERENCE), cameraPreferenceWithDefault) } - cameraStartCaptureForFlatWizard(camera: Camera) { - return new PreferenceData(this.storage, `camera.${camera.name}.flatWizard`, () => this.cameraPreference(camera).get()) + mount(mount: Mount) { + return new PreferenceData(this.storage, `mount.${mount.name}`, () => structuredClone(DEFAULT_MOUNT_PREFERENCE), mountPreferenceWithDefault) } - cameraStartCaptureForDARV(camera: Camera) { - return new PreferenceData(this.storage, `camera.${camera.name}.darv`, () => this.cameraPreference(camera).get()) - } - - cameraStartCaptureForTPPA(camera: Camera) { - return new PreferenceData(this.storage, `camera.${camera.name}.tppa`, () => this.cameraPreference(camera).get()) - } - - cameraStartCaptureForAutoFocus(camera: Camera) { - return new PreferenceData(this.storage, `camera.${camera.name}.autoFocus`, () => this.cameraPreference(camera).get()) - } - - mountPreference(mount: Mount) { - return new PreferenceData(this.storage, `mount.${mount.name}`, () => structuredClone(EMPTY_MOUNT_PREFERENCE)) - } - - plateSolverRequest(type: PlateSolverType) { - return new PreferenceData(this.storage, `plateSolver.${type}`, () => ({ ...EMPTY_PLATE_SOLVER_REQUEST, type }) as PlateSolverRequest) - } - - starDetectionRequest(type: StarDetectorType) { - return new PreferenceData(this.storage, `starDetection.${type}`, () => ({ ...EMPTY_STAR_DETECTION_REQUEST, type }) as StarDetectionRequest) - } - - liveStackingRequest(type: LiveStackerType) { - return new PreferenceData(this.storage, `liveStacking.${type}`, () => ({ ...EMPTY_LIVE_STACKING_REQUEST, type }) as LiveStackingRequest) + focusOffsets(wheel: Wheel, focuser: Focuser) { + return new PreferenceData(this.storage, `focusOffsets.${wheel.name}.${focuser.name}`, () => new Array(wheel.count).fill(0)) } - equipmentForDevice(device: Device) { - return new PreferenceData(this.storage, `equipment.${device.name}`, () => ({}) as Equipment) + focuser(focuser: Focuser) { + return new PreferenceData(this.storage, `focuser.${focuser.name}`, () => structuredClone(DEFAULT_FOCUSER_PREFERENCE), focuserPreferenceWithDefault) } - focusOffsets(wheel: FilterWheel, focuser: Focuser) { - return new PreferenceData(this.storage, `focusOffsets.${wheel.name}.${focuser.name}`, () => new Array(wheel.count).fill(0)) + rotator(rotator: Rotator) { + return new PreferenceData(this.storage, `rotator.${rotator.name}`, () => structuredClone(DEFAULT_ROTATOR_PREFERENCE), rotatorPreferenceWithDefault) } - focuserPreference(focuser: Focuser) { - return new PreferenceData(this.storage, `focuser.${focuser.name}`, {}) + flatWizard(camera: Camera) { + return new PreferenceData(this.storage, `flatWizard.${camera.name}`, () => structuredClone(DEFAULT_FLAT_WIZARD_PREFERENCE), flatWizardPreferenceWithDefault) } - rotatorPreference(rotator: Rotator) { - return new PreferenceData(this.storage, `rotator.${rotator.name}`, {}) + autoFocus(camera: Camera) { + return new PreferenceData(this.storage, `autoFocus.${camera.name}`, () => structuredClone(DEFAULT_AUTO_FOCUS_PREFERENCE), autoFocusPreferenceWithDefault) } - readonly connections = new PreferenceData(this.storage, 'home.connections', () => []) - readonly locations = new PreferenceData(this.storage, 'locations', () => [structuredClone(EMPTY_LOCATION)]) - readonly selectedLocation = new PreferenceData(this.storage, 'locations.selected', () => structuredClone(EMPTY_LOCATION)) - readonly homePreference = new PreferenceData(this.storage, 'home', () => ({}) as HomePreference) - readonly imagePreference = new PreferenceData(this.storage, 'image', () => structuredClone(EMPTY_IMAGE_PREFERENCE)) - readonly skyAtlasPreference = new PreferenceData(this.storage, 'atlas', () => structuredClone(EMPTY_SKY_ATLAS_PREFERENCE)) - readonly alignmentPreference = new PreferenceData(this.storage, 'alignment', () => structuredClone(EMPTY_ALIGNMENT_PREFERENCE)) - readonly imageFOVs = new PreferenceData(this.storage, 'image.fovs', () => []) - readonly calibrationPreference = new PreferenceData(this.storage, 'calibration', () => ({}) as CalibrationPreference) - readonly autoFocusPreference = new PreferenceData(this.storage, 'autoFocus', () => structuredClone(EMPTY_AUTO_FOCUS_PREFERENCE)) + readonly home = new PreferenceData(this.storage, 'home', () => structuredClone(DEFAULT_HOME_PREFERENCE), homePreferenceWithDefault) + readonly imagePreference = new PreferenceData(this.storage, 'image', () => structuredClone(DEFAULT_IMAGE_PREFERENCE), imagePreferenceWithDefault) + readonly skyAtlasPreference = new PreferenceData(this.storage, 'atlas', () => structuredClone(DEFAULT_SKY_ATLAS_PREFERENCE), skyAtlasPreferenceWithDefault) + readonly alignment = new PreferenceData(this.storage, 'alignment', () => structuredClone(DEFAULT_ALIGNMENT_PREFERENCE), alignmentPreferenceWithDefault) + readonly calibrationPreference = new PreferenceData(this.storage, 'calibration', () => structuredClone(DEFAULT_CALIBRATION_PREFERENCE), calibrationPreferenceWithDefault) + readonly sequencerPreference = new PreferenceData(this.storage, 'sequencer', () => structuredClone(DEFAULT_SEQUENCER_PREFERENCE), sequencerPreferenceWithDefault) + readonly stacker = new PreferenceData(this.storage, 'stacker', () => structuredClone(DEFAULT_STACKER_PREFERENCE), stackerPreferenceWithDefault) + readonly guider = new PreferenceData(this.storage, 'guider', () => structuredClone(DEFAULT_GUIDER_PREFERENCE), guiderPreferenceWithDefault) + readonly framing = new PreferenceData(this.storage, 'framing', () => structuredClone(DEFAULT_FRAMING_PREFERENCE), framingPreferenceWithDefault) + readonly settings = new PreferenceData(this.storage, 'settings', () => structuredClone(DEFAULT_SETTINGS_PREFERENCE), settingsPreferenceWithDefault) } diff --git a/desktop/src/shared/services/remote-storage.service.ts b/desktop/src/shared/services/remote-storage.service.ts deleted file mode 100644 index ade4eba8f..000000000 --- a/desktop/src/shared/services/remote-storage.service.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Injectable } from '@angular/core' -import { Undefinable } from '../utils/types' -import { ApiService } from './api.service' -import { StorageService } from './storage.service' - -@Injectable({ providedIn: 'root' }) -export class RemoteStorageService implements StorageService { - constructor(private readonly api: ApiService) {} - - clear() { - return this.api.clearPreferences() - } - - delete(key: string) { - return this.api.deletePreference(key) - } - - async get(key: string, defaultValue: T) { - return (await this.api.getPreference>(key)) ?? defaultValue - } - - has(key: string) { - return this.api.hasPreference(key) - } - - set(key: string, value: unknown) { - if (value === null || value === undefined) return this.delete(key) - else return this.api.setPreference(key, value) - } -} diff --git a/desktop/src/shared/services/ticker.service.ts b/desktop/src/shared/services/ticker.service.ts new file mode 100644 index 000000000..04ddd9afc --- /dev/null +++ b/desktop/src/shared/services/ticker.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core' + +export interface Tickable { + tick(): void +} + +@Injectable({ providedIn: 'root' }) +export class Ticker { + private readonly tickables = new Map() + + isRegistered(tickable: Tickable) { + return this.tickables.has(tickable) + } + + register(tickable: Tickable, interval: number, initialDelay: number = 1000) { + this.unregister(tickable) + + if (interval > 0) { + if (initialDelay > 0 && initialDelay < interval - 1000) { + setTimeout(() => { + tickable.tick() + }, initialDelay) + } + + const ping = setInterval(() => { + tickable.tick() + }, interval) as unknown as number + + this.tickables.set(tickable, ping) + } + } + + unregister(tickable: Tickable) { + clearInterval(this.tickables.get(tickable)) + this.tickables.delete(tickable) + } +} diff --git a/desktop/src/shared/types/about.types.ts b/desktop/src/shared/types/about.types.ts new file mode 100644 index 000000000..23ef5a80f --- /dev/null +++ b/desktop/src/shared/types/about.types.ts @@ -0,0 +1,13 @@ +export interface IconItem { + name: string + author: string + link: string +} + +export interface DependencyItem { + name: string + version: string + link: string +} + +export const FLAT_ICON_URL = 'https://www.flaticon.com/free-icon' diff --git a/desktop/src/shared/types/alignment.types.ts b/desktop/src/shared/types/alignment.types.ts index 40e91d669..9bb245529 100644 --- a/desktop/src/shared/types/alignment.types.ts +++ b/desktop/src/shared/types/alignment.types.ts @@ -1,7 +1,7 @@ import type { Angle } from './atlas.types' -import type { Camera, CameraCaptureEvent, CameraStartCapture } from './camera.types' +import { cameraStartCaptureWithDefault, DEFAULT_CAMERA_START_CAPTURE, type Camera, type CameraCaptureEvent, type CameraStartCapture } from './camera.types' import type { GuideDirection } from './guider.types' -import type { PlateSolverRequest, PlateSolverType } from './settings.types' +import { DEFAULT_PLATE_SOLVER_REQUEST, plateSolverRequestWithDefault, type PlateSolverRequest } from './platesolver.types' export type Hemisphere = 'NORTHERN' | 'SOUTHERN' @@ -12,33 +12,13 @@ export type TPPAState = 'IDLE' | 'SLEWING' | 'SLEWED' | 'SETTLING' | 'EXPOSURING export type AlignmentMethod = 'DARV' | 'TPPA' export interface AlignmentPreference { - darvInitialPause: number - darvExposureTime: number darvHemisphere: Hemisphere - tppaStartFromCurrentPosition: boolean - tppaStepDirection: GuideDirection - tppaCompensateRefraction: boolean - tppaStopTrackingWhenDone: boolean - tppaStepDuration: number - tppaPlateSolverType: PlateSolverType -} - -export const EMPTY_ALIGNMENT_PREFERENCE: AlignmentPreference = { - darvInitialPause: 5, - darvExposureTime: 30, - darvHemisphere: 'NORTHERN', - tppaStartFromCurrentPosition: true, - tppaStepDirection: 'EAST', - tppaCompensateRefraction: true, - tppaStopTrackingWhenDone: true, - tppaStepDuration: 5, - tppaPlateSolverType: 'ASTAP', + darvRequest: DARVStart + tppaRequest: TPPAStart } export interface DARVStart { capture: CameraStartCapture - exposureTime: number - initialPause: number direction: GuideDirection reversed: boolean } @@ -61,6 +41,17 @@ export interface TPPAStart { stepSpeed?: string } +export interface TPPAResult { + failed: boolean + rightAscension: Angle + declination: Angle + azimuthError: Angle + azimuthErrorDirection: string + altitudeError: Angle + altitudeErrorDirection: string + totalError: Angle +} + export interface TPPAEvent extends MessageEvent { camera: Camera state: TPPAState @@ -73,3 +64,80 @@ export interface TPPAEvent extends MessageEvent { altitudeErrorDirection: string capture?: CameraCaptureEvent } + +export interface DARVResult { + direction?: GuideDirection +} + +export const DEFAULT_CAMERA_START_CAPTURE_TPPA: CameraStartCapture = { + ...DEFAULT_CAMERA_START_CAPTURE, +} + +export const DEFAULT_TPPA_START: TPPAStart = { + capture: DEFAULT_CAMERA_START_CAPTURE_TPPA, + plateSolver: DEFAULT_PLATE_SOLVER_REQUEST, + startFromCurrentPosition: true, + stepDirection: 'EAST', + compensateRefraction: true, + stopTrackingWhenDone: true, + stepDuration: 5, +} + +export const DEFAULT_TPPA_RESULT: TPPAResult = { + failed: false, + rightAscension: `00h00m00s`, + declination: `00°00'00"`, + azimuthError: `00°00'00"`, + azimuthErrorDirection: '', + altitudeError: `00°00'00"`, + altitudeErrorDirection: '', + totalError: `00°00'00"`, +} + +export const DEFAULT_CAMERA_START_CAPTURE_DARV: CameraStartCapture = { + ...DEFAULT_CAMERA_START_CAPTURE, + exposureDelay: 5, + exposureTime: 30000000, +} + +export const DEFAULT_DARV_START: DARVStart = { + capture: DEFAULT_CAMERA_START_CAPTURE_DARV, + direction: 'NORTH', + reversed: false, +} + +export const DEFAULT_DARV_RESULT: DARVResult = {} + +export const DEFAULT_ALIGNMENT_PREFERENCE: AlignmentPreference = { + darvHemisphere: 'NORTHERN', + darvRequest: DEFAULT_DARV_START, + tppaRequest: DEFAULT_TPPA_START, +} + +export function darvStartWithDefault(request?: Partial, source: DARVStart = DEFAULT_DARV_START) { + if (!request) return structuredClone(source) + request.capture = cameraStartCaptureWithDefault(request.capture, source.capture) + request.direction ||= source.direction + request.reversed ??= source.reversed + return request as DARVStart +} + +export function tppaStartWithDefault(request?: Partial, source: TPPAStart = DEFAULT_TPPA_START) { + if (!request) return structuredClone(source) + request.capture = cameraStartCaptureWithDefault(request.capture, source.capture) + request.plateSolver = plateSolverRequestWithDefault(request.plateSolver, source.plateSolver) + request.startFromCurrentPosition ??= source.startFromCurrentPosition + request.stepDirection ||= source.stepDirection + request.compensateRefraction ??= source.compensateRefraction + request.stopTrackingWhenDone ??= source.stopTrackingWhenDone + request.stepDuration ??= source.stepDuration + return request as TPPAStart +} + +export function alignmentPreferenceWithDefault(preference?: Partial, source: AlignmentPreference = DEFAULT_ALIGNMENT_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.darvHemisphere ||= source.darvHemisphere + preference.darvRequest = darvStartWithDefault(preference.darvRequest, source.darvRequest) + preference.tppaRequest = tppaStartWithDefault(preference.tppaRequest, source.tppaRequest) + return preference as AlignmentPreference +} diff --git a/desktop/src/shared/types/angular.types.ts b/desktop/src/shared/types/angular.types.ts new file mode 100644 index 000000000..7abbd0d26 --- /dev/null +++ b/desktop/src/shared/types/angular.types.ts @@ -0,0 +1,30 @@ +export type Severity = 'success' | 'info' | 'warning' | 'danger' + +export type TooltipPosition = 'right' | 'left' | 'top' | 'bottom' + +export interface DropdownItem { + label: string + value: T +} + +export function extractDateTime(date: Date) { + return [extractDate(date), extractTime(date)] +} + +function padNumber(value: number) { + return value <= 9 ? `0${value}` : `${value}` +} + +export function extractDate(date: Date) { + return `${date.getFullYear()}-${padNumber(date.getMonth() + 1)}-${padNumber(date.getDate())}` +} + +export function extractTime(date: Date, hasSeconds: boolean = true) { + const time = `${padNumber(date.getHours())}:${padNumber(date.getMinutes())}` + + if (hasSeconds) { + return `${time}:${padNumber(date.getSeconds())}` + } else { + return time + } +} diff --git a/desktop/src/shared/types/api.types.ts b/desktop/src/shared/types/api.types.ts index a1edbff8e..aea0aa242 100644 --- a/desktop/src/shared/types/api.types.ts +++ b/desktop/src/shared/types/api.types.ts @@ -1,5 +1,7 @@ import type { Device } from './device.types' +export type ApiEventType = (typeof API_EVENT_TYPES)[number] + export interface MessageEvent { eventName: string } @@ -53,5 +55,3 @@ export const API_EVENT_TYPES = [ // Auto Focus. 'AUTO_FOCUS.ELAPSED', ] as const - -export type ApiEventType = (typeof API_EVENT_TYPES)[number] diff --git a/desktop/src/shared/types/app.types.ts b/desktop/src/shared/types/app.types.ts index a819efe98..742b3181f 100644 --- a/desktop/src/shared/types/app.types.ts +++ b/desktop/src/shared/types/app.types.ts @@ -1,8 +1,9 @@ +import type { Severity } from './angular.types' import type { MessageEvent } from './api.types' -export type Severity = 'success' | 'info' | 'warning' | 'danger' +export type InternalEventType = (typeof INTERNAL_EVENT_TYPES)[number] -export type TooltipPosition = 'right' | 'left' | 'top' | 'bottom' +export type SaveJson = OpenFile & JsonFile export interface NotificationEvent extends MessageEvent { target?: string @@ -16,28 +17,6 @@ export interface ConfirmationEvent extends MessageEvent { idempotencyKey: string } -export const INTERNAL_EVENT_TYPES = [ - 'DIRECTORY.OPEN', - 'FILE.OPEN', - 'FILE.SAVE', - 'WINDOW.OPEN', - 'WINDOW.CLOSE', - 'WINDOW.PIN', - 'WINDOW.UNPIN', - 'WINDOW.MINIMIZE', - 'WINDOW.MAXIMIZE', - 'WINDOW.RESIZE', - 'WHEEL.RENAMED', - 'LOCATION.CHANGED', - 'JSON.WRITE', - 'JSON.READ', - 'CALIBRATION.CHANGED', - 'WINDOW.FULLSCREEN', - 'ROI.SELECTED', -] as const - -export type InternalEventType = (typeof INTERNAL_EVENT_TYPES)[number] - export interface WindowPreference { modal?: boolean autoResizable?: boolean @@ -80,6 +59,7 @@ export interface OpenDirectory extends WindowCommand { export interface OpenFile extends OpenDirectory { filters?: Electron.FileFilter[] + multiple?: boolean } export interface JsonFile { @@ -87,15 +67,22 @@ export interface JsonFile { json: T } -export interface SaveJson extends OpenFile, JsonFile {} - -export type StoredWindowDataKey = `window.${string}` - -export interface StoredWindowDataValue { - x: number - y: number - width: number - height: number -} - -export type StoredWindowData = Record +export const INTERNAL_EVENT_TYPES = [ + 'DIRECTORY.OPEN', + 'FILE.OPEN', + 'FILE.SAVE', + 'WINDOW.OPEN', + 'WINDOW.CLOSE', + 'WINDOW.PIN', + 'WINDOW.UNPIN', + 'WINDOW.MINIMIZE', + 'WINDOW.MAXIMIZE', + 'WINDOW.RESIZE', + 'WHEEL.RENAMED', + 'LOCATION.CHANGED', + 'JSON.WRITE', + 'JSON.READ', + 'CALIBRATION.CHANGED', + 'WINDOW.FULLSCREEN', + 'ROI.SELECTED', +] as const diff --git a/desktop/src/shared/types/atlas.types.ts b/desktop/src/shared/types/atlas.types.ts index 8ef9bca6b..4071a0d87 100644 --- a/desktop/src/shared/types/atlas.types.ts +++ b/desktop/src/shared/types/atlas.types.ts @@ -1,14 +1,121 @@ +import type { Subscription } from 'rxjs' +import type { Severity } from './angular.types' import type { PierSide } from './mount.types' export type Angle = string | number -export interface PlanetTableItem { +export type Constellation = (typeof CONSTELLATIONS)[number] + +export type ClassificationType = (typeof CLASSIFICATION_TYPES)[number] + +export type SkyObjectType = (typeof SKY_OBJECT_TYPES)[number] + +export type MinorPlanetKind = 'ASTEROID' | 'COMET' + +export type Star = DeepSkyObject & SpectralSkyObject + +export type SatelliteGroupType = (typeof SATELLITE_GROUPS)[number] + +export type PlanetType = 'PLANET' | 'DWARF_PLANET' | 'MOON_OF_MARS' | 'MOON_OF_JUPITER' | 'MOON_OF_SATURN' | 'MOON_OF_URANUS' | 'MOON_OF_NEPTUNE' | 'MOON_OF_PLUTO' | 'ASTEROID' + +export type AltitudeDataPoint = [number, number] + +export type SatelliteSearchGroups = Record + +export enum BodyTabType { + SUN, + MOON, + PLANET, + MINOR_PLANET, + SKY_OBJECT, + SATELLITE, +} + +export interface BodyTag { + label: string + severity: Severity +} + +export interface BodyTab { + position: BodyPosition name: string - type: string + tags: BodyTag[] +} + +export interface SunTab extends BodyTab { + image: string +} + +export type MoonTab = BodyTab + +export interface PlanetItem { + name: string + type: PlanetType code: string } -export interface SearchFilter { +export interface PlanetTab extends BodyTab { + selected?: PlanetItem + readonly planets: PlanetItem[] +} + +export interface OrbitalPhysicalParameter { + name: string + description: string + value: string +} + +export interface MinorPlanetListItem { + name: string + pdes: string +} + +export interface MinorPlanet { + found: boolean + name: string + spkId: number + kind?: MinorPlanetKind + pha: boolean + neo: boolean + orbitType: string + parameters: OrbitalPhysicalParameter[] + list: MinorPlanetListItem[] +} + +export interface CloseApproach { + name: string + designation: string + dateTime: number + distance: number + absoluteMagnitude: number +} + +export interface MinorPlanetTab extends BodyTab { + tab: number + search: { + text: string + result?: MinorPlanet + } + closeApproach: { + days: number + lunarDistance: number + result: CloseApproach[] + selected?: CloseApproach + } + list: { + items: MinorPlanetListItem[] + showDialog: boolean + } +} + +export interface SkyObjectTab extends BodyTab { + search: SkyObjectSearchDialog & { + result: DeepSkyObject[] + selected?: DeepSkyObject + } +} + +export interface SkyObjectSearchFilter { text: string rightAscension: Angle declination: Angle @@ -16,10 +123,234 @@ export interface SearchFilter { constellation: Constellation | 'ALL' magnitude: [number, number] type: SkyObjectType | 'ALL' - types: (SkyObjectType | 'ALL')[] } -export const EMPTY_SEARCH_FILTER: SearchFilter = { +export interface SkyObjectSearchDialog { + showDialog: boolean + filter: SkyObjectSearchFilter +} + +export interface SatelliteSearchFilter { + text: string + groups: SatelliteSearchGroups +} + +export interface SatelliteSearchDialog { + showDialog: boolean + filter: SatelliteSearchFilter +} + +export interface Satellite { + id: number + name: string + tle: string + groups: SatelliteGroupType[] +} + +export interface SatelliteTab extends BodyTab { + search: SatelliteSearchDialog & { + result: Satellite[] + selected?: Satellite + } +} + +export interface BodyTabRefresh { + count: number + timer?: Subscription + position: boolean + chart: boolean +} + +export interface DateTimeAndLocation { + manual: boolean + dateTime: Date + location: Location +} + +export interface Location { + id: number + name: string + latitude: number + longitude: number + elevation: number + offsetInMinutes: number +} + +export interface SkyAtlasPreference { + satellites: SatelliteSearchGroups + location: Location + fast: boolean +} + +export interface SkyAtlasInput { + tab: BodyTabType + filter?: Partial> +} + +export interface EquatorialCoordinate { + rightAscension: Angle + declination: Angle +} + +export interface EquatorialCoordinateJ2000 { + rightAscensionJ2000: Angle + declinationJ2000: Angle +} + +export interface HorizontalCoordinate { + azimuth: Angle + altitude: Angle +} + +export interface BodyPosition extends EquatorialCoordinate, EquatorialCoordinateJ2000, HorizontalCoordinate { + magnitude: number + constellation: Constellation + distance: number + distanceUnit: string + illuminated: number + elongation: number + leading: boolean +} + +export interface Twilight { + civilDusk: number[] + nauticalDusk: number[] + astronomicalDusk: number[] + night: number[] + astronomicalDawn: number[] + nauticalDawn: number[] + civilDawn: number[] +} + +export interface AstronomicalObject extends EquatorialCoordinateJ2000 { + id: number + name: string + magnitude: number +} + +export interface SpectralSkyObject { + spType: string +} + +export interface OrientedSkyObject { + majorAxis: number + minorAxis: number + orientation: number +} + +export interface DeepSkyObject extends AstronomicalObject { + type: SkyObjectType + redshift: number + parallax: number + radialVelocity: number + distance: number + pmRA: number + pmDEC: number + constellation: Constellation +} + +export interface ComputedLocation extends EquatorialCoordinate, EquatorialCoordinateJ2000, HorizontalCoordinate { + constellation: Constellation + meridianAt: string + timeLeftToMeridianFlip: string + lst: string + pierSide: PierSide +} + +export const DEFAULT_BODY_POSITION: BodyPosition = { + rightAscensionJ2000: '00h00m00s', + declinationJ2000: `+000°00'00"`, + rightAscension: '00h00m00s', + declination: `+000°00'00"`, + azimuth: `000°00'00"`, + altitude: `+00°00'00"`, + magnitude: 0, + constellation: 'AND', + distance: 0, + distanceUnit: 'ly', + illuminated: 0, + elongation: 0, + leading: false, +} + +export const DEFAULT_SUN: SunTab = { + name: 'Sun', + position: DEFAULT_BODY_POSITION, + tags: [], + image: '', +} + +export const DEFAULT_MOON: MoonTab = { + name: 'Moon', + position: DEFAULT_BODY_POSITION, + tags: [], +} + +export const DEFAULT_PLANET_ITEMS: PlanetItem[] = [ + { name: 'Mercury', type: 'PLANET', code: '199' }, + { name: 'Venus', type: 'PLANET', code: '299' }, + { name: 'Mars', type: 'PLANET', code: '499' }, + { name: 'Jupiter', type: 'PLANET', code: '599' }, + { name: 'Saturn', type: 'PLANET', code: '699' }, + { name: 'Uranus', type: 'PLANET', code: '799' }, + { name: 'Neptune', type: 'PLANET', code: '899' }, + { name: 'Pluto', type: 'DWARF_PLANET', code: '999' }, + { name: 'Phobos', type: 'MOON_OF_MARS', code: '401' }, + { name: 'Deimos', type: 'MOON_OF_MARS', code: '402' }, + { name: 'Io', type: 'MOON_OF_JUPITER', code: '501' }, + { name: 'Europa', type: 'MOON_OF_JUPITER', code: '402' }, + { name: 'Ganymede', type: 'MOON_OF_JUPITER', code: '403' }, + { name: 'Callisto', type: 'MOON_OF_JUPITER', code: '504' }, + { name: 'Mimas', type: 'MOON_OF_SATURN', code: '601' }, + { name: 'Enceladus', type: 'MOON_OF_SATURN', code: '602' }, + { name: 'Tethys', type: 'MOON_OF_SATURN', code: '603' }, + { name: 'Dione', type: 'MOON_OF_SATURN', code: '604' }, + { name: 'Rhea', type: 'MOON_OF_SATURN', code: '605' }, + { name: 'Titan', type: 'MOON_OF_SATURN', code: '606' }, + { name: 'Hyperion', type: 'MOON_OF_SATURN', code: '607' }, + { name: 'Iapetus', type: 'MOON_OF_SATURN', code: '608' }, + { name: 'Ariel', type: 'MOON_OF_URANUS', code: '701' }, + { name: 'Umbriel', type: 'MOON_OF_URANUS', code: '702' }, + { name: 'Titania', type: 'MOON_OF_URANUS', code: '703' }, + { name: 'Oberon', type: 'MOON_OF_URANUS', code: '704' }, + { name: 'Miranda', type: 'MOON_OF_URANUS', code: '705' }, + { name: 'Triton', type: 'MOON_OF_NEPTUNE', code: '801' }, + { name: 'Charon', type: 'MOON_OF_PLUTO', code: '901' }, + { name: '1 Ceres', type: 'DWARF_PLANET', code: '1;' }, + { name: '90377 Sedna', type: 'DWARF_PLANET', code: '90377;' }, + { name: '136199 Eris', type: 'DWARF_PLANET', code: '136199;' }, + { name: '2 Pallas', type: 'ASTEROID', code: '2;' }, + { name: '3 Juno', type: 'ASTEROID', code: '3;' }, + { name: '4 Vesta', type: 'ASTEROID', code: '4;' }, +] + +export const DEFAULT_PLANET: PlanetTab = { + name: '', + position: DEFAULT_BODY_POSITION, + tags: [], + planets: DEFAULT_PLANET_ITEMS, +} + +export const DEFAULT_MINOR_PLANET: MinorPlanetTab = { + tab: 0, + name: '', + position: DEFAULT_BODY_POSITION, + tags: [], + search: { + text: '', + }, + closeApproach: { + days: 7, + lunarDistance: 10, + result: [], + }, + list: { + showDialog: false, + items: [], + }, +} + +export const DEFAULT_SKY_OBJECT_SEARCH_FILTER: SkyObjectSearchFilter = { text: '', rightAscension: '00h00m00s', declination: `+000°00'00"`, @@ -27,42 +358,141 @@ export const EMPTY_SEARCH_FILTER: SearchFilter = { constellation: 'ALL', magnitude: [-30, 30], type: 'ALL', - types: ['ALL'], } -export interface SatelliteGroupFilterItem { - group: SatelliteGroupType - enabled: boolean +export const DEFAULT_SKY_OBJECT_SEARCH_DIALOG: SkyObjectSearchDialog = { + showDialog: false, + filter: DEFAULT_SKY_OBJECT_SEARCH_FILTER, } -export interface SkyAtlasPreference { - satellites: SatelliteGroupFilterItem[] - fast: boolean +export const DEFAULT_SKY_OBJECT: SkyObjectTab = { + name: '', + search: { + ...DEFAULT_SKY_OBJECT_SEARCH_DIALOG, + result: [], + }, + position: DEFAULT_BODY_POSITION, + tags: [], } -export const EMPTY_SKY_ATLAS_PREFERENCE: SkyAtlasPreference = { - satellites: [], - fast: false, +export const DEFAULT_SATELLITE_SEARCH_GROUPS: SatelliteSearchGroups = { + ACTIVE: false, + AMATEUR: true, + ANALYST: false, + ARGOS: false, + BEIDOU: true, + COSMOS_1408_DEBRIS: false, + COSMOS_2251_DEBRIS: false, + CUBESAT: false, + DMC: false, + EDUCATION: false, + ENGINEERING: false, + FENGYUN_1C_DEBRIS: false, + GALILEO: true, + GEO: false, + GEODETIC: false, + GLO_OPS: true, + GLOBALSTAR: false, + GNSS: true, + GOES: false, + GORIZONT: false, + GPS_OPS: true, + INTELSAT: false, + IRIDIUM_33_DEBRIS: false, + IRIDIUM_NEXT: false, + IRIDIUM: false, + LAST_30_DAYS: false, + MILITARY: false, + MOLNIYA: false, + MUSSON: false, + NNSS: false, + NOAA: false, + ONEWEB: true, + ORBCOMM: false, + OTHER_COMM: false, + OTHER: false, + PLANET: false, + RADAR: false, + RADUGA: false, + RESOURCE: false, + SARSAT: false, + SATNOGS: false, + SBAS: false, + SCIENCE: true, + SES: false, + SPIRE: false, + STARLINK: true, + STATIONS: true, + SWARM: false, + TDRSS: false, + VISUAL: true, + WEATHER: false, + X_COMM: false, } -export enum SkyAtlasTab { - SUN, - MOON, - PLANET, - MINOR_PLANET, - SKY_OBJECT, - SATELLITE, +export const DEFAULT_SATELLITE_SEARCH_FILTER: SatelliteSearchFilter = { + text: '', + groups: DEFAULT_SATELLITE_SEARCH_GROUPS, } -export interface SkyAtlasInput { - tab: SkyAtlasTab - filter?: Partial> +export const DEFAULT_SATELLITE_SEARCH_DIALOG: SatelliteSearchDialog = { + showDialog: false, + filter: DEFAULT_SATELLITE_SEARCH_FILTER, } -export interface SettingsDialog { - showDialog: boolean +export const DEFAULT_SATELLITE: SatelliteTab = { + name: '', + search: { + ...DEFAULT_SATELLITE_SEARCH_DIALOG, + result: [], + }, + position: DEFAULT_BODY_POSITION, + tags: [], +} + +export const DEFAULT_COMPUTED_LOCATION: ComputedLocation = { + constellation: 'AND', + meridianAt: '00:00', + timeLeftToMeridianFlip: '00:00', + lst: '00:00', + pierSide: 'NEITHER', + rightAscensionJ2000: '00h00m00s', + declinationJ2000: `+000°00'00"`, + rightAscension: '00h00m00s', + declination: `+000°00'00"`, + azimuth: `000°00'00"`, + altitude: `+00°00'00"`, } +export const DEFAULT_LOCATION: Location = { + id: 0, + name: 'Null Island', + latitude: 0, + longitude: 0, + elevation: 0, + offsetInMinutes: 0, +} + +export const DEFAULT_BODY_TAB_REFRESH: BodyTabRefresh = { + count: 0, + position: false, + chart: false, +} + +export const DEFAULT_DATE_TIME_AND_LOCATION: DateTimeAndLocation = { + manual: false, + dateTime: new Date(), + location: DEFAULT_LOCATION, +} + +export const DEFAULT_SKY_ATLAS_PREFERENCE: SkyAtlasPreference = { + satellites: DEFAULT_SATELLITE_SEARCH_GROUPS, + location: DEFAULT_DATE_TIME_AND_LOCATION.location, + fast: false, +} + +export const CLASSIFICATION_TYPES = ['STAR', 'SET_OF_STARS', 'INTERSTELLAR_MEDIUM', 'GALAXY', 'SET_OF_GALAXIES', 'GRAVITATION', 'SPECTRAL', 'OTHER'] as const + export const CONSTELLATIONS = [ 'AND', 'ANT', @@ -154,12 +584,6 @@ export const CONSTELLATIONS = [ 'VUL', ] as const -export type Constellation = (typeof CONSTELLATIONS)[number] - -export const CLASSIFICATION_TYPES = ['STAR', 'SET_OF_STARS', 'INTERSTELLAR_MEDIUM', 'GALAXY', 'SET_OF_GALAXIES', 'GRAVITATION', 'SPECTRAL', 'OTHER'] as const - -export type ClassificationType = (typeof CLASSIFICATION_TYPES)[number] - export const SKY_OBJECT_TYPES = [ 'ACTIVE_GALAXY_NUCLEUS', 'ALPHA2_CVN_VARIABLE', @@ -315,143 +739,6 @@ export const SKY_OBJECT_TYPES = [ 'YOUNG_STELLAR_OBJECT', ] as const -export type SkyObjectType = (typeof SKY_OBJECT_TYPES)[number] - -export interface EquatorialCoordinate { - rightAscension: Angle - declination: Angle -} - -export interface EquatorialCoordinateJ2000 { - rightAscensionJ2000: Angle - declinationJ2000: Angle -} - -export interface HorizontalCoordinate { - azimuth: Angle - altitude: Angle -} - -export interface BodyPosition extends EquatorialCoordinate, EquatorialCoordinateJ2000, HorizontalCoordinate { - magnitude: number - constellation: Constellation - distance: number - distanceUnit: string - illuminated: number - elongation: number - leading: boolean -} - -export const EMPTY_BODY_POSITION: BodyPosition = { - rightAscensionJ2000: '00h00m00s', - declinationJ2000: `+000°00'00"`, - rightAscension: '00h00m00s', - declination: `+000°00'00"`, - azimuth: `000°00'00"`, - altitude: `+00°00'00"`, - magnitude: 0, - constellation: 'AND', - distance: 0, - distanceUnit: 'ly', - illuminated: 0, - elongation: 0, - leading: false, -} - -export interface Twilight { - civilDusk: number[] - nauticalDusk: number[] - astronomicalDusk: number[] - night: number[] - astronomicalDawn: number[] - nauticalDawn: number[] - civilDawn: number[] -} - -export type MinorPlanetKind = 'ASTEROID' | 'COMET' - -export interface MinorPlanetSearchItem { - name: string - pdes: string -} - -export interface MinorPlanet { - found: boolean - name: string - spkId: number - kind?: MinorPlanetKind - pha: boolean - neo: boolean - orbitType: string - parameters: OrbitalPhysicalParameter[] - searchItems: MinorPlanetSearchItem[] -} - -export interface OrbitalPhysicalParameter { - name: string - description: string - value: string -} - -export interface CloseApproach { - name: string - designation: string - dateTime: number - distance: number - absoluteMagnitude: number -} - -export interface AstronomicalObject extends EquatorialCoordinateJ2000 { - id: number - name: string - magnitude: number -} - -export interface SpectralSkyObject { - spType: string -} - -export type Star = DeepSkyObject & SpectralSkyObject - -export interface OrientedSkyObject { - majorAxis: number - minorAxis: number - orientation: number -} - -export interface DeepSkyObject extends AstronomicalObject { - type: SkyObjectType - redshift: number - parallax: number - radialVelocity: number - distance: number - pmRA: number - pmDEC: number - constellation: Constellation -} - -export interface ComputedLocation extends EquatorialCoordinate, EquatorialCoordinateJ2000, HorizontalCoordinate { - constellation: Constellation - meridianAt: string - timeLeftToMeridianFlip: string - lst: string - pierSide: PierSide -} - -export const EMPTY_COMPUTED_LOCATION: ComputedLocation = { - constellation: 'AND', - meridianAt: '00:00', - timeLeftToMeridianFlip: '00:00', - lst: '00:00', - pierSide: 'NEITHER', - rightAscensionJ2000: '00h00m00s', - declinationJ2000: `+000°00'00"`, - rightAscension: '00h00m00s', - declination: `+000°00'00"`, - azimuth: `000°00'00"`, - altitude: `+00°00'00"`, -} - export const SATELLITE_GROUPS = [ 'LAST_30_DAYS', 'STATIONS', @@ -507,29 +794,54 @@ export const SATELLITE_GROUPS = [ 'OTHER', ] as const -export type SatelliteGroupType = (typeof SATELLITE_GROUPS)[number] +export function searchFilterWithDefault(filter?: Partial, source: SkyObjectSearchFilter = DEFAULT_SKY_OBJECT_SEARCH_FILTER) { + if (!filter) return structuredClone(source) + filter.rightAscension ??= source.rightAscension + filter.declination ??= source.declination + filter.radius ||= source.radius + filter.constellation ??= source.constellation + filter.magnitude ??= source.magnitude + filter.type ??= source.type + return filter as SkyObjectSearchFilter +} -export interface Satellite { - id: number - name: string - tle: string - groups: SatelliteGroupType[] +export function satelliteSearchGroupsWithDefault(groups?: Partial, source: SatelliteSearchGroups = DEFAULT_SATELLITE_SEARCH_GROUPS) { + if (!groups) return structuredClone(source) + + if ('ACTIVE' in groups) { + for (const entry of Object.entries(source)) { + const key = entry[0] as SatelliteGroupType + groups[key] ??= source[key] + } + + return groups as SatelliteSearchGroups + } else { + return structuredClone(source) + } } -export interface Location { - id: number - name: string - latitude: number - longitude: number - elevation: number - offsetInMinutes: number +export function resetSatelliteSearchGroup(groups: SatelliteSearchGroups, source: SatelliteSearchGroups = DEFAULT_SATELLITE_SEARCH_GROUPS) { + for (const entry of Object.entries(source)) { + const key = entry[0] as SatelliteGroupType + groups[key] = source[key] + } } -export const EMPTY_LOCATION: Location = { - id: 0, - name: 'Null Island', - latitude: 0, - longitude: 0, - elevation: 0, - offsetInMinutes: 0, +export function locationWithDefault(location?: Partial, source: Location = DEFAULT_LOCATION) { + if (!location) return structuredClone(source) + location.id ??= source.id + location.name ||= source.name + location.latitude ??= source.latitude + location.longitude ??= source.longitude + location.elevation ??= source.elevation + location.offsetInMinutes ??= source.offsetInMinutes + return location as Location +} + +export function skyAtlasPreferenceWithDefault(preference?: Partial, source: SkyAtlasPreference = DEFAULT_SKY_ATLAS_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.satellites = satelliteSearchGroupsWithDefault(preference.satellites, source.satellites) + preference.location = locationWithDefault(preference.location, source.location) + preference.fast ??= source.fast + return preference as SkyAtlasPreference } diff --git a/desktop/src/shared/types/autofocus.type.ts b/desktop/src/shared/types/autofocus.type.ts index ee0156c74..874f452e5 100644 --- a/desktop/src/shared/types/autofocus.type.ts +++ b/desktop/src/shared/types/autofocus.type.ts @@ -1,7 +1,7 @@ import type { Point } from 'electron' -import type { CameraCaptureEvent, CameraStartCapture } from './camera.types' -import type { StarDetectionRequest } from './settings.types' -import { EMPTY_STAR_DETECTION_REQUEST } from './settings.types' +import { cameraStartCaptureWithDefault, DEFAULT_CAMERA_START_CAPTURE, type CameraCaptureEvent, type CameraStartCapture } from './camera.types' +import type { StarDetectionRequest } from './stardetector.types' +import { DEFAULT_STAR_DETECTION_REQUEST, starDetectionRequestWithDefault } from './stardetector.types' export type AutoFocusState = 'IDLE' | 'MOVING' | 'EXPOSURING' | 'EXPOSURED' | 'ANALYSING' | 'ANALYSED' | 'CURVE_FITTED' | 'FAILED' | 'FINISHED' @@ -26,20 +26,8 @@ export interface AutoFocusRequest { starDetector: StarDetectionRequest } -export type AutoFocusPreference = Omit - -export const EMPTY_AUTO_FOCUS_PREFERENCE: AutoFocusPreference = { - fittingMode: 'HYPERBOLIC', - rSquaredThreshold: 0.5, - initialOffsetSteps: 4, - stepSize: 100, - totalNumberOfAttempts: 1, - backlashCompensation: { - mode: 'NONE', - backlashIn: 0, - backlashOut: 0, - }, - starDetector: EMPTY_STAR_DETECTION_REQUEST, +export interface AutoFocusPreference { + request: AutoFocusRequest } export interface Curve { @@ -91,3 +79,55 @@ export interface AutoFocusEvent { chart?: AutoFocusChart capture?: CameraCaptureEvent } + +export const DEFAULT_CAMERA_START_CAPTURE_AUTO_FOCUS: CameraStartCapture = { + ...DEFAULT_CAMERA_START_CAPTURE, +} + +export const DEFAULT_BACKLASH_COMPENSATION: BacklashCompensation = { + mode: 'NONE', + backlashIn: 0, + backlashOut: 0, +} + +export const DEFAULT_AUTO_FOCUS_REQUEST: AutoFocusRequest = { + capture: DEFAULT_CAMERA_START_CAPTURE_AUTO_FOCUS, + fittingMode: 'HYPERBOLIC', + rSquaredThreshold: 0.5, + initialOffsetSteps: 4, + stepSize: 100, + totalNumberOfAttempts: 1, + backlashCompensation: DEFAULT_BACKLASH_COMPENSATION, + starDetector: DEFAULT_STAR_DETECTION_REQUEST, +} + +export const DEFAULT_AUTO_FOCUS_PREFERENCE: AutoFocusPreference = { + request: DEFAULT_AUTO_FOCUS_REQUEST, +} + +export function backlashCompensationWithDefault(compensation?: Partial, source: BacklashCompensation = DEFAULT_BACKLASH_COMPENSATION) { + if (!compensation) return structuredClone(source) + compensation.mode ||= source.mode + compensation.backlashIn ??= source.backlashIn + compensation.backlashOut ??= source.backlashOut + return compensation as BacklashCompensation +} + +export function autoFocusRequestWithDefault(request?: Partial, source: AutoFocusRequest = DEFAULT_AUTO_FOCUS_REQUEST) { + if (!request) return structuredClone(source) + request.capture = cameraStartCaptureWithDefault(request.capture, source.capture) + request.fittingMode ??= source.fittingMode + request.rSquaredThreshold ??= source.rSquaredThreshold + request.initialOffsetSteps ??= source.initialOffsetSteps + request.stepSize ??= source.stepSize + request.totalNumberOfAttempts ??= source.totalNumberOfAttempts + request.backlashCompensation = backlashCompensationWithDefault(request.backlashCompensation, source.backlashCompensation) + request.starDetector = starDetectionRequestWithDefault(request.starDetector, source.starDetector) + return request as AutoFocusRequest +} + +export function autoFocusPreferenceWithDefault(preference?: Partial, source: AutoFocusPreference = DEFAULT_AUTO_FOCUS_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.request = autoFocusRequestWithDefault(preference.request, source.request) + return preference as AutoFocusPreference +} diff --git a/desktop/src/shared/types/calibration.types.ts b/desktop/src/shared/types/calibration.types.ts index 51b3c445f..e5b798708 100644 --- a/desktop/src/shared/types/calibration.types.ts +++ b/desktop/src/shared/types/calibration.types.ts @@ -1,28 +1,33 @@ -import type { FrameType } from './camera.types' +import type { Image } from './image.types' -export interface CalibrationFrame { +export interface CalibrationFrame extends Image { id: number - type: FrameType - name: string - filter?: string - exposureTime: number - temperature: number - width: number - height: number - binX: number - binY: number - gain: number + group: string path: string enabled: boolean } -export interface CalibrationFrameGroup { - id: number - name: string - key: Omit - frames: CalibrationFrame[] +export interface CalibrationGroupDialog { + showDialog: boolean + group: string + save?: () => Promise | void } export interface CalibrationPreference { - openPath?: string + filePath?: string + directoryPath?: string +} + +export const DEFAULT_CALIBRATION_GROUP_DIALOG: CalibrationGroupDialog = { + showDialog: false, + group: '', +} + +export const DEFAULT_CALIBRATION_PREFERENCE: CalibrationPreference = {} + +export function calibrationPreferenceWithDefault(preference?: Partial, source: CalibrationPreference = DEFAULT_CALIBRATION_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.filePath ||= source.filePath + preference.directoryPath ||= source.directoryPath + return preference as CalibrationPreference } diff --git a/desktop/src/shared/types/camera.types.ts b/desktop/src/shared/types/camera.types.ts index 8df82172f..2bdebb1c8 100644 --- a/desktop/src/shared/types/camera.types.ts +++ b/desktop/src/shared/types/camera.types.ts @@ -2,9 +2,13 @@ import type { MessageEvent } from './api.types' import type { Thermometer } from './auxiliary.types' import type { CompanionDevice, Device, PropertyState } from './device.types' import { isCompanionDevice } from './device.types' +import type { Focuser } from './focuser.types' import type { GuideOutput } from './guider.types' +import type { Mount } from './mount.types' +import type { Rotator } from './rotator.types' +import type { Wheel } from './wheel.types' -export type CameraDialogMode = 'CAPTURE' | 'SEQUENCER' | 'FLAT_WIZARD' | 'TPPA' | 'DARV' | 'AUTO_FOCUS' +export type CameraMode = 'CAPTURE' | 'SEQUENCER' | 'FLAT_WIZARD' | 'TPPA' | 'DARV' | 'AUTO_FOCUS' export type FrameType = 'LIGHT' | 'DARK' | 'FLAT' | 'BIAS' @@ -16,73 +20,182 @@ export type ExposureMode = 'SINGLE' | 'FIXED' | 'LOOP' export type LiveStackerType = 'SIRIL' | 'PIXINSIGHT' -export enum ExposureTimeUnit { - MINUTE = 'm', - SECOND = 's', - MILLISECOND = 'ms', - MICROSECOND = 'µs', +export type CameraCaptureState = 'IDLE' | 'CAPTURE_STARTED' | 'EXPOSURE_STARTED' | 'EXPOSURING' | 'WAITING' | 'SETTLING' | 'DITHERING' | 'STACKING' | 'PAUSING' | 'PAUSED' | 'EXPOSURE_FINISHED' | 'CAPTURE_FINISHED' + +export type ExposureTimeUnit = 'MINUTE' | 'SECOND' | 'MILLISECOND' | 'MICROSECOND' + +export interface Camera extends GuideOutput, Thermometer { + readonly exposuring: boolean + readonly hasCoolerControl: boolean + readonly coolerPower: number + readonly cooler: boolean + readonly hasDewHeater: boolean + readonly dewHeater: boolean + readonly frameFormats: string[] + readonly canAbort: boolean + readonly cfaOffsetX: number + readonly cfaOffsetY: number + readonly cfaType: CfaPattern + readonly exposureMin: number + readonly exposureMax: number + readonly exposureState: PropertyState + readonly exposureTime: number + readonly hasCooler: boolean + readonly canSetTemperature: boolean + readonly canSubFrame: boolean + readonly x: number + readonly minX: number + readonly maxX: number + readonly y: number + readonly minY: number + readonly maxY: number + readonly width: number + readonly minWidth: number + readonly maxWidth: number + readonly height: number + readonly minHeight: number + readonly maxHeight: number + readonly canBin: boolean + readonly maxBinX: number + readonly maxBinY: number + readonly binX: number + readonly binY: number + readonly gain: number + readonly gainMin: number + readonly gainMax: number + readonly offset: number + readonly offsetMin: number + readonly offsetMax: number + readonly hasGuideHead: boolean + readonly pixelSizeX: number + readonly pixelSizeY: number + readonly capturesPath: string + readonly guideHead?: Device } -export function isCamera(device?: Device): device is Camera { - return !!device && 'exposuring' in device +export interface GuideHead extends Camera, CompanionDevice {} + +export interface Dither { + enabled: boolean + amount: number + raOnly: boolean + afterExposures: number } -export function isGuideHead(device?: Device): device is GuideHead { - return isCamera(device) && isCompanionDevice(device) && !!device.main +export interface CameraCaptureNamingFormat { + light?: string + dark?: string + flat?: string + bias?: string } -export interface Camera extends GuideOutput, Thermometer { - exposuring: boolean - hasCoolerControl: boolean - coolerPower: number - cooler: boolean - hasDewHeater: boolean - dewHeater: boolean - frameFormats: string[] - canAbort: boolean - cfaOffsetX: number - cfaOffsetY: number - cfaType: CfaPattern - exposureMin: number - exposureMax: number - exposureState: PropertyState +export interface CameraStartCapture { + enabled: boolean exposureTime: number - hasCooler: boolean - canSetTemperature: boolean - canSubFrame: boolean + exposureAmount: number + exposureDelay: number x: number - minX: number - maxX: number y: number - minY: number - maxY: number width: number - minWidth: number - maxWidth: number height: number - minHeight: number - maxHeight: number - canBin: boolean - maxBinX: number - maxBinY: number + frameFormat?: string + frameType: FrameType binX: number binY: number gain: number - gainMin: number - gainMax: number offset: number - offsetMin: number - offsetMax: number - hasGuideHead: boolean - pixelSizeX: number - pixelSizeY: number - capturesPath: string - guideHead?: Device + autoSave: boolean + savePath?: string + autoSubFolderMode: AutoSubFolderMode + dither: Dither + filterPosition: number + shutterPosition: number + focusOffset: number + calibrationGroup?: string + liveStacking: LiveStackingRequest + namingFormat: CameraCaptureNamingFormat } -export interface GuideHead extends Camera, CompanionDevice {} +export interface CameraCaptureEvent extends MessageEvent { + camera: Camera + exposureAmount: number + exposureCount: number + captureElapsedTime: number + captureProgress: number + captureRemainingTime: number + stepElapsedTime: number + stepProgress: number + stepRemainingTime: number + savedPath?: string + liveStackedPath?: string + state: CameraCaptureState + capture?: CameraStartCapture +} + +export interface CameraDialogInput { + mode: CameraMode + camera: Camera + request: CameraStartCapture +} + +export interface CameraPreference { + request: CameraStartCapture + setpointTemperature: number + exposureTimeUnit: ExposureTimeUnit + exposureMode: ExposureMode + subFrame: boolean + mount?: Mount + focuser?: Focuser + wheel?: Wheel + rotator?: Rotator +} + +export interface CameraStepInfo { + remainingTime: number + progress: number + elapsedTime: number +} -export const EMPTY_CAMERA: Camera = { +export interface CameraCaptureInfo { + looping: boolean + amount: number + remainingTime: number + elapsedTime: number + progress: number + count: number +} + +export interface LiveStackerSettings { + executablePath: string + slot: number +} + +export interface LiveStackingRequest extends LiveStackerSettings { + enabled: boolean + type: LiveStackerType + darkPath?: string + flatPath?: string + biasPath?: string + use32Bits: boolean +} + +export interface CameraDitherDialog { + showDialog: boolean + request: Dither +} + +export interface CameraLiveStackingDialog { + showDialog: boolean + request: LiveStackingRequest +} + +export interface CameraNamingFormatDialog { + showDialog: boolean + format: CameraCaptureNamingFormat +} + +export const DEFAULT_CAMERA: Camera = { + type: 'CAMERA', sender: '', id: '', exposuring: false, @@ -138,40 +251,50 @@ export const EMPTY_CAMERA: Camera = { temperature: 0, } -export interface Dither { - enabled: boolean - amount: number - raOnly: boolean - afterExposures: number +export const DEFAULT_CAMERA_CAPTURE_INFO: CameraCaptureInfo = { + looping: false, + amount: 0, + remainingTime: 0, + elapsedTime: 0, + progress: 0, + count: 0, } -export interface CameraStartCapture { - enabled?: boolean - exposureTime: number - exposureAmount: number - exposureDelay: number - x: number - y: number - width: number - height: number - frameFormat?: string - frameType: FrameType - binX: number - binY: number - gain: number - offset: number - autoSave: boolean - savePath?: string - autoSubFolderMode: AutoSubFolderMode - dither: Dither - filterPosition?: number - shutterPosition?: number - focusOffset?: number - calibrationGroup?: string - liveStacking: LiveStackingRequest +export const DEFAULT_LIVE_STACKER_SETTINGS: LiveStackerSettings = { + executablePath: '', + slot: 0, } -export const EMPTY_CAMERA_START_CAPTURE: CameraStartCapture = { +export const DEFAULT_LIVE_STACKING_REQUEST: LiveStackingRequest = { + ...DEFAULT_LIVE_STACKER_SETTINGS, + enabled: false, + type: 'SIRIL', + use32Bits: false, +} + +export const CAMERA_CAPTURE_NAMING_FORMAT_LIGHT = '[camera]_[type]_[year:2][month][day][hour][min][sec][ms]_[filter]_[width]_[height]_[exp]_[bin]_[gain]' +export const CAMERA_CAPTURE_NAMING_FORMAT_DARK = '[camera]_[type]_[width]_[height]_[exp]_[bin]_[gain]' +export const CAMERA_CAPTURE_NAMING_FORMAT_FLAT = '[camera]_[type]_[filter]_[width]_[height]_[bin]' +export const CAMERA_CAPTURE_NAMING_FORMAT_BIAS = '[camera]_[type]_[width]_[height]_[bin]_[gain]' + +export const EMPTY_CAMERA_CAPTURE_NAMING_FORMAT: CameraCaptureNamingFormat = {} + +export const DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT: CameraCaptureNamingFormat = { + light: CAMERA_CAPTURE_NAMING_FORMAT_LIGHT, + dark: CAMERA_CAPTURE_NAMING_FORMAT_DARK, + flat: CAMERA_CAPTURE_NAMING_FORMAT_FLAT, + bias: CAMERA_CAPTURE_NAMING_FORMAT_BIAS, +} + +export const DEFAULT_DITHER: Dither = { + enabled: false, + amount: 1.5, + raOnly: false, + afterExposures: 5, +} + +export const DEFAULT_CAMERA_START_CAPTURE: CameraStartCapture = { + enabled: true, exposureTime: 1, exposureAmount: 1, exposureDelay: 0, @@ -186,19 +309,55 @@ export const EMPTY_CAMERA_START_CAPTURE: CameraStartCapture = { offset: 0, autoSave: false, autoSubFolderMode: 'OFF', - dither: { - enabled: false, - afterExposures: 1, - amount: 1.5, - raOnly: false, - }, - liveStacking: { - enabled: false, - type: 'SIRIL', - executablePath: '', - use32Bits: false, - slot: 1, - }, + filterPosition: 0, + shutterPosition: 0, + focusOffset: 0, + dither: DEFAULT_DITHER, + liveStacking: DEFAULT_LIVE_STACKING_REQUEST, + namingFormat: {}, +} + +export const DEFAULT_CAMERA_PREFERENCE: CameraPreference = { + request: DEFAULT_CAMERA_START_CAPTURE, + setpointTemperature: 0, + exposureTimeUnit: 'MICROSECOND', + exposureMode: 'SINGLE', + subFrame: false, +} + +export const DEFAULT_CAMERA_STEP_INFO: CameraStepInfo = { + remainingTime: 0, + progress: 0, + elapsedTime: 0, +} + +export function cameraStartCaptureWithDefault(request?: Partial, source: CameraStartCapture = DEFAULT_CAMERA_START_CAPTURE) { + if (!request) return structuredClone(source) + request.enabled ??= source.enabled + request.exposureTime ??= source.exposureTime + request.exposureAmount ??= source.exposureAmount + request.exposureDelay ??= source.exposureDelay + request.x ??= source.x + request.y ??= source.y + request.width ??= source.width + request.height ??= source.height + request.frameFormat ||= source.frameFormat + request.frameType ||= source.frameType + request.binX ??= source.binX + request.binY ??= source.binY + request.gain ??= source.gain + request.offset ??= source.offset + request.autoSave ??= source.autoSave + request.savePath ||= source.savePath + request.autoSubFolderMode ||= source.autoSubFolderMode + request.filterPosition ??= source.filterPosition + request.shutterPosition ??= source.shutterPosition + request.focusOffset ??= source.focusOffset + request.calibrationGroup ||= source.calibrationGroup + request.dither = ditherWithDefault(request.dither, source.dither) + request.liveStacking = liveStackingRequestWithDefault(request.liveStacking, source.liveStacking) + request.namingFormat = cameraCaptureNamingFormatWithDefault(request.namingFormat, source.namingFormat) + return request as CameraStartCapture } export function updateCameraStartCaptureFromCamera(request: CameraStartCapture, camera: Camera) { @@ -215,92 +374,57 @@ export function updateCameraStartCaptureFromCamera(request: CameraStartCapture, if (camera.gainMax) request.gain = Math.max(camera.gainMin, Math.min(request.gain, camera.gainMax)) if (camera.offsetMax) request.offset = Math.max(camera.offsetMin, Math.min(request.offset, camera.offsetMax)) if (camera.frameFormats.length && (!request.frameFormat || !camera.frameFormats.includes(request.frameFormat))) request.frameFormat = camera.frameFormats[0] + if (camera.exposureMin > 1 && camera.exposureMax > camera.exposureMin) request.exposureTime = Math.max(camera.exposureMin, Math.min(request.exposureTime, camera.exposureMax)) } -export interface CameraCaptureEvent extends MessageEvent { - camera: Camera - exposureAmount: number - exposureCount: number - captureElapsedTime: number - captureProgress: number - captureRemainingTime: number - stepElapsedTime: number - stepProgress: number - stepRemainingTime: number - savedPath?: string - liveStackedPath?: string - state: CameraCaptureState - capture?: CameraStartCapture +export function cameraPreferenceWithDefault(preference?: Partial, source: CameraPreference = DEFAULT_CAMERA_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.request = cameraStartCaptureWithDefault(preference.request, source.request) + preference.setpointTemperature ??= source.setpointTemperature + preference.exposureTimeUnit ??= source.exposureTimeUnit + preference.exposureMode ||= source.exposureMode + preference.subFrame ??= source.subFrame + return preference as CameraPreference } -export type CameraCaptureState = 'IDLE' | 'CAPTURE_STARTED' | 'EXPOSURE_STARTED' | 'EXPOSURING' | 'WAITING' | 'SETTLING' | 'DITHERING' | 'STACKING' | 'PAUSING' | 'PAUSED' | 'EXPOSURE_FINISHED' | 'CAPTURE_FINISHED' - -export interface CameraDialogInput { - mode: CameraDialogMode - camera: Camera - request: CameraStartCapture -} - -export interface CameraPreference extends CameraStartCapture { - setpointTemperature: number - exposureTimeUnit: ExposureTimeUnit - exposureMode: ExposureMode - subFrame: boolean -} - -export const EMPTY_CAMERA_PREFERENCE: CameraPreference = { - ...EMPTY_CAMERA_START_CAPTURE, - setpointTemperature: 0, - exposureTimeUnit: ExposureTimeUnit.MICROSECOND, - exposureMode: 'SINGLE', - subFrame: false, +export function liveStackerSettingsWithDefault(settings?: Partial, source: LiveStackerSettings = DEFAULT_LIVE_STACKER_SETTINGS) { + if (!settings) return structuredClone(source) + settings.executablePath ||= source.executablePath + settings.slot ??= source.slot + return settings as LiveStackerSettings } -export interface CameraStepInfo { - remainingTime: number - progress: number - elapsedTime: number +export function liveStackingRequestWithDefault(request?: Partial, source: LiveStackingRequest = DEFAULT_LIVE_STACKING_REQUEST) { + if (!request) return structuredClone(source) + liveStackerSettingsWithDefault(request, source) + request.enabled ??= source.enabled + request.type ??= source.type + request.use32Bits ??= source.use32Bits + return request as LiveStackingRequest } -export const EMPTY_CAMERA_STEP_INFO: CameraStepInfo = { - remainingTime: 0, - progress: 0, - elapsedTime: 0, -} - -export interface CameraCaptureInfo { - looping: boolean - amount: number - remainingTime: number - elapsedTime: number - progress: number - count: number +export function cameraCaptureNamingFormatWithDefault(format?: Partial, source: CameraCaptureNamingFormat = DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT) { + if (!format) return structuredClone(source) + format.light ||= source.light + format.dark ||= source.dark + format.flat ||= source.flat + format.bias ||= source.bias + return format as CameraCaptureNamingFormat } -export const EMPTY_CAMERA_CAPTURE_INFO: CameraCaptureInfo = { - looping: false, - amount: 0, - remainingTime: 0, - elapsedTime: 0, - progress: 0, - count: 0, +export function ditherWithDefault(dither?: Partial, source: Dither = DEFAULT_DITHER) { + if (!dither) return structuredClone(source) + dither.enabled ??= source.enabled + dither.amount ??= source.amount + dither.raOnly ??= source.raOnly + dither.afterExposures ??= source.afterExposures + return dither as Dither } -export interface LiveStackingRequest { - enabled: boolean - type: LiveStackerType - executablePath: string - dark?: string - flat?: string - bias?: string - use32Bits: boolean - slot: number +export function isCamera(device?: Device): device is Camera { + return !!device && device.type === 'CAMERA' } -export const EMPTY_LIVE_STACKING_REQUEST: LiveStackingRequest = { - enabled: false, - type: 'SIRIL', - executablePath: '', - use32Bits: false, - slot: 1, +export function isGuideHead(device?: Device): device is GuideHead { + return isCamera(device) && isCompanionDevice(device) && !!device.main } diff --git a/desktop/src/shared/types/device.types.ts b/desktop/src/shared/types/device.types.ts index 63729dac7..90e2f2c44 100644 --- a/desktop/src/shared/types/device.types.ts +++ b/desktop/src/shared/types/device.types.ts @@ -11,6 +11,7 @@ export type SwitchRule = 'ONE_OF_MANY' | 'AT_MOST_ONE' | 'ANY_OF_MANY' export type DeviceType = 'CAMERA' | 'MOUNT' | 'WHEEL' | 'FOCUSER' | 'ROTATOR' | 'GPS' | 'DOME' | 'SWITCH' export interface Device { + readonly type: DeviceType readonly sender: string readonly id: string readonly name: string diff --git a/desktop/src/shared/types/flat-wizard.types.ts b/desktop/src/shared/types/flat-wizard.types.ts index d2f9aa9e8..d7565dfe5 100644 --- a/desktop/src/shared/types/flat-wizard.types.ts +++ b/desktop/src/shared/types/flat-wizard.types.ts @@ -1,4 +1,10 @@ -import type { CameraCaptureEvent, CameraStartCapture } from './camera.types' +import { cameraStartCaptureWithDefault, DEFAULT_CAMERA_START_CAPTURE, type CameraCaptureEvent, type CameraStartCapture } from './camera.types' + +export type FlatWizardState = 'EXPOSURING' | 'CAPTURED' | 'FAILED' + +export interface FlatWizardPreference { + request: FlatWizardRequest +} export interface FlatWizardRequest { capture: CameraStartCapture @@ -8,11 +14,42 @@ export interface FlatWizardRequest { meanTolerance: number } -export type FlatWizardState = 'EXPOSURING' | 'CAPTURED' | 'FAILED' - export interface FlatWizardEvent { state: FlatWizardState exposureTime: number capture?: CameraCaptureEvent savedPath?: string } + +export const DEFAULT_CAMERA_START_CAPTURE_FLAT_WIZARD: CameraStartCapture = { + ...DEFAULT_CAMERA_START_CAPTURE, + frameType: 'FLAT', +} + +export const DEFAULT_FLAT_WIZARD_REQUEST: FlatWizardRequest = { + capture: DEFAULT_CAMERA_START_CAPTURE_FLAT_WIZARD, + exposureMin: 1, + exposureMax: 2000, + meanTarget: 32768, + meanTolerance: 10, +} + +export const DEFAULT_FLAT_WIZARD_PREFERENCE: FlatWizardPreference = { + request: DEFAULT_FLAT_WIZARD_REQUEST, +} + +export function flatWizardRequestWithDefault(request?: Partial, source: FlatWizardRequest = DEFAULT_FLAT_WIZARD_REQUEST) { + if (!request) return structuredClone(source) + request.capture = cameraStartCaptureWithDefault(request.capture, source.capture) + request.exposureMin ??= source.exposureMin + request.exposureMax ??= source.exposureMax + request.meanTarget ??= source.meanTarget + request.meanTolerance ??= source.meanTolerance + return request as FlatWizardRequest +} + +export function flatWizardPreferenceWithDefault(preference?: Partial, source: FlatWizardPreference = DEFAULT_FLAT_WIZARD_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.request = flatWizardRequestWithDefault(preference.request, source.request) + return preference as FlatWizardPreference +} diff --git a/desktop/src/shared/types/focuser.types.ts b/desktop/src/shared/types/focuser.types.ts index b2263cc71..b73e9f815 100644 --- a/desktop/src/shared/types/focuser.types.ts +++ b/desktop/src/shared/types/focuser.types.ts @@ -14,7 +14,13 @@ export interface Focuser extends Device, Thermometer { maxPosition: number } -export const EMPTY_FOCUSER: Focuser = { +export interface FocuserPreference { + stepsRelative: number + stepsAbsolute: number +} + +export const DEFAULT_FOCUSER: Focuser = { + type: 'FOCUSER', sender: '', id: '', moving: false, @@ -33,11 +39,18 @@ export const EMPTY_FOCUSER: Focuser = { temperature: 0, } -export interface FocuserPreference { - stepsRelative?: number - stepsAbsolute?: number +export const DEFAULT_FOCUSER_PREFERENCE: FocuserPreference = { + stepsRelative: 100, + stepsAbsolute: 0, } export function isFocuser(device?: Device): device is Focuser { - return !!device && 'maxPosition' in device + return !!device && device.type === 'FOCUSER' +} + +export function focuserPreferenceWithDefault(preference?: Partial, source: FocuserPreference = DEFAULT_FOCUSER_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.stepsAbsolute ??= source.stepsAbsolute + preference.stepsAbsolute ??= source.stepsAbsolute + return preference as FocuserPreference } diff --git a/desktop/src/shared/types/framing.types.ts b/desktop/src/shared/types/framing.types.ts index a4952b463..3791856f7 100644 --- a/desktop/src/shared/types/framing.types.ts +++ b/desktop/src/shared/types/framing.types.ts @@ -1,3 +1,5 @@ +import type { Angle } from './atlas.types' + export interface HipsSurvey { id: string category: string @@ -7,3 +9,43 @@ export interface HipsSurvey { pixelScale: number skyFraction: number } + +export interface FramingPreference { + rightAscension: Angle + declination: Angle + width: number + height: number + fov: number + rotation: number + hipsSurvey?: HipsSurvey +} + +export interface LoadFraming { + rightAscension: Angle + declination: Angle + width?: number + height?: number + fov?: number + rotation?: number +} + +export const DEFAULT_FRAMING_PREFERENCE: FramingPreference = { + rightAscension: '00h00m00s', + declination: `000°00'00"`, + width: 1024, + height: 720, + fov: 1, + rotation: 0, +} + +export function framingPreferenceWithDefault(preference?: Partial, source: FramingPreference = DEFAULT_FRAMING_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.rightAscension ??= source.rightAscension + preference.declination ??= source.declination + preference.width ??= source.width + preference.height ??= source.height + preference.fov ??= source.fov + preference.rotation ??= source.rotation + preference.hipsSurvey ??= source.hipsSurvey + return preference as FramingPreference +} diff --git a/desktop/src/shared/types/guider.types.ts b/desktop/src/shared/types/guider.types.ts index 4ba757fcf..8a55d2327 100644 --- a/desktop/src/shared/types/guider.types.ts +++ b/desktop/src/shared/types/guider.types.ts @@ -6,16 +6,16 @@ export type GuideDirection = | 'WEST' // RA+ | 'EAST' // RA- -export const GUIDE_STATES = ['STOPPED', 'SELECTED', 'CALIBRATING', 'GUIDING', 'LOST_LOCK', 'PAUSED', 'LOOPING'] as const -export type GuideState = (typeof GUIDE_STATES)[number] +export type GuideState = 'STOPPED' | 'SELECTED' | 'CALIBRATING' | 'GUIDING' | 'LOST_LOCK' | 'PAUSED' | 'LOOPING' -export const GUIDER_TYPES = ['PHD2'] as const -export type GuiderType = (typeof GUIDER_TYPES)[number] +export type GuiderType = 'PHD2' export type GuiderPlotMode = 'RA/DEC' | 'DX/DY' export type GuiderYAxisUnit = 'ARCSEC' | 'PIXEL' +export type GuidePulseDurations = Record, number> + export interface GuidePoint { x: number y: number @@ -61,7 +61,54 @@ export interface GuideOutput extends Device { pulseGuiding: boolean } -export const EMPTY_GUIDE_OUTPUT: GuideOutput = { +export interface Guider { + connected: boolean + state: GuideState + settling: boolean + pixelScale: number +} + +export interface SettleInfo { + amount: number + time: number + timeout: number +} + +export interface GuiderMessageEvent extends MessageEvent { + data: T +} + +export interface GuiderPreference { + host: string + port: number + plotMode: GuiderPlotMode + yAxisUnit: GuiderYAxisUnit + settle: SettleInfo + pulseDuration: GuidePulseDurations +} + +export interface GuiderPHD2 { + connected: boolean + state: GuideState + step?: GuideStep + message: string +} + +export interface GuiderPulse { + connected: boolean + pulsing: boolean +} + +export interface GuiderChartInfo { + pixelScale: number + rmsRA: number + rmsDEC: number + rmsTotal: number + durationScale: number +} + +export const DEFAULT_GUIDE_OUTPUT: GuideOutput = { + type: 'CAMERA', sender: '', id: '', canPulseGuide: false, @@ -70,11 +117,45 @@ export const EMPTY_GUIDE_OUTPUT: GuideOutput = { connected: false, } -export interface Guider { - connected: boolean - state: GuideState - settling: boolean - pixelScale: number +export const DEFAULT_SETTLE: SettleInfo = { + amount: 1.5, + time: 10, + timeout: 30, +} + +export const DEFAULT_GUIDE_PULSE_DURATIONS: GuidePulseDurations = { + north: 1000, + south: 1000, + east: 1000, + west: 1000, +} + +export const DEFAULT_GUIDER_PHD2: GuiderPHD2 = { + connected: false, + state: 'STOPPED', + message: '', +} + +export const DEFAULT_GUIDER_PULSE: GuiderPulse = { + connected: false, + pulsing: false, +} + +export const DEFAULT_GUIDER_PREFERENCE: GuiderPreference = { + host: 'localhost', + port: 4400, + plotMode: 'RA/DEC', + yAxisUnit: 'ARCSEC', + settle: DEFAULT_SETTLE, + pulseDuration: DEFAULT_GUIDE_PULSE_DURATIONS, +} + +export const DEFAULT_GUIDER_CHART_INFO: GuiderChartInfo = { + pixelScale: 1.0, + rmsRA: 0.0, + rmsDEC: 0.0, + rmsTotal: 0.0, + durationScale: 1.0, } export function reverseGuideDirection(direction: GuideDirection): GuideDirection { @@ -92,12 +173,30 @@ export function reverseGuideDirection(direction: GuideDirection): GuideDirection } } -export interface SettleInfo { - amount: number - time: number - timeout: number +export function settleWithDefault(settle?: Partial, source: SettleInfo = DEFAULT_SETTLE) { + if (!settle) return structuredClone(source) + settle.amount ??= source.amount + settle.time ??= source.time + settle.timeout ??= source.timeout + return settle as SettleInfo } -export interface GuiderMessageEvent extends MessageEvent { - data: T +export function pulseDurationWithDefault(duration?: Partial, source: GuidePulseDurations = DEFAULT_GUIDE_PULSE_DURATIONS) { + if (!duration) return structuredClone(source) + duration.north ??= source.north + duration.south ??= source.south + duration.east ??= source.east + duration.west ??= source.west + return duration as GuidePulseDurations +} + +export function guiderPreferenceWithDefault(preference?: Partial, source: GuiderPreference = DEFAULT_GUIDER_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.host ||= source.host + preference.port ??= source.port + preference.plotMode ||= source.plotMode + preference.yAxisUnit ||= source.yAxisUnit + preference.settle = settleWithDefault(preference.settle, source.settle) + preference.pulseDuration = pulseDurationWithDefault(preference.pulseDuration, source.pulseDuration) + return preference as GuiderPreference } diff --git a/desktop/src/shared/types/home.types.ts b/desktop/src/shared/types/home.types.ts index 3af6d20fb..5004688fb 100644 --- a/desktop/src/shared/types/home.types.ts +++ b/desktop/src/shared/types/home.types.ts @@ -1,15 +1,10 @@ -import type { Camera } from './camera.types' import type { DeviceType } from './device.types' -import type { Focuser } from './focuser.types' -import type { Mount } from './mount.types' -import type { Rotator } from './rotator.types' -import type { FilterWheel } from './wheel.types' -export type HomeWindowType = DeviceType | 'GUIDER' | 'SKY_ATLAS' | 'ALIGNMENT' | 'SEQUENCER' | 'IMAGE' | 'FRAMING' | 'INDI' | 'SETTINGS' | 'CALCULATOR' | 'ABOUT' | 'FLAT_WIZARD' | 'AUTO_FOCUS' +export type HomeWindowType = DeviceType | 'GUIDER' | 'SKY_ATLAS' | 'ALIGNMENT' | 'SEQUENCER' | 'IMAGE' | 'FRAMING' | 'INDI' | 'SETTINGS' | 'CALCULATOR' | 'ABOUT' | 'FLAT_WIZARD' | 'AUTO_FOCUS' | 'STACKER' | 'CALIBRATION' -export const CONNECTION_TYPES = ['INDI', 'ALPACA'] as const +export type ConnectionType = 'INDI' | 'ALPACA' -export type ConnectionType = (typeof CONNECTION_TYPES)[number] +export type ConnectionStatus = Omit, 'connected' | 'name' | 'connectedAt'> export interface ConnectionDetails { name: string @@ -22,29 +17,45 @@ export interface ConnectionDetails { id?: string } -export type ConnectionStatus = Omit, 'connected' | 'name' | 'connectedAt'> +export interface ConnectionClosed { + id: string +} + +export interface HomePreference { + connections: ConnectionDetails[] + imagePath?: string +} + +export interface HomeConnectionDialog { + showDialog: boolean + connection: ConnectionDetails + edited: boolean +} + +export const DEFAULT_CONNECTION_HOST: string = 'localhost' +export const DEFAULT_CONNECTION_PORT: number = 7624 -export const EMPTY_CONNECTION_DETAILS: ConnectionDetails = { - name: '', - host: 'localhost', - port: 7624, +export const DEFAULT_CONNECTION_DETAILS: ConnectionDetails = { + name: 'Local', + host: DEFAULT_CONNECTION_HOST, + port: DEFAULT_CONNECTION_PORT, type: 'INDI', connected: false, } -export interface ConnectionClosed { - id: string +export const DEFAULT_HOME_PREFERENCE: HomePreference = { + connections: [], } -export interface HomePreference { - imagePath?: string +export const DEFAULT_HOME_CONNECTION_DIALOG: HomeConnectionDialog = { + showDialog: false, + edited: false, + connection: DEFAULT_CONNECTION_DETAILS, } -export interface Equipment { - camera?: Camera - guider?: Camera - mount?: Mount - focuser?: Focuser - wheel?: FilterWheel - rotator?: Rotator +export function homePreferenceWithDefault(preference?: Partial, source: HomePreference = DEFAULT_HOME_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.connections ??= source.connections + preference.imagePath ??= source.imagePath + return preference as HomePreference } diff --git a/desktop/src/shared/types/image.types.ts b/desktop/src/shared/types/image.types.ts index e9ca592d8..5fbf79c7e 100644 --- a/desktop/src/shared/types/image.types.ts +++ b/desktop/src/shared/types/image.types.ts @@ -1,13 +1,16 @@ import type { Point, Size } from 'electron' +import type { PanZoom } from 'panzoom' +import type { CoordinateInterpolator, InterpolatedCoordinate } from '../utils/coordinate-interpolation' import type { Angle, AstronomicalObject, DeepSkyObject, EquatorialCoordinateJ2000, Star } from './atlas.types' -import type { Camera, CameraStartCapture } from './camera.types' -import type { PlateSolverRequest, StarDetectionRequest } from './settings.types' +import type { Camera, CameraStartCapture, FrameType } from './camera.types' +import { DEFAULT_PLATE_SOLVER_REQUEST, plateSolverRequestWithDefault, type PlateSolverRequest } from './platesolver.types' +import { DEFAULT_STAR_DETECTION_REQUEST, starDetectionRequestWithDefault, type StarDetectionRequest } from './stardetector.types' export type ImageChannel = 'RED' | 'GREEN' | 'BLUE' | 'GRAY' export type SCNRProtectionMethod = 'MAXIMUM_MASK' | 'ADDITIVE_MASK' | 'AVERAGE_NEUTRAL' | 'MAXIMUM_NEUTRAL' | 'MINIMUM_NEUTRAL' -export type ImageSource = 'FRAMING' | 'PATH' | 'CAMERA' | 'FLAT_WIZARD' | 'SEQUENCER' | 'ALIGNMENT' | 'AUTO_FOCUS' +export type ImageSource = 'FRAMING' | 'PATH' | 'CAMERA' | 'FLAT_WIZARD' | 'SEQUENCER' | 'ALIGNMENT' | 'AUTO_FOCUS' | 'STACKER' export type ImageFormat = 'FITS' | 'XISF' | 'PNG' | 'JPG' @@ -15,7 +18,34 @@ export type Bitpix = 'BYTE' | 'SHORT' | 'INTEGER' | 'LONG' | 'FLOAT' | 'DOUBLE' export type LiveStackingMode = 'NONE' | 'RAW' | 'STACKED' -export interface FITSHeaderItem { +export type ImageCalibrationSource = 'CAMERA' | 'MENU' + +export type ImageMousePosition = Point + +export interface Image { + type: FrameType + width: number + height: number + binX: number + binY: number + exposureTime: number + temperature?: number + gain: number + filter?: string +} + +export interface ImagePreference { + savePath?: string + crossHair: boolean + transformation: ImageTransformation + solver: PlateSolverRequest + starDetector: StarDetectionRequest + annotation: AnnotateImageRequest + fovs: FOV[] + pixelated: boolean +} + +export interface ImageHeaderItem { name: string value: string } @@ -26,13 +56,11 @@ export interface ImageInfo { width: number height: number mono: boolean - stretchShadow: number - stretchHighlight: number - stretchMidtone: number + stretch: ImageStretch rightAscension?: Angle declination?: Angle solved?: ImageSolved - headers: FITSHeaderItem[] + headers: ImageHeaderItem[] bitpix: Bitpix statistics: ImageStatistics } @@ -54,17 +82,6 @@ export interface ImageSolved extends EquatorialCoordinateJ2000 { radius: number } -export const EMPTY_IMAGE_SOLVED: ImageSolved = { - solved: false, - orientation: 0, - scale: 0, - width: 0, - height: 0, - radius: 0, - rightAscensionJ2000: '00h00m00s', - declinationJ2000: '+000°00\'00"', -} - export interface CoordinateInterpolation { ma: number[] md: number[] @@ -98,16 +115,6 @@ export interface ImageStatisticsBitOption { bitLength: number } -export const IMAGE_STATISTICS_BIT_OPTIONS: ImageStatisticsBitOption[] = [ - { name: 'Normalized: [0, 1]', rangeMax: 1, bitLength: 16 }, - { name: '8-bit: [0, 255]', rangeMax: 255, bitLength: 8 }, - { name: '9-bit: [0, 511]', rangeMax: 511, bitLength: 9 }, - { name: '10-bit: [0, 1023]', rangeMax: 1023, bitLength: 10 }, - { name: '12-bit: [0, 4095]', rangeMax: 4095, bitLength: 12 }, - { name: '14-bit: [0, 16383]', rangeMax: 16383, bitLength: 14 }, - { name: '16-bit: [0, 65535]', rangeMax: 65535, bitLength: 16 }, -] as const - export interface ImageStatistics { count: number maxCount: number @@ -121,34 +128,6 @@ export interface ImageStatistics { maximum: number } -export type StarDetectionImagePreference = Pick - -export interface PlateSolverImagePreference extends Pick { - radius: number - focalLength: number - pixelSize: number -} - -export interface ImagePreference { - savePath?: string - solver?: PlateSolverImagePreference - starDetection?: StarDetectionImagePreference -} - -export const EMPTY_IMAGE_PREFERENCE: ImagePreference = { - solver: { - type: 'ASTAP', - radius: 4, - focalLength: 0, - pixelSize: 0, - }, - starDetection: { - type: 'ASTAP', - minSNR: 0, - maxStars: 0, - }, -} - export interface OpenImage { camera?: Camera path: string @@ -161,11 +140,10 @@ export interface OpenImage { export interface ImageData { camera?: Camera path?: string - liveStackedPath?: string - source?: ImageSource + source: ImageSource title?: string capture?: CameraStartCapture - exposureCount?: number + exposureCount: number } export interface FOV { @@ -186,24 +164,6 @@ export interface FOV { } } -export const DEFAULT_FOV: FOV = { - enabled: true, - focalLength: 600, - aperture: 80, - cameraSize: { - width: 1392, - height: 1040, - }, - pixelSize: { - width: 6.45, - height: 6.45, - }, - barlowReducer: 1, - bin: 1, - rotation: 0, - color: '#FFFF00', -} - export interface FOVEquipment { id: number name: string @@ -221,39 +181,45 @@ export interface FOVTelescope extends FOVEquipment { focalLength: number } -export interface ImageSCNRDialog { - showDialog: boolean +export interface ImageSCNR { channel?: ImageChannel amount: number method: SCNRProtectionMethod } -export interface ImageFITSHeadersDialog { +export interface ImageSCNRDialog { showDialog: boolean - headers: FITSHeaderItem[] + transformation: ImageSCNR } -export interface ImageStretchDialog { +export interface ImageHeadersDialog { showDialog: boolean + headers: ImageHeaderItem[] +} + +export interface ImageStretch { auto: boolean shadow: number highlight: number midtone: number } -export interface ImageSolverDialog extends PlateSolverImagePreference { +export interface ImageStretchDialog { + showDialog: boolean + transformation: ImageStretch +} + +export interface ImageSolverDialog { showDialog: boolean running: boolean - blind: boolean - centerRA: Angle - centerDEC: Angle + request: PlateSolverRequest readonly solved: ImageSolved } -export interface ImageFOVDialog extends FOV { +export interface ImageFOVDialog { showDialog: boolean + selected: FOV fovs: FOV[] - edited?: FOV showCameraDialog: boolean cameras: FOVCamera[] camera?: FOVCamera @@ -262,11 +228,8 @@ export interface ImageFOVDialog extends FOV { telescope?: FOVTelescope } -export interface ImageROI { - x: number - y: number - width: number - height: number +export interface ImageROI extends Size, Point { + show: boolean } export interface ImageSaveDialog { @@ -282,22 +245,27 @@ export interface ImageTransformation { force: boolean calibrationGroup?: string debayer: boolean - stretch: Omit + stretch: ImageStretch mirrorHorizontal: boolean mirrorVertical: boolean invert: boolean - scnr: Pick + scnr: ImageSCNR + useJPEG: boolean +} + +export interface AnnotateImageRequest { + starsAndDSOs: boolean + minorPlanets: boolean + minorPlanetsMagLimit: number + includeMinorPlanetsWithoutMagnitude: boolean + useSimbad: boolean } export interface ImageAnnotationDialog { showDialog: boolean running: boolean visible: boolean - useStarsAndDSOs: boolean - useMinorPlanets: boolean - minorPlanetsMagLimit: number - includeMinorPlanetsWithoutMagnitude: boolean - useSimbad: boolean + request: AnnotateImageRequest data: ImageAnnotation[] } @@ -309,16 +277,317 @@ export interface ROISelected { height: number } -export interface StarDetectionDialog extends StarDetectionImagePreference { +export interface StarDetectorDialog { showDialog: boolean running: boolean visible: boolean stars: DetectedStar[] computed: ComputedDetectedStars selected: DetectedStar + request: StarDetectionRequest } -export interface AnnotationInfoDialog { +export interface AstronomicalObjectDialog { showDialog: boolean info?: AstronomicalObject & Partial } + +export interface ImageStatisticsDialog { + showDialog: boolean + statistics: ImageStatistics + bitOption: ImageStatisticsBitOption +} + +export interface ImageMouseCoordinates extends InterpolatedCoordinate, ImageMousePosition { + show: boolean + interpolator?: CoordinateInterpolator +} + +export interface ImageCalibration { + source: ImageCalibrationSource +} + +export interface ImageLiveStacking { + mode: LiveStackingMode + path?: string +} + +export interface ImageZoom { + scale: number + panZoom?: PanZoom +} + +export interface ImageSettingsDialog { + showDialog: boolean + preference: ImagePreference +} + +export const DEFAULT_IMAGE_SOLVED: ImageSolved = { + solved: false, + orientation: 0, + scale: 0, + width: 0, + height: 0, + radius: 0, + rightAscensionJ2000: '00h00m00s', + declinationJ2000: '+000°00\'00"', +} + +export const DEFAULT_IMAGE_STRETCH: ImageStretch = { + auto: true, + shadow: 0, + highlight: 1, + midtone: 0.5, +} + +export const DEFAULT_IMAGE_STRETCH_DIALOG: ImageStretchDialog = { + showDialog: false, + transformation: DEFAULT_IMAGE_STRETCH, +} + +export const DEFAULT_IMAGE_SCNR: ImageSCNR = { + amount: 0.5, + method: 'AVERAGE_NEUTRAL', +} + +export const DEFAULT_IMAGE_SCNR_DIALOG: ImageSCNRDialog = { + showDialog: false, + transformation: DEFAULT_IMAGE_SCNR, +} + +export const DEFAULT_IMAGE_TRANSFORMATION: ImageTransformation = { + force: false, + debayer: true, + stretch: DEFAULT_IMAGE_STRETCH, + mirrorHorizontal: false, + mirrorVertical: false, + invert: false, + scnr: DEFAULT_IMAGE_SCNR, + useJPEG: true, +} + +export const DEFAULT_IMAGE_SOLVER_DIALOG: ImageSolverDialog = { + showDialog: false, + running: false, + request: DEFAULT_PLATE_SOLVER_REQUEST, + solved: DEFAULT_IMAGE_SOLVED, +} + +export const IMAGE_STATISTICS_BIT_OPTIONS: ImageStatisticsBitOption[] = [ + { name: 'Normalized: [0, 1]', rangeMax: 1, bitLength: 16 }, + { name: '8-bit: [0, 255]', rangeMax: 255, bitLength: 8 }, + { name: '9-bit: [0, 511]', rangeMax: 511, bitLength: 9 }, + { name: '10-bit: [0, 1023]', rangeMax: 1023, bitLength: 10 }, + { name: '12-bit: [0, 4095]', rangeMax: 4095, bitLength: 12 }, + { name: '14-bit: [0, 16383]', rangeMax: 16383, bitLength: 14 }, + { name: '16-bit: [0, 65535]', rangeMax: 65535, bitLength: 16 }, +] as const + +export const DEFAULT_FOV: FOV = { + enabled: true, + focalLength: 600, + aperture: 80, + cameraSize: { + width: 1392, + height: 1040, + }, + pixelSize: { + width: 6.45, + height: 6.45, + }, + barlowReducer: 1, + bin: 1, + rotation: 0, + color: '#FFFF00', +} + +export const DEFAULT_IMAGE_FOV_DIALOG: ImageFOVDialog = { + selected: DEFAULT_FOV, + showDialog: false, + fovs: [], + showCameraDialog: false, + cameras: [], + showTelescopeDialog: false, + telescopes: [], +} + +export const DEFAULT_COMPUTED_DETECTED_STARS: ComputedDetectedStars = { + hfd: 0, + snr: 0, + stdDev: 0, + fluxMax: 0, + fluxMin: 0, +} + +export const DEFAULT_DETECTED_STAR: DetectedStar = { + x: 0, + y: 0, + snr: 0, + hfd: 0, + flux: 0, +} + +export const DEFAULT_STAR_DETECTOR_DIALOG: StarDetectorDialog = { + showDialog: false, + running: false, + visible: false, + stars: [], + computed: DEFAULT_COMPUTED_DETECTED_STARS, + selected: DEFAULT_DETECTED_STAR, + request: DEFAULT_STAR_DETECTION_REQUEST, +} + +export const DEFAULT_ANNOTATE_IMAGE_REQUEST: AnnotateImageRequest = { + starsAndDSOs: true, + minorPlanets: false, + minorPlanetsMagLimit: 15.0, + includeMinorPlanetsWithoutMagnitude: false, + useSimbad: false, +} + +export const DEFAULT_IMAGE_ANNOTATION_DIALOG: ImageAnnotationDialog = { + showDialog: false, + running: false, + visible: false, + data: [], + request: DEFAULT_ANNOTATE_IMAGE_REQUEST, +} + +export const DEFAULT_IMAGE_ROI: ImageROI = { + show: false, + x: 0, + y: 0, + width: 0, + height: 0, +} + +export const DEFAULT_IMAGE_SAVE_DIALOG: ImageSaveDialog = { + showDialog: false, + format: 'FITS', + bitpix: 'BYTE', + path: '', + shouldBeTransformed: true, + transformation: DEFAULT_IMAGE_TRANSFORMATION, +} + +export const DEFAULT_IMAGE_STATISTICS: ImageStatistics = { + count: 0, + maxCount: 0, + mean: 0, + sumOfSquares: 0, + median: 0, + variance: 0, + stdDev: 0, + avgDev: 0, + minimum: 0, + maximum: 0, +} + +export const DEFAULT_IMAGE_STATISTICS_DIALOG: ImageStatisticsDialog = { + showDialog: false, + statistics: DEFAULT_IMAGE_STATISTICS, + bitOption: IMAGE_STATISTICS_BIT_OPTIONS[0], +} + +export const DEFAULT_IMAGE_DATA: ImageData = { + source: 'PATH', + exposureCount: 0, +} + +export const DEFAULT_IMAGE_MOUSE_POSITION: ImageMousePosition = { + x: 0, + y: 0, +} + +export const DEFAULT_IMAGE_MOUSE_COORDINATES: ImageMouseCoordinates = { + show: false, + ...DEFAULT_IMAGE_MOUSE_POSITION, + alpha: '', + delta: '', + rightAscensionJ2000: '', + declinationJ2000: '', +} + +export const DEFAULT_IMAGE_CALIBRATION: ImageCalibration = { + source: 'CAMERA', +} + +export const DEFAULT_IMAGE_LIVE_STACKING: ImageLiveStacking = { + mode: 'NONE', +} + +export const DEFAULT_IMAGE_ZOOM: ImageZoom = { + scale: 1, +} + +export const DEFAULT_IMAGE_PREFERENCE: ImagePreference = { + crossHair: false, + transformation: DEFAULT_IMAGE_TRANSFORMATION, + solver: DEFAULT_PLATE_SOLVER_REQUEST, + starDetector: DEFAULT_STAR_DETECTION_REQUEST, + annotation: DEFAULT_ANNOTATE_IMAGE_REQUEST, + fovs: [], + pixelated: true, +} + +export const DEFAULT_IMAGE_SETTINGS_DIALOG: ImageSettingsDialog = { + showDialog: false, + preference: DEFAULT_IMAGE_PREFERENCE, +} + +export function imageFormatFromExtension(extension: string): ImageFormat { + return ( + extension === '.xisf' ? 'XISF' + : extension === '.png' ? 'PNG' + : extension === '.jpg' ? 'JPG' + : 'FITS' + ) +} + +export function imageStretchWithDefault(stretch?: Partial, source: ImageStretch = DEFAULT_IMAGE_STRETCH) { + if (!stretch) return structuredClone(source) + stretch.auto ??= source.auto + stretch.shadow ??= source.shadow + stretch.highlight ??= source.highlight + stretch.midtone ??= source.midtone + return stretch as ImageStretch +} + +export function annotateImageRequestWithDefault(request?: Partial, source: AnnotateImageRequest = DEFAULT_ANNOTATE_IMAGE_REQUEST) { + if (!request) return structuredClone(source) + request.starsAndDSOs ??= source.starsAndDSOs + request.minorPlanets ??= source.minorPlanets + request.minorPlanetsMagLimit ??= source.minorPlanetsMagLimit + request.includeMinorPlanetsWithoutMagnitude ??= source.includeMinorPlanetsWithoutMagnitude + request.useSimbad ??= source.useSimbad + return request as AnnotateImageRequest +} + +export function imageTransformationWithDefault(transformation?: Partial, source: ImageTransformation = DEFAULT_IMAGE_TRANSFORMATION) { + if (!transformation) return structuredClone(source) + transformation.force ??= source.force + transformation.calibrationGroup ||= source.calibrationGroup + transformation.debayer ??= source.debayer + transformation.stretch = imageStretchWithDefault(transformation.stretch, source.stretch) + transformation.mirrorHorizontal ??= source.mirrorHorizontal + transformation.mirrorVertical ??= source.mirrorVertical + transformation.invert ??= source.invert + transformation.scnr ??= source.scnr + transformation.useJPEG ??= source.useJPEG + return transformation as ImageTransformation +} + +export function imagePreferenceWithDefault(preference?: Partial, source: ImagePreference = DEFAULT_IMAGE_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.savePath ||= source.savePath + preference.crossHair ??= source.crossHair + preference.transformation = imageTransformationWithDefault(preference.transformation, source.transformation) + preference.solver = plateSolverRequestWithDefault(preference.solver, source.solver) + preference.starDetector = starDetectionRequestWithDefault(preference.starDetector, source.starDetector) + preference.annotation = annotateImageRequestWithDefault(preference.annotation, source.annotation) + preference.fovs ??= structuredClone(source.fovs) + preference.pixelated ??= source.pixelated + preference.fovs.forEach((e) => (e.enabled = false)) + preference.fovs.forEach((e) => (e.computed = undefined)) + return preference as ImagePreference +} diff --git a/desktop/src/shared/types/mount.types.ts b/desktop/src/shared/types/mount.types.ts index 3f9c409f6..f88e21e4b 100644 --- a/desktop/src/shared/types/mount.types.ts +++ b/desktop/src/shared/types/mount.types.ts @@ -11,9 +11,13 @@ export type TrackMode = 'SIDEREAL' | ' LUNAR' | 'SOLAR' | 'KING' | 'CUSTOM' export type CelestialLocationType = 'ZENITH' | 'NORTH_POLE' | 'SOUTH_POLE' | 'GALACTIC_CENTER' | 'MERIDIAN_EQUATOR' | 'MERIDIAN_ECLIPTIC' | 'EQUATOR_ECLIPTIC' -export type MountRemoteControlType = 'LX200' | 'STELLARIUM' +export type MountRemoteControlProtocol = 'LX200' | 'STELLARIUM' -export type MoveDirectionType = 'N' | 'S' | 'W' | 'E' | 'NW' | 'NE' | 'SW' | 'SE' +export type CardinalDirection = 'N' | 'S' | 'W' | 'E' + +export type OrdinalDirection = 'NW' | 'NE' | 'SW' | 'SE' + +export type MountSlewDirection = CardinalDirection | OrdinalDirection export interface SlewRate { name: string @@ -42,7 +46,38 @@ export interface Mount extends EquatorialCoordinate, GPS, GuideOutput, Parkable guideRateNS: number } -export const EMPTY_MOUNT: Mount = { +export interface MountRemoteControl { + protocol: MountRemoteControlProtocol + mount: Mount + running: boolean + rightAscension: Angle + declination: Angle + latitude: Angle + longitude: Angle + slewing: boolean + tracking: boolean + parked: boolean + host: string + port: number +} + +export interface MountRemoteControlDialog { + showDialog: boolean + protocol: MountRemoteControlProtocol + host: string + port: number + controls: MountRemoteControl[] +} + +export interface MountPreference { + targetCoordinateType: TargetCoordinateType + targetRightAscension: Angle + targetDeclination: Angle + targetCoordinateCommand: number +} + +export const DEFAULT_MOUNT: Mount = { + type: 'MOUNT', sender: '', id: '', slewing: false, @@ -74,41 +109,30 @@ export const EMPTY_MOUNT: Mount = { parked: false, } -export interface MountRemoteControl { - type: MountRemoteControlType - mount: Mount - running: boolean - rightAscension: Angle - declination: Angle - latitude: Angle - longitude: Angle - slewing: boolean - tracking: boolean - parked: boolean - host: string - port: number +export const DEFAULT_MOUNT_REMOTE_CONTROL_DIALOG: MountRemoteControlDialog = { + showDialog: false, + protocol: 'LX200', + host: '0.0.0.0', + port: 10001, + controls: [], } -export interface MountRemoteControlDialog { - showDialog: boolean - type: MountRemoteControlType - host: string - port: number - data: MountRemoteControl[] -} - -export interface MountPreference { - targetCoordinateType: TargetCoordinateType - targetRightAscension: Angle - targetDeclination: Angle -} - -export const EMPTY_MOUNT_PREFERENCE: MountPreference = { +export const DEFAULT_MOUNT_PREFERENCE: MountPreference = { targetCoordinateType: 'JNOW', - targetRightAscension: '', - targetDeclination: '', + targetRightAscension: '00h00m00s', + targetDeclination: `000°00'00"`, + targetCoordinateCommand: 0, } export function isMount(device?: Device): device is Mount { - return !!device && 'tracking' in device + return !!device && device.type === 'MOUNT' +} + +export function mountPreferenceWithDefault(preference?: Partial, source: MountPreference = DEFAULT_MOUNT_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.targetCoordinateType ||= source.targetCoordinateType + preference.targetRightAscension ??= source.targetRightAscension + preference.targetDeclination ??= source.targetDeclination + preference.targetCoordinateCommand ??= source.targetCoordinateCommand + return preference as MountPreference } diff --git a/desktop/src/shared/types/platesolver.types.ts b/desktop/src/shared/types/platesolver.types.ts new file mode 100644 index 000000000..b79bc1f52 --- /dev/null +++ b/desktop/src/shared/types/platesolver.types.ts @@ -0,0 +1,64 @@ +import type { Angle } from './atlas.types' + +export type PlateSolverType = 'ASTROMETRY_NET' | 'ASTROMETRY_NET_ONLINE' | 'ASTAP' | 'SIRIL' | 'PIXINSIGHT' + +export interface PlateSolverSettings { + executablePath: string + downsampleFactor: number + apiUrl: string + apiKey: string + timeout: number + slot: number +} + +export interface PlateSolverRequest extends PlateSolverSettings { + type: PlateSolverType + blind: boolean + centerRA: Angle + centerDEC: Angle + radius: Angle + pixelSize: number + focalLength: number +} + +export const NOVA_ASTROMETRY_NET_URL = 'https://nova.astrometry.net/' + +export const DEFAULT_PLATE_SOLVER_SETTINGS: PlateSolverSettings = { + executablePath: '', + downsampleFactor: 0, + apiUrl: NOVA_ASTROMETRY_NET_URL, + apiKey: '', + timeout: 300, + slot: 1, +} + +export const DEFAULT_PLATE_SOLVER_REQUEST: PlateSolverRequest = { + ...DEFAULT_PLATE_SOLVER_SETTINGS, + type: 'ASTAP', + blind: true, + centerRA: 0, + centerDEC: 0, + radius: 4, + focalLength: 0, + pixelSize: 0, +} + +export function plateSolverSettingsWithDefault(settings?: Partial, source: PlateSolverSettings = DEFAULT_PLATE_SOLVER_SETTINGS) { + if (!settings) return structuredClone(source) + settings.executablePath ||= source.executablePath + settings.downsampleFactor ??= source.downsampleFactor + settings.apiUrl ||= source.apiUrl + settings.apiKey ||= source.apiKey + settings.timeout ??= source.timeout + settings.slot ??= source.slot + return settings as PlateSolverSettings +} + +export function plateSolverRequestWithDefault(request?: Partial, source: PlateSolverRequest = DEFAULT_PLATE_SOLVER_REQUEST) { + if (!request) return structuredClone(source) + plateSolverSettingsWithDefault(request, source) + request.type ??= source.type + request.pixelSize ??= source.pixelSize + request.focalLength ??= source.focalLength + return request as PlateSolverRequest +} diff --git a/desktop/src/shared/types/rotator.types.ts b/desktop/src/shared/types/rotator.types.ts index 2a73995e9..0b003d434 100644 --- a/desktop/src/shared/types/rotator.types.ts +++ b/desktop/src/shared/types/rotator.types.ts @@ -13,7 +13,12 @@ export interface Rotator extends Device { maxAngle: number } -export const EMPTY_ROTATOR: Rotator = { +export interface RotatorPreference { + angle: number +} + +export const DEFAULT_ROTATOR: Rotator = { + type: 'ROTATOR', sender: '', id: '', name: '', @@ -30,10 +35,16 @@ export const EMPTY_ROTATOR: Rotator = { connected: false, } -export interface RotatorPreference { - angle?: number +export const DEFAULT_ROTATOR_PREFERENCE: RotatorPreference = { + angle: 0, } export function isRotator(device?: Device): device is Rotator { - return !!device && 'angle' in device + return !!device && device.type === 'ROTATOR' +} + +export function rotatorPreferenceWithDefault(preference?: Partial, source: RotatorPreference = DEFAULT_ROTATOR_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.angle ??= source.angle + return preference as RotatorPreference } diff --git a/desktop/src/shared/types/sequencer.types.ts b/desktop/src/shared/types/sequencer.types.ts index 7672dcce3..c92cd986b 100644 --- a/desktop/src/shared/types/sequencer.types.ts +++ b/desktop/src/shared/types/sequencer.types.ts @@ -1,14 +1,31 @@ -import type { AutoSubFolderMode, Camera, CameraCaptureEvent, CameraStartCapture, Dither } from './camera.types' +import type { Camera } from './camera.types' +import { + cameraCaptureNamingFormatWithDefault, + DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT, + DEFAULT_CAMERA_START_CAPTURE, + DEFAULT_DITHER, + ditherWithDefault, + EMPTY_CAMERA_CAPTURE_NAMING_FORMAT, + type AutoSubFolderMode, + type CameraCaptureEvent, + type CameraCaptureNamingFormat, + type CameraStartCapture, + type Dither, +} from './camera.types' import type { Focuser } from './focuser.types' import type { Mount } from './mount.types' import type { Rotator } from './rotator.types' -import type { FilterWheel } from './wheel.types' +import type { Wheel } from './wheel.types' -export type SequenceCaptureMode = 'FULLY' | 'INTERLEAVED' +export type Sequence = CameraStartCapture -export const SEQUENCE_ENTRY_PROPERTIES = ['EXPOSURE_TIME', 'EXPOSURE_AMOUNT', 'EXPOSURE_DELAY', 'FRAME_TYPE', 'X', 'Y', 'WIDTH', 'HEIGHT', 'BIN', 'FRAME_FORMAT', 'GAIN', 'OFFSET'] as const +export type SequencerCaptureMode = 'FULLY' | 'INTERLEAVED' -export type SequenceEntryProperty = (typeof SEQUENCE_ENTRY_PROPERTIES)[number] +export type SequencerState = 'IDLE' | 'PAUSING' | 'PAUSED' | 'RUNNING' + +export type SequenceProperty = 'EXPOSURE_TIME' | 'EXPOSURE_AMOUNT' | 'EXPOSURE_DELAY' | 'FRAME_TYPE' | 'X' | 'Y' | 'WIDTH' | 'HEIGHT' | 'BIN' | 'FRAME_FORMAT' | 'GAIN' | 'OFFSET' + +export type SequenceProperties = Record export interface AutoFocusAfterConditions { enabled: boolean @@ -24,51 +41,148 @@ export interface AutoFocusAfterConditions { afterHFDIncreaseEnabled: boolean } -export interface SequencePlan { +export interface SequencerPlan { initialDelay: number - captureMode: SequenceCaptureMode + captureMode: SequencerCaptureMode autoSubFolderMode: AutoSubFolderMode savePath?: string - entries: CameraStartCapture[] + sequences: Sequence[] dither: Dither autoFocus: AutoFocusAfterConditions + namingFormat: CameraCaptureNamingFormat camera?: Camera mount?: Mount - wheel?: FilterWheel + wheel?: Wheel focuser?: Focuser rotator?: Rotator } -export const EMPTY_SEQUENCE_PLAN: SequencePlan = { - initialDelay: 0, - captureMode: 'FULLY', - autoSubFolderMode: 'OFF', - entries: [], - dither: { - enabled: false, - amount: 1.5, - raOnly: false, - afterExposures: 1, - }, - autoFocus: { - enabled: false, - onStart: false, - onFilterChange: false, - afterElapsedTime: 1800, // 30 min - afterExposures: 10, - afterTemperatureChange: 5, - afterHFDIncrease: 10, - afterElapsedTimeEnabled: false, - afterExposuresEnabled: false, - afterTemperatureChangeEnabled: false, - afterHFDIncreaseEnabled: false, - }, -} - export interface SequencerEvent extends MessageEvent { id: number elapsedTime: number remainingTime: number progress: number capture?: CameraCaptureEvent + state: SequencerState +} + +export interface SequencePropertyDialog { + showDialog: boolean + sequence?: Sequence + count: [number, number] + properties: SequenceProperties +} + +export interface SequencerPreference { + loadPath?: string + plan: SequencerPlan + properties: SequenceProperties +} + +export const DEFAULT_AUTO_FOCUS_AFTER_CONDITIONS: AutoFocusAfterConditions = { + enabled: false, + onStart: false, + onFilterChange: false, + afterElapsedTime: 1800, // 30 min + afterExposures: 10, + afterTemperatureChange: 5, + afterHFDIncrease: 10, + afterElapsedTimeEnabled: false, + afterExposuresEnabled: false, + afterTemperatureChangeEnabled: false, + afterHFDIncreaseEnabled: false, +} + +export const DEFAULT_SEQUENCE: Sequence = { + ...DEFAULT_CAMERA_START_CAPTURE, + autoSave: true, + autoSubFolderMode: 'OFF', + filterPosition: 0, + shutterPosition: 0, + focusOffset: 0, + namingFormat: EMPTY_CAMERA_CAPTURE_NAMING_FORMAT, +} + +export const DEFAULT_SEQUENCER_PLAN: SequencerPlan = { + initialDelay: 0, + captureMode: 'FULLY', + autoSubFolderMode: 'OFF', + dither: DEFAULT_DITHER, + autoFocus: DEFAULT_AUTO_FOCUS_AFTER_CONDITIONS, + namingFormat: DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT, + sequences: [], +} + +export const DEFAULT_SEQUENCE_PROPERTIES: SequenceProperties = { + EXPOSURE_TIME: true, + EXPOSURE_AMOUNT: true, + EXPOSURE_DELAY: true, + FRAME_TYPE: true, + X: true, + Y: true, + WIDTH: true, + HEIGHT: true, + BIN: true, + FRAME_FORMAT: true, + GAIN: true, + OFFSET: true, +} + +export const DEFAULT_SEQUENCE_PROPERTY_DIALOG: SequencePropertyDialog = { + showDialog: false, + count: [0, 0], + properties: DEFAULT_SEQUENCE_PROPERTIES, +} + +export const DEFAULT_SEQUENCER_PREFERENCE: SequencerPreference = { + plan: DEFAULT_SEQUENCER_PLAN, + properties: DEFAULT_SEQUENCE_PROPERTY_DIALOG.properties, +} + +export function autoFocusAfterConditionsWithDefault(conditions?: Partial, source: AutoFocusAfterConditions = DEFAULT_AUTO_FOCUS_AFTER_CONDITIONS) { + if (!conditions) return structuredClone(source) + conditions.enabled ??= source.enabled + conditions.onStart ??= source.onStart + conditions.onFilterChange ??= source.onFilterChange + conditions.afterElapsedTime ??= source.afterElapsedTime + conditions.afterElapsedTimeEnabled ??= source.afterElapsedTimeEnabled + conditions.afterExposures ??= source.afterExposures + conditions.afterExposuresEnabled ??= source.afterExposuresEnabled + conditions.afterTemperatureChange ??= source.afterTemperatureChange + conditions.afterTemperatureChangeEnabled ??= source.afterTemperatureChangeEnabled + conditions.afterHFDIncrease ??= source.afterHFDIncrease + conditions.afterHFDIncreaseEnabled ??= source.afterHFDIncreaseEnabled + return conditions as AutoFocusAfterConditions +} + +export function sequencePropertiesWithDefault(properties?: Partial, source: SequenceProperties = DEFAULT_SEQUENCE_PROPERTIES) { + if (!properties) return structuredClone(source) + + for (const entry of Object.entries(source)) { + const key = entry[0] as SequenceProperty + properties[key] ??= source[key] + } + + return properties as SequenceProperties +} + +export function sequencerPlanWithDefault(plan?: Partial, source: SequencerPlan = DEFAULT_SEQUENCER_PLAN) { + if (!plan) return structuredClone(source) + plan.initialDelay ??= source.initialDelay + plan.captureMode ||= source.captureMode + plan.autoSubFolderMode ||= source.autoSubFolderMode + plan.savePath ||= source.savePath + plan.sequences ??= source.sequences + plan.dither = ditherWithDefault(plan.dither, source.dither) + plan.autoFocus = autoFocusAfterConditionsWithDefault(plan.autoFocus, source.autoFocus) + plan.namingFormat = cameraCaptureNamingFormatWithDefault(plan.namingFormat, source.namingFormat) + return plan as SequencerPlan +} + +export function sequencerPreferenceWithDefault(preference?: Partial, source: SequencerPreference = DEFAULT_SEQUENCER_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.loadPath ||= source.loadPath + preference.plan = sequencerPlanWithDefault(preference.plan, source.plan) + preference.properties = sequencePropertiesWithDefault(preference.properties, source.properties) + return preference as SequencerPreference } diff --git a/desktop/src/shared/types/settings.types.ts b/desktop/src/shared/types/settings.types.ts index 278504800..f7956d1a9 100644 --- a/desktop/src/shared/types/settings.types.ts +++ b/desktop/src/shared/types/settings.types.ts @@ -1,39 +1,92 @@ -export type PlateSolverType = 'ASTROMETRY_NET' | 'ASTROMETRY_NET_ONLINE' | 'ASTAP' | 'SIRIL' - -export interface PlateSolverRequest { - type: PlateSolverType - executablePath: string - downsampleFactor: number - apiUrl: string - apiKey: string - timeout: number +import type { Location } from './atlas.types' +import { DEFAULT_LOCATION, locationWithDefault } from './atlas.types' +import type { LiveStackerSettings, LiveStackerType } from './camera.types' +import { cameraCaptureNamingFormatWithDefault, DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT, DEFAULT_LIVE_STACKER_SETTINGS, liveStackerSettingsWithDefault, type CameraCaptureNamingFormat, type FrameType } from './camera.types' +import { DEFAULT_PLATE_SOLVER_SETTINGS, plateSolverSettingsWithDefault, type PlateSolverSettings, type PlateSolverType } from './platesolver.types' +import { DEFAULT_STACKER_SETTINGS, stackerSettingsWithDefault, type StackerSettings, type StackerType } from './stacker.types' +import { DEFAULT_STAR_DETECTOR_SETTINGS, starDetectorSettingsWithDefault, type StarDetectorSettings, type StarDetectorType } from './stardetector.types' + +export type SettingsTabKey = 'LOCATION' | 'PLATE_SOLVER' | 'STAR_DETECTOR' | 'LIVE_STACKER' | 'STACKER' | 'CAPTURE_NAMING_FORMAT' + +export interface SettingsPreference { + plateSolver: Record + starDetector: Record + liveStacker: Record + stacker: Record + namingFormat: CameraCaptureNamingFormat + locations: Location[] + location: Location } -export const EMPTY_PLATE_SOLVER_REQUEST: PlateSolverRequest = { - type: 'ASTAP', - executablePath: '', - downsampleFactor: 0, - apiUrl: 'https://nova.astrometry.net/', - apiKey: '', - timeout: 300, +export const DEFAULT_SETTINGS_PREFERENCE: SettingsPreference = { + plateSolver: { + ASTROMETRY_NET: structuredClone(DEFAULT_PLATE_SOLVER_SETTINGS), + ASTROMETRY_NET_ONLINE: structuredClone(DEFAULT_PLATE_SOLVER_SETTINGS), + ASTAP: structuredClone(DEFAULT_PLATE_SOLVER_SETTINGS), + SIRIL: structuredClone(DEFAULT_PLATE_SOLVER_SETTINGS), + PIXINSIGHT: structuredClone(DEFAULT_PLATE_SOLVER_SETTINGS), + }, + starDetector: { + ASTAP: structuredClone(DEFAULT_STAR_DETECTOR_SETTINGS), + SIRIL: structuredClone(DEFAULT_STAR_DETECTOR_SETTINGS), + PIXINSIGHT: structuredClone(DEFAULT_STAR_DETECTOR_SETTINGS), + }, + liveStacker: { + SIRIL: structuredClone(DEFAULT_LIVE_STACKER_SETTINGS), + PIXINSIGHT: structuredClone(DEFAULT_LIVE_STACKER_SETTINGS), + }, + stacker: { + PIXINSIGHT: structuredClone(DEFAULT_STACKER_SETTINGS), + }, + namingFormat: DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT, + locations: [DEFAULT_LOCATION], + location: DEFAULT_LOCATION, } -export type StarDetectorType = 'ASTAP' | 'PIXINSIGHT' | 'SIRIL' +export function settingsPreferenceWithDefault(preference?: Partial, source: SettingsPreference = DEFAULT_SETTINGS_PREFERENCE) { + if (!preference) return structuredClone(source) + + preference.plateSolver ??= structuredClone(source.plateSolver) + preference.starDetector ??= structuredClone(source.starDetector) + preference.liveStacker ??= structuredClone(source.liveStacker) + preference.stacker ??= structuredClone(source.stacker) + + for (const [key, value] of Object.entries(preference.plateSolver)) { + plateSolverSettingsWithDefault(value, source.plateSolver[key as never]) + } + for (const [key, value] of Object.entries(preference.starDetector)) { + starDetectorSettingsWithDefault(value, source.starDetector[key as never]) + } + for (const [key, value] of Object.entries(preference.liveStacker)) { + liveStackerSettingsWithDefault(value, source.liveStacker[key as never]) + } + for (const [key, value] of Object.entries(preference.stacker)) { + stackerSettingsWithDefault(value, source.stacker[key as never]) + } + + preference.namingFormat = cameraCaptureNamingFormatWithDefault(preference.namingFormat, source.namingFormat) + preference.location = locationWithDefault(preference.location, source.location) + + if (!preference.locations?.length) { + preference.locations = structuredClone(source.locations) + } -export interface StarDetectionRequest { - type: StarDetectorType - executablePath: string - timeout: number - minSNR?: number - maxStars?: number - slot: number + return preference as SettingsPreference } -export const EMPTY_STAR_DETECTION_REQUEST: StarDetectionRequest = { - type: 'ASTAP', - executablePath: '', - timeout: 300, - minSNR: 0, - maxStars: 0, - slot: 1, +export function resetCameraCaptureNamingFormat(type: FrameType, format: CameraCaptureNamingFormat, defaultValue?: CameraCaptureNamingFormat) { + switch (type) { + case 'LIGHT': + format.light = defaultValue?.light || DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT.light + break + case 'DARK': + format.dark = defaultValue?.dark || DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT.dark + break + case 'FLAT': + format.flat = defaultValue?.flat || DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT.flat + break + case 'BIAS': + format.bias = defaultValue?.bias || DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT.bias + break + } } diff --git a/desktop/src/shared/types/stacker.types.ts b/desktop/src/shared/types/stacker.types.ts new file mode 100644 index 000000000..c7250dd8c --- /dev/null +++ b/desktop/src/shared/types/stacker.types.ts @@ -0,0 +1,101 @@ +import type { FrameType } from './camera.types' + +export type StackerType = 'PIXINSIGHT' + +export type StackerGroupType = 'LUMINANCE' | 'RED' | 'GREEN' | 'BLUE' | 'MONO' | 'RGB' + +export interface StackerSettings { + executablePath: string + slot: number +} + +export interface StackingRequest extends StackerSettings { + outputDirectory: string + type: StackerType + darkPath?: string + darkEnabled: boolean + flatPath?: string + flatEnabled: boolean + biasPath?: string + biasEnabled: boolean + use32Bits: boolean + referencePath: string + targets: StackingTarget[] +} + +export interface StackingTarget { + enabled: boolean + path: string + type: FrameType + group: StackerGroupType + reference: boolean + analyzed?: AnalyzedTarget +} + +export interface AnalyzedTarget { + width: number + height: number + binX: number + binY: number + gain: number + exposureTime: number + type: FrameType + group: StackerGroupType +} + +export interface StackerPreference { + request: StackingRequest + defaultPath?: string +} + +export const DEFAULT_STACKER_SETTINGS: StackerSettings = { + executablePath: '', + slot: 0, +} + +export const DEFAULT_STACKING_REQUEST: StackingRequest = { + ...DEFAULT_STACKER_SETTINGS, + outputDirectory: '', + type: 'PIXINSIGHT', + use32Bits: false, + referencePath: '', + targets: [], + darkEnabled: false, + flatEnabled: false, + biasEnabled: false, +} + +export const DEFAULT_STACKER_PREFERENCE: StackerPreference = { + request: DEFAULT_STACKING_REQUEST, +} + +export function stackerSettingsWithDefault(preference?: Partial, source: StackerSettings = DEFAULT_STACKER_SETTINGS) { + if (!preference) return structuredClone(source) + preference.executablePath ||= source.executablePath + preference.slot ??= source.slot + return preference as StackerSettings +} + +export function stackingRequestWithDefault(request?: Partial, source: StackingRequest = DEFAULT_STACKING_REQUEST) { + if (!request) return structuredClone(source) + stackerSettingsWithDefault(request, source) + request.outputDirectory ||= source.outputDirectory + request.type ||= source.type + request.darkPath ||= source.darkPath + request.darkEnabled ??= source.darkEnabled + request.flatPath ||= source.flatPath + request.flatEnabled ??= source.flatEnabled + request.biasPath ||= source.biasPath + request.biasEnabled ??= source.biasEnabled + request.use32Bits ??= source.use32Bits + request.referencePath ||= source.referencePath + request.targets ??= source.targets + return request as StackingRequest +} + +export function stackerPreferenceWithDefault(preference?: Partial, source: StackerPreference = DEFAULT_STACKER_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.request = stackingRequestWithDefault(preference.request, source.request) + preference.defaultPath ??= source.defaultPath + return preference as StackerPreference +} diff --git a/desktop/src/shared/types/stardetector.types.ts b/desktop/src/shared/types/stardetector.types.ts new file mode 100644 index 000000000..5093e3ccf --- /dev/null +++ b/desktop/src/shared/types/stardetector.types.ts @@ -0,0 +1,43 @@ +export type StarDetectorType = 'ASTAP' | 'PIXINSIGHT' | 'SIRIL' + +export interface StarDetectorSettings { + executablePath: string + timeout: number + slot: number +} + +export interface StarDetectionRequest extends StarDetectorSettings { + type: StarDetectorType + minSNR?: number + maxStars?: number +} + +export const DEFAULT_STAR_DETECTOR_SETTINGS: StarDetectorSettings = { + executablePath: '', + timeout: 300, + slot: 0, +} + +export const DEFAULT_STAR_DETECTION_REQUEST: StarDetectionRequest = { + ...DEFAULT_STAR_DETECTOR_SETTINGS, + type: 'ASTAP', + minSNR: 0, + maxStars: 0, +} + +export function starDetectorSettingsWithDefault(settings?: Partial, source: StarDetectorSettings = DEFAULT_STAR_DETECTOR_SETTINGS) { + if (!settings) return structuredClone(source) + settings.executablePath ||= source.executablePath + settings.timeout ??= source.timeout + settings.slot ??= source.slot + return settings as StarDetectorSettings +} + +export function starDetectionRequestWithDefault(request?: Partial, source: StarDetectionRequest = DEFAULT_STAR_DETECTION_REQUEST) { + if (!request) return structuredClone(source) + starDetectorSettingsWithDefault(request, source) + request.type ||= source.type + request.minSNR ??= source.minSNR + request.maxStars ??= source.maxStars + return request as StarDetectionRequest +} diff --git a/desktop/src/shared/types/wheel.types.ts b/desktop/src/shared/types/wheel.types.ts index bb2c7168f..2ebb46491 100644 --- a/desktop/src/shared/types/wheel.types.ts +++ b/desktop/src/shared/types/wheel.types.ts @@ -1,52 +1,41 @@ import type { CameraStartCapture } from './camera.types' import type { Device } from './device.types' -export type WheelDialogMode = 'CAPTURE' | 'SEQUENCER' | 'FLAT_WIZARD' +export type WheelMode = 'CAPTURE' | 'SEQUENCER' | 'FLAT_WIZARD' -export interface FilterWheel extends Device { +export interface Wheel extends Device { count: number position: number moving: boolean names: string[] } -export const EMPTY_WHEEL: FilterWheel = { - sender: '', - id: '', - count: 0, - position: 0, - moving: false, - name: '', - connected: false, - names: [], -} - export interface WheelDialogInput { - mode: WheelDialogMode - wheel: FilterWheel + mode: WheelMode + wheel: Wheel request: CameraStartCapture } export interface WheelPreference { - shutterPosition?: number + shutterPosition: number } -export interface FilterSlot { +export interface Filter { position: number name: string dark: boolean } export interface WheelRenamed { - wheel: FilterWheel - filter: FilterSlot + wheel: Wheel + filter: Filter } -export function makeFilterSlots(wheel: FilterWheel, filters: FilterSlot[], shutterPosition: number = 0) { +export function makeFilter(wheel: Wheel, filters: Filter[], shutterPosition: number = 0) { if (wheel.count <= 0) { filters = [] } else if (wheel.count !== filters.length) { - filters = new Array(wheel.count) + filters = new Array(wheel.count) } if (filters.length) { @@ -66,6 +55,28 @@ export function makeFilterSlots(wheel: FilterWheel, filters: FilterSlot[], shutt return filters } -export function isFilterWheel(device?: Device): device is FilterWheel { - return !!device && 'count' in device +export const DEFAULT_WHEEL: Wheel = { + type: 'WHEEL', + sender: '', + id: '', + count: 0, + position: 0, + moving: false, + name: '', + connected: false, + names: [], +} + +export const DEFAULT_WHEEL_PREFERENCE: WheelPreference = { + shutterPosition: 0, +} + +export function isWheel(device?: Device): device is Wheel { + return !!device && device.type === 'WHEEL' +} + +export function wheelPreferenceWithDefault(preference?: Partial, source: WheelPreference = DEFAULT_WHEEL_PREFERENCE) { + if (!preference) return structuredClone(source) + preference.shutterPosition ??= source.shutterPosition + return preference as WheelPreference } diff --git a/desktop/src/styles.scss b/desktop/src/styles.scss index 003caf10f..73efde931 100644 --- a/desktop/src/styles.scss +++ b/desktop/src/styles.scss @@ -93,13 +93,16 @@ p-table { } .mdi:before { - font-size: 1.25rem; display: inline-flex; justify-content: center; align-items: center; aspect-ratio: 1; } +.mdi:before { + font-size: 1.25rem; +} + .mdi.mdi-sm:before { font-size: 1rem; } @@ -216,11 +219,14 @@ p-calendar.border-0 .p-calendar-w-btn { } .p-tag { - padding: 0.1rem 0.4rem; border-radius: 2px; + padding: 1px 4px; + display: flex; + min-height: 1rem; + .p-tag-icon, .p-tag-value { - line-height: 1.2 !important; + line-height: normal; } } @@ -243,8 +249,8 @@ i.mdi { .p-menuitem-link { &.p-menuitem-selected { - background-color: $successButtonBg; - color: #212121 !important; + background-color: rgb(0 255 0 / 10%); + border-radius: 4px; } } @@ -312,6 +318,10 @@ p-tieredmenu *, } } +.p-listbox-header { + border-bottom: 0px; +} + .pixelated { image-rendering: pixelated; } @@ -328,6 +338,14 @@ p-tieredmenu *, gap: 1px; } +.mb-1px { + margin-bottom: 1px; +} + +.p-2-4 { + padding: 2px 4px; +} + .min-w-0 { min-width: 0px !important; } @@ -473,8 +491,8 @@ p-tieredmenu *, } ::-webkit-scrollbar { - width: 6px; - height: 6px; + width: 4px; + height: 4px; } ::-webkit-scrollbar-thumb { @@ -495,27 +513,3 @@ p-tieredmenu *, ::-webkit-scrollbar-corner { background-color: transparent; } - -@font-face { - font-family: 'icomoon'; - src: url('assets/fonts/icomoon.ttf'); - font-weight: normal; - font-style: normal; - font-display: block; -} - -.icomoon { - font-family: 'icomoon' !important; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - font-size: 1.25rem; - - &.random-dither:before { - content: '\e900'; - } -} diff --git a/desktop/stacker.png b/desktop/stacker.png new file mode 100644 index 000000000..f7dabfdd5 Binary files /dev/null and b/desktop/stacker.png differ diff --git a/desktop/tsconfig.json b/desktop/tsconfig.json index 69c0debbb..10bca42d5 100644 --- a/desktop/tsconfig.json +++ b/desktop/tsconfig.json @@ -3,7 +3,7 @@ "compilerOptions": { "strict": true, "outDir": "./dist/out-tsc", - "module": "ES2022", + "module": "ESNext", "sourceMap": true, "declaration": false, "moduleResolution": "node", @@ -12,7 +12,7 @@ "resolveJsonModule": true, "allowSyntheticDefaultImports": true, "noUncheckedIndexedAccess": false, - "noUnusedLocals": true, + "noUnusedLocals": false, "noUnusedParameters": false, "noImplicitReturns": true, "noImplicitThis": true, diff --git a/desktop/tsconfig.serve.json b/desktop/tsconfig.serve.json index 1c64234ca..06b36ba89 100644 --- a/desktop/tsconfig.serve.json +++ b/desktop/tsconfig.serve.json @@ -2,18 +2,18 @@ "compilerOptions": { "sourceMap": true, "declaration": false, - "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true, "resolveJsonModule": true, - "module": "CommonJS", + "module": "NodeNext", "target": "ES2022", "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, "incremental": true, "types": ["node"], "lib": ["es2022", "es2018", "es2017", "es2016", "es2015", "dom"] }, - "files": ["app/main.ts", "app/preload.ts", "app/argument.parser.ts", "app/local.storage.ts", "app/window.manager.ts"], + "files": ["app/main.ts", "app/preload.ts", "app/argument.parser.ts", "app/window.manager.ts"], "include": ["src/shared/types/*.ts", "src/typings.d.ts"], "exclude": ["node_modules", "**/*.spec.ts"] } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6f7a6eb33..dedd5d1e6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a426..b740cf133 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/nebulosa-adql/src/test/kotlin/QueryBuilderTest.kt b/nebulosa-adql/src/test/kotlin/QueryBuilderTest.kt index d6fb6b864..b9e508e21 100644 --- a/nebulosa-adql/src/test/kotlin/QueryBuilderTest.kt +++ b/nebulosa-adql/src/test/kotlin/QueryBuilderTest.kt @@ -1,639 +1,745 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.adql.* import nebulosa.math.deg +import org.junit.jupiter.api.Test -class QueryBuilderTest : StringSpec() { +class QueryBuilderTest { - init { - val oid = Column("b.oid") - val rightAscension = Column("b.ra") - val declination = Column("b.dec") - val magnitude = Column("f.V") - val name = Column("ident.id") - - "basic" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + @Test + fun basic() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic """.trimIndent() - } - "limit" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(Limit(100)) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun limit() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(Limit(100)) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT TOP 100 b.oid FROM basic """.trimIndent() - } - "multiple columns" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(rightAscension) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun multipleColumns() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(RA) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid , b.ra FROM basic """.trimIndent() - } - "column alias" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(Column("b.plx_value as plx")) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun columnAlias() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(Column("b.plx_value as plx")) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid , b.plx_value as plx FROM basic """.trimIndent() - } - "table alias" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun tableAlias() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b """.trimIndent() - } - "distinct" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(Distinct) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun distinct() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(Distinct) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT DISTINCT b.oid FROM basic """.trimIndent() - } - "is not null" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(rightAscension.isNotNull) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun isNotNull() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(RA.isNotNull) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.ra IS NOT NULL """.trimIndent() - } - "is null" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(rightAscension.isNull) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun isNull() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(RA.isNull) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.ra IS NULL """.trimIndent() - } - "equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun equal() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid = 8 """.trimIndent() - } - "not equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid notEqual 8) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun notEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID notEqual 8) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid != 8 """.trimIndent() - } - "between" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude between -8.0..4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun between() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG between -8.0..4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V BETWEEN -8.0 AND 4.0 """.trimIndent() - } - "not between" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude notBetween -8.0..4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun notBetween() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG notBetween -8.0..4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V NOT BETWEEN -8.0 AND 4.0 """.trimIndent() - } - "less than" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude lessThan 4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun lessThan() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG lessThan 4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V < 4.0 """.trimIndent() - } - "less or equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude lessOrEqual 4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun lessOrEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG lessOrEqual 4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V <= 4.0 """.trimIndent() - } - "greater than" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude greaterThan 4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun greaterThan() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG greaterThan 4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V > 4.0 """.trimIndent() - } - "greater or equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude greaterOrEqual 4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun greaterOrEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG greaterOrEqual 4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V >= 4.0 """.trimIndent() - } - "like" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(name like "NGC%") - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun like() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(NAME like "NGC%") + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ident.id LIKE 'NGC%' """.trimIndent() - } - "not like" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(name notLike "NGC%") - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun notLike() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(NAME notLike "NGC%") + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ident.id NOT LIKE 'NGC%' """.trimIndent() - } - "negated is not null" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!rightAscension.isNotNull) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedIsNotNull() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!RA.isNotNull) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.ra IS NULL """.trimIndent() - } - "negated is null" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!rightAscension.isNull) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedIsNull() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!RA.isNull) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.ra IS NOT NULL """.trimIndent() - } - "negated equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(oid equal 8)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(OID equal 8)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid != 8 """.trimIndent() - } - "negated not equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(oid notEqual 8)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedNotEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(OID notEqual 8)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid = 8 """.trimIndent() - } - "negated between" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude between -8.0..4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedBetween() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG between -8.0..4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V NOT BETWEEN -8.0 AND 4.0 """.trimIndent() - } - "negated not between" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude notBetween -8.0..4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedNotBetween() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG notBetween -8.0..4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V BETWEEN -8.0 AND 4.0 """.trimIndent() - } - "negated less than" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude lessThan 4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedLessThan() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG lessThan 4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V >= 4.0 """.trimIndent() - } - "negated less or equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude lessOrEqual 4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedLessOrEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG lessOrEqual 4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V > 4.0 """.trimIndent() - } - "negated greater than" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude greaterThan 4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedGreaterThan() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG greaterThan 4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V <= 4.0 """.trimIndent() - } - "negated greater or equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude greaterOrEqual 4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedGreaterOrEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG greaterOrEqual 4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V < 4.0 """.trimIndent() - } - "negated not like" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(name notLike "NGC%")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedNotLike() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(NAME notLike "NGC%")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ident.id LIKE 'NGC%' """.trimIndent() - } - "negated like" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(name like "NGC%")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedLike() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(NAME like "NGC%")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ident.id NOT LIKE 'NGC%' """.trimIndent() - } - "and" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8) - builder.add(magnitude lessOrEqual 8) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun and() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8) + builder.add(MAG lessOrEqual 8) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid = 8 AND f.V <= 8 """.trimIndent() - } - "or" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8 or (oid equal 9)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun or() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8 or (OID equal 9)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE (b.oid = 8 OR b.oid = 9) """.trimIndent() - } - "and & or" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8 or (oid equal 9 and (magnitude lessOrEqual 8.5))) - var query = builder.build().toString() - query shouldBe """ + } + + @Test + fun andAndOr() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8 or (OID equal 9 and (MAG lessOrEqual 8.5))) + var query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE (b.oid = 8 OR (b.oid = 9 AND f.V <= 8.5)) """.trimIndent() - builder.clear() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8 or (oid equal 9) and (magnitude lessOrEqual 8.5)) - query = builder.build().toString() - query shouldBe """ + builder.clear() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8 or (OID equal 9) and (MAG lessOrEqual 8.5)) + query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ((b.oid = 8 OR b.oid = 9) AND f.V <= 8.5) """.trimIndent() - } - "negated and" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(oid equal 8 and (magnitude lessOrEqual 8))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedAnd() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(OID equal 8 and (MAG lessOrEqual 8))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE NOT (b.oid = 8 AND f.V <= 8) """.trimIndent() - } - "negated or" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(oid equal 8 or (oid equal 9))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedOr() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(OID equal 8 or (OID equal 9))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE NOT (b.oid = 8 OR b.oid = 9) """.trimIndent() - } - "negative" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude greaterOrEqual -(4.0.operand))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negative() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG greaterOrEqual -(4.0.operand))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V < -4.0 """.trimIndent() - } - "double negative should be positive" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude greaterOrEqual -(-(4.0.operand)))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun doubleNegativeShouldBePositive() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG greaterOrEqual -(-(4.0.operand)))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V < 4.0 """.trimIndent() - } - "sort by" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(SortBy(oid, SortDirection.DESCENDING)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun sortBy() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(SortBy(OID, SortDirection.DESCENDING)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic ORDER BY oid DESC """.trimIndent() - } - "left join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(LeftJoin(From("basic"), From("ident"), arrayOf(oid equal Column("ident.oidref")))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun leftJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(LeftJoin(From("basic"), From("ident"), arrayOf(OID equal Column("ident.oidref")))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic LEFT OUTER JOIN ident ON b.oid = ident.oidref """.trimIndent() - } - "right join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(RightJoin(From("basic"), From("ident"), arrayOf(oid equal Column("ident.oidref")))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun rightJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(RightJoin(From("basic"), From("ident"), arrayOf(OID equal Column("ident.oidref")))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic RIGHT OUTER JOIN ident ON b.oid = ident.oidref """.trimIndent() - } - "full join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(FullJoin(From("basic"), From("ident"), arrayOf(oid equal Column("ident.oidref")))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun fullJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(FullJoin(From("basic"), From("ident"), arrayOf(OID equal Column("ident.oidref")))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic FULL OUTER JOIN ident ON b.oid = ident.oidref """.trimIndent() - } - "inner join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(InnerJoin(From("basic"), From("ident"), arrayOf(oid equal Column("ident.oidref")))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun innerJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(InnerJoin(From("basic"), From("ident"), arrayOf(OID equal Column("ident.oidref")))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic INNER JOIN ident ON b.oid = ident.oidref """.trimIndent() - } - "natural left join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(LeftJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun naturalLeftJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(LeftJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic NATURAL LEFT OUTER JOIN ident """.trimIndent() - } - "natural right join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(RightJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun naturalRightJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(RightJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic NATURAL RIGHT OUTER JOIN ident """.trimIndent() - } - "natural full join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(FullJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun naturalFullJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(FullJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic NATURAL FULL OUTER JOIN ident """.trimIndent() - } - "natural inner join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(InnerJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun naturalInnerJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(InnerJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic NATURAL INNER JOIN ident """.trimIndent() - } - "cross join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(CrossJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun crossJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(CrossJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic CROSS JOIN ident """.trimIndent() - } - "multiple join" { - val builder = QueryBuilder() - builder.add(oid) - var join: Table = LeftJoin(From("basic").alias("b"), From("ident"), arrayOf(oid equal Column("ident.oidref"))) - join = LeftJoin(join, From("allfluxes").alias("f"), arrayOf(oid equal Column("f.oidref"))) - join = LeftJoin(join, From("ids"), arrayOf(oid equal Column("ids.oidref"))) - builder.add(join) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun multipleJoin() { + val builder = QueryBuilder() + builder.add(OID) + var join: Table = LeftJoin(From("basic").alias("b"), From("ident"), arrayOf(OID equal Column("ident.oidref"))) + join = LeftJoin(join, From("allfluxes").alias("f"), arrayOf(OID equal Column("f.oidref"))) + join = LeftJoin(join, From("ids"), arrayOf(OID equal Column("ids.oidref"))) + builder.add(join) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b LEFT OUTER JOIN ident ON b.oid = ident.oidref LEFT OUTER JOIN allfluxes AS f ON b.oid = f.oidref LEFT OUTER JOIN ids ON b.oid = ids.oidref """.trimIndent() - } - "contains" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(SkyPoint(rightAscension, declination) contains Circle(250.42.deg, 36.46.deg, 0.1.deg)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun contains() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(SkyPoint(RA, DEC) contains Circle(250.42.deg, 36.46.deg, 0.1.deg)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE CONTAINS(POINT('ICRS', b.ra, b.dec), CIRCLE('ICRS', 250.42, 36.46, 0.1)) = 1 """.trimIndent() - } - "not contains" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(SkyPoint(rightAscension, declination) notContains Box(250.42.deg, 36.46.deg, 0.1.deg, 0.2.deg)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun notContains() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(SkyPoint(RA, DEC) notContains Box(250.42.deg, 36.46.deg, 0.1.deg, 0.2.deg)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE CONTAINS(POINT('ICRS', b.ra, b.dec), BOX('ICRS', 250.42, 36.46, 0.1, 0.2)) = 0 """.trimIndent() - } - "negated contains" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(!(SkyPoint(rightAscension, declination) contains Circle(250.42.deg, 36.46.deg, 0.1.deg))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedContains() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(!(SkyPoint(RA, DEC) contains Circle(250.42.deg, 36.46.deg, 0.1.deg))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE CONTAINS(POINT('ICRS', b.ra, b.dec), CIRCLE('ICRS', 250.42, 36.46, 0.1)) = 0 """.trimIndent() - } - "distance" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(SkyPoint(rightAscension, declination) distance SkyPoint(250.42.deg, 36.46.deg) lessOrEqual 8.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun distance() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(SkyPoint(RA, DEC) distance SkyPoint(250.42.deg, 36.46.deg) lessOrEqual 8.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE DISTANCE(POINT('ICRS', b.ra, b.dec), POINT('ICRS', 250.42, 36.46)) <= 8.0 """.trimIndent() - } - "area" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(Area(Box(rightAscension, declination, 0.1.deg, 0.2.deg)) lessOrEqual 8.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun area() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(Area(Box(RA, DEC, 0.1.deg, 0.2.deg)) lessOrEqual 8.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE AREA(BOX('ICRS', b.ra, b.dec, 0.1, 0.2)) <= 8.0 """.trimIndent() - } + } + + companion object { + + @JvmStatic private val OID = Column("b.oid") + @JvmStatic private val RA = Column("b.ra") + @JvmStatic private val DEC = Column("b.dec") + @JvmStatic private val MAG = Column("f.V") + @JvmStatic private val NAME = Column("ident.id") } } diff --git a/nebulosa-alignment/src/test/kotlin/ThreePointPolarAlignmentTest.kt b/nebulosa-alignment/src/test/kotlin/ThreePointPolarAlignmentTest.kt index 5808474b9..deed0bab5 100644 --- a/nebulosa-alignment/src/test/kotlin/ThreePointPolarAlignmentTest.kt +++ b/nebulosa-alignment/src/test/kotlin/ThreePointPolarAlignmentTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe @@ -12,74 +11,84 @@ import nebulosa.math.toArcsec import nebulosa.platesolver.PlateSolution import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC +import org.junit.jupiter.api.Test -class ThreePointPolarAlignmentTest : StringSpec() { +class ThreePointPolarAlignmentTest { - init { - // Based on logs generated by N.I.N.A. using Telescope Simulator for .NET and Sky Simulator (ASCOM). - // https://sourceforge.net/projects/sky-simulator/ + // Based on logs generated by N.I.N.A. using Telescope Simulator for .NET and Sky Simulator (ASCOM). + // https://sourceforge.net/projects/sky-simulator/ - "position" { - val a = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) - val b = Position(a.vector, LNG, SLAT) + @Test + fun position() { + val a = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) + val b = Position(a.vector, LNG, SLAT) - a.topocentric.azimuth shouldBeExactly b.topocentric.azimuth - a.topocentric.altitude shouldBeExactly b.topocentric.altitude - } - "stenographic projection" { - val coordinates = doubleArrayOf(5.0.arcsec, 8.0.arcsec) - val projected = coordinates.stenographicProjection(0.0, 0.0, 100.0, 100.0, 1.0.arcsec, 0.0) - projected[0] shouldBe (95.0 plusOrMinus 1e-8) - projected[1] shouldBe (92.0 plusOrMinus 1e-8) - } - "destination coordinates" { - val position1 = Position("05:35:18".hours, "-05 23 26".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 42.4979))) - val position2 = Position("04:54:45".hours, "-05 24 50".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 58.1655))) - val position3 = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) - val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:08".hours, "-05 26 10".deg, 1280.0, 1024.0) - val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) + a.topocentric.azimuth shouldBeExactly b.topocentric.azimuth + a.topocentric.altitude shouldBeExactly b.topocentric.altitude + } - with(pe.destinationCoordinates(0.0, 0.0)) { - this[0] shouldBe ("04:14:08".hours plusOrMinus 1e-14) - this[1] shouldBe ("-05 26 10".deg plusOrMinus 1e-14) - } - } - "perfectly aligned" { - val position1 = Position("05:35:18".hours, "-05 23 26".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 42.4979))) - val position2 = Position("04:54:45".hours, "-05 24 50".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 58.1655))) - val position3 = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) - val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:08".hours, "-05 26 10".deg, 1280.0, 1024.0) - val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) - val (az, alt) = pe.compute() + @Test + fun stenographicProjection() { + val coordinates = doubleArrayOf(5.0.arcsec, 8.0.arcsec) + val projected = coordinates.stenographicProjection(0.0, 0.0, 100.0, 100.0, 1.0.arcsec, 0.0) + projected[0] shouldBe (95.0 plusOrMinus 1e-8) + projected[1] shouldBe (92.0 plusOrMinus 1e-8) + } - // Calculated Error: Az: -00° 00' 04", Alt: -00° 00' 07", Tot: 00° 00' 08" - az.toArcsec shouldBe (-4.0 plusOrMinus 2.5) - alt.toArcsec shouldBe (-7.0 plusOrMinus 2.5) - } - "bad southern polar aligned" { - val position1 = Position("05:35:29".hours, "-05 23 44".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 28.0693))) - val position2 = Position("04:54:48".hours, "-05 23 16".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 43.0120))) - val position3 = Position("04:14:05".hours, "-05 22 47".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 57.8800))) - val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:05".hours, "-05 22 47".deg, 1280.0, 1024.0) - val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) - val (az, alt) = pe.compute() + @Test + fun destinationCoordinates() { + val position1 = Position("05:35:18".hours, "-05 23 26".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 42.4979))) + val position2 = Position("04:54:45".hours, "-05 24 50".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 58.1655))) + val position3 = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) + val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:08".hours, "-05 26 10".deg, 1280.0, 1024.0) + val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) - // Calculated Error: Az: 00° 10' 10", Alt: 00° 04' 41", Tot: 00° 11' 11" - az.toArcsec shouldBe (610.0 plusOrMinus 7.0) - alt.toArcsec shouldBe (281.0 plusOrMinus 7.0) + with(pe.destinationCoordinates(0.0, 0.0)) { + this[0] shouldBe ("04:14:08".hours plusOrMinus 1e-14) + this[1] shouldBe ("-05 26 10".deg plusOrMinus 1e-14) } - "bad northern polar aligned" { - val position1 = Position("05:35:35".hours, "-05 32 31".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 19, 31.1390))) - val position2 = Position("04:54:49".hours, "-05 34 43".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 19, 46.2383))) - val position3 = Position("04:13:55".hours, "-05 36 32".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 20, 1.6394))) - val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:13:55".hours, "-05 36 32".deg, 1280.0, 1024.0) - val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, NLAT) - val (az, alt) = pe.compute() + } - // Calculated Error: Az: -00° 09' 58", Alt: 00° 04' 51", Tot: 00° 11' 05" - az.toArcsec shouldBe (-598.0 plusOrMinus 7.0) - alt.toArcsec shouldBe (291.0 plusOrMinus 7.0) - } + @Test + fun perfectlyAligned() { + val position1 = Position("05:35:18".hours, "-05 23 26".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 42.4979))) + val position2 = Position("04:54:45".hours, "-05 24 50".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 58.1655))) + val position3 = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) + val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:08".hours, "-05 26 10".deg, 1280.0, 1024.0) + val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) + val (az, alt) = pe.compute() + + // Calculated Error: Az: -00° 00' 04", Alt: -00° 00' 07", Tot: 00° 00' 08" + az.toArcsec shouldBe (-4.0 plusOrMinus 2.5) + alt.toArcsec shouldBe (-7.0 plusOrMinus 2.5) + } + + @Test + fun badSouthernPolarAligned() { + val position1 = Position("05:35:29".hours, "-05 23 44".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 28.0693))) + val position2 = Position("04:54:48".hours, "-05 23 16".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 43.0120))) + val position3 = Position("04:14:05".hours, "-05 22 47".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 57.8800))) + val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:05".hours, "-05 22 47".deg, 1280.0, 1024.0) + val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) + val (az, alt) = pe.compute() + + // Calculated Error: Az: 00° 10' 10", Alt: 00° 04' 41", Tot: 00° 11' 11" + az.toArcsec shouldBe (610.0 plusOrMinus 7.0) + alt.toArcsec shouldBe (281.0 plusOrMinus 7.0) + } + + @Test + fun badNorthernPolarAligned() { + val position1 = Position("05:35:35".hours, "-05 32 31".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 19, 31.1390))) + val position2 = Position("04:54:49".hours, "-05 34 43".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 19, 46.2383))) + val position3 = Position("04:13:55".hours, "-05 36 32".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 20, 1.6394))) + val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:13:55".hours, "-05 36 32".deg, 1280.0, 1024.0) + val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, NLAT) + val (az, alt) = pe.compute() + + // Calculated Error: Az: -00° 09' 58", Alt: 00° 04' 51", Tot: 00° 11' 05" + az.toArcsec shouldBe (-598.0 plusOrMinus 7.0) + alt.toArcsec shouldBe (291.0 plusOrMinus 7.0) } companion object { diff --git a/nebulosa-alpaca-api/src/test/kotlin/AlpacaServiceTest.kt b/nebulosa-alpaca-api/src/test/kotlin/AlpacaServiceTest.kt index 462085682..3c31b157c 100644 --- a/nebulosa-alpaca-api/src/test/kotlin/AlpacaServiceTest.kt +++ b/nebulosa-alpaca-api/src/test/kotlin/AlpacaServiceTest.kt @@ -1,21 +1,22 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.nulls.shouldNotBeNull import nebulosa.alpaca.api.AlpacaService -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class AlpacaServiceTest : StringSpec() { +@NonGitHubOnly +class AlpacaServiceTest { - init { - val client = AlpacaService("http://localhost:11111/") + @Test + fun management() { + val body = CLIENT.management.configuredDevices().execute().body().shouldNotBeNull() - "management" { - val body = client.management.configuredDevices().execute().body().shouldNotBeNull() - - for (device in body.value) { - println(device) - } + for (device in body.value) { + println(device) } } + + companion object { + + @JvmStatic val CLIENT = AlpacaService("http://localhost:11111/") + } } diff --git a/nebulosa-alpaca-discovery-protocol/src/main/kotlin/nebulosa/alpaca/discovery/AlpacaDiscoveryProtocol.kt b/nebulosa-alpaca-discovery-protocol/src/main/kotlin/nebulosa/alpaca/discovery/AlpacaDiscoveryProtocol.kt index 4fd35fd1e..f4ebbb2d4 100644 --- a/nebulosa-alpaca-discovery-protocol/src/main/kotlin/nebulosa/alpaca/discovery/AlpacaDiscoveryProtocol.kt +++ b/nebulosa-alpaca-discovery-protocol/src/main/kotlin/nebulosa/alpaca/discovery/AlpacaDiscoveryProtocol.kt @@ -84,7 +84,7 @@ class AlpacaDiscoveryProtocol : Runnable, Closeable { } val message = packet.data.decodeToString(0, packet.length) - val port = ALPACA_PORT_REGEX.matchEntire(message)?.groupValues?.get(1)?.toIntOrNull() ?: continue + val port = ALPACA_PORT_REGEX.find(message)?.groupValues?.get(1)?.toIntOrNull() ?: continue LOG.info("server found at {}:{}", packet.address, port) listeners.forEach { it.onServerFound(packet.address, port) } } @@ -100,7 +100,7 @@ class AlpacaDiscoveryProtocol : Runnable, Closeable { private const val ALPACA_DISCOVERY_MESSAGE = "alpacadiscovery1" - @JvmStatic private val ALPACA_PORT_REGEX = Regex("\\{\"AlpacaPort\":(\\d+)\\}") + @JvmStatic private val ALPACA_PORT_REGEX = Regex("\\{\"AlpacaPort\":(\\d+)}") @JvmStatic private val LOG = loggerFor() } } diff --git a/nebulosa-alpaca-discovery-protocol/src/test/kotlin/AlpacaDiscoveryProtocolTest.kt b/nebulosa-alpaca-discovery-protocol/src/test/kotlin/AlpacaDiscoveryProtocolTest.kt index 515dc40f5..755f32658 100644 --- a/nebulosa-alpaca-discovery-protocol/src/test/kotlin/AlpacaDiscoveryProtocolTest.kt +++ b/nebulosa-alpaca-discovery-protocol/src/test/kotlin/AlpacaDiscoveryProtocolTest.kt @@ -1,21 +1,19 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import nebulosa.alpaca.discovery.AlpacaDiscoveryProtocol import nebulosa.alpaca.discovery.DiscoveryListener -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test import java.net.InetAddress import kotlin.concurrent.thread -@EnabledIf(NonGitHubOnlyCondition::class) -class AlpacaDiscoveryProtocolTest : StringSpec(), DiscoveryListener { +@NonGitHubOnly +class AlpacaDiscoveryProtocolTest : DiscoveryListener { - init { - "discovery" { - val discoverer = AlpacaDiscoveryProtocol() - discoverer.registerDiscoveryListener(this@AlpacaDiscoveryProtocolTest) - thread { Thread.sleep(10000); discoverer.close() } - discoverer.run() - } + @Test + fun discovery() { + val discoverer = AlpacaDiscoveryProtocol() + discoverer.registerDiscoveryListener(this@AlpacaDiscoveryProtocolTest) + thread { Thread.sleep(10000); discoverer.close() } + discoverer.run() } override fun onServerFound(address: InetAddress, port: Int) { diff --git a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/mounts/ASCOMMount.kt b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/mounts/ASCOMMount.kt index dc79d320a..b540a3f68 100644 --- a/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/mounts/ASCOMMount.kt +++ b/nebulosa-alpaca-indi/src/main/kotlin/nebulosa/alpaca/indi/device/mounts/ASCOMMount.kt @@ -13,6 +13,7 @@ import nebulosa.log.loggerFor import nebulosa.math.* import nebulosa.nova.position.ICRF import nebulosa.time.CurrentTime +import nebulosa.time.SystemClock import java.math.BigDecimal import java.time.Duration import java.time.OffsetDateTime @@ -53,7 +54,7 @@ data class ASCOMMount( @Volatile final override var longitude = 0.0 @Volatile final override var latitude = 0.0 @Volatile final override var elevation = 0.0 - @Volatile final override var dateTime = OffsetDateTime.now()!! + @Volatile final override var dateTime = OffsetDateTime.now(SystemClock)!! override val snoopedDevices = emptyList() @@ -269,7 +270,7 @@ data class ASCOMMount( longitude = 0.0 latitude = 0.0 elevation = 0.0 - dateTime = OffsetDateTime.now()!! + dateTime = OffsetDateTime.now(SystemClock)!! axisRates.clear() } diff --git a/nebulosa-astrobin-api/src/test/kotlin/AstrobinServiceTest.kt b/nebulosa-astrobin-api/src/test/kotlin/AstrobinServiceTest.kt index 1b3379a01..9ba653df9 100644 --- a/nebulosa-astrobin-api/src/test/kotlin/AstrobinServiceTest.kt +++ b/nebulosa-astrobin-api/src/test/kotlin/AstrobinServiceTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.doubles.shouldBeExactly @@ -8,72 +6,83 @@ import io.kotest.matchers.longs.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.astrobin.api.AstrobinService import nebulosa.astrobin.api.SensorColor -import nebulosa.test.NonGitHubOnlyCondition - -@EnabledIf(NonGitHubOnlyCondition::class) -class AstrobinServiceTest : StringSpec() { - - init { - val service = AstrobinService() - - "sensors" { - val page = service.sensors(1).execute().body()!! - - page.count shouldBeExactly 469 - page.results.size shouldBeExactly 50 - - println(page.results[0]) - } - "sensor" { - val sensor = service.sensor(184).execute().body()!! - - sensor.id shouldBeExactly 184 - sensor.brandName shouldBe "Sony" - sensor.name shouldBe "IMX492 (mono)" - sensor.quantumEfficiency shouldBeExactly 90.0 - sensor.pixelSize shouldBeExactly 2.32 - sensor.pixelWidth shouldBeExactly 8240 - sensor.pixelHeight shouldBeExactly 5628 - sensor.readNoise shouldBeExactly 1.3 - sensor.fullWellCapacity shouldBeExactly 14.0 - sensor.adc shouldBeExactly 12 - sensor.color shouldBe SensorColor.MONO - sensor.cameras.toList().shouldContain(529) - } - "cameras" { - val page = service.cameras(1).execute().body()!! - - page.count shouldBeExactly 3362 - page.results.size shouldBeExactly 50 - - println(page.results[0]) - } - "camera" { - val camera = service.camera(529).execute().body()!! - - camera.id shouldBeExactly 529 - camera.brandName shouldBe "ZWO" - camera.name shouldBe "ASI294MM" - camera.cooled.shouldBeFalse() - camera.sensor shouldBeExactly 184 - } - "telescopes" { - val page = service.telescopes(1).execute().body()!! - - page.count shouldBeExactly 3813 - page.results.size shouldBeExactly 50 - - println(page.results[0]) - } - "telescope" { - val telescope = service.telescope(1097).execute().body()!! - - telescope.id shouldBeExactly 1097 - telescope.brandName shouldBe "GSO" - telescope.name shouldBe "6\" f/9 Ritchey-Chretien" - telescope.aperture shouldBeExactly 152.0 - telescope.minFocalLength shouldBeExactly 1368.0 - telescope.maxFocalLength shouldBeExactly 1368.0 - } +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test + +@NonGitHubOnly +class AstrobinServiceTest { + + @Test + fun sensors() { + val page = SERVICE.sensors(1).execute().body()!! + + page.count shouldBeExactly 469 + page.results.size shouldBeExactly 50 + } + + @Test + fun sensor() { + val sensor = SERVICE.sensor(184).execute().body()!! + + sensor.id shouldBeExactly 184 + sensor.brandName shouldBe "Sony" + sensor.name shouldBe "IMX492 (mono)" + sensor.quantumEfficiency shouldBeExactly 90.0 + sensor.pixelSize shouldBeExactly 2.32 + sensor.pixelWidth shouldBeExactly 8240 + sensor.pixelHeight shouldBeExactly 5628 + sensor.readNoise shouldBeExactly 1.3 + sensor.fullWellCapacity shouldBeExactly 14.0 + sensor.adc shouldBeExactly 12 + sensor.color shouldBe SensorColor.MONO + sensor.cameras.toList().shouldContain(529) + } + + @Test + fun cameras() { + val page = SERVICE.cameras(1).execute().body()!! + + page.count shouldBeExactly 3362 + page.results.size shouldBeExactly 50 + + println(page.results[0]) + } + + @Test + fun camera() { + val camera = SERVICE.camera(529).execute().body()!! + + camera.id shouldBeExactly 529 + camera.brandName shouldBe "ZWO" + camera.name shouldBe "ASI294MM" + camera.cooled.shouldBeFalse() + camera.sensor shouldBeExactly 184 + } + + @Test + fun telescopes() { + val page = SERVICE.telescopes(1).execute().body()!! + + page.count shouldBeExactly 3813 + page.results.size shouldBeExactly 50 + + println(page.results[0]) + } + + @Test + fun telescope() { + val telescope = SERVICE.telescope(1097).execute().body()!! + + telescope.id shouldBeExactly 1097 + telescope.brandName shouldBe "GSO" + telescope.name shouldBe "6\" f/9 Ritchey-Chretien" + telescope.aperture shouldBeExactly 152.0 + telescope.minFocalLength shouldBeExactly 1368.0 + telescope.maxFocalLength shouldBeExactly 1368.0 + } + + companion object { + + @JvmStatic private val SERVICE = AstrobinService() } } diff --git a/nebulosa-astrometrynet-jna/src/test/kotlin/LibAstrometryNetTest.kt b/nebulosa-astrometrynet-jna/src/test/kotlin/LibAstrometryNetTest.kt index c382f5ae8..24dc7b26d 100644 --- a/nebulosa-astrometrynet-jna/src/test/kotlin/LibAstrometryNetTest.kt +++ b/nebulosa-astrometrynet-jna/src/test/kotlin/LibAstrometryNetTest.kt @@ -1,122 +1,132 @@ import com.sun.jna.Pointer -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import nebulosa.astrometrynet.platesolver.* -import nebulosa.test.NonGitHubOnlyCondition -import java.nio.file.Path +import nebulosa.test.NonGitHubOnly +import nebulosa.test.concat +import nebulosa.test.homeDirectory +import org.junit.jupiter.api.Test import kotlin.io.path.listDirectoryEntries import kotlin.math.hypot import kotlin.math.ln import kotlin.math.min -@EnabledIf(NonGitHubOnlyCondition::class) -class LibAstrometryNetTest : StringSpec(), Solver.RecordMatchCallback { +@NonGitHubOnly +class LibAstrometryNetTest : Solver.RecordMatchCallback { init { - System.setProperty("LIBASTROMETRYNET_PATH", "/home/tiagohm/Git/astrometry.net/solver/libastrometry.so") - - val lib = LibAstrometryNet.INSTANCE + System.setProperty(LibAstrometryNet.PATH, "$homeDirectory/Git/astrometry.net/solver/libastrometry.so") + } - // http://data.astrometry.net/ - val indexDir = Path.of("/home/tiagohm/Downloads/Index Files") + @Test + fun structureSizes() { + val tan = Tan.ByValue() + tan.size() shouldBeExactly 88 - "structure sizes" { - val tan = Tan.ByValue() - tan.size() shouldBeExactly 88 + val sip = Sip.ByValue() + sip.size() shouldBeExactly 3304 - val sip = Sip.ByValue() - sip.size() shouldBeExactly 3304 + val index = Index.ByValue() + index.size() shouldBeExactly 136 - val index = Index.ByValue() - index.size() shouldBeExactly 136 + val xylist = XYList.ByValue() + xylist.size() shouldBeExactly 72 - val xylist = XYList.ByValue() - xylist.size() shouldBeExactly 72 + val starxy = StarXY.ByValue() + starxy.size() shouldBeExactly 72 - val starxy = StarXY.ByValue() - starxy.size() shouldBeExactly 72 + val matched = Matched.ByValue() + matched.size() shouldBeExactly 752 - val matched = Matched.ByValue() - matched.size() shouldBeExactly 752 + val solver = Solver.ByValue() + solver.size() shouldBeExactly 1208 + } - val solver = Solver.ByValue() - solver.size() shouldBeExactly 1208 - } - "load index" { - for (i in 7..19) { - val index = lib.index_load("$indexDir/index-41%02d.fits".format(i), 0, null) - index.indexName shouldBe "$indexDir/index-41%02d.fits".format(i) - index.indexId shouldBeExactly 4100 + i - index.cutnsweep shouldBeExactly 10 - lib.index_close(index) - lib.index_free(index) - } + @Test + fun loadIndex() { + for (i in 7..19) { + val index = LibAstrometryNet.INSTANCE.index_load("$INDEX_DIR/index-41%02d.fits".format(i), 0, null) + index.indexName shouldBe "$INDEX_DIR/index-41%02d.fits".format(i) + index.indexId shouldBeExactly 4100 + i + index.cutnsweep shouldBeExactly 10 + LibAstrometryNet.INSTANCE.index_close(index) + LibAstrometryNet.INSTANCE.index_free(index) } - "open xyls" { - val xyls = lib.xylist_open("/home/tiagohm/Git/astrometry.net/solver/apod4.xy") - xyls.xname shouldBe "X" - xyls.yname shouldBe "Y" - xyls.xtype shouldBeExactly 8 - xyls.ytype shouldBeExactly 8 - xyls.includeFlux shouldBe LibAstrometryNet.YES - xyls.includeBackground shouldBe LibAstrometryNet.YES - } - "new solver" { - val solver = lib.solver_new() - solver shouldNotBe Pointer.NULL - lib.solver_free(solver) - } - "run solver" { - val solver = lib.solver_new() - - // https://github.com/dstndstn/astrometry.net/blob/main/solver/control-program.c - - solver.recordMatchCallback = this@LibAstrometryNetTest - solver.funitsLower = 0.1 - solver.funitsUpper = 10.0 - solver.distanceFromQuadBonus = 1 - solver.quadSizeMin = 0.1 * min(719, 507) // image width, height - solver.quadSizeMax = hypot(719.0, 507.0) - solver.doTweak = 1 - solver.tweakAbOrder = 1 - solver.tweakAbpOrder = 4 - solver.write() - - lib.solver_set_keep_logodds(solver, ln(1e12)) - - indexDir.listDirectoryEntries("*.fits").sorted().forEach { - val index = lib.index_load("$it", 0, null) - println(it) - lib.solver_add_index(solver, index) - } - - val xyls = lib.xylist_open("/home/tiagohm/Git/astrometry.net/solver/apod4.xy") - val xy = lib.xylist_read_field(xyls, null) - lib.solver_reset_counters(solver) - lib.solver_reset_best_match(solver) - lib.solver_set_field(solver, xy) - lib.solver_set_field_bounds(solver, 0.0, 719.0, 0.0, 507.0) - lib.solver_preprocess_field(solver) - solver.read() - println(solver) - lib.solver_run(solver) - solver.read() - println(solver) - - lib.solver_did_solve(solver).shouldBeTrue() - - lib.xylist_close(xyls) - lib.solver_free_field(solver) - lib.solver_free(solver) + } + + @Test + fun openXyls() { + val xyls = LibAstrometryNet.INSTANCE.xylist_open("$homeDirectory/Git/astrometry.net/solver/apod4.xy") + xyls.xname shouldBe "X" + xyls.yname shouldBe "Y" + xyls.xtype shouldBeExactly 8 + xyls.ytype shouldBeExactly 8 + xyls.includeFlux shouldBe LibAstrometryNet.YES + xyls.includeBackground shouldBe LibAstrometryNet.YES + } + + @Test + fun newSolver() { + val solver = LibAstrometryNet.INSTANCE.solver_new() + solver shouldNotBe Pointer.NULL + LibAstrometryNet.INSTANCE.solver_free(solver) + } + + @Test + fun runSolver() { + val solver = LibAstrometryNet.INSTANCE.solver_new() + + // https://github.com/dstndstn/astrometry.net/blob/main/solver/control-program.c + + solver.recordMatchCallback = this@LibAstrometryNetTest + solver.funitsLower = 0.1 + solver.funitsUpper = 10.0 + solver.distanceFromQuadBonus = 1 + solver.quadSizeMin = 0.1 * min(719, 507) // image width, height + solver.quadSizeMax = hypot(719.0, 507.0) + solver.doTweak = 1 + solver.tweakAbOrder = 1 + solver.tweakAbpOrder = 4 + solver.write() + + LibAstrometryNet.INSTANCE.solver_set_keep_logodds(solver, ln(1e12)) + + INDEX_DIR.listDirectoryEntries("*.fits").sorted().forEach { + val index = LibAstrometryNet.INSTANCE.index_load("$it", 0, null) + println(it) + LibAstrometryNet.INSTANCE.solver_add_index(solver, index) } + + val xyls = LibAstrometryNet.INSTANCE.xylist_open("$homeDirectory/Git/astrometry.net/solver/apod4.xy") + val xy = LibAstrometryNet.INSTANCE.xylist_read_field(xyls, null) + LibAstrometryNet.INSTANCE.solver_reset_counters(solver) + LibAstrometryNet.INSTANCE.solver_reset_best_match(solver) + LibAstrometryNet.INSTANCE.solver_set_field(solver, xy) + LibAstrometryNet.INSTANCE.solver_set_field_bounds(solver, 0.0, 719.0, 0.0, 507.0) + LibAstrometryNet.INSTANCE.solver_preprocess_field(solver) + solver.read() + println(solver) + LibAstrometryNet.INSTANCE.solver_run(solver) + solver.read() + println(solver) + + LibAstrometryNet.INSTANCE.solver_did_solve(solver).shouldBeTrue() + + LibAstrometryNet.INSTANCE.xylist_close(xyls) + LibAstrometryNet.INSTANCE.solver_free_field(solver) + LibAstrometryNet.INSTANCE.solver_free(solver) } override fun matchFound(matched: Matched.ByReference, userData: Pointer?): Byte { println(matched) return LibAstrometryNet.YES } + + companion object { + + // http://data.astrometry.net/ + @JvmStatic private val INDEX_DIR = homeDirectory.concat("Downloads", "Index Files") + } } diff --git a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/nova/NovaAstrometryNetService.kt b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/nova/NovaAstrometryNetService.kt index 6f051c649..b3cf6590e 100644 --- a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/nova/NovaAstrometryNetService.kt +++ b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/nova/NovaAstrometryNetService.kt @@ -86,6 +86,7 @@ class NovaAstrometryNetService( companion object { const val URL = "https://nova.astrometry.net/" + const val ANONYMOUS_API_KEY = "XXXXXXXX" @JvmStatic private val TEXT_PLAIN_MEDIA_TYPE = "text/plain".toMediaType() @JvmStatic private val OCTET_STREAM_MEDIA_TYPE = "application/octet-stream".toMediaType() diff --git a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/LocalAstrometryNetPlateSolver.kt b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/LocalAstrometryNetPlateSolver.kt index 2cdda406a..03b14705e 100644 --- a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/LocalAstrometryNetPlateSolver.kt +++ b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/LocalAstrometryNetPlateSolver.kt @@ -99,32 +99,32 @@ data class LocalAstrometryNetPlateSolver(private val executablePath: Path) : Pla private const val NUMBER_REGEX = "([\\d.+-]+)" - @JvmStatic private val FIELD_CENTER_REGEX = Regex(".*Field center: \\(RA,Dec\\) = \\($NUMBER_REGEX, $NUMBER_REGEX\\).*") - @JvmStatic private val FIELD_SIZE_REGEX = Regex(".*Field size: $NUMBER_REGEX x $NUMBER_REGEX arcminutes.*") - @JvmStatic private val FIELD_ROTATION_REGEX = Regex(".*Field rotation angle: up is $NUMBER_REGEX degrees.*") - @JvmStatic private val PIXEL_SCALE_REGEX = Regex(".*pixel scale $NUMBER_REGEX arcsec/pix.*") + @JvmStatic private val FIELD_CENTER_REGEX = Regex("Field center: \\(RA,Dec\\) = \\($NUMBER_REGEX, $NUMBER_REGEX\\)") + @JvmStatic private val FIELD_SIZE_REGEX = Regex("Field size: $NUMBER_REGEX x $NUMBER_REGEX arcminutes") + @JvmStatic private val FIELD_ROTATION_REGEX = Regex("Field rotation angle: up is $NUMBER_REGEX degrees") + @JvmStatic private val PIXEL_SCALE_REGEX = Regex("pixel scale $NUMBER_REGEX arcsec/pix") @JvmStatic private fun fieldCenter(line: String): DoubleArray? { - return FIELD_CENTER_REGEX.matchEntire(line) + return FIELD_CENTER_REGEX.find(line) ?.let { doubleArrayOf(it.groupValues[1].toDouble().deg, it.groupValues[2].toDouble().deg) } } @JvmStatic private fun fieldSize(line: String): DoubleArray? { - return FIELD_SIZE_REGEX.matchEntire(line) + return FIELD_SIZE_REGEX.find(line) ?.let { doubleArrayOf(it.groupValues[1].toDouble().arcmin, it.groupValues[2].toDouble().arcmin) } } @JvmStatic private fun fieldRotation(line: String): Angle? { - return FIELD_ROTATION_REGEX.matchEntire(line) + return FIELD_ROTATION_REGEX.find(line) ?.let { it.groupValues[1].toDouble().deg } } @JvmStatic private fun pixelScale(line: String): Angle? { - return PIXEL_SCALE_REGEX.matchEntire(line) + return PIXEL_SCALE_REGEX.find(line) ?.let { it.groupValues[1].toDouble().arcsec } } } diff --git a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt index 0719e5d0b..ace5f2ad4 100644 --- a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt +++ b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt @@ -1,6 +1,7 @@ package nebulosa.astrometrynet.platesolver import nebulosa.astrometrynet.nova.NovaAstrometryNetService +import nebulosa.astrometrynet.nova.NovaAstrometryNetService.Companion.ANONYMOUS_API_KEY import nebulosa.astrometrynet.nova.Session import nebulosa.astrometrynet.nova.Upload import nebulosa.common.concurrency.cancel.CancellationToken @@ -103,8 +104,6 @@ data class NovaAstrometryNetPlateSolver( companion object { - const val ANONYMOUS_API_KEY = "XXXXXXXX" - private const val SESSION_EXPIRATION_TIME = 1000L * 60 * 15 @JvmStatic private val LOG = loggerFor() diff --git a/nebulosa-astrometrynet/src/test/kotlin/AstrometryNetServiceTest.kt b/nebulosa-astrometrynet/src/test/kotlin/AstrometryNetServiceTest.kt index e92259020..0d6496a27 100644 --- a/nebulosa-astrometrynet/src/test/kotlin/AstrometryNetServiceTest.kt +++ b/nebulosa-astrometrynet/src/test/kotlin/AstrometryNetServiceTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly @@ -8,73 +7,90 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldNotBeBlank import nebulosa.astrometrynet.nova.NovaAstrometryNetService +import nebulosa.astrometrynet.nova.NovaAstrometryNetService.Companion.ANONYMOUS_API_KEY import nebulosa.astrometrynet.nova.Parity import nebulosa.astrometrynet.nova.Upload -import java.nio.file.Path +import nebulosa.test.concat +import nebulosa.test.dataDirectory +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -class AstrometryNetServiceTest : StringSpec() { +class AstrometryNetServiceTest { - init { - val file = Path.of("../data/ldn673s_block1123.jpg") + @Test + fun login() { + val session = SERVICE.login(ANONYMOUS_API_KEY).execute().body().shouldNotBeNull() + session.status shouldBe "success" + session.session.shouldNotBeBlank() + } + + @Test + @Disabled + fun uploadFromUrl() { + val session = SERVICE.login(ANONYMOUS_API_KEY).execute().body()!!.session + + val upload = Upload( + session, "http://apod.nasa.gov/apod/image/1206/ldn673s_block1123.jpg", + scaleLower = 0.5, scaleUpper = 1.0, centerRA = 290.0, centerDEC = 11.0, radius = 2.0 + ) - val service = NovaAstrometryNetService() + val submission = SERVICE.uploadFromUrl(upload).execute().body()!! + submission.status shouldBe "success" + submission.subId shouldBeGreaterThan 0 + } - "login" { - val session = service.login("XXXXXXXX").execute().body().shouldNotBeNull() - session.status shouldBe "success" - session.session.shouldNotBeBlank() - } - "!upload from url" { - val session = service.login("XXXXXXXX").execute().body()!!.session + @Test + @Disabled + fun uploadFromFile() { + val session = SERVICE.login(ANONYMOUS_API_KEY).execute().body()!!.session + val upload = Upload(session, scaleLower = 0.5, scaleUpper = 1.0, centerRA = 290.0, centerDEC = 11.0, radius = 2.0) + val file = dataDirectory.concat("ldn673s_block1123.jpg") + val submission = SERVICE.uploadFromFile(file, upload).execute().body()!! + submission.status shouldBe "success" + submission.subId shouldBeGreaterThan 0 + } - val upload = Upload( - session, "http://apod.nasa.gov/apod/image/1206/ldn673s_block1123.jpg", - scaleLower = 0.5, scaleUpper = 1.0, centerRA = 290.0, centerDEC = 11.0, radius = 2.0 - ) + @Test + fun submissionStatus() { + val status = SERVICE.submissionStatus(7232358).execute().body()!! + status.jobs.size shouldBeExactly 1 + status.jobs[0] shouldBeExactly 7973139 + status.userImages.size shouldBeExactly 1 + status.userImages[0] shouldBeExactly 7402591 + status.jobCalibrations.size shouldBeExactly 1 + status.jobCalibrations[0].size shouldBeExactly 2 + status.jobCalibrations[0][0] shouldBeExactly 7973139 + status.jobCalibrations[0][1] shouldBeExactly 5950379 + status.user shouldBeExactly 1000 + status.started.shouldBeTrue() + status.solved.shouldBeTrue() + } - val submission = service.uploadFromUrl(upload).execute().body()!! - submission.status shouldBe "success" - submission.subId shouldBeGreaterThan 0 - } - "!upload from file" { - val session = service.login("XXXXXXXX").execute().body()!!.session + @Test + fun jobStatus() { + val status = SERVICE.jobStatus(7973139).execute().body()!! + status.status shouldBe "success" + } + + @Test + fun jobCalibration() { + val calibration = SERVICE.jobCalibration(7973139).execute().body()!! + calibration.parity shouldBe Parity.NEGATIVE + calibration.orientation shouldBeExactly 90.0397051079753 + calibration.pixScale shouldBeExactly 2.0675124414774606 + calibration.radius shouldBeExactly 0.36561535148882157 + calibration.ra shouldBeExactly 290.237669307 + calibration.dec shouldBeExactly 11.1397773954 + } + + @Test + fun wcs() { + val text = SERVICE.wcs(7973139).execute().body()!! + text.decodeToString() shouldContain "SIMPLE" + } - val upload = Upload(session, scaleLower = 0.5, scaleUpper = 1.0, centerRA = 290.0, centerDEC = 11.0, radius = 2.0) + companion object { - val submission = service.uploadFromFile(file, upload).execute().body()!! - submission.status shouldBe "success" - submission.subId shouldBeGreaterThan 0 - } - "submission status" { - val status = service.submissionStatus(7232358).execute().body()!! - status.jobs.size shouldBeExactly 1 - status.jobs[0] shouldBeExactly 7973139 - status.userImages.size shouldBeExactly 1 - status.userImages[0] shouldBeExactly 7402591 - status.jobCalibrations.size shouldBeExactly 1 - status.jobCalibrations[0].size shouldBeExactly 2 - status.jobCalibrations[0][0] shouldBeExactly 7973139 - status.jobCalibrations[0][1] shouldBeExactly 5950379 - status.user shouldBeExactly 1000 - status.started.shouldBeTrue() - status.solved.shouldBeTrue() - } - "job status" { - val status = service.jobStatus(7973139).execute().body()!! - status.status shouldBe "success" - } - "job calibration" { - val calibration = service.jobCalibration(7973139).execute().body()!! - calibration.parity shouldBe Parity.NEGATIVE - calibration.orientation shouldBeExactly 90.0397051079753 - calibration.pixScale shouldBeExactly 2.0675124414774606 - calibration.radius shouldBeExactly 0.36561535148882157 - calibration.ra shouldBeExactly 290.237669307 - calibration.dec shouldBeExactly 11.1397773954 - } - "wcs" { - val text = service.wcs(7973139).execute().body()!! - text.decodeToString() shouldContain "SIMPLE" - } + @JvmStatic private val SERVICE = NovaAstrometryNetService() } } diff --git a/nebulosa-astrometrynet/src/test/kotlin/NovaAstrometryNetPlateSolverTest.kt b/nebulosa-astrometrynet/src/test/kotlin/NovaAstrometryNetPlateSolverTest.kt index 115d16d36..dbd13b6cf 100644 --- a/nebulosa-astrometrynet/src/test/kotlin/NovaAstrometryNetPlateSolverTest.kt +++ b/nebulosa-astrometrynet/src/test/kotlin/NovaAstrometryNetPlateSolverTest.kt @@ -1,42 +1,43 @@ -import io.kotest.core.annotation.Ignored -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.shouldBeExactly import nebulosa.astrometrynet.nova.NovaAstrometryNetService import nebulosa.astrometrynet.platesolver.NovaAstrometryNetPlateSolver import nebulosa.math.deg import nebulosa.math.toArcsec import nebulosa.math.toDegrees -import java.nio.file.Path +import nebulosa.test.concat +import nebulosa.test.dataDirectory +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -@Ignored -class NovaAstrometryNetPlateSolverTest : StringSpec() { +@Disabled +class NovaAstrometryNetPlateSolverTest { - init { - val file = Path.of("../data/ldn673s_block1123.jpg") + @Test + fun solve() { + val calibration = SOLVER.solve(FILE, null, centerRA = 290.0.deg, centerDEC = 11.0.deg, radius = 2.0.deg) - "solve" { - val service = NovaAstrometryNetService() - val solver = NovaAstrometryNetPlateSolver(service) + calibration.orientation.toDegrees shouldBeExactly 90.0397051079753 + calibration.scale.toArcsec shouldBeExactly 2.0675124414774606 + calibration.radius.toDegrees shouldBeExactly 0.36561535148882157 + calibration.rightAscension.toDegrees shouldBeExactly 290.237669307 + calibration.declination.toDegrees shouldBeExactly 11.1397773954 + } - val calibration = solver.solve(file, null, centerRA = 290.0.deg, centerDEC = 11.0.deg, radius = 2.0.deg) + @Test + fun blindSolve() { + val calibration = SOLVER.solve(FILE, null) - calibration.orientation.toDegrees shouldBeExactly 90.0397051079753 - calibration.scale.toArcsec shouldBeExactly 2.0675124414774606 - calibration.radius.toDegrees shouldBeExactly 0.36561535148882157 - calibration.rightAscension.toDegrees shouldBeExactly 290.237669307 - calibration.declination.toDegrees shouldBeExactly 11.1397773954 - } - "blind solve" { - val service = NovaAstrometryNetService() - val solver = NovaAstrometryNetPlateSolver(service) + calibration.orientation.toDegrees shouldBeExactly 90.0397051079753 + calibration.scale.toArcsec shouldBeExactly 2.0675124414774606 + calibration.radius.toDegrees shouldBeExactly 0.36561535148882157 + calibration.rightAscension.toDegrees shouldBeExactly 290.237669307 + calibration.declination.toDegrees shouldBeExactly 11.1397773954 + } - val calibration = solver.solve(file, null) + companion object { - calibration.orientation.toDegrees shouldBeExactly 90.0397051079753 - calibration.scale.toArcsec shouldBeExactly 2.0675124414774606 - calibration.radius.toDegrees shouldBeExactly 0.36561535148882157 - calibration.rightAscension.toDegrees shouldBeExactly 290.237669307 - calibration.declination.toDegrees shouldBeExactly 11.1397773954 - } + @JvmStatic private val FILE = dataDirectory.concat("ldn673s_block1123.jpg") + @JvmStatic private val SERVICE = NovaAstrometryNetService() + @JvmStatic private val SOLVER = NovaAstrometryNetPlateSolver(SERVICE) } } diff --git a/nebulosa-common/build.gradle.kts b/nebulosa-common/build.gradle.kts index 0c9e3b2a9..24ca9524d 100644 --- a/nebulosa-common/build.gradle.kts +++ b/nebulosa-common/build.gradle.kts @@ -4,7 +4,6 @@ plugins { } dependencies { - compileOnly(libs.bundles.jackson) implementation(project(":nebulosa-log")) testImplementation(project(":nebulosa-test")) } diff --git a/nebulosa-common/src/main/kotlin/nebulosa/common/concurrency/atomic/Incrementer.kt b/nebulosa-common/src/main/kotlin/nebulosa/common/concurrency/atomic/Incrementer.kt index 981bd1d65..b79f9cb25 100644 --- a/nebulosa-common/src/main/kotlin/nebulosa/common/concurrency/atomic/Incrementer.kt +++ b/nebulosa-common/src/main/kotlin/nebulosa/common/concurrency/atomic/Incrementer.kt @@ -2,16 +2,19 @@ package nebulosa.common.concurrency.atomic import nebulosa.common.Resettable import java.util.concurrent.atomic.AtomicLong +import java.util.function.Supplier -class Incrementer(initialValue: Long = 0L) : Number(), Resettable { +class Incrementer(private val initialValue: Long = 0L) : Number(), Supplier, Resettable { private val incrementer = AtomicLong(initialValue) fun increment() = incrementer.incrementAndGet() - override fun reset() = incrementer.set(0) + override fun reset() = reset(initialValue) - fun get() = incrementer.get() + fun reset(value: Long) = incrementer.set(value) + + override fun get() = incrementer.get() override fun toByte() = toLong().toByte() @@ -24,4 +27,6 @@ class Incrementer(initialValue: Long = 0L) : Number(), Resettable { override fun toLong() = get() override fun toShort() = toLong().toShort() + + override fun toString() = "Incrementer(value=${get()})" } diff --git a/nebulosa-common/src/main/kotlin/nebulosa/common/concurrency/cancel/CancellationToken.kt b/nebulosa-common/src/main/kotlin/nebulosa/common/concurrency/cancel/CancellationToken.kt index 3ee538fb8..d371b9ea0 100644 --- a/nebulosa-common/src/main/kotlin/nebulosa/common/concurrency/cancel/CancellationToken.kt +++ b/nebulosa-common/src/main/kotlin/nebulosa/common/concurrency/cancel/CancellationToken.kt @@ -2,6 +2,7 @@ package nebulosa.common.concurrency.cancel import nebulosa.common.concurrency.latch.Pauser import java.io.Closeable +import java.util.concurrent.CancellationException import java.util.concurrent.CompletableFuture import java.util.concurrent.Future import java.util.concurrent.TimeUnit @@ -43,6 +44,11 @@ class CancellationToken private constructor(private val completable: Completable listeners.remove(listener) } + @Synchronized + fun unlistenAll() { + listeners.clear() + } + fun cancel() { cancel(true) } @@ -74,12 +80,18 @@ class CancellationToken private constructor(private val completable: Completable return completable?.get(timeout, unit) ?: CancellationSource.None } + fun throwIfCancelled() { + if (isCancelled) throw CancellationException() + } + override fun close() { super.close() if (!isDone) { completable?.complete(CancellationSource.Close) } + + unlistenAll() } companion object { diff --git a/nebulosa-common/src/main/kotlin/nebulosa/common/exec/CommandLine.kt b/nebulosa-common/src/main/kotlin/nebulosa/common/exec/CommandLine.kt index 73a923280..2f34a5c0f 100644 --- a/nebulosa-common/src/main/kotlin/nebulosa/common/exec/CommandLine.kt +++ b/nebulosa-common/src/main/kotlin/nebulosa/common/exec/CommandLine.kt @@ -129,14 +129,17 @@ data class CommandLine internal constructor( override fun run() { try { - if (timeout > 0L) { + val exited = if (timeout > 0L) { process.waitFor(timeout, TimeUnit.MILLISECONDS) } else { process.waitFor() + true } - inputReader?.waitFor() - errorReader?.waitFor() + if (exited) { + inputReader?.waitFor() + errorReader?.waitFor() + } } catch (ignored: InterruptedException) { } finally { if (process.isAlive) { diff --git a/nebulosa-common/src/test/kotlin/CancellationTokenTest.kt b/nebulosa-common/src/test/kotlin/CancellationTokenTest.kt index 84688e538..c5faaad61 100644 --- a/nebulosa-common/src/test/kotlin/CancellationTokenTest.kt +++ b/nebulosa-common/src/test/kotlin/CancellationTokenTest.kt @@ -1,63 +1,70 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.shouldBe import nebulosa.common.concurrency.cancel.CancellationSource import nebulosa.common.concurrency.cancel.CancellationToken +import org.junit.jupiter.api.Test -class CancellationTokenTest : StringSpec() { +class CancellationTokenTest { - init { - "cancel" { - var source: CancellationSource? = null - val token = CancellationToken() - token.listen { source = it } - token.cancel(false) - token.get() shouldBe source - source shouldBe CancellationSource.Cancel(false) - token.isCancelled.shouldBeTrue() - token.isDone.shouldBeTrue() - } - "cancel may interrupt if running" { - var source: CancellationSource? = null - val token = CancellationToken() - token.listen { source = it } - token.cancel() - token.get() shouldBe source - source shouldBe CancellationSource.Cancel(true) - token.isCancelled.shouldBeTrue() - token.isDone.shouldBeTrue() - } - "close" { - var source: CancellationSource? = null - val token = CancellationToken() - token.listen { source = it } - token.close() - token.get() shouldBe source - source shouldBe CancellationSource.Close - token.isCancelled.shouldBeTrue() - token.isDone.shouldBeTrue() - } - "listen after cancel" { - var source: CancellationSource? = null - val token = CancellationToken() - token.cancel() - token.listen { source = it } - token.get() shouldBe CancellationSource.Cancel(true) - source shouldBe CancellationSource.Listen - token.isCancelled.shouldBeTrue() - token.isDone.shouldBeTrue() - } - "none" { - var source: CancellationSource? = null - val token = CancellationToken.NONE - token.listen { source = it } - token.cancel() - token.get() shouldBe CancellationSource.None - source.shouldBeNull() - token.isCancelled.shouldBeFalse() - token.isDone.shouldBeTrue() - } + @Test + fun cancel() { + var source: CancellationSource? = null + val token = CancellationToken() + token.listen { source = it } + token.cancel(false) + token.get() shouldBe source + source shouldBe CancellationSource.Cancel(false) + token.isCancelled.shouldBeTrue() + token.isDone.shouldBeTrue() + } + + @Test + fun cancelMayInterruptIfRunning() { + var source: CancellationSource? = null + val token = CancellationToken() + token.listen { source = it } + token.cancel() + token.get() shouldBe source + source shouldBe CancellationSource.Cancel(true) + token.isCancelled.shouldBeTrue() + token.isDone.shouldBeTrue() + } + + @Test + fun close() { + var source: CancellationSource? = null + val token = CancellationToken() + token.listen { source = it } + token.close() + token.get() shouldBe source + source shouldBe CancellationSource.Close + token.isCancelled.shouldBeTrue() + token.isDone.shouldBeTrue() + } + + @Test + fun listenAfterCancel() { + var source: CancellationSource? = null + val token = CancellationToken() + token.cancel() + token.listen { source = it } + token.get() shouldBe CancellationSource.Cancel(true) + source shouldBe CancellationSource.Listen + token.isCancelled.shouldBeTrue() + token.isDone.shouldBeTrue() + } + + @Test + fun none() { + var source: CancellationSource? = null + val token = CancellationToken.NONE + token.listen { source = it } + token.cancel() + token.get() shouldBe CancellationSource.None + source.shouldBeNull() + token.isCancelled.shouldBeFalse() + token.isDone.shouldBeTrue() } } diff --git a/nebulosa-common/src/test/kotlin/CommandLineTest.kt b/nebulosa-common/src/test/kotlin/CommandLineTest.kt index 89cea34bf..fdfcf58f3 100644 --- a/nebulosa-common/src/test/kotlin/CommandLineTest.kt +++ b/nebulosa-common/src/test/kotlin/CommandLineTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldNotBeEmpty import io.kotest.matchers.ints.shouldBeExactly @@ -8,65 +6,71 @@ import io.kotest.matchers.longs.shouldBeGreaterThanOrEqual import io.kotest.matchers.longs.shouldBeLessThan import nebulosa.common.exec.CommandLineListener import nebulosa.common.exec.commandLine -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.LinuxOnly +import org.junit.jupiter.api.Test import java.nio.file.Path import java.time.Duration import kotlin.concurrent.thread import kotlin.system.measureTimeMillis -@EnabledIf(NonGitHubOnlyCondition::class) -class CommandLineTest : StringSpec() { +@LinuxOnly +class CommandLineTest { - init { - "sleep" { - val cmd = commandLine { - executable("sleep") - putArg("2") - } - - measureTimeMillis { - cmd.start().get() shouldBeExactly 0 - } shouldBeGreaterThanOrEqual 2000 + @Test + fun sleep() { + val cmd = commandLine { + executable("sleep") + putArg("2") } - "sleep with timeout" { - val cmd = commandLine { - executable("sleep") - putArg("10") - } - measureTimeMillis { - cmd.start(Duration.ofSeconds(2)).get() shouldNotBeExactly 0 - } shouldBeGreaterThanOrEqual 2000 + measureTimeMillis { + cmd.start().get() shouldBeExactly 0 + } shouldBeGreaterThanOrEqual 2000 + } + + @Test + fun sleepWithTimeout() { + val cmd = commandLine { + executable("sleep") + putArg("10") } - "kill sleep" { - val cmd = commandLine { - executable("sleep") - putArg("10") - } - thread { Thread.sleep(2000); cmd.stop() } + measureTimeMillis { + cmd.start(Duration.ofSeconds(2)).get() shouldNotBeExactly 0 + } shouldBeGreaterThanOrEqual 2000 shouldBeLessThan 10000 + } - measureTimeMillis { - cmd.start().get() shouldNotBeExactly 0 - } shouldBeGreaterThanOrEqual 2000 shouldBeLessThan 10000 + @Test + fun killSleep() { + val cmd = commandLine { + executable("sleep") + putArg("10") } - "ls" { - val lineReadListener = object : CommandLineListener.OnLineRead, ArrayList(64) { - override fun onLineRead(line: String) { - add(line) - } - } + thread { Thread.sleep(2000); cmd.stop() } + + measureTimeMillis { + cmd.start().get() shouldNotBeExactly 0 + } shouldBeGreaterThanOrEqual 2000 shouldBeLessThan 10000 + } + + @Test + fun ls() { + val lineReadListener = object : CommandLineListener.OnLineRead, ArrayList(64) { - val cmd = commandLine { - executable("ls") - workingDirectory(Path.of("../")) - registerCommandLineListener(lineReadListener) + override fun onLineRead(line: String) { + add(line) } + } - cmd.start().get() shouldBeExactly 0 - lineReadListener.shouldNotBeEmpty() - lineReadListener.shouldContain("nebulosa-image") + val cmd = commandLine { + executable("ls") + workingDirectory(Path.of("../")) + registerCommandLineListener(lineReadListener) } + + cmd.start().get() shouldBeExactly 0 + lineReadListener.shouldNotBeEmpty() + lineReadListener.shouldContain("nebulosa-image") } } diff --git a/nebulosa-common/src/test/kotlin/CountUpDownLatchTest.kt b/nebulosa-common/src/test/kotlin/CountUpDownLatchTest.kt index 5c96bf082..ad393cbd6 100644 --- a/nebulosa-common/src/test/kotlin/CountUpDownLatchTest.kt +++ b/nebulosa-common/src/test/kotlin/CountUpDownLatchTest.kt @@ -1,103 +1,113 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.longs.shouldBeGreaterThanOrEqual -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import nebulosa.common.concurrency.latch.CountUpDownLatch +import org.junit.jupiter.api.Test import java.util.concurrent.TimeUnit +import kotlin.concurrent.thread import kotlin.system.measureTimeMillis -@Suppress("OPT_IN_USAGE") -class CountUpDownLatchTest : StringSpec() { - - init { - "count up to 1 and count down to 0" { - val latch = CountUpDownLatch() - - latch.countUp() shouldBeExactly 1 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown() shouldBeExactly 0 } - latch.await() - } shouldBeGreaterThanOrEqual 2000L - } - "count up to 2 and timeout on count down to 1" { - val latch = CountUpDownLatch() - latch.countUp(2) shouldBeExactly 2 - GlobalScope.launch { delay(2000L); latch.countDown() shouldBeExactly 1 } - latch.await(3L, TimeUnit.SECONDS).shouldBeFalse() - } - "count up to 2 and count down to 0" { - val latch = CountUpDownLatch() - - latch.countUp(2) shouldBeExactly 2 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown() shouldBeExactly 1 } - GlobalScope.launch { delay(4000L); latch.countDown() shouldBeExactly 0 } - latch.await() - } shouldBeGreaterThanOrEqual 4000L - } - "count up to 10 and reset to 0" { - val latch = CountUpDownLatch() - - latch.countUp(10) shouldBeExactly 10 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.reset() shouldBeExactly 0 } - latch.await() - } shouldBeGreaterThanOrEqual 2000L - } - "count up to 5 and count down to 3" { - val latch = CountUpDownLatch() - - latch.countUp(5) shouldBeExactly 5 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown(2) shouldBeExactly 3 } - latch.await(3) - } shouldBeGreaterThanOrEqual 2000L - } - "count up to 5 and reset to 3" { - val latch = CountUpDownLatch() - - latch.countUp(5) shouldBeExactly 5 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.reset(3) shouldBeExactly 3 } - latch.await(3) - } shouldBeGreaterThanOrEqual 2000L - } - "count up to 1 and count down to 0 repeatly" { - val latch = CountUpDownLatch() - - measureTimeMillis { - repeat(3) { - latch.countUp() shouldBeExactly 1 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown() shouldBeExactly 0 } - latch.await(3L, TimeUnit.SECONDS).shouldBeTrue() - } shouldBeGreaterThanOrEqual 2000L - } - } shouldBeGreaterThanOrEqual 6000L - } - "count up to n and count down to 0 repeatly" { - val latch = CountUpDownLatch() - - measureTimeMillis { - for (n in 1..10) { - latch.countUp(n) shouldBeExactly n - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown(n) shouldBeExactly 0 } - latch.await(3L, TimeUnit.SECONDS).shouldBeTrue() - } shouldBeGreaterThanOrEqual 2000L - } - } shouldBeGreaterThanOrEqual 20000L - } +class CountUpDownLatchTest { + + @Test + fun countUpTo1AndCountDownTo0() { + val latch = CountUpDownLatch() + + latch.countUp() shouldBeExactly 1 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown() shouldBeExactly 0 } + latch.await() + } shouldBeGreaterThanOrEqual 2000L + } + + @Test + fun countUpTo2AndTimeoutOnCountDownTo1() { + val latch = CountUpDownLatch() + latch.countUp(2) shouldBeExactly 2 + thread { Thread.sleep(2000L); latch.countDown() shouldBeExactly 1 } + latch.await(3L, TimeUnit.SECONDS).shouldBeFalse() + } + + @Test + fun countUpTo2AndCountDownTo0() { + val latch = CountUpDownLatch() + + latch.countUp(2) shouldBeExactly 2 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown() shouldBeExactly 1 } + thread { Thread.sleep(4000L); latch.countDown() shouldBeExactly 0 } + latch.await() + } shouldBeGreaterThanOrEqual 4000L + } + + @Test + fun countUpTo10AndResetTo0() { + val latch = CountUpDownLatch() + + latch.countUp(10) shouldBeExactly 10 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.reset() shouldBeExactly 0 } + latch.await() + } shouldBeGreaterThanOrEqual 2000L + } + + @Test + fun countUpTo5AndCountDownTo3() { + val latch = CountUpDownLatch() + + latch.countUp(5) shouldBeExactly 5 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown(2) shouldBeExactly 3 } + latch.await(3) + } shouldBeGreaterThanOrEqual 2000L + } + + @Test + fun countUpTo5AndResetTo3() { + val latch = CountUpDownLatch() + + latch.countUp(5) shouldBeExactly 5 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.reset(3) shouldBeExactly 3 } + latch.await(3) + } shouldBeGreaterThanOrEqual 2000L + } + + @Test + fun countUpTo1AndCountDownTo0Repeatly() { + val latch = CountUpDownLatch() + + measureTimeMillis { + repeat(3) { + latch.countUp() shouldBeExactly 1 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown() shouldBeExactly 0 } + latch.await(3L, TimeUnit.SECONDS).shouldBeTrue() + } shouldBeGreaterThanOrEqual 2000L + } + } shouldBeGreaterThanOrEqual 6000L + } + + @Test + fun countUpToNAndCountDownTo0Repeatly() { + val latch = CountUpDownLatch() + + measureTimeMillis { + for (n in 1..10) { + latch.countUp(n) shouldBeExactly n + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown(n) shouldBeExactly 0 } + latch.await(3L, TimeUnit.SECONDS).shouldBeTrue() + } shouldBeGreaterThanOrEqual 2000L + } + } shouldBeGreaterThanOrEqual 20000L } } diff --git a/nebulosa-common/src/test/kotlin/PauserTest.kt b/nebulosa-common/src/test/kotlin/PauserTest.kt index 45724801f..d4161dba6 100644 --- a/nebulosa-common/src/test/kotlin/PauserTest.kt +++ b/nebulosa-common/src/test/kotlin/PauserTest.kt @@ -1,30 +1,31 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import nebulosa.common.concurrency.latch.Pauser +import org.junit.jupiter.api.Test import java.util.concurrent.TimeUnit import kotlin.concurrent.thread -class PauserTest : StringSpec() { +class PauserTest { - init { - "pause and wait for unpause" { - val pauser = Pauser() - pauser.isPaused.shouldBeFalse() - pauser.pause() - pauser.isPaused.shouldBeTrue() - thread { Thread.sleep(1000); pauser.unpause() } - pauser.waitForPause() - pauser.isPaused.shouldBeFalse() - } - "pause and not wait for unpause" { - val pauser = Pauser() - pauser.isPaused.shouldBeFalse() - pauser.pause() - pauser.isPaused.shouldBeTrue() - thread { Thread.sleep(1000); pauser.unpause() } - pauser.waitForPause(500, TimeUnit.MILLISECONDS).shouldBeFalse() - pauser.isPaused.shouldBeTrue() - } + @Test + fun pauseAndWaitForUnpause() { + val pauser = Pauser() + pauser.isPaused.shouldBeFalse() + pauser.pause() + pauser.isPaused.shouldBeTrue() + thread { Thread.sleep(1000); pauser.unpause() } + pauser.waitForPause() + pauser.isPaused.shouldBeFalse() + } + + @Test + fun pauseAndNotWaitForUnpause() { + val pauser = Pauser() + pauser.isPaused.shouldBeFalse() + pauser.pause() + pauser.isPaused.shouldBeTrue() + thread { Thread.sleep(1000); pauser.unpause() } + pauser.waitForPause(500, TimeUnit.MILLISECONDS).shouldBeFalse() + pauser.isPaused.shouldBeTrue() } } diff --git a/nebulosa-curve-fitting/src/main/kotlin/nebulosa/curve/fitting/HyperbolicFitting.kt b/nebulosa-curve-fitting/src/main/kotlin/nebulosa/curve/fitting/HyperbolicFitting.kt index fe5f55bea..1c74df37f 100644 --- a/nebulosa-curve-fitting/src/main/kotlin/nebulosa/curve/fitting/HyperbolicFitting.kt +++ b/nebulosa-curve-fitting/src/main/kotlin/nebulosa/curve/fitting/HyperbolicFitting.kt @@ -1,7 +1,10 @@ package nebulosa.curve.fitting import nebulosa.math.squared -import kotlin.math.* +import kotlin.math.asinh +import kotlin.math.cosh +import kotlin.math.round +import kotlin.math.sqrt // https://bitbucket.org/Isbeorn/nina/src/master/NINA.Core.WPF/Utility/AutoFocus/HyperbolicFitting.cs diff --git a/nebulosa-curve-fitting/src/test/kotlin/AutoFocusTest.kt b/nebulosa-curve-fitting/src/test/kotlin/AutoFocusTest.kt index 003ab2617..7bf9e5558 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/AutoFocusTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/AutoFocusTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe @@ -7,91 +6,118 @@ import nebulosa.curve.fitting.CurvePoint.Companion.midPoint import nebulosa.curve.fitting.HyperbolicFitting import nebulosa.curve.fitting.QuadraticFitting import nebulosa.curve.fitting.TrendLineFitting +import org.junit.jupiter.api.Test import kotlin.math.roundToInt -class AutoFocusTest : StringSpec() { - - init { - // ASCOM Sky Simulator: The best focus is 8000. - - "ascom:hyperbolic" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_ASCOM) - curve.minimum.x.roundToInt() shouldBeExactly 8031 - curve.rSquared shouldBe (0.89 plusOrMinus 1e-2) - } - "ascom:parabolic" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_ASCOM) - curve.minimum.x.roundToInt() shouldBeExactly 8051 - curve.rSquared shouldBe (0.74 plusOrMinus 1e-2) - } - "ascom:trendline" { - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) - line.intersection.x.roundToInt() shouldBeExactly 7873 - line.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "ascom:hyperbolic + trendline" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_ASCOM) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 7952 - } - "ascom:parabolic + trendline" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_ASCOM) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 7962 - } - - // INDI CCD Simulator: The best focus is 36700. - - "indi:hyperbolic" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - curve.minimum.x.roundToInt() shouldBeExactly 36500 - curve.rSquared shouldBe (0.84 plusOrMinus 1e-2) - } - "indi:parabolic" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - curve.minimum.x.roundToInt() shouldBeExactly 36829 - curve.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "indi:trendline" { - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - line.intersection.x.roundToInt() shouldBeExactly 39445 - line.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "indi:hyperbolic + trendline" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 37973 - } - "indi:parabolic + trendline" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 38137 - } - "indi:hyperbolic:without weights" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI) - curve.minimum.x.roundToInt() shouldBeExactly 36000 - curve.rSquared shouldBe (0.95 plusOrMinus 1e-2) - } - "indi:parabolic:without weights" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI) - curve.minimum.x.roundToInt() shouldBeExactly 36815 - curve.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "indi:trendline:without weights" { - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) - line.intersection.x.roundToInt() shouldBeExactly 39653 - line.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "indi:hyperbolic + trendline:without weights" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 37826 - } - "indi:parabolic + trendline:without weights" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 38234 - } +class AutoFocusTest { + + // ASCOM Sky Simulator: The best focus is 8000. + + @Test + fun ascomHyperbolic() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_ASCOM) + curve.minimum.x.roundToInt() shouldBeExactly 8031 + curve.rSquared shouldBe (0.89 plusOrMinus 1e-2) + } + + @Test + fun ascomParabolic() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_ASCOM) + curve.minimum.x.roundToInt() shouldBeExactly 8051 + curve.rSquared shouldBe (0.74 plusOrMinus 1e-2) + } + + @Test + fun ascomTrendline() { + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) + line.intersection.x.roundToInt() shouldBeExactly 7873 + line.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun ascomHyperbolicAndTrendline() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_ASCOM) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 7952 + } + + @Test + fun ascomParabolicAndTrendline() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_ASCOM) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 7962 + } + + // INDI CCD Simulator: The best focus is 36700. + + @Test + fun indiHyperbolic() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + curve.minimum.x.roundToInt() shouldBeExactly 36500 + curve.rSquared shouldBe (0.84 plusOrMinus 1e-2) + } + + @Test + fun indiParabolic() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + curve.minimum.x.roundToInt() shouldBeExactly 36829 + curve.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun indiTrendline() { + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + line.intersection.x.roundToInt() shouldBeExactly 39445 + line.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun indiHyperbolicAndTrendline() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 37973 + } + + @Test + fun indiParabolicAndTrendline() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 38137 + } + + @Test + fun indiHyperbolicWithoutWeights() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI) + curve.minimum.x.roundToInt() shouldBeExactly 36000 + curve.rSquared shouldBe (0.95 plusOrMinus 1e-2) + } + + @Test + fun indiParabolicWithoutWeights() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI) + curve.minimum.x.roundToInt() shouldBeExactly 36815 + curve.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun indiTrendlineWithoutWeights() { + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) + line.intersection.x.roundToInt() shouldBeExactly 39653 + line.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun indiHyperbolicAndTrendlineWithoutWeights() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 37826 + } + + @Test + fun indiParabolicAndTrendlineWithoutWeights() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 38234 } companion object { diff --git a/nebulosa-curve-fitting/src/test/kotlin/HyperbolicFittingTest.kt b/nebulosa-curve-fitting/src/test/kotlin/HyperbolicFittingTest.kt index 22830f606..375a80b7a 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/HyperbolicFittingTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/HyperbolicFittingTest.kt @@ -1,31 +1,32 @@ import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.curve.fitting.CurveFitting import nebulosa.curve.fitting.HyperbolicFitting +import org.junit.jupiter.api.Test -class HyperbolicFittingTest : StringSpec(), CurveFitting by HyperbolicFitting { +class HyperbolicFittingTest : CurveFitting by HyperbolicFitting { - init { - "perfect V-curve with only one minimum point" { - val curve = calculate( - 1.0, 18.0, 2.0, 11.0, 3.0, 6.0, 4.0, 3.0, 5.0, 2.0, - 6.0, 3.0, 7.0, 6.0, 8.0, 11.0, 9.0, 18.0, - ) + @Test + fun perfectVCurveWithOnlyOneMinimumPoint() { + val curve = calculate( + 1.0, 18.0, 2.0, 11.0, 3.0, 6.0, 4.0, 3.0, 5.0, 2.0, + 6.0, 3.0, 7.0, 6.0, 8.0, 11.0, 9.0, 18.0, + ) - curve.minimum.x shouldBe (5.0 plusOrMinus 1e-12) - curve.minimum.y shouldBe (1.2 plusOrMinus 1e-12) - } - "bad data:prevent infinit loop" { - shouldThrow { calculate(1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } - .message shouldBe "Not enough valid data points to fit a curve." - shouldThrow { calculate(1000.0, 18.0, 1000.0, 18.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } - .message shouldBe "Not enough valid data points to fit a curve." - shouldThrow { calculate(900.0, 18.0, 1000.0, 18.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } - .message shouldBe "Not enough valid data points to fit a curve." - shouldThrow { calculate(800.0, 18.0, 900.0, 0.0, 1000.0, 0.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } - .message shouldBe "Not enough valid data points to fit a curve." - } + curve.minimum.x shouldBe (5.0 plusOrMinus 1e-12) + curve.minimum.y shouldBe (1.2 plusOrMinus 1e-12) + } + + @Test + fun badDataPreventInfinitLoop() { + shouldThrow { calculate(1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } + .message shouldBe "Not enough valid data points to fit a curve." + shouldThrow { calculate(1000.0, 18.0, 1000.0, 18.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } + .message shouldBe "Not enough valid data points to fit a curve." + shouldThrow { calculate(900.0, 18.0, 1000.0, 18.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } + .message shouldBe "Not enough valid data points to fit a curve." + shouldThrow { calculate(800.0, 18.0, 900.0, 0.0, 1000.0, 0.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } + .message shouldBe "Not enough valid data points to fit a curve." } } diff --git a/nebulosa-curve-fitting/src/test/kotlin/QuadraticFittingTest.kt b/nebulosa-curve-fitting/src/test/kotlin/QuadraticFittingTest.kt index 6839b07b6..63667076a 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/QuadraticFittingTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/QuadraticFittingTest.kt @@ -1,25 +1,24 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.curve.fitting.CurveFitting import nebulosa.curve.fitting.QuadraticFitting +import org.junit.jupiter.api.Test -class QuadraticFittingTest : StringSpec(), CurveFitting by QuadraticFitting { +class QuadraticFittingTest : CurveFitting by QuadraticFitting { - init { - "perfect V-curve" { - // (x-5)² + 2 - val curve = calculate( - 1.0, 18.0, 2.0, 11.0, 3.0, 6.0, - 4.0, 3.0, 5.0, 2.0, 6.0, 3.0, - 7.0, 6.0, 8.0, 11.0, 9.0, 18.0, - ) + @Test + fun perfectVCurve() { + // (x-5)² + 2 + val curve = calculate( + 1.0, 18.0, 2.0, 11.0, 3.0, 6.0, + 4.0, 3.0, 5.0, 2.0, 6.0, 3.0, + 7.0, 6.0, 8.0, 11.0, 9.0, 18.0, + ) - curve(5.0) shouldBeExactly 2.0 - curve.minimum.x shouldBe (5.0 plusOrMinus 1e-12) - curve.minimum.y shouldBe (2.0 plusOrMinus 1e-12) - curve.rSquared shouldBe (1.0 plusOrMinus 1e-12) - } + curve(5.0) shouldBeExactly 2.0 + curve.minimum.x shouldBe (5.0 plusOrMinus 1e-12) + curve.minimum.y shouldBe (2.0 plusOrMinus 1e-12) + curve.rSquared shouldBe (1.0 plusOrMinus 1e-12) } } diff --git a/nebulosa-curve-fitting/src/test/kotlin/TrendLineFittingTest.kt b/nebulosa-curve-fitting/src/test/kotlin/TrendLineFittingTest.kt index 57ed6bd11..3ee0b1672 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/TrendLineFittingTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/TrendLineFittingTest.kt @@ -1,31 +1,32 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.curve.fitting.CurveFitting import nebulosa.curve.fitting.TrendLineFitting +import org.junit.jupiter.api.Test -class TrendLineFittingTest : StringSpec(), CurveFitting by TrendLineFitting { +class TrendLineFittingTest : CurveFitting by TrendLineFitting { - init { - "perfect V-curve with only one minimum point" { - val curve = calculate( - 1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0, // left - 9.0, 10.0, 8.0, 8.0, 7.0, 6.0, 6.0, 4.0, // right - 5.0, 2.0, // tip - ) + @Test + fun perfectVCurveWithOnlyOneMinimumPoint() { + val curve = calculate( + 1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0, // left + 9.0, 10.0, 8.0, 8.0, 7.0, 6.0, 6.0, 4.0, // right + 5.0, 2.0, // tip + ) - curve.intersection.x shouldBe (5.0 plusOrMinus 1e-12) - curve.intersection.y shouldBe (2.0 plusOrMinus 1e-12) - } - "perfect V-curve with flat tip with multiple points" { - val curve = calculate( - 1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0, // left - 11.0, 10.0, 10.0, 8.0, 9.0, 6.0, 8.0, 4.0, // right - 5.0, 2.1, 6.0, 2.0, 7.0, 2.1, // tip - ) + curve.intersection.x shouldBe (5.0 plusOrMinus 1e-12) + curve.intersection.y shouldBe (2.0 plusOrMinus 1e-12) + } + + @Test + fun perfectVCurveWithFlatTipWithMultiplePoints() { + val curve = calculate( + 1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0, // left + 11.0, 10.0, 10.0, 8.0, 9.0, 6.0, 8.0, 4.0, // right + 5.0, 2.1, 6.0, 2.0, 7.0, 2.1, // tip + ) - curve.intersection.x shouldBe (6.0 plusOrMinus 1e-12) - curve.intersection.y shouldBe (0.0 plusOrMinus 1e-12) - } + curve.intersection.x shouldBe (6.0 plusOrMinus 1e-12) + curve.intersection.y shouldBe (0.0 plusOrMinus 1e-12) } } diff --git a/nebulosa-curve-fitting/src/test/kotlin/TrendLineTest.kt b/nebulosa-curve-fitting/src/test/kotlin/TrendLineTest.kt index 96f7a5e2d..540307650 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/TrendLineTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/TrendLineTest.kt @@ -1,105 +1,116 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.curve.fitting.CurvePoint import nebulosa.curve.fitting.TrendLine +import org.junit.jupiter.api.Test -class TrendLineTest : StringSpec() { - - init { - "no points" { - val line = TrendLine.ZERO - - line.slope shouldBeExactly 0.0 - line.intercept shouldBeExactly 0.0 - } - "one point" { - val line = TrendLine(5.0, 5.0) - - line.slope shouldBeExactly 0.0 - line.intercept shouldBeExactly 0.0 - } - "two points" { - val line = TrendLine(0.0, 0.0, 1.0, 1.0) - - line.slope shouldBe (1.0 plusOrMinus 1e-12) - line.intercept shouldBe (0.0 plusOrMinus 1e-12) - } - "multiple points" { - val line = TrendLine(1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0) - - line.slope shouldBe (-2.0 plusOrMinus 1e-12) - line.intercept shouldBe (12.0 plusOrMinus 1e-12) - } - "simple weighted regression" { - val line = TrendLine( - CurvePoint(1.0, 1.0, 1.0), - CurvePoint(2.0, 3.0, 1.0), - CurvePoint(3.0, 2.0, 1.0), - CurvePoint(4.0, 5.0, 2.0), - CurvePoint(5.0, 4.0, 2.0), - ) - - line.slope shouldBe (0.7812 plusOrMinus 1e-4) - line.intercept shouldBe (0.75 plusOrMinus 1e-2) - // line.rSquared shouldBeExactly 0.61 - } - "weighted regression with heteroscedastic data" { - // np.random.seed(0) - // X = np.linspace(1, 10, 100) - // Y = 2 * X + 1 + np.random.normal(0, X) - // W = 1 / X - - val x = doubleArrayOf( - 1.0, 1.47368421, 1.94736842, 2.42105263, 2.89473684, 3.36842105, - 3.84210526, 4.31578947, 4.78947368, 5.26315789, 5.73684211, 6.21052632, - 6.68421053, 7.15789474, 7.63157895, 8.10526316, 8.57894737, 9.05263158, - 9.52631579, 10.0 - ) - - val y = doubleArrayOf( - 4.76405235, 4.53707378, 6.80070028, 11.26742564, 12.1955626, 4.44495872, - 12.33455024, 8.9783531, 10.08458339, 13.68736054, 13.30003943, 22.45285652, - 19.45535743, 16.18672643, 19.6505352, 19.91504455, 30.97552047, 17.24804098, - 23.03501337, 12.45904261 - ) - - val weights = doubleArrayOf( - 1.0, 0.67857143, 0.51351351, 0.41304348, 0.34545455, 0.296875, - 0.26027397, 0.23170732, 0.20879121, 0.19, 0.17431193, 0.16101695, - 0.1496063, 0.13970588, 0.13103448, 0.12337662, 0.11656442, 0.11046512, - 0.10497238, 0.1 - ) - - val points = x.indices.map { CurvePoint(x[it], y[it], weights[it]) } - val line = TrendLine(points) - - line.slope shouldBe (2.0541 plusOrMinus 1e-4) - line.intercept shouldBe (2.8909 plusOrMinus 1e-4) - // line.rSquared shouldBeExactly 0.725 - } - "standard OLS regression" { - val x = doubleArrayOf( - 1.0, 1.47368421, 1.94736842, 2.42105263, 2.89473684, 3.36842105, - 3.84210526, 4.31578947, 4.78947368, 5.26315789, 5.73684211, 6.21052632, - 6.68421053, 7.15789474, 7.63157895, 8.10526316, 8.57894737, 9.05263158, - 9.52631579, 10.0 - ) - - val y = doubleArrayOf( - 4.76405235, 4.53707378, 6.80070028, 11.26742564, 12.1955626, 4.44495872, - 12.33455024, 8.9783531, 10.08458339, 13.68736054, 13.30003943, 22.45285652, - 19.45535743, 16.18672643, 19.6505352, 19.91504455, 30.97552047, 17.24804098, - 23.03501337, 12.45904261 - ) - - val points = x.indices.map { CurvePoint(x[it], y[it]) } - val line = TrendLine(points) - - line.slope shouldBe (1.9229 plusOrMinus 1e-4) - line.intercept shouldBe (3.6128 plusOrMinus 1e-4) - // line.rSquared shouldBeExactly 0.595 - } +class TrendLineTest { + + @Test + fun noPoints() { + val line = TrendLine.ZERO + + line.slope shouldBeExactly 0.0 + line.intercept shouldBeExactly 0.0 + } + + @Test + fun onePoint() { + val line = TrendLine(5.0, 5.0) + + line.slope shouldBeExactly 0.0 + line.intercept shouldBeExactly 0.0 + } + + @Test + fun twoPoints() { + val line = TrendLine(0.0, 0.0, 1.0, 1.0) + + line.slope shouldBe (1.0 plusOrMinus 1e-12) + line.intercept shouldBe (0.0 plusOrMinus 1e-12) + } + + @Test + fun multiplePoints() { + val line = TrendLine(1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0) + + line.slope shouldBe (-2.0 plusOrMinus 1e-12) + line.intercept shouldBe (12.0 plusOrMinus 1e-12) + } + + @Test + fun simpleWeightedRegression() { + val line = TrendLine( + CurvePoint(1.0, 1.0, 1.0), + CurvePoint(2.0, 3.0, 1.0), + CurvePoint(3.0, 2.0, 1.0), + CurvePoint(4.0, 5.0, 2.0), + CurvePoint(5.0, 4.0, 2.0), + ) + + line.slope shouldBe (0.7812 plusOrMinus 1e-4) + line.intercept shouldBe (0.75 plusOrMinus 1e-2) + // line.rSquared shouldBeExactly 0.61 + } + + @Test + fun weightedRegressionWithHeteroscedasticData() { + // np.random.seed(0) + // X = np.linspace(1, 10, 100) + // Y = 2 * X + 1 + np.random.normal(0, X) + // W = 1 / X + + val x = doubleArrayOf( + 1.0, 1.47368421, 1.94736842, 2.42105263, 2.89473684, 3.36842105, + 3.84210526, 4.31578947, 4.78947368, 5.26315789, 5.73684211, 6.21052632, + 6.68421053, 7.15789474, 7.63157895, 8.10526316, 8.57894737, 9.05263158, + 9.52631579, 10.0 + ) + + val y = doubleArrayOf( + 4.76405235, 4.53707378, 6.80070028, 11.26742564, 12.1955626, 4.44495872, + 12.33455024, 8.9783531, 10.08458339, 13.68736054, 13.30003943, 22.45285652, + 19.45535743, 16.18672643, 19.6505352, 19.91504455, 30.97552047, 17.24804098, + 23.03501337, 12.45904261 + ) + + val weights = doubleArrayOf( + 1.0, 0.67857143, 0.51351351, 0.41304348, 0.34545455, 0.296875, + 0.26027397, 0.23170732, 0.20879121, 0.19, 0.17431193, 0.16101695, + 0.1496063, 0.13970588, 0.13103448, 0.12337662, 0.11656442, 0.11046512, + 0.10497238, 0.1 + ) + + val points = x.indices.map { CurvePoint(x[it], y[it], weights[it]) } + val line = TrendLine(points) + + line.slope shouldBe (2.0541 plusOrMinus 1e-4) + line.intercept shouldBe (2.8909 plusOrMinus 1e-4) + // line.rSquared shouldBeExactly 0.725 + } + + @Test + fun standardOlsRegression() { + val x = doubleArrayOf( + 1.0, 1.47368421, 1.94736842, 2.42105263, 2.89473684, 3.36842105, + 3.84210526, 4.31578947, 4.78947368, 5.26315789, 5.73684211, 6.21052632, + 6.68421053, 7.15789474, 7.63157895, 8.10526316, 8.57894737, 9.05263158, + 9.52631579, 10.0 + ) + + val y = doubleArrayOf( + 4.76405235, 4.53707378, 6.80070028, 11.26742564, 12.1955626, 4.44495872, + 12.33455024, 8.9783531, 10.08458339, 13.68736054, 13.30003943, 22.45285652, + 19.45535743, 16.18672643, 19.6505352, 19.91504455, 30.97552047, 17.24804098, + 23.03501337, 12.45904261 + ) + + val points = x.indices.map { CurvePoint(x[it], y[it]) } + val line = TrendLine(points) + + line.slope shouldBe (1.9229 plusOrMinus 1e-4) + line.intercept shouldBe (3.6128 plusOrMinus 1e-4) + // line.rSquared shouldBeExactly 0.595 } } diff --git a/nebulosa-erfa/src/test/kotlin/ErfaTest.kt b/nebulosa-erfa/src/test/kotlin/ErfaTest.kt index 8dad7452c..a2201277a 100644 --- a/nebulosa-erfa/src/test/kotlin/ErfaTest.kt +++ b/nebulosa-erfa/src/test/kotlin/ErfaTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly @@ -8,1148 +7,1380 @@ import io.kotest.matchers.shouldBe import nebulosa.constants.TAU import nebulosa.erfa.* import nebulosa.math.* +import org.junit.jupiter.api.Test @Suppress("FloatingPointLiteralPrecision") -class ErfaTest : StringSpec() { - - init { - "eraRx" { - val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val s = r.rotateX(0.3456789.rad) - - s[0] shouldBe (2.0 plusOrMinus 1e-12) - s[1] shouldBe (3.0 plusOrMinus 1e-12) - s[2] shouldBe (2.0 plusOrMinus 1e-12) - s[3] shouldBe (3.839043388235612460 plusOrMinus 1e-12) - s[4] shouldBe (3.237033249594111899 plusOrMinus 1e-12) - s[5] shouldBe (4.516714379005982719 plusOrMinus 1e-12) - s[6] shouldBe (1.806030415924501684 plusOrMinus 1e-12) - s[7] shouldBe (3.085711545336372503 plusOrMinus 1e-12) - s[8] shouldBe (3.687721683977873065 plusOrMinus 1e-12) - - (s == Matrix3D.rotX(0.3456789.rad) * r).shouldBeTrue() - } - "eraRy" { - val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val s = r.rotateY(0.3456789.rad) - - s[0] shouldBe (0.8651847818978159930 plusOrMinus 1e-12) - s[1] shouldBe (1.467194920539316554 plusOrMinus 1e-12) - s[2] shouldBe (0.1875137911274457342 plusOrMinus 1e-12) - s[3] shouldBe (3.0 plusOrMinus 1e-12) - s[4] shouldBe (2.0 plusOrMinus 1e-12) - s[5] shouldBe (3.0 plusOrMinus 1e-12) - s[6] shouldBe (3.500207892850427330 plusOrMinus 1e-12) - s[7] shouldBe (4.779889022262298150 plusOrMinus 1e-12) - s[8] shouldBe (5.381899160903798712 plusOrMinus 1e-12) - - (s == Matrix3D.rotY(0.3456789.rad) * r).shouldBeTrue() - } - "eraRz" { - val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val s = r.rotateZ(0.3456789.rad) - - s[0] shouldBe (2.898197754208926769 plusOrMinus 1e-12) - s[1] shouldBe (3.500207892850427330 plusOrMinus 1e-12) - s[2] shouldBe (2.898197754208926769 plusOrMinus 1e-12) - s[3] shouldBe (2.144865911309686813 plusOrMinus 1e-12) - s[4] shouldBe (0.865184781897815993 plusOrMinus 1e-12) - s[5] shouldBe (2.144865911309686813 plusOrMinus 1e-12) - s[6] shouldBe (3.0 plusOrMinus 1e-12) - s[7] shouldBe (4.0 plusOrMinus 1e-12) - s[8] shouldBe (5.0 plusOrMinus 1e-12) - - (s == Matrix3D.rotZ(0.3456789.rad) * r).shouldBeTrue() - } - "eraRxr" { - val a = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val b = Matrix3D(1.0, 2.0, 2.0, 4.0, 1.0, 1.0, 3.0, 0.0, 1.0) - val c = a * b - - c[0] shouldBe (20.0 plusOrMinus 1e-12) - c[1] shouldBe (7.0 plusOrMinus 1e-12) - c[2] shouldBe (9.0 plusOrMinus 1e-12) - c[3] shouldBe (20.0 plusOrMinus 1e-12) - c[4] shouldBe (8.0 plusOrMinus 1e-12) - c[5] shouldBe (11.0 plusOrMinus 1e-12) - c[6] shouldBe (34.0 plusOrMinus 1e-12) - c[7] shouldBe (10.0 plusOrMinus 1e-12) - c[8] shouldBe (15.0 plusOrMinus 1e-12) - } - "eraC2s" { - val (theta, phi) = eraC2s(100.0.au, (-50.0).au, 25.0.au) - theta shouldBe (-0.4636476090008061162 plusOrMinus 1e-14) - phi shouldBe (0.2199879773954594463 plusOrMinus 1e-14) - } - "eraS2c" { - val c = eraS2c(3.0123, -0.999) - c[0] shouldBe (-0.5366267667260523906 plusOrMinus 1e-12) - c[1] shouldBe (0.0697711109765145365 plusOrMinus 1e-12) - c[2] shouldBe (-0.8409302618566214041 plusOrMinus 1e-12) - } - "eraP2s" { - val (theta, phi, r) = eraP2s(100.0.au, (-50.0).au, 25.0.au) - theta shouldBe (-0.4636476090008061162 plusOrMinus 1e-12) - phi shouldBe (0.2199879773954594463 plusOrMinus 1e-12) - r shouldBe (114.5643923738960002 plusOrMinus 1e-9) - } - "eraAnpm" { - eraAnpm((-4.0).rad) shouldBe (2.283185307179586477 plusOrMinus 1e-12) - } - "eraGc2Gde" { - val (e1, p1, h1) = eraGc2Gde(6378137.0, 1.0 / 298.257223563, 2e6, 3e6, 5.244e6) - e1 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) - p1 shouldBe (0.97160184819075459 plusOrMinus 1e-14) - h1 shouldBe (331.4172461426059892 plusOrMinus 1e-8) - - val (e2, p2, h2) = eraGc2Gde(6378137.0, 1.0 / 298.257222101, 2e6, 3e6, 5.244e6) - e2 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) - p2 shouldBe (0.97160184820607853 plusOrMinus 1e-14) - h2 shouldBe (331.41731754844348 plusOrMinus 1e-8) - - val (e3, p3, h3) = eraGc2Gde(6378135.0, 1.0 / 298.26, 2e6, 3e6, 5.244e6) - e3 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) - p3 shouldBe (0.9716018181101511937 plusOrMinus 1e-14) - h3 shouldBe (333.2770726130318123 plusOrMinus 1e-8) - } - "eraGd2Gc" { - val (x1, y1, z1) = eraGd2Gce(6378137.0, 1.0 / 298.257223563, 3.1.rad, (-0.5).rad, 2500.0) - x1 shouldBe (-5599000.5577049947 plusOrMinus 1e-7) - y1 shouldBe (233011.67223479203 plusOrMinus 1e-7) - z1 shouldBe (-3040909.4706983363 plusOrMinus 1e-7) - - val (x2, y2, z2) = eraGd2Gce(6378137.0, 1.0 / 298.257222101, 3.1.rad, (-0.5).rad, 2500.0) - x2 shouldBe (-5599000.5577260984 plusOrMinus 1e-7) - y2 shouldBe (233011.6722356702949 plusOrMinus 1e-7) - z2 shouldBe (-3040909.4706095476 plusOrMinus 1e-7) - - val (x3, y3, z3) = eraGd2Gce(6378135.0, 1.0 / 298.26, 3.1.rad, (-0.5).rad, 2500.0) - x3 shouldBe (-5598998.7626301490 plusOrMinus 1e-7) - y3 shouldBe (233011.5975297822211 plusOrMinus 1e-7) - z3 shouldBe (-3040908.6861467111 plusOrMinus 1e-7) - } - "eraC2ixys" { - val m = eraC2ixys(0.5791308486706011000e-3, 0.4020579816732961219e-4, (-0.1220040848472271978e-7).rad) - m[0, 0] shouldBe (0.9999998323037157138 plusOrMinus 1e-12) - m[0, 1] shouldBe (0.5581984869168499149e-9 plusOrMinus 1e-12) - m[0, 2] shouldBe (-0.5791308491611282180e-3 plusOrMinus 1e-12) - m[1, 0] shouldBe (-0.2384261642670440317e-7 plusOrMinus 1e-12) - m[1, 1] shouldBe (0.9999999991917468964 plusOrMinus 1e-12) - m[1, 2] shouldBe (-0.4020579110169668931e-4 plusOrMinus 1e-12) - m[2, 0] shouldBe (0.5791308486706011000e-3 plusOrMinus 1e-12) - m[2, 1] shouldBe (0.4020579816732961219e-4 plusOrMinus 1e-12) - m[2, 2] shouldBe (0.9999998314954627590 plusOrMinus 1e-12) - } - "eraPom00" { - val m = eraPom00(2.55060238e-7.rad, 1.860359247e-6.rad, (-0.1367174580728891460e-10).rad) - m[0, 0] shouldBe (0.9999999999999674721 plusOrMinus 1e-12) - m[0, 1] shouldBe (-0.1367174580728846989e-10 plusOrMinus 1e-12) - m[0, 2] shouldBe (0.2550602379999972345e-6 plusOrMinus 1e-12) - m[1, 0] shouldBe (0.1414624947957029801e-10 plusOrMinus 1e-12) - m[1, 1] shouldBe (0.9999999999982695317 plusOrMinus 1e-12) - m[1, 2] shouldBe (-0.1860359246998866389e-5 plusOrMinus 1e-12) - m[2, 0] shouldBe (-0.2550602379741215021e-6 plusOrMinus 1e-12) - m[2, 1] shouldBe (0.1860359247002414021e-5 plusOrMinus 1e-12) - m[2, 2] shouldBe (0.9999999999982370039 plusOrMinus 1e-12) - } - "eraApcs" { - val astro = eraApcs( - 2456384.5, 0.970031644, - Vector3D(-1836024.09, 1056607.7, -5998795.26), - Vector3D(-77.0361767, -133.310856, 0.0971855934), - Vector3D(-0.974170438, -0.211520082, -0.0917583024), - Vector3D(0.00364365824, -0.0154287319, -0.00668922024), - Vector3D(-0.973458265, -0.209215307, -0.0906996477), - ) - - astro.pmt shouldBe (13.25248468622587269 plusOrMinus 1e-11) - astro.eb.x shouldBe (-0.9741827110629881886 plusOrMinus 1e-12) - astro.eb.y shouldBe (-0.2115130190136415986 plusOrMinus 1e-12) - astro.eb.z shouldBe (-0.09179840186954412099 plusOrMinus 1e-12) - astro.eh.x shouldBe (-0.9736425571689454706 plusOrMinus 1e-12) - astro.eh.y shouldBe (-0.2092452125850435930 plusOrMinus 1e-12) - astro.eh.z shouldBe (-0.09075578152248299218 plusOrMinus 1e-12) - astro.em shouldBe (0.9998233241709796859 plusOrMinus 1e-12) - astro.v.x shouldBe (0.2078704993282685510e-4 plusOrMinus 1e-16) - astro.v.y shouldBe (-0.8955360106989405683e-4 plusOrMinus 1e-16) - astro.v.z shouldBe (-0.3863338994289409097e-4 plusOrMinus 1e-16) - astro.bm1 shouldBe (0.9999999950277561237 plusOrMinus 1e-12) - } - "eraApco" { - val astro = eraApco( - 2456384.5, 0.970031644, - Vector3D(-0.974170438, -0.211520082, -0.0917583024), - Vector3D(0.00364365824, -0.0154287319, -0.00668922024), - Vector3D(-0.973458265, -0.209215307, -0.0906996477), - 0.0013122272, -2.92808623e-5, 3.05749468e-8.rad, - 3.14540971.rad, (-0.527800806).rad, (-1.2345856).rad, - 2738.0, - 2.47230737e-7.rad, 1.82640464e-6.rad, (-3.01974337e-11).rad, - 0.000201418779.rad, (-2.36140831e-7).rad, - ) - - astro.pmt shouldBe (13.25248468622587269 plusOrMinus 1e-11) - astro.eb.x shouldBe (-0.9741827110630322720 plusOrMinus 1e-12) - astro.eb.y shouldBe (-0.2115130190135344832 plusOrMinus 1e-12) - astro.eb.z shouldBe (-0.09179840186949532298 plusOrMinus 1e-12) - astro.eh.x shouldBe (-0.9736425571689739035 plusOrMinus 1e-12) - astro.eh.y shouldBe (-0.2092452125849330936 plusOrMinus 1e-12) - astro.eh.z shouldBe (-0.09075578152243272599 plusOrMinus 1e-12) - astro.em shouldBe (0.9998233241709957653 plusOrMinus 1e-12) - astro.v.x shouldBe (0.2078704992916728762e-4 plusOrMinus 1e-16) - astro.v.y shouldBe (-0.8955360107151952319e-4 plusOrMinus 1e-16) - astro.v.z shouldBe (-0.3863338994288951082e-4 plusOrMinus 1e-16) - astro.bm1 shouldBe (0.9999999950277561236 plusOrMinus 1e-12) - astro.bpn[0, 0] shouldBe (0.9999991390295159156 plusOrMinus 1e-12) - astro.bpn[1, 0] shouldBe (0.4978650072505016932e-7 plusOrMinus 1e-12) - astro.bpn[2, 0] shouldBe (0.1312227200000000000e-2 plusOrMinus 1e-12) - astro.bpn[0, 1] shouldBe (-0.1136336653771609630e-7 plusOrMinus 1e-12) - astro.bpn[1, 1] shouldBe (0.9999999995713154868 plusOrMinus 1e-12) - astro.bpn[2, 1] shouldBe (-0.2928086230000000000e-4 plusOrMinus 1e-12) - astro.bpn[0, 2] shouldBe (-0.1312227200895260194e-2 plusOrMinus 1e-12) - astro.bpn[1, 2] shouldBe (0.2928082217872315680e-4 plusOrMinus 1e-12) - astro.bpn[2, 2] shouldBe (0.9999991386008323373 plusOrMinus 1e-12) - astro.along shouldBe (-0.5278008060295995734 plusOrMinus 1e-12) - astro.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) - astro.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) - astro.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) - astro.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) - astro.eral shouldBe (2.617608903970400427 plusOrMinus 1e-12) - astro.refa shouldBe (0.2014187790000000000e-3 plusOrMinus 1e-17) - astro.refb shouldBe (-0.2361408310000000000e-6 plusOrMinus 1e-18) - astro.diurab shouldBeExactly 0.0 - } - "eraSp00" { - eraSp00(2400000.5, 52541.0) shouldBe (-0.6216698469981019309e-11 plusOrMinus 1e-12) - } - "eraObl06" { - eraObl06(2400000.5, 54388.0) shouldBe (0.4090749229387258204 plusOrMinus 1e-14) - } - "eraPfw06" { - val (gamb, phib, psib, epsa) = eraPfw06(2400000.5, 50123.9999) - gamb shouldBe (-0.2243387670997995690e-5 plusOrMinus 1e-16) - phib shouldBe (0.4091014602391312808 plusOrMinus 1e-12) - psib shouldBe (-0.9501954178013031895e-3 plusOrMinus 1e-14) - epsa shouldBe (0.4091014316587367491 plusOrMinus 1e-12) - } - "eraFal03" { - eraFal03(0.80) shouldBe (5.132369751108684150 plusOrMinus 1e-12) - } - "eraFaf03" { - eraFaf03(0.80) shouldBe (0.2597711366745499518 plusOrMinus 1e-12) - } - "eraFaom03" { - eraFaom03(0.80) shouldBe (TAU - 5.973618440951302183 plusOrMinus 1e-12) - } - "eraFapa03" { - eraFapa03(0.80) shouldBe (0.1950884762240000000e-1 plusOrMinus 1e-12) - } - "eraFame03" { - eraFame03(0.80) shouldBe (5.417338184297289661 plusOrMinus 1e-12) - } - "eraFave03" { - eraFave03(0.80) shouldBe (3.424900460533758000 plusOrMinus 1e-12) - } - "eraFae03" { - eraFae03(0.80) shouldBe (1.744713738913081846 plusOrMinus 1e-12) - } - "eraFama03" { - eraFama03(0.80) shouldBe (3.275506840277781492 plusOrMinus 1e-12) - } - "eraFaju03" { - eraFaju03(0.80) shouldBe (5.275711665202481138 plusOrMinus 1e-12) - } - "eraFasa03" { - eraFasa03(0.80) shouldBe (5.371574539440827046 plusOrMinus 1e-12) - } - "eraFaur03" { - eraFaur03(0.80) shouldBe (5.180636450180413523 plusOrMinus 1e-12) - } - "eraFw2m" { - val m = eraFw2m((-0.2243387670997992368e-5).rad, 0.4091014602391312982.rad, (-0.9501954178013015092e-3).rad, 0.4091014316587367472.rad) - m[0, 0] shouldBe (0.9999995505176007047 plusOrMinus 1e-12) - m[0, 1] shouldBe (0.8695404617348192957e-3 plusOrMinus 1e-12) - m[0, 2] shouldBe (0.3779735201865582571e-3 plusOrMinus 1e-12) - m[1, 0] shouldBe (-0.8695404723772016038e-3 plusOrMinus 1e-12) - m[1, 1] shouldBe (0.9999996219496027161 plusOrMinus 1e-12) - m[1, 2] shouldBe (-0.1361752496887100026e-6 plusOrMinus 1e-12) - m[2, 0] shouldBe (-0.3779734957034082790e-3 plusOrMinus 1e-12) - m[2, 1] shouldBe (-0.1924880848087615651e-6 plusOrMinus 1e-12) - m[2, 2] shouldBe (0.9999999285679971958 plusOrMinus 1e-12) - } - "era00" { - era00(2400000.0 + 54388.0, 0.5) shouldBe (0.4022837240028158102 plusOrMinus 1e-12) - } - "eraRefco" { - val (refa, refb) = eraRefco(800.0, 10.0, 0.9, 0.4) - refa shouldBe (0.2264949956241415009e-3 plusOrMinus 1e-15) - refb shouldBe (-0.2598658261729343970e-6 plusOrMinus 1e-18) - } - "eraApcg" { - val astrom = eraApcg( - 2456165.5, 0.401182685, - Vector3D(0.901310875, -0.417402664, -0.180982288), - Vector3D(0.00742727954, 0.0140507459, 0.00609045792), - Vector3D(0.903358544, -0.415395237, -0.180084014), - ) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb.x shouldBe (0.901310875 plusOrMinus 1e-12) - astrom.eb.y shouldBe (-0.417402664 plusOrMinus 1e-12) - astrom.eb.z shouldBe (-0.180982288 plusOrMinus 1e-12) - astrom.eh.x shouldBe (0.8940025429324143045 plusOrMinus 1e-12) - astrom.eh.y shouldBe (-0.4110930268679817955 plusOrMinus 1e-12) - astrom.eh.z shouldBe (-0.1782189004872870264 plusOrMinus 1e-12) - astrom.em shouldBe (1.010465295811013146 plusOrMinus 1e-12) - astrom.v.x shouldBe (0.4289638913597693554e-4 plusOrMinus 1e-16) - astrom.v.y shouldBe (0.8115034051581320575e-4 plusOrMinus 1e-16) - astrom.v.z shouldBe (0.3517555136380563427e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999951686012981 plusOrMinus 1e-12) - } - "eraEpv00" { - val (h, b) = eraEpv00(2400000.5, 53411.52501161) - h.position[0] shouldBe (-0.7757238809297706813 plusOrMinus 1e-14) - h.position[1] shouldBe (0.5598052241363340596 plusOrMinus 1e-14) - h.position[2] shouldBe (0.2426998466481686993 plusOrMinus 1e-14) - - h.velocity[0] shouldBe (-0.1091891824147313846e-1 plusOrMinus 1e-15) - h.velocity[1] shouldBe (-0.1247187268440845008e-1 plusOrMinus 1e-15) - h.velocity[2] shouldBe (-0.5407569418065039061e-2 plusOrMinus 1e-15) - - b.position[0] shouldBe (-0.7714104440491111971 plusOrMinus 1e-14) - b.position[1] shouldBe (0.5598412061824171323 plusOrMinus 1e-14) - b.position[2] shouldBe (0.2425996277722452400 plusOrMinus 1e-14) - - b.velocity[0] shouldBe (-0.1091874268116823295e-1 plusOrMinus 1e-15) - b.velocity[1] shouldBe (-0.1246525461732861538e-1 plusOrMinus 1e-15) - b.velocity[2] shouldBe (-0.5404773180966231279e-2 plusOrMinus 1e-15) - } - "eraApcg13" { - val astrom = eraApcg13(2456165.5, 0.401182685) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb.x shouldBe (0.9013108747340644755 plusOrMinus 1e-12) - astrom.eb.y shouldBe (-0.4174026640406119957 plusOrMinus 1e-12) - astrom.eb.z shouldBe (-0.1809822877867817771 plusOrMinus 1e-12) - astrom.eh.x shouldBe (0.8940025429255499549 plusOrMinus 1e-12) - astrom.eh.y shouldBe (-0.4110930268331896318 plusOrMinus 1e-12) - astrom.eh.z shouldBe (-0.1782189006019749850 plusOrMinus 1e-12) - astrom.em shouldBe (1.010465295964664178 plusOrMinus 1e-12) - astrom.v.x shouldBe (0.4289638912941341125e-4 plusOrMinus 1e-16) - astrom.v.y shouldBe (0.8115034032405042132e-4 plusOrMinus 1e-16) - astrom.v.z shouldBe (0.3517555135536470279e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999951686013142 plusOrMinus 1e-12) - } - "eraAe2hd" { - val (ha, dec) = eraAe2hd(5.5.rad, 1.1.rad, 0.7.rad) +class ErfaTest { + + @Test + fun eraRx() { + val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val s = r.rotateX(0.3456789.rad) + + s[0] shouldBe (2.0 plusOrMinus 1e-12) + s[1] shouldBe (3.0 plusOrMinus 1e-12) + s[2] shouldBe (2.0 plusOrMinus 1e-12) + s[3] shouldBe (3.839043388235612460 plusOrMinus 1e-12) + s[4] shouldBe (3.237033249594111899 plusOrMinus 1e-12) + s[5] shouldBe (4.516714379005982719 plusOrMinus 1e-12) + s[6] shouldBe (1.806030415924501684 plusOrMinus 1e-12) + s[7] shouldBe (3.085711545336372503 plusOrMinus 1e-12) + s[8] shouldBe (3.687721683977873065 plusOrMinus 1e-12) + + (s == Matrix3D.rotX(0.3456789.rad) * r).shouldBeTrue() + } - ha shouldBe (0.5933291115507309663 plusOrMinus 1E-14) - dec shouldBe (0.9613934761647817620 plusOrMinus 1E-14) - } - "eraTcbTdb" { - val (whole, fraction) = eraTcbTdb(2453750.5, 0.893019599) - whole shouldBe (2453750.5 plusOrMinus 1E-6) - fraction shouldBe (0.8928551362746343397 plusOrMinus 1E-12) - } - "eraTcgTt" { - val (whole, fraction) = eraTcgTt(2453750.5, 0.892862531) - whole shouldBe (2453750.5 plusOrMinus 1E-6) - fraction shouldBe (0.8928551387488816828 plusOrMinus 1E-12) - } - "eraTdbTcb" { - val (whole, fraction) = eraTdbTcb(2453750.5, 0.892855137) - whole shouldBe (2453750.5 plusOrMinus 1E-6) - fraction shouldBe (0.8930195997253656716 plusOrMinus 1E-12) - } - "eraTtTcg" { - val (whole, fraction) = eraTtTcg(2453750.5, 0.892482639) - whole shouldBe (2453750.5 plusOrMinus 1E-6) - fraction shouldBe (0.8924900312508587113 plusOrMinus 1E-12) - } - "eraDtDb" { - val dt = eraDtDb(2448939.5, 0.123, 0.76543, 5.0123.rad, 5525.242.km, 3190.0.km) - dt shouldBe (-0.1280368005936998991e-2 plusOrMinus 1E-15) - } - "eraPvtob" { - val (p, v) = eraPvtob(2.0.rad, 0.5.rad, 3000.0, 1e-6.rad, (-0.5e-6).rad, 1e-8.rad, 5.0.rad) - p[0] shouldBe (4225081.367071159207 plusOrMinus 1e-5) - p[1] shouldBe (3681943.215856198144 plusOrMinus 1e-5) - p[2] shouldBe (3041149.399241260785 plusOrMinus 1e-5) - v[0] shouldBe (-268.4915389365998787 plusOrMinus 1e-5) - v[1] shouldBe (308.0977983288903123 plusOrMinus 1e-5) - v[2] shouldBe (0.0 plusOrMinus 1e-5) - } - "eraNut00a" { - val (psi, eps) = eraNut00a(2400000.5, 53736.0) - psi shouldBe (-0.9630909107115518431e-5 plusOrMinus 1e-13) - eps shouldBe (0.4063239174001678710e-4 plusOrMinus 1e-13) - } - "eraNut06a" { - val (psi, eps) = eraNut06a(2400000.5, 53736.0) - psi shouldBe (-0.9630912025820308797e-5 plusOrMinus 1e-13) - eps shouldBe (0.4063238496887249798e-4 plusOrMinus 1e-13) - } - "eraPnm06a" { - val rbpn = eraPnm06a(2400000.5, 50123.9999) - - rbpn[0, 0] shouldBe (0.9999995832794205484 plusOrMinus 1e-12) - rbpn[0, 1] shouldBe (0.8372382772630962111e-3 plusOrMinus 1e-14) - rbpn[0, 2] shouldBe (0.3639684771140623099e-3 plusOrMinus 1e-14) - rbpn[1, 0] shouldBe (-0.8372533744743683605e-3 plusOrMinus 1e-14) - rbpn[1, 1] shouldBe (0.9999996486492861646 plusOrMinus 1e-12) - rbpn[1, 2] shouldBe (0.4132905944611019498e-4 plusOrMinus 1e-14) - rbpn[2, 0] shouldBe (-0.3639337469629464969e-3 plusOrMinus 1e-14) - rbpn[2, 1] shouldBe (-0.4163377605910663999e-4 plusOrMinus 1e-14) - rbpn[2, 2] shouldBe (0.9999999329094260057 plusOrMinus 1e-12) - } - "eraAb" { - val pnat = Vector3D(-0.76321968546737951, -0.60869453983060384, -0.21676408580639883) - val v = Vector3D(2.1044018893653786e-5, -8.9108923304429319e-5, -3.8633714797716569e-5) - val ppr = eraAb(pnat, v, 0.99980921395708788.au, 0.99999999506209258) - - ppr[0] shouldBe (-0.7631631094219556269 plusOrMinus 1e-12) - ppr[1] shouldBe (-0.6087553082505590832 plusOrMinus 1e-12) - ppr[2] shouldBe (-0.2167926269368471279 plusOrMinus 1e-12) - } - "eraEect00" { - val eect = eraEect00(2400000.5, 53736.0) - eect shouldBe (0.2046085004885125264e-8 plusOrMinus 1e-20) - } - "eraEra00" { - val era00 = eraEra00(2454388.0, 0.5) - era00 shouldBe (0.4022837240028158102 plusOrMinus 1e-12) - } - "eraEe00" { - val ee = eraEe00(2453736.0, 0.5, 0.4090789763356509900.rad, (-0.9630909107115582393e-5).rad) - ee shouldBe (-0.8834193235367965479e-5 plusOrMinus 1e-18) - } - "eraGmst00" { - val theta = eraGmst00(2453736.0, 0.5, 2453736.0, 0.5) - theta shouldBe (1.754174972210740592 plusOrMinus 1e-12) - } - "eraGmst06" { - val theta = eraGmst06(2453736.0, 0.5, 2453736.0, 0.5) - theta shouldBe (1.754174971870091203 plusOrMinus 1e-12) - } - "eraEe06a" { - val ee = eraEe06a(2453736.0, 0.5) - ee shouldBe (-0.8834195072043790156e-5 plusOrMinus 1e-15) - } - "eraGst06a" { - val theta = eraGst06a(2453736.0, 0.5, 2453736.0, 0.5) - theta shouldBe (1.754166137675019159 plusOrMinus 1e-12) - } - "eraGst06" { - val rnpb = Matrix3D( - 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, - 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, - 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, - ) - - val theta = eraGst06(2453736.0, 0.5, 2453736.0, 0.5, rnpb) - theta shouldBe (1.754166138018167568 plusOrMinus 1e-12) - } - "eraS06" { - val s = eraS06(2400000.5, 53736.0, 0.5791308486706011000e-3, 0.4020579816732961219e-4) - s shouldBe (-0.1220032213076463117e-7 plusOrMinus 1e-18) - } - "eraS06a" { - val s = eraS06a(2400000.5, 52541.0) - s shouldBe (-0.1340680437291812383e-7 plusOrMinus 1e-18) - } - "eraEors" { - val rnpb = Matrix3D( - 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, - 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, - 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, - ) - - val eo = eraEors(rnpb, (-0.1220040848472271978e-7).rad) - eo shouldBe (-0.1332882715130744606e-2 plusOrMinus 1e-14) - } - "eraGst00a" { - val theta = eraGst00a(2453736.0, 0.5, 2453736.0, 0.5) - theta shouldBe (1.754166138018281369 plusOrMinus 1e-12) - } - "eraGst00b" { - val theta = eraGst00b(2453736.0, 0.5) - theta shouldBe (1.754166136510680589 plusOrMinus 1e-12) - } - "eraEe00a" { - val ee = eraEe00a(2453736.0, 0.5) - ee shouldBe (-0.8834192459222588227e-5 plusOrMinus 1e-18) - } - "eraEe00b" { - val ee = eraEe00b(2453736.0, 0.5) - ee shouldBe (-0.8835700060003032831e-5 plusOrMinus 1e-18) - } - "eraPr00" { - val (dpsipr, depspr) = eraPr00(2453736.0, 0.5) - dpsipr shouldBe (-0.8716465172668347629e-7 plusOrMinus 1e-22) - depspr shouldBe (-0.7342018386722813087e-8 plusOrMinus 1e-22) - } - "eraObl80" { - val eps0 = eraObl80(2454388.0, 0.5) - eps0 shouldBe (0.4090751347643816218 plusOrMinus 1e-14) - } - "eraNut00b" { - val (dpsi, deps) = eraNut00b(2453736.0, 0.5) - dpsi shouldBe (-0.9632552291148362783e-5 plusOrMinus 1e-13) - deps shouldBe (0.4063197106621159367e-4 plusOrMinus 1e-13) - } - "eraC2tcio" { - val rc2i = Matrix3D( - 0.9999998323037164738, 0.5581526271714303683e-9, -0.5791308477073443903e-3, - -0.2384266227524722273e-7, 0.9999999991917404296, -0.4020594955030704125e-4, - 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, - ) - - val rpom = Matrix3D( - 0.9999999999999674705, -0.1367174580728847031e-10, 0.2550602379999972723e-6, - 0.1414624947957029721e-10, 0.9999999999982694954, -0.1860359246998866338e-5, - -0.2550602379741215275e-6, 0.1860359247002413923e-5, 0.9999999999982369658, - ) - - val rc2t = eraC2tcio(rc2i, 1.75283325530307.rad, rpom) - - rc2t[0] shouldBe (-0.1810332128307110439 plusOrMinus 1e-12) - rc2t[1] shouldBe (0.9834769806938470149 plusOrMinus 1e-12) - rc2t[2] shouldBe (0.6555535638685466874e-4 plusOrMinus 1e-12) - rc2t[3] shouldBe (-0.9834768134135996657 plusOrMinus 1e-12) - rc2t[4] shouldBe (-0.1810332203649448367 plusOrMinus 1e-12) - rc2t[5] shouldBe (0.5749801116141106528e-3 plusOrMinus 1e-12) - rc2t[6] shouldBe (0.5773474014081407076e-3 plusOrMinus 1e-12) - rc2t[7] shouldBe (0.3961832391772658944e-4 plusOrMinus 1e-12) - rc2t[8] shouldBe (0.9999998325501691969 plusOrMinus 1e-12) - } - "eraEcm06" { - val rm = eraEcm06(2456165.5, 0.401182685) - - rm[0, 0] shouldBe (0.9999952427708701137 plusOrMinus 1e-14) - rm[0, 1] shouldBe (-0.2829062057663042347e-2 plusOrMinus 1e-14) - rm[0, 2] shouldBe (-0.1229163741100017629e-2 plusOrMinus 1e-14) - rm[1, 0] shouldBe (0.3084546876908653562e-2 plusOrMinus 1e-14) - rm[1, 1] shouldBe (0.9174891871550392514 plusOrMinus 1e-14) - rm[1, 2] shouldBe (0.3977487611849338124 plusOrMinus 1e-14) - rm[2, 0] shouldBe (0.2488512951527405928e-5 plusOrMinus 1e-14) - rm[2, 1] shouldBe (-0.3977506604161195467 plusOrMinus 1e-14) - rm[2, 2] shouldBe (0.9174935488232863071 plusOrMinus 1e-14) - } - "eraPmat06" { - val rbp = eraPmat06(2400000.5, 50123.9999) - - rbp[0, 0] shouldBe (0.9999995505176007047 plusOrMinus 1e-12) - rbp[0, 1] shouldBe (0.8695404617348208406e-3 plusOrMinus 1e-14) - rbp[0, 2] shouldBe (0.3779735201865589104e-3 plusOrMinus 1e-14) - rbp[1, 0] shouldBe (-0.8695404723772031414e-3 plusOrMinus 1e-14) - rbp[1, 1] shouldBe (0.9999996219496027161 plusOrMinus 1e-12) - rbp[1, 2] shouldBe (-0.1361752497080270143e-6 plusOrMinus 1e-14) - rbp[2, 0] shouldBe (-0.3779734957034089490e-3 plusOrMinus 1e-14) - rbp[2, 1] shouldBe (-0.1924880847894457113e-6 plusOrMinus 1e-14) - rbp[2, 2] shouldBe (0.9999999285679971958 plusOrMinus 1e-12) - } - "eraP06e" { - val e = eraP06e(2452541.0, 0.5) - - e.eps0 shouldBe (0.4090926006005828715 plusOrMinus 1e-14) - e.psia shouldBe (0.6664369630191613431e-3 plusOrMinus 1e-14) - e.oma shouldBe (0.4090925973783255982 plusOrMinus 1e-14) - e.bpa shouldBe (0.5561149371265209445e-6 plusOrMinus 1e-14) - e.bqa shouldBe (-0.6191517193290621270e-5 plusOrMinus 1e-14) - e.pia shouldBe (0.6216441751884382923e-5 plusOrMinus 1e-14) - e.bpia shouldBe (3.052014180023779882 plusOrMinus 1e-14) - e.epsa shouldBe (0.4090864054922431688 plusOrMinus 1e-14) - e.chia shouldBe (0.1387703379530915364e-5 plusOrMinus 1e-14) - e.za shouldBe (0.2921789846651790546e-3 plusOrMinus 1e-14) - e.zetaa shouldBe (0.3178773290332009310e-3 plusOrMinus 1e-14) - e.thetaa shouldBe (0.2650932701657497181e-3 plusOrMinus 1e-14) - e.pa shouldBe (0.6651637681381016288e-3 plusOrMinus 1e-14) - e.gam shouldBe (0.1398077115963754987e-5 plusOrMinus 1e-14) - e.phi shouldBe (0.4090864090837462602 plusOrMinus 1e-14) - e.psi shouldBe (0.6664464807480920325e-3 plusOrMinus 1e-14) - } - "eraNumat" { - val rmatn = eraNumat(0.4090789763356509900.rad, (-0.9630909107115582393e-5).rad, 0.4063239174001678826e-4.rad) - - rmatn[0, 0] shouldBe (0.9999999999536227949 plusOrMinus 1e-12) - rmatn[0, 1] shouldBe (0.8836239320236250577e-5 plusOrMinus 1e-12) - rmatn[0, 2] shouldBe (0.3830833447458251908e-5 plusOrMinus 1e-12) - rmatn[1, 0] shouldBe (-0.8836083657016688588e-5 plusOrMinus 1e-12) - rmatn[1, 1] shouldBe (0.9999999991354654959 plusOrMinus 1e-12) - rmatn[1, 2] shouldBe (-0.4063240865361857698e-4 plusOrMinus 1e-12) - rmatn[2, 0] shouldBe (-0.3831192481833385226e-5 plusOrMinus 1e-12) - rmatn[2, 1] shouldBe (0.4063237480216934159e-4 plusOrMinus 1e-12) - rmatn[2, 2] shouldBe (0.9999999991671660407 plusOrMinus 1e-12) - } - "eraNum06a" { - val rmatn = eraNum06a(2453736.0, 0.5) - - rmatn[0, 0] shouldBe (0.9999999999536227668 plusOrMinus 1e-12) - rmatn[0, 1] shouldBe (0.8836241998111535233e-5 plusOrMinus 1e-12) - rmatn[0, 2] shouldBe (0.3830834608415287707e-5 plusOrMinus 1e-12) - rmatn[1, 0] shouldBe (-0.8836086334870740138e-5 plusOrMinus 1e-12) - rmatn[1, 1] shouldBe (0.9999999991354657474 plusOrMinus 1e-12) - rmatn[1, 2] shouldBe (-0.4063240188248455065e-4 plusOrMinus 1e-12) - rmatn[2, 0] shouldBe (-0.3831193642839398128e-5 plusOrMinus 1e-12) - rmatn[2, 1] shouldBe (0.4063236803101479770e-4 plusOrMinus 1e-12) - rmatn[2, 2] shouldBe (0.9999999991671663114 plusOrMinus 1e-12) - } - "eraC2teqx" { - val rbpn = Matrix3D( - 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, - 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, - 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, - ) - - val rpom = Matrix3D( - 0.9999999999999674705, -0.1367174580728847031e-10, 0.2550602379999972723e-6, - 0.1414624947957029721e-10, 0.9999999999982694954, -0.1860359246998866338e-5, - -0.2550602379741215275e-6, 0.1860359247002413923e-5, 0.9999999999982369658, - ) - - val rc2t = eraC2teqx(rbpn, 1.754166138040730516.rad, rpom) - - rc2t[0, 0] shouldBe (-0.1810332128528685730 plusOrMinus 1e-12) - rc2t[0, 1] shouldBe (0.9834769806897685071 plusOrMinus 1e-12) - rc2t[0, 2] shouldBe (0.6555535639982634449e-4 plusOrMinus 1e-12) - - rc2t[1, 0] shouldBe (-0.9834768134095211257 plusOrMinus 1e-12) - rc2t[1, 1] shouldBe (-0.1810332203871023800 plusOrMinus 1e-12) - rc2t[1, 2] shouldBe (0.5749801116126438962e-3 plusOrMinus 1e-12) - - rc2t[2, 0] shouldBe (0.5773474014081539467e-3 plusOrMinus 1e-12) - rc2t[2, 1] shouldBe (0.3961832391768640871e-4 plusOrMinus 1e-12) - rc2t[2, 2] shouldBe (0.9999998325501691969 plusOrMinus 1e-12) - } - "eraTpors" { - val (a, b) = eraTpors((-0.03).rad, 0.07.rad, 1.3.rad, 1.5.rad).shouldNotBeNull() + @Test + fun eraRy() { + val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val s = r.rotateY(0.3456789.rad) + + s[0] shouldBe (0.8651847818978159930 plusOrMinus 1e-12) + s[1] shouldBe (1.467194920539316554 plusOrMinus 1e-12) + s[2] shouldBe (0.1875137911274457342 plusOrMinus 1e-12) + s[3] shouldBe (3.0 plusOrMinus 1e-12) + s[4] shouldBe (2.0 plusOrMinus 1e-12) + s[5] shouldBe (3.0 plusOrMinus 1e-12) + s[6] shouldBe (3.500207892850427330 plusOrMinus 1e-12) + s[7] shouldBe (4.779889022262298150 plusOrMinus 1e-12) + s[8] shouldBe (5.381899160903798712 plusOrMinus 1e-12) + + (s == Matrix3D.rotY(0.3456789.rad) * r).shouldBeTrue() + } - a shouldBe (4.004971075806584490 plusOrMinus 1e-13) - b shouldBe (1.565084088476417917 plusOrMinus 1e-13) - } - "eraTpsts" { - val (ra, dec) = eraTpsts((-0.03).rad, 0.07.rad, 2.3.rad, 1.5.rad) + @Test + fun eraRz() { + val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val s = r.rotateZ(0.3456789.rad) + + s[0] shouldBe (2.898197754208926769 plusOrMinus 1e-12) + s[1] shouldBe (3.500207892850427330 plusOrMinus 1e-12) + s[2] shouldBe (2.898197754208926769 plusOrMinus 1e-12) + s[3] shouldBe (2.144865911309686813 plusOrMinus 1e-12) + s[4] shouldBe (0.865184781897815993 plusOrMinus 1e-12) + s[5] shouldBe (2.144865911309686813 plusOrMinus 1e-12) + s[6] shouldBe (3.0 plusOrMinus 1e-12) + s[7] shouldBe (4.0 plusOrMinus 1e-12) + s[8] shouldBe (5.0 plusOrMinus 1e-12) + + (s == Matrix3D.rotZ(0.3456789.rad) * r).shouldBeTrue() + } - ra shouldBe (0.7596127167359629775 plusOrMinus 1e-14) - dec shouldBe (1.540864645109263028 plusOrMinus 1e-13) - } - "eraTporv" { - val s = CartesianCoordinate.of(1.3.rad, 1.5.rad, 1.0.au) - val v = doubleArrayOf(s.x, s.y, s.z) + @Test + fun eraRxr() { + val a = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val b = Matrix3D(1.0, 2.0, 2.0, 4.0, 1.0, 1.0, 3.0, 0.0, 1.0) + val c = a * b + + c[0] shouldBe (20.0 plusOrMinus 1e-12) + c[1] shouldBe (7.0 plusOrMinus 1e-12) + c[2] shouldBe (9.0 plusOrMinus 1e-12) + c[3] shouldBe (20.0 plusOrMinus 1e-12) + c[4] shouldBe (8.0 plusOrMinus 1e-12) + c[5] shouldBe (11.0 plusOrMinus 1e-12) + c[6] shouldBe (34.0 plusOrMinus 1e-12) + c[7] shouldBe (10.0 plusOrMinus 1e-12) + c[8] shouldBe (15.0 plusOrMinus 1e-12) + } - val (a, b, c) = eraTporv((-0.03).rad, 0.07.rad, v).shouldNotBeNull() + @Test + fun eraC2s() { + val (theta, phi) = eraC2s(100.0.au, (-50.0).au, 25.0.au) + theta shouldBe (-0.4636476090008061162 plusOrMinus 1e-14) + phi shouldBe (0.2199879773954594463 plusOrMinus 1e-14) + } - a shouldBe (-0.003712211763801968173 plusOrMinus 1e-16) - b shouldBe (-0.004341519956299836813 plusOrMinus 1e-16) - c shouldBe (0.9999836852110587012 plusOrMinus 1e-14) - } - "eraTpstv" { - val s = CartesianCoordinate.of(2.3.rad, 1.5.rad, 1.0.au) - val v = doubleArrayOf(s.x, s.y, s.z) + @Test + fun eraS2c() { + val c = eraS2c(3.0123, -0.999) + c[0] shouldBe (-0.5366267667260523906 plusOrMinus 1e-12) + c[1] shouldBe (0.0697711109765145365 plusOrMinus 1e-12) + c[2] shouldBe (-0.8409302618566214041 plusOrMinus 1e-12) + } - val (a, b, c) = eraTpstv((-0.03).rad, 0.07.rad, v).shouldNotBeNull() + @Test + fun eraP2s() { + val (theta, phi, r) = eraP2s(100.0.au, (-50.0).au, 25.0.au) + theta shouldBe (-0.4636476090008061162 plusOrMinus 1e-12) + phi shouldBe (0.2199879773954594463 plusOrMinus 1e-12) + r shouldBe (114.5643923738960002 plusOrMinus 1e-9) + } - a shouldBe (0.02170030454907376677 plusOrMinus 1e-15) - b shouldBe (0.02060909590535367447 plusOrMinus 1e-15) - c shouldBe (0.999552080658352380 plusOrMinus 1e-14) - } - "eraTpxes" { - val (xi, eta, j) = eraTpxes(1.3.rad, 1.55.rad, 2.3.rad, 1.5.rad) + @Test + fun eraAnpm() { + eraAnpm((-4.0).rad) shouldBe (2.283185307179586477 plusOrMinus 1e-12) + } - xi shouldBe (-0.01753200983236980595 plusOrMinus 1e-15) - eta shouldBe (0.05962940005778712891 plusOrMinus 1e-15) - j shouldBeExactly 0 - } - "eraTpxev" { - val s = CartesianCoordinate.of(1.3.rad, 1.55.rad, 1.0.au) - val v = doubleArrayOf(s.x, s.y, s.z) + @Test + fun eraGc2Gde() { + val (e1, p1, h1) = eraGc2Gde(6378137.0, 1.0 / 298.257223563, 2e6, 3e6, 5.244e6) + e1 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) + p1 shouldBe (0.97160184819075459 plusOrMinus 1e-14) + h1 shouldBe (331.4172461426059892 plusOrMinus 1e-8) + + val (e2, p2, h2) = eraGc2Gde(6378137.0, 1.0 / 298.257222101, 2e6, 3e6, 5.244e6) + e2 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) + p2 shouldBe (0.97160184820607853 plusOrMinus 1e-14) + h2 shouldBe (331.41731754844348 plusOrMinus 1e-8) + + val (e3, p3, h3) = eraGc2Gde(6378135.0, 1.0 / 298.26, 2e6, 3e6, 5.244e6) + e3 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) + p3 shouldBe (0.9716018181101511937 plusOrMinus 1e-14) + h3 shouldBe (333.2770726130318123 plusOrMinus 1e-8) + } - val s0 = CartesianCoordinate.of(2.3.rad, 1.5.rad, 1.0.au) - val v0 = doubleArrayOf(s0.x, s0.y, s0.z) + @Test + fun eraGd2Gc() { + val (x1, y1, z1) = eraGd2Gce(6378137.0, 1.0 / 298.257223563, 3.1.rad, (-0.5).rad, 2500.0) + x1 shouldBe (-5599000.5577049947 plusOrMinus 1e-7) + y1 shouldBe (233011.67223479203 plusOrMinus 1e-7) + z1 shouldBe (-3040909.4706983363 plusOrMinus 1e-7) + + val (x2, y2, z2) = eraGd2Gce(6378137.0, 1.0 / 298.257222101, 3.1.rad, (-0.5).rad, 2500.0) + x2 shouldBe (-5599000.5577260984 plusOrMinus 1e-7) + y2 shouldBe (233011.6722356702949 plusOrMinus 1e-7) + z2 shouldBe (-3040909.4706095476 plusOrMinus 1e-7) + + val (x3, y3, z3) = eraGd2Gce(6378135.0, 1.0 / 298.26, 3.1.rad, (-0.5).rad, 2500.0) + x3 shouldBe (-5598998.7626301490 plusOrMinus 1e-7) + y3 shouldBe (233011.5975297822211 plusOrMinus 1e-7) + z3 shouldBe (-3040908.6861467111 plusOrMinus 1e-7) + } - val (xi, eta, j) = eraTpxev(v, v0) + @Test + fun eraC2ixys() { + val m = eraC2ixys(0.5791308486706011000e-3, 0.4020579816732961219e-4, (-0.1220040848472271978e-7).rad) + m[0, 0] shouldBe (0.9999998323037157138 plusOrMinus 1e-12) + m[0, 1] shouldBe (0.5581984869168499149e-9 plusOrMinus 1e-12) + m[0, 2] shouldBe (-0.5791308491611282180e-3 plusOrMinus 1e-12) + m[1, 0] shouldBe (-0.2384261642670440317e-7 plusOrMinus 1e-12) + m[1, 1] shouldBe (0.9999999991917468964 plusOrMinus 1e-12) + m[1, 2] shouldBe (-0.4020579110169668931e-4 plusOrMinus 1e-12) + m[2, 0] shouldBe (0.5791308486706011000e-3 plusOrMinus 1e-12) + m[2, 1] shouldBe (0.4020579816732961219e-4 plusOrMinus 1e-12) + m[2, 2] shouldBe (0.9999998314954627590 plusOrMinus 1e-12) + } - xi shouldBe (-0.01753200983236980595 plusOrMinus 1e-15) - eta shouldBe (0.05962940005778712891 plusOrMinus 1e-15) - j shouldBeExactly 0 - } - "eraPb06" { - val (zeta, z, theta) = eraPb06(2400000.5, 50123.9999) + @Test + fun eraPom00() { + val m = eraPom00(2.55060238e-7.rad, 1.860359247e-6.rad, (-0.1367174580728891460e-10).rad) + m[0, 0] shouldBe (0.9999999999999674721 plusOrMinus 1e-12) + m[0, 1] shouldBe (-0.1367174580728846989e-10 plusOrMinus 1e-12) + m[0, 2] shouldBe (0.2550602379999972345e-6 plusOrMinus 1e-12) + m[1, 0] shouldBe (0.1414624947957029801e-10 plusOrMinus 1e-12) + m[1, 1] shouldBe (0.9999999999982695317 plusOrMinus 1e-12) + m[1, 2] shouldBe (-0.1860359246998866389e-5 plusOrMinus 1e-12) + m[2, 0] shouldBe (-0.2550602379741215021e-6 plusOrMinus 1e-12) + m[2, 1] shouldBe (0.1860359247002414021e-5 plusOrMinus 1e-12) + m[2, 2] shouldBe (0.9999999999982370039 plusOrMinus 1e-12) + } - zeta shouldBe (-0.5092634016326478238e-3 plusOrMinus 1e-12) - z shouldBe (-0.3602772060566044413e-3 plusOrMinus 1e-12) - theta shouldBe (-0.3779735537167811177e-3 plusOrMinus 1e-12) - } - "eraJd2Cal" { - val (y, m, d, f) = eraJd2Cal(2400000.5, 50123.9999) - y shouldBeExactly 1996 - m shouldBeExactly 2 - d shouldBeExactly 10 - f shouldBe (0.9999 plusOrMinus 1e-7) - } - "eraCal2Jd" { - eraCal2Jd(2003, 6, 1) shouldBeExactly 52791.0 - } - "eraDat" { - eraDat(2003, 6, 1, 0.0) shouldBeExactly 32.0 - eraDat(2008, 1, 17, 0.0) shouldBeExactly 33.0 - eraDat(2017, 9, 1, 0.0) shouldBeExactly 37.0 - } - "eraUt1Utc" { - val (u1, u2) = eraUt1Utc(2453750.5, 0.892104561, 0.3341) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921006941018518519 plusOrMinus 1e-12) - } - "eraUtcTai" { - val (u1, u2) = eraUtcTai(2453750.5, 0.892100694) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8924826384444444444 plusOrMinus 1e-12) - } - "eraTaiUt1" { - val (u1, u2) = eraTaiUt1(2453750.5, 0.892482639, -32.6659) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921045614537037037 plusOrMinus 1e-12) - } - "eraUtcUt1" { - val (u1, u2) = eraUtcUt1(2453750.5, 0.892100694, 0.3341) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921045608981481481 plusOrMinus 1e-12) - } - "eraTaiUtc" { - val (u1, u2) = eraTaiUtc(2453750.5, 0.892482639) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921006945555555556 plusOrMinus 1e-12) - } - "eraTaiTt" { - val (u1, u2) = eraTaiTt(2453750.5, 0.892482639) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.892855139 plusOrMinus 1e-12) - } - "eraTtTai" { - val (u1, u2) = eraTtTai(2453750.5, 0.892482639) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.892110139 plusOrMinus 1e-12) - } - "eraTtTdb" { - val (u1, u2) = eraTtTdb(2453750.5, 0.892855139, -0.000201) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8928551366736111111 plusOrMinus 1e-12) - } - "eraTdbTt" { - val (u1, u2) = eraTdbTt(2453750.5, 0.892855137, -0.000201) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8928551393263888889 plusOrMinus 1e-12) - } - "eraUt1Tai" { - val (u1, u2) = eraUt1Tai(2453750.5, 0.892104561, -32.6659) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8924826385462962963 plusOrMinus 1e-12) - } - "eraTtUt1" { - val (u1, u2) = eraTtUt1(2453750.5, 0.892855139, 64.8499) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921045614537037037 plusOrMinus 1e-12) - } - "eraBp00" { - val (rb, rp, rbp) = eraBp00(2400000.5, 50123.9999) - - rb[0, 0] shouldBe (0.9999999999999942498 plusOrMinus 1e-12) - rb[0, 1] shouldBe (-0.7078279744199196626e-7 plusOrMinus 1e-16) - rb[0, 2] shouldBe (0.8056217146976134152e-7 plusOrMinus 1e-16) - rb[1, 0] shouldBe (0.7078279477857337206e-7 plusOrMinus 1e-16) - rb[1, 1] shouldBe (0.9999999999999969484 plusOrMinus 1e-12) - rb[1, 2] shouldBe (0.3306041454222136517e-7 plusOrMinus 1e-16) - rb[2, 0] shouldBe (-0.8056217380986972157e-7 plusOrMinus 1e-16) - rb[2, 1] shouldBe (-0.3306040883980552500e-7 plusOrMinus 1e-16) - rb[2, 2] shouldBe (0.9999999999999962084 plusOrMinus 1e-12) - rp[0, 0] shouldBe (0.9999995504864048241 plusOrMinus 1e-12) - rp[0, 1] shouldBe (0.8696113836207084411e-3 plusOrMinus 1e-14) - rp[0, 2] shouldBe (0.3778928813389333402e-3 plusOrMinus 1e-14) - rp[1, 0] shouldBe (-0.8696113818227265968e-3 plusOrMinus 1e-14) - rp[1, 1] shouldBe (0.9999996218879365258 plusOrMinus 1e-12) - rp[1, 2] shouldBe (-0.1690679263009242066e-6 plusOrMinus 1e-14) - rp[2, 0] shouldBe (-0.3778928854764695214e-3 plusOrMinus 1e-14) - rp[2, 1] shouldBe (-0.1595521004195286491e-6 plusOrMinus 1e-14) - rp[2, 2] shouldBe (0.9999999285984682756 plusOrMinus 1e-12) - rbp[0, 0] shouldBe (0.9999995505175087260 plusOrMinus 1e-12) - rbp[0, 1] shouldBe (0.8695405883617884705e-3 plusOrMinus 1e-14) - rbp[0, 2] shouldBe (0.3779734722239007105e-3 plusOrMinus 1e-14) - rbp[1, 0] shouldBe (-0.8695405990410863719e-3 plusOrMinus 1e-14) - rbp[1, 1] shouldBe (0.9999996219494925900 plusOrMinus 1e-12) - rbp[1, 2] shouldBe (-0.1360775820404982209e-6 plusOrMinus 1e-14) - rbp[2, 0] shouldBe (-0.3779734476558184991e-3 plusOrMinus 1e-14) - rbp[2, 1] shouldBe (-0.1925857585832024058e-6 plusOrMinus 1e-14) - rbp[2, 2] shouldBe (0.9999999285680153377 plusOrMinus 1e-12) - } - "eraPn00" { - val (_, _, epsa, rb, rp, rbp, rn, rbpn) = eraPn00(2400000.5, 53736.0, -0.9632552291149335877e-5, 0.4063197106621141414e-4) + @Test + fun eraApcs() { + val astro = eraApcs( + 2456384.5, 0.970031644, + Vector3D(-1836024.09, 1056607.7, -5998795.26), + Vector3D(-77.0361767, -133.310856, 0.0971855934), + Vector3D(-0.974170438, -0.211520082, -0.0917583024), + Vector3D(0.00364365824, -0.0154287319, -0.00668922024), + Vector3D(-0.973458265, -0.209215307, -0.0906996477), + ) + + astro.pmt shouldBe (13.25248468622587269 plusOrMinus 1e-11) + astro.eb.x shouldBe (-0.9741827110629881886 plusOrMinus 1e-12) + astro.eb.y shouldBe (-0.2115130190136415986 plusOrMinus 1e-12) + astro.eb.z shouldBe (-0.09179840186954412099 plusOrMinus 1e-12) + astro.eh.x shouldBe (-0.9736425571689454706 plusOrMinus 1e-12) + astro.eh.y shouldBe (-0.2092452125850435930 plusOrMinus 1e-12) + astro.eh.z shouldBe (-0.09075578152248299218 plusOrMinus 1e-12) + astro.em shouldBe (0.9998233241709796859 plusOrMinus 1e-12) + astro.v.x shouldBe (0.2078704993282685510e-4 plusOrMinus 1e-16) + astro.v.y shouldBe (-0.8955360106989405683e-4 plusOrMinus 1e-16) + astro.v.z shouldBe (-0.3863338994289409097e-4 plusOrMinus 1e-16) + astro.bm1 shouldBe (0.9999999950277561237 plusOrMinus 1e-12) + } - epsa shouldBe (0.4090791789404229916 plusOrMinus 1e-12) + @Test + fun eraApco() { + val astro = eraApco( + 2456384.5, 0.970031644, + Vector3D(-0.974170438, -0.211520082, -0.0917583024), + Vector3D(0.00364365824, -0.0154287319, -0.00668922024), + Vector3D(-0.973458265, -0.209215307, -0.0906996477), + 0.0013122272, -2.92808623e-5, 3.05749468e-8.rad, + 3.14540971.rad, (-0.527800806).rad, (-1.2345856).rad, + 2738.0, + 2.47230737e-7.rad, 1.82640464e-6.rad, (-3.01974337e-11).rad, + 0.000201418779.rad, (-2.36140831e-7).rad, + ) + + astro.pmt shouldBe (13.25248468622587269 plusOrMinus 1e-11) + astro.eb.x shouldBe (-0.9741827110630322720 plusOrMinus 1e-12) + astro.eb.y shouldBe (-0.2115130190135344832 plusOrMinus 1e-12) + astro.eb.z shouldBe (-0.09179840186949532298 plusOrMinus 1e-12) + astro.eh.x shouldBe (-0.9736425571689739035 plusOrMinus 1e-12) + astro.eh.y shouldBe (-0.2092452125849330936 plusOrMinus 1e-12) + astro.eh.z shouldBe (-0.09075578152243272599 plusOrMinus 1e-12) + astro.em shouldBe (0.9998233241709957653 plusOrMinus 1e-12) + astro.v.x shouldBe (0.2078704992916728762e-4 plusOrMinus 1e-16) + astro.v.y shouldBe (-0.8955360107151952319e-4 plusOrMinus 1e-16) + astro.v.z shouldBe (-0.3863338994288951082e-4 plusOrMinus 1e-16) + astro.bm1 shouldBe (0.9999999950277561236 plusOrMinus 1e-12) + astro.bpn[0, 0] shouldBe (0.9999991390295159156 plusOrMinus 1e-12) + astro.bpn[1, 0] shouldBe (0.4978650072505016932e-7 plusOrMinus 1e-12) + astro.bpn[2, 0] shouldBe (0.1312227200000000000e-2 plusOrMinus 1e-12) + astro.bpn[0, 1] shouldBe (-0.1136336653771609630e-7 plusOrMinus 1e-12) + astro.bpn[1, 1] shouldBe (0.9999999995713154868 plusOrMinus 1e-12) + astro.bpn[2, 1] shouldBe (-0.2928086230000000000e-4 plusOrMinus 1e-12) + astro.bpn[0, 2] shouldBe (-0.1312227200895260194e-2 plusOrMinus 1e-12) + astro.bpn[1, 2] shouldBe (0.2928082217872315680e-4 plusOrMinus 1e-12) + astro.bpn[2, 2] shouldBe (0.9999991386008323373 plusOrMinus 1e-12) + astro.along shouldBe (-0.5278008060295995734 plusOrMinus 1e-12) + astro.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) + astro.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) + astro.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) + astro.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) + astro.eral shouldBe (2.617608903970400427 plusOrMinus 1e-12) + astro.refa shouldBe (0.2014187790000000000e-3 plusOrMinus 1e-17) + astro.refb shouldBe (-0.2361408310000000000e-6 plusOrMinus 1e-18) + astro.diurab shouldBeExactly 0.0 + } - rb[0, 0] shouldBe (0.9999999999999942498 plusOrMinus 1e-12) - rb[0, 1] shouldBe (-0.7078279744199196626e-7 plusOrMinus 1e-18) - rb[0, 2] shouldBe (0.8056217146976134152e-7 plusOrMinus 1e-18) + @Test + fun eraSp00() { + eraSp00(2400000.5, 52541.0) shouldBe (-0.6216698469981019309e-11 plusOrMinus 1e-12) + } - rb[1, 0] shouldBe (0.7078279477857337206e-7 plusOrMinus 1e-18) - rb[1, 1] shouldBe (0.9999999999999969484 plusOrMinus 1e-12) - rb[1, 2] shouldBe (0.3306041454222136517e-7 plusOrMinus 1e-18) + @Test + fun eraObl06() { + eraObl06(2400000.5, 54388.0) shouldBe (0.4090749229387258204 plusOrMinus 1e-14) + } - rb[2, 0] shouldBe (-0.8056217380986972157e-7 plusOrMinus 1e-18) - rb[2, 1] shouldBe (-0.3306040883980552500e-7 plusOrMinus 1e-18) - rb[2, 2] shouldBe (0.9999999999999962084 plusOrMinus 1e-12) + @Test + fun eraPfw06() { + val (gamb, phib, psib, epsa) = eraPfw06(2400000.5, 50123.9999) + gamb shouldBe (-0.2243387670997995690e-5 plusOrMinus 1e-16) + phib shouldBe (0.4091014602391312808 plusOrMinus 1e-12) + psib shouldBe (-0.9501954178013031895e-3 plusOrMinus 1e-14) + epsa shouldBe (0.4091014316587367491 plusOrMinus 1e-12) + } - rp[0, 0] shouldBe (0.9999989300532289018 plusOrMinus 1e-12) - rp[0, 1] shouldBe (-0.1341647226791824349e-2 plusOrMinus 1e-14) - rp[0, 2] shouldBe (-0.5829880927190296547e-3 plusOrMinus 1e-14) + @Test + fun eraFal03() { + eraFal03(0.80) shouldBe (5.132369751108684150 plusOrMinus 1e-12) + } - rp[1, 0] shouldBe (0.1341647231069759008e-2 plusOrMinus 1e-14) - rp[1, 1] shouldBe (0.9999990999908750433 plusOrMinus 1e-12) - rp[1, 2] shouldBe (-0.3837444441583715468e-6 plusOrMinus 1e-14) + @Test + fun eraFaf03() { + eraFaf03(0.80) shouldBe (0.2597711366745499518 plusOrMinus 1e-12) + } - rp[2, 0] shouldBe (0.5829880828740957684e-3 plusOrMinus 1e-14) - rp[2, 1] shouldBe (-0.3984203267708834759e-6 plusOrMinus 1e-14) - rp[2, 2] shouldBe (0.9999998300623538046 plusOrMinus 1e-12) + @Test + fun eraFaom03() { + eraFaom03(0.80) shouldBe (TAU - 5.973618440951302183 plusOrMinus 1e-12) + } - rbp[0, 0] shouldBe (0.9999989300052243993 plusOrMinus 1e-12) - rbp[0, 1] shouldBe (-0.1341717990239703727e-2 plusOrMinus 1e-14) - rbp[0, 2] shouldBe (-0.5829075749891684053e-3 plusOrMinus 1e-14) + @Test + fun eraFapa03() { + eraFapa03(0.80) shouldBe (0.1950884762240000000e-1 plusOrMinus 1e-12) + } - rbp[1, 0] shouldBe (0.1341718013831739992e-2 plusOrMinus 1e-14) - rbp[1, 1] shouldBe (0.9999990998959191343 plusOrMinus 1e-12) - rbp[1, 2] shouldBe (-0.3505759733565421170e-6 plusOrMinus 1e-14) + @Test + fun eraFame03() { + eraFame03(0.80) shouldBe (5.417338184297289661 plusOrMinus 1e-12) + } - rbp[2, 0] shouldBe (0.5829075206857717883e-3 plusOrMinus 1e-14) - rbp[2, 1] shouldBe (-0.4315219955198608970e-6 plusOrMinus 1e-14) - rbp[2, 2] shouldBe (0.9999998301093036269 plusOrMinus 1e-12) + @Test + fun eraFave03() { + eraFave03(0.80) shouldBe (3.424900460533758000 plusOrMinus 1e-12) + } - rn[0, 0] shouldBe (0.9999999999536069682 plusOrMinus 1e-12) - rn[0, 1] shouldBe (0.8837746144872140812e-5 plusOrMinus 1e-16) - rn[0, 2] shouldBe (0.3831488838252590008e-5 plusOrMinus 1e-16) + @Test + fun eraFae03() { + eraFae03(0.80) shouldBe (1.744713738913081846 plusOrMinus 1e-12) + } - rn[1, 0] shouldBe (-0.8837590456633197506e-5 plusOrMinus 1e-16) - rn[1, 1] shouldBe (0.9999999991354692733 plusOrMinus 1e-12) - rn[1, 2] shouldBe (-0.4063198798559573702e-4 plusOrMinus 1e-16) + @Test + fun eraFama03() { + eraFama03(0.80) shouldBe (3.275506840277781492 plusOrMinus 1e-12) + } - rn[2, 0] shouldBe (-0.3831847930135328368e-5 plusOrMinus 1e-16) - rn[2, 1] shouldBe (0.4063195412258150427e-4 plusOrMinus 1e-16) - rn[2, 2] shouldBe (0.9999999991671806225 plusOrMinus 1e-12) + @Test + fun eraFaju03() { + eraFaju03(0.80) shouldBe (5.275711665202481138 plusOrMinus 1e-12) + } - rbpn[0, 0] shouldBe (0.9999989440499982806 plusOrMinus 1e-12) - rbpn[0, 1] shouldBe (-0.1332880253640848301e-2 plusOrMinus 1e-14) - rbpn[0, 2] shouldBe (-0.5790760898731087295e-3 plusOrMinus 1e-14) + @Test + fun eraFasa03() { + eraFasa03(0.80) shouldBe (5.371574539440827046 plusOrMinus 1e-12) + } - rbpn[1, 0] shouldBe (0.1332856746979948745e-2 plusOrMinus 1e-14) - rbpn[1, 1] shouldBe (0.9999991109064768883 plusOrMinus 1e-12) - rbpn[1, 2] shouldBe (-0.4097740555723063806e-4 plusOrMinus 1e-14) + @Test + fun eraFaur03() { + eraFaur03(0.80) shouldBe (5.180636450180413523 plusOrMinus 1e-12) + } - rbpn[2, 0] shouldBe (0.5791301929950205000e-3 plusOrMinus 1e-14) - rbpn[2, 1] shouldBe (0.4020553681373702931e-4 plusOrMinus 1e-14) - rbpn[2, 2] shouldBe (0.9999998314958529887 plusOrMinus 1e-12) - } - "eraC2tpe" { - val rc2t = - eraC2tpe(2400000.5, 53736.0, 2400000.5, 53736.0, -0.9630909107115582393e-5, 0.4090789763356509900, 2.55060238e-7, 1.860359247e-6) + @Test + fun eraFw2m() { + val m = eraFw2m((-0.2243387670997992368e-5).rad, 0.4091014602391312982.rad, (-0.9501954178013015092e-3).rad, 0.4091014316587367472.rad) + m[0, 0] shouldBe (0.9999995505176007047 plusOrMinus 1e-12) + m[0, 1] shouldBe (0.8695404617348192957e-3 plusOrMinus 1e-12) + m[0, 2] shouldBe (0.3779735201865582571e-3 plusOrMinus 1e-12) + m[1, 0] shouldBe (-0.8695404723772016038e-3 plusOrMinus 1e-12) + m[1, 1] shouldBe (0.9999996219496027161 plusOrMinus 1e-12) + m[1, 2] shouldBe (-0.1361752496887100026e-6 plusOrMinus 1e-12) + m[2, 0] shouldBe (-0.3779734957034082790e-3 plusOrMinus 1e-12) + m[2, 1] shouldBe (-0.1924880848087615651e-6 plusOrMinus 1e-12) + m[2, 2] shouldBe (0.9999999285679971958 plusOrMinus 1e-12) + } - rc2t[0, 0] shouldBe (-0.1813677995763029394 plusOrMinus 1e-12) - rc2t[0, 1] shouldBe (0.9023482206891683275 plusOrMinus 1e-12) - rc2t[0, 2] shouldBe (-0.3909902938641085751 plusOrMinus 1e-12) + @Test + fun era00() { + era00(2400000.0 + 54388.0, 0.5) shouldBe (0.4022837240028158102 plusOrMinus 1e-12) + } - rc2t[1, 0] shouldBe (-0.9834147641476804807 plusOrMinus 1e-12) - rc2t[1, 1] shouldBe (-0.1659883635434995121 plusOrMinus 1e-12) - rc2t[1, 2] shouldBe (0.7309763898042819705e-1 plusOrMinus 1e-12) + @Test + fun eraRefco() { + val (refa, refb) = eraRefco(800.0, 10.0, 0.9, 0.4) + refa shouldBe (0.2264949956241415009e-3 plusOrMinus 1e-15) + refb shouldBe (-0.2598658261729343970e-6 plusOrMinus 1e-18) + } - rc2t[2, 0] shouldBe (0.1059685430673215247e-2 plusOrMinus 1e-12) - rc2t[2, 1] shouldBe (0.3977631855605078674 plusOrMinus 1e-12) - rc2t[2, 2] shouldBe (0.9174875068792735362 plusOrMinus 1e-12) - } - "eraS00" { - val s = eraS00(2400000.5, 53736.0, 0.5791308486706011000e-3, 0.4020579816732961219e-4) - s shouldBe (-0.1220036263270905693e-7 plusOrMinus 1e-18) - } - "eraS00b" { - val s = eraS00b(2400000.5, 52541.0) - s shouldBe (-0.1340695782951026584e-7 plusOrMinus 1e-18) - } - "eraS00a" { - val s = eraS00a(2400000.5, 52541.0) - s shouldBe (-0.1340684448919163584e-7 plusOrMinus 1e-18) - } - "eraApco13" { - val (astrom, eo) = eraApco13( - 2456384.5, 0.969254051, 0.1550675, - -0.527800806, -1.2345856, 2738.0, - 2.47230737e-7, 1.82640464e-6, - 731.0, 12.8, 0.59, 0.55 - ) - - astrom.pmt shouldBe (13.25248468622475727 plusOrMinus 1e-11) - astrom.eb.x shouldBe (-0.9741827107320875162 plusOrMinus 1e-12) - astrom.eb.y shouldBe (-0.2115130190489716682 plusOrMinus 1e-12) - astrom.eb.z shouldBe (-0.09179840189496755339 plusOrMinus 1e-12) - astrom.eh.x shouldBe (-0.9736425572586935247 plusOrMinus 1e-12) - astrom.eh.y shouldBe (-0.2092452121603336166 plusOrMinus 1e-12) - astrom.eh.z shouldBe (-0.09075578153885665295 plusOrMinus 1e-12) - astrom.em shouldBe (0.9998233240913898141 plusOrMinus 1e-12) - astrom.v.x shouldBe (0.2078704994520489246e-4 plusOrMinus 1e-16) - astrom.v.y shouldBe (-0.8955360133238868938e-4 plusOrMinus 1e-16) - astrom.v.z shouldBe (-0.3863338993055887398e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999950277561004 plusOrMinus 1e-12) - astrom.bpn[0, 0] shouldBe (0.9999991390295147999 plusOrMinus 1e-12) - astrom.bpn[1, 0] shouldBe (0.4978650075315529277e-7 plusOrMinus 1e-12) - astrom.bpn[2, 0] shouldBe (0.001312227200850293372 plusOrMinus 1e-12) - astrom.bpn[0, 1] shouldBe (-0.1136336652812486604e-7 plusOrMinus 1e-12) - astrom.bpn[1, 1] shouldBe (0.9999999995713154865 plusOrMinus 1e-12) - astrom.bpn[2, 1] shouldBe (-0.2928086230975367296e-4 plusOrMinus 1e-12) - astrom.bpn[0, 2] shouldBe (-0.001312227201745553566 plusOrMinus 1e-12) - astrom.bpn[1, 2] shouldBe (0.2928082218847679162e-4 plusOrMinus 1e-12) - astrom.bpn[2, 2] shouldBe (0.9999991386008312212 plusOrMinus 1e-12) - astrom.along shouldBe (-0.5278008060295995733 plusOrMinus 1e-12) - astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) - astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) - astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) - astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) - astrom.diurab shouldBeExactly 0.0 - astrom.eral shouldBe (2.617608909189664000 plusOrMinus 1e-12) - astrom.refa shouldBe (0.2014187785940396921e-3 plusOrMinus 1e-15) - astrom.refb shouldBe (-0.2361408314943696227e-6 plusOrMinus 1e-18) - eo shouldBe (-0.003020548354802412839 plusOrMinus 1e-14) - } - "eraPmpx" { - val pco = eraPmpx(1.234, 0.789, 1e-5, -2e-5, 1e-2.arcsec, 10.0.kms, 8.75, Vector3D(0.9, 0.4, 0.1)) - pco[0] shouldBe (0.2328137623960308438 plusOrMinus 1e-12) - pco[1] shouldBe (0.6651097085397855328 plusOrMinus 1e-12) - pco[2] shouldBe (0.7095257765896359837 plusOrMinus 1e-12) - } - "eraAtciq" { - val (astrom) = eraApci13(2456165.5, 0.401182685) - val (ri, di) = eraAtciq(2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, astrom) - ri shouldBe (2.710121572968696744 plusOrMinus 1e-12) - di shouldBe (0.1729371367219539137 plusOrMinus 1e-12) - } - "eraAtciqz" { - val (astrom) = eraApci13(2456165.5, 0.401182685) - val (ri, di) = eraAtciqz(2.71, 0.174, astrom) - ri shouldBe (2.709994899247256984 plusOrMinus 1e-12) - di shouldBe (0.1728740720984931891 plusOrMinus 1e-12) - } - "eraAtci13" { - val (ri, di, eo) = eraAtci13(2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, 2456165.5, 0.401182685) - ri shouldBe (2.710121572968696744 plusOrMinus 1e-12) - di shouldBe (0.1729371367219539137 plusOrMinus 1e-12) - eo shouldBe (-0.002900618712657375647 plusOrMinus 1e-14) - } - "eraApci13" { - val (astrom, eo) = eraApci13(2456165.5, 0.401182685) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb[0] shouldBe (0.9013108747340644755 plusOrMinus 1e-12) - astrom.eb[1] shouldBe (-0.4174026640406119957 plusOrMinus 1e-12) - astrom.eb[2] shouldBe (-0.1809822877867817771 plusOrMinus 1e-12) - astrom.eh[0] shouldBe (0.8940025429255499549 plusOrMinus 1e-12) - astrom.eh[1] shouldBe (-0.4110930268331896318 plusOrMinus 1e-12) - astrom.eh[2] shouldBe (-0.1782189006019749850 plusOrMinus 1e-12) - astrom.em shouldBe (1.010465295964664178 plusOrMinus 1e-12) - astrom.v[0] shouldBe (0.4289638912941341125e-4 plusOrMinus 1e-16) - astrom.v[1] shouldBe (0.8115034032405042132e-4 plusOrMinus 1e-16) - astrom.v[2] shouldBe (0.3517555135536470279e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999951686013142 plusOrMinus 1e-12) - astrom.bpn[0, 0] shouldBe (0.9999992060376761710 plusOrMinus 1e-12) - astrom.bpn[1, 0] shouldBe (0.4124244860106037157e-7 plusOrMinus 1e-12) - astrom.bpn[2, 0] shouldBe (0.1260128571051709670e-2 plusOrMinus 1e-12) - astrom.bpn[0, 1] shouldBe (-0.1282291987222130690e-7 plusOrMinus 1e-12) - astrom.bpn[1, 1] shouldBe (0.9999999997456835325 plusOrMinus 1e-12) - astrom.bpn[2, 1] shouldBe (-0.2255288829420524935e-4 plusOrMinus 1e-12) - astrom.bpn[0, 2] shouldBe (-0.1260128571661374559e-2 plusOrMinus 1e-12) - astrom.bpn[1, 2] shouldBe (0.2255285422953395494e-4 plusOrMinus 1e-12) - astrom.bpn[2, 2] shouldBe (0.9999992057833604343 plusOrMinus 1e-12) - eo shouldBe (-0.2900618712657375647e-2 plusOrMinus 1e-12) - } - "eraLdsun" { - val p = Vector3D(-0.763276255, -0.608633767, -0.216735543) - val e = Vector3D(-0.973644023, -0.20925523, -0.0907169552) - val p1 = eraLdsun(p, e, 0.999809214) - p1[0] shouldBe (-0.7632762580731413169 plusOrMinus 1e-12) - p1[1] shouldBe (-0.6086337635262647900 plusOrMinus 1e-12) - p1[2] shouldBe (-0.2167355419322321302 plusOrMinus 1e-12) - } - "eraLd" { - val p = Vector3D(-0.763276255, -0.608633767, -0.216735543) - val q = Vector3D(-0.763276255, -0.608633767, -0.216735543) - val e = Vector3D(0.76700421, 0.605629598, 0.211937094) - val p1 = eraLd(0.00028574, p, q, e, 8.91276983, 3e-10) - p1[0] shouldBe (-0.7632762548968159627 plusOrMinus 1e-12) - p1[1] shouldBe (-0.6086337670823762701 plusOrMinus 1e-12) - p1[2] shouldBe (-0.2167355431320546947 plusOrMinus 1e-12) - } - "eraApci" { - val ebp = Vector3D(0.901310875, -0.417402664, -0.180982288) - val ebv = Vector3D(0.00742727954, 0.0140507459, 0.00609045792) - val ehp = Vector3D(0.903358544, -0.415395237, -0.180084014) - val astrom = eraApci(2456165.5, 0.401182685, ebp, ebv, ehp, 0.0013122272, -2.92808623e-5, 3.05749468e-8) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb[0] shouldBe (0.901310875 plusOrMinus 1e-12) - astrom.eb[1] shouldBe (-0.417402664 plusOrMinus 1e-12) - astrom.eb[2] shouldBe (-0.180982288 plusOrMinus 1e-12) - astrom.eh[0] shouldBe (0.8940025429324143045 plusOrMinus 1e-12) - astrom.eh[1] shouldBe (-0.4110930268679817955 plusOrMinus 1e-12) - astrom.eh[2] shouldBe (-0.1782189004872870264 plusOrMinus 1e-12) - astrom.em shouldBe (1.010465295811013146 plusOrMinus 1e-12) - astrom.v[0] shouldBe (0.4289638913597693554e-4 plusOrMinus 1e-16) - astrom.v[1] shouldBe (0.8115034051581320575e-4 plusOrMinus 1e-16) - astrom.v[2] shouldBe (0.3517555136380563427e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999951686012981 plusOrMinus 1e-12) - astrom.bpn[0, 0] shouldBe (0.9999991390295159156 plusOrMinus 1e-12) - astrom.bpn[1, 0] shouldBe (0.4978650072505016932e-7 plusOrMinus 1e-12) - astrom.bpn[2, 0] shouldBe (0.1312227200000000000e-2 plusOrMinus 1e-12) - astrom.bpn[0, 1] shouldBe (-0.1136336653771609630e-7 plusOrMinus 1e-12) - astrom.bpn[1, 1] shouldBe (0.9999999995713154868 plusOrMinus 1e-12) - astrom.bpn[2, 1] shouldBe (-0.2928086230000000000e-4 plusOrMinus 1e-12) - astrom.bpn[0, 2] shouldBe (-0.1312227200895260194e-2 plusOrMinus 1e-12) - astrom.bpn[1, 2] shouldBe (0.2928082217872315680e-4 plusOrMinus 1e-12) - astrom.bpn[2, 2] shouldBe (0.9999991386008323373 plusOrMinus 1e-12) - } - "eraApcs13" { - val p = Vector3D(-6241497.16, 401346.896, -1251136.04) - val v = Vector3D(-29.264597, -455.021831, 0.0266151194) - val astrom = eraApcs13(2456165.5, 0.401182685, p, v) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb[0] shouldBe (0.9012691529025250644 plusOrMinus 1e-12) - astrom.eb[1] shouldBe (-0.4173999812023194317 plusOrMinus 1e-12) - astrom.eb[2] shouldBe (-0.1809906511146429670 plusOrMinus 1e-12) - astrom.eh[0] shouldBe (0.8939939101760130792 plusOrMinus 1e-12) - astrom.eh[1] shouldBe (-0.4111053891734021478 plusOrMinus 1e-12) - astrom.eh[2] shouldBe (-0.1782336880636997374 plusOrMinus 1e-12) - astrom.em shouldBe (1.010428384373491095 plusOrMinus 1e-12) - astrom.v[0] shouldBe (0.4279877294121697570e-4 plusOrMinus 1e-16) - astrom.v[1] shouldBe (0.7963255087052120678e-4 plusOrMinus 1e-16) - astrom.v[2] shouldBe (0.3517564013384691531e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999952947980978 plusOrMinus 1e-12) - astrom.bpn shouldBe Matrix3D.IDENTITY - } - "eraAtioq" { - val astrom = - eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) - val (aob, zob, hob, dob, rob) = eraAtioq(2.710121572969038991, 0.1729371367218230438, astrom) - - aob shouldBe (0.9233952224895122499e-1 plusOrMinus 1e-12) - zob shouldBe (1.407758704513549991 plusOrMinus 1e-12) - hob shouldBe (-0.9247619879881698140e-1 plusOrMinus 1e-12) - dob shouldBe (0.1717653435756234676 plusOrMinus 1e-12) - rob shouldBe (2.710085107988480746 plusOrMinus 1e-12) - } - "eraApio13" { - val astrom = - eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) - - astrom.along shouldBe (-0.5278008060295995733 plusOrMinus 1e-12) - astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) - astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) - astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) - astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) - astrom.diurab shouldBe (0.5135843661699913529e-6 plusOrMinus 1e-12) - astrom.eral shouldBe (2.617608909189664000 plusOrMinus 1e-12) - astrom.refa shouldBe (0.2014187785940396921e-3 plusOrMinus 1e-15) - astrom.refb shouldBe (-0.2361408314943696227e-6 plusOrMinus 1e-18) - } - "eraApio" { - val astrom = - eraApio(-3.01974337e-11, 3.14540971, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 0.000201418779, -2.36140831e-7) - - astrom.along shouldBe (-0.5278008060295995734 plusOrMinus 1e-12) - astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) - astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) - astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) - astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) - astrom.diurab shouldBe (0.5135843661699913529e-6 plusOrMinus 1e-12) - astrom.eral shouldBe (2.617608903970400427 plusOrMinus 1e-12) - astrom.refa shouldBe (0.2014187790000000000e-3 plusOrMinus 1e-15) - astrom.refb shouldBe (-0.2361408310000000000e-6 plusOrMinus 1e-18) - } - "eraAtco13" { - val (b, eo) = eraAtco13( - 2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, 2456384.5, 0.969254051, - 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, - 0.59, 0.55 - ) - - val (aob, zob, hob, dob, rob) = b - - aob shouldBe (0.9251774485485515207e-1 plusOrMinus 1e-12) - zob shouldBe (1.407661405256499357 plusOrMinus 1e-12) - hob shouldBe (-0.9265154431529724692e-1 plusOrMinus 1e-12) - dob shouldBe (0.1716626560072526200 plusOrMinus 1e-12) - rob shouldBe (2.710260453504961012 plusOrMinus 1e-12) - eo shouldBe (-0.003020548354802412839 plusOrMinus 1e-14) - } - "eraAticq" { - val (astrom) = eraApci13(2456165.5, 0.401182685) - val (ri, di) = eraAticq(2.710121572969038991, 0.1729371367218230438, astrom) - ri shouldBe (2.710126504531716819 plusOrMinus 1e-12) - di shouldBe (0.1740632537627034482 plusOrMinus 1e-12) - } - "eraAtic13" { - val (rc, dc, eo) = eraAtic13(2.710121572969038991, 0.1729371367218230438, 2456165.5, 0.401182685) + @Test + fun eraApcg() { + val astrom = eraApcg( + 2456165.5, 0.401182685, + Vector3D(0.901310875, -0.417402664, -0.180982288), + Vector3D(0.00742727954, 0.0140507459, 0.00609045792), + Vector3D(0.903358544, -0.415395237, -0.180084014), + ) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb.x shouldBe (0.901310875 plusOrMinus 1e-12) + astrom.eb.y shouldBe (-0.417402664 plusOrMinus 1e-12) + astrom.eb.z shouldBe (-0.180982288 plusOrMinus 1e-12) + astrom.eh.x shouldBe (0.8940025429324143045 plusOrMinus 1e-12) + astrom.eh.y shouldBe (-0.4110930268679817955 plusOrMinus 1e-12) + astrom.eh.z shouldBe (-0.1782189004872870264 plusOrMinus 1e-12) + astrom.em shouldBe (1.010465295811013146 plusOrMinus 1e-12) + astrom.v.x shouldBe (0.4289638913597693554e-4 plusOrMinus 1e-16) + astrom.v.y shouldBe (0.8115034051581320575e-4 plusOrMinus 1e-16) + astrom.v.z shouldBe (0.3517555136380563427e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999951686012981 plusOrMinus 1e-12) + } - rc shouldBe (2.710126504531716819 plusOrMinus 1e-12) - dc shouldBe (0.1740632537627034482 plusOrMinus 1e-12) - eo shouldBe (-0.002900618712657375647 plusOrMinus 1e-14) - } - "eraS2pv" { - val pv = eraS2pv(-3.21, 0.123, 0.456, -7.8e-6, 9.01e-6, -1.23e-5) + @Test + fun eraEpv00() { + val (h, b) = eraEpv00(2400000.5, 53411.52501161) + h.position[0] shouldBe (-0.7757238809297706813 plusOrMinus 1e-14) + h.position[1] shouldBe (0.5598052241363340596 plusOrMinus 1e-14) + h.position[2] shouldBe (0.2426998466481686993 plusOrMinus 1e-14) - pv.position[0] shouldBe (-0.4514964673880165228 plusOrMinus 1e-12) - pv.position[1] shouldBe (0.0309339427734258688 plusOrMinus 1e-12) - pv.position[2] shouldBe (0.0559466810510877933 plusOrMinus 1e-12) + h.velocity[0] shouldBe (-0.1091891824147313846e-1 plusOrMinus 1e-15) + h.velocity[1] shouldBe (-0.1247187268440845008e-1 plusOrMinus 1e-15) + h.velocity[2] shouldBe (-0.5407569418065039061e-2 plusOrMinus 1e-15) - pv.velocity[0] shouldBe (0.1292270850663260170e-4 plusOrMinus 1e-16) - pv.velocity[1] shouldBe (0.2652814182060691422e-5 plusOrMinus 1e-16) - pv.velocity[2] shouldBe (0.2568431853930292259e-5 plusOrMinus 1e-16) - } - "eraStarpv" { - val pv = eraStarpv(0.01686756, -1.093989828, -1.78323516e-5, 2.336024047e-6, 0.74723.arcsec, (-21.6).kms) + b.position[0] shouldBe (-0.7714104440491111971 plusOrMinus 1e-14) + b.position[1] shouldBe (0.5598412061824171323 plusOrMinus 1e-14) + b.position[2] shouldBe (0.2425996277722452400 plusOrMinus 1e-14) + + b.velocity[0] shouldBe (-0.1091874268116823295e-1 plusOrMinus 1e-15) + b.velocity[1] shouldBe (-0.1246525461732861538e-1 plusOrMinus 1e-15) + b.velocity[2] shouldBe (-0.5404773180966231279e-2 plusOrMinus 1e-15) + } + + @Test + fun eraApcg13() { + val astrom = eraApcg13(2456165.5, 0.401182685) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb.x shouldBe (0.9013108747340644755 plusOrMinus 1e-12) + astrom.eb.y shouldBe (-0.4174026640406119957 plusOrMinus 1e-12) + astrom.eb.z shouldBe (-0.1809822877867817771 plusOrMinus 1e-12) + astrom.eh.x shouldBe (0.8940025429255499549 plusOrMinus 1e-12) + astrom.eh.y shouldBe (-0.4110930268331896318 plusOrMinus 1e-12) + astrom.eh.z shouldBe (-0.1782189006019749850 plusOrMinus 1e-12) + astrom.em shouldBe (1.010465295964664178 plusOrMinus 1e-12) + astrom.v.x shouldBe (0.4289638912941341125e-4 plusOrMinus 1e-16) + astrom.v.y shouldBe (0.8115034032405042132e-4 plusOrMinus 1e-16) + astrom.v.z shouldBe (0.3517555135536470279e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999951686013142 plusOrMinus 1e-12) + } + + @Test + fun eraAe2hd() { + val (ha, dec) = eraAe2hd(5.5.rad, 1.1.rad, 0.7.rad) + + ha shouldBe (0.5933291115507309663 plusOrMinus 1E-14) + dec shouldBe (0.9613934761647817620 plusOrMinus 1E-14) + } + + @Test + fun eraTcbTdb() { + val (whole, fraction) = eraTcbTdb(2453750.5, 0.893019599) + whole shouldBe (2453750.5 plusOrMinus 1E-6) + fraction shouldBe (0.8928551362746343397 plusOrMinus 1E-12) + } + + @Test + fun eraTcgTt() { + val (whole, fraction) = eraTcgTt(2453750.5, 0.892862531) + whole shouldBe (2453750.5 plusOrMinus 1E-6) + fraction shouldBe (0.8928551387488816828 plusOrMinus 1E-12) + } + + @Test + fun eraTdbTcb() { + val (whole, fraction) = eraTdbTcb(2453750.5, 0.892855137) + whole shouldBe (2453750.5 plusOrMinus 1E-6) + fraction shouldBe (0.8930195997253656716 plusOrMinus 1E-12) + } + + @Test + fun eraTtTcg() { + val (whole, fraction) = eraTtTcg(2453750.5, 0.892482639) + whole shouldBe (2453750.5 plusOrMinus 1E-6) + fraction shouldBe (0.8924900312508587113 plusOrMinus 1E-12) + } + + @Test + fun eraDtDb() { + val dt = eraDtDb(2448939.5, 0.123, 0.76543, 5.0123.rad, 5525.242.km, 3190.0.km) + dt shouldBe (-0.1280368005936998991e-2 plusOrMinus 1E-15) + } + + @Test + fun eraPvtob() { + val (p, v) = eraPvtob(2.0.rad, 0.5.rad, 3000.0, 1e-6.rad, (-0.5e-6).rad, 1e-8.rad, 5.0.rad) + p[0] shouldBe (4225081.367071159207 plusOrMinus 1e-5) + p[1] shouldBe (3681943.215856198144 plusOrMinus 1e-5) + p[2] shouldBe (3041149.399241260785 plusOrMinus 1e-5) + v[0] shouldBe (-268.4915389365998787 plusOrMinus 1e-5) + v[1] shouldBe (308.0977983288903123 plusOrMinus 1e-5) + v[2] shouldBe (0.0 plusOrMinus 1e-5) + } + + @Test + fun eraNut00a() { + val (psi, eps) = eraNut00a(2400000.5, 53736.0) + psi shouldBe (-0.9630909107115518431e-5 plusOrMinus 1e-13) + eps shouldBe (0.4063239174001678710e-4 plusOrMinus 1e-13) + } + + @Test + fun eraNut06a() { + val (psi, eps) = eraNut06a(2400000.5, 53736.0) + psi shouldBe (-0.9630912025820308797e-5 plusOrMinus 1e-13) + eps shouldBe (0.4063238496887249798e-4 plusOrMinus 1e-13) + } + + @Test + fun eraPnm06a() { + val rbpn = eraPnm06a(2400000.5, 50123.9999) + + rbpn[0, 0] shouldBe (0.9999995832794205484 plusOrMinus 1e-12) + rbpn[0, 1] shouldBe (0.8372382772630962111e-3 plusOrMinus 1e-14) + rbpn[0, 2] shouldBe (0.3639684771140623099e-3 plusOrMinus 1e-14) + rbpn[1, 0] shouldBe (-0.8372533744743683605e-3 plusOrMinus 1e-14) + rbpn[1, 1] shouldBe (0.9999996486492861646 plusOrMinus 1e-12) + rbpn[1, 2] shouldBe (0.4132905944611019498e-4 plusOrMinus 1e-14) + rbpn[2, 0] shouldBe (-0.3639337469629464969e-3 plusOrMinus 1e-14) + rbpn[2, 1] shouldBe (-0.4163377605910663999e-4 plusOrMinus 1e-14) + rbpn[2, 2] shouldBe (0.9999999329094260057 plusOrMinus 1e-12) + } + + @Test + fun eraAb() { + val pnat = Vector3D(-0.76321968546737951, -0.60869453983060384, -0.21676408580639883) + val v = Vector3D(2.1044018893653786e-5, -8.9108923304429319e-5, -3.8633714797716569e-5) + val ppr = eraAb(pnat, v, 0.99980921395708788.au, 0.99999999506209258) + + ppr[0] shouldBe (-0.7631631094219556269 plusOrMinus 1e-12) + ppr[1] shouldBe (-0.6087553082505590832 plusOrMinus 1e-12) + ppr[2] shouldBe (-0.2167926269368471279 plusOrMinus 1e-12) + } + + @Test + fun eraEect00() { + val eect = eraEect00(2400000.5, 53736.0) + eect shouldBe (0.2046085004885125264e-8 plusOrMinus 1e-20) + } + + @Test + fun eraEra00() { + val era00 = eraEra00(2454388.0, 0.5) + era00 shouldBe (0.4022837240028158102 plusOrMinus 1e-12) + } - pv.position[0] shouldBe (126668.5912743160601 plusOrMinus 1e-10) - pv.position[1] shouldBe (2136.792716839935195 plusOrMinus 1e-12) - pv.position[2] shouldBe (-245251.2339876830091 plusOrMinus 1e-10) + @Test + fun eraEe00() { + val ee = eraEe00(2453736.0, 0.5, 0.4090789763356509900.rad, (-0.9630909107115582393e-5).rad) + ee shouldBe (-0.8834193235367965479e-5 plusOrMinus 1e-18) + } + + @Test + fun eraGmst00() { + val theta = eraGmst00(2453736.0, 0.5, 2453736.0, 0.5) + theta shouldBe (1.754174972210740592 plusOrMinus 1e-12) + } + + @Test + fun eraGmst06() { + val theta = eraGmst06(2453736.0, 0.5, 2453736.0, 0.5) + theta shouldBe (1.754174971870091203 plusOrMinus 1e-12) + } + + @Test + fun eraEe06a() { + val ee = eraEe06a(2453736.0, 0.5) + ee shouldBe (-0.8834195072043790156e-5 plusOrMinus 1e-15) + } + + @Test + fun eraGst06a() { + val theta = eraGst06a(2453736.0, 0.5, 2453736.0, 0.5) + theta shouldBe (1.754166137675019159 plusOrMinus 1e-12) + } + + @Test + fun eraGst06() { + val rnpb = Matrix3D( + 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, + 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, + 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, + ) + + val theta = eraGst06(2453736.0, 0.5, 2453736.0, 0.5, rnpb) + theta shouldBe (1.754166138018167568 plusOrMinus 1e-12) + } + + @Test + fun eraS06() { + val s = eraS06(2400000.5, 53736.0, 0.5791308486706011000e-3, 0.4020579816732961219e-4) + s shouldBe (-0.1220032213076463117e-7 plusOrMinus 1e-18) + } + + @Test + fun eraS06a() { + val s = eraS06a(2400000.5, 52541.0) + s shouldBe (-0.1340680437291812383e-7 plusOrMinus 1e-18) + } + + @Test + fun eraEors() { + val rnpb = Matrix3D( + 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, + 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, + 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, + ) + + val eo = eraEors(rnpb, (-0.1220040848472271978e-7).rad) + eo shouldBe (-0.1332882715130744606e-2 plusOrMinus 1e-14) + } + + @Test + fun eraGst00a() { + val theta = eraGst00a(2453736.0, 0.5, 2453736.0, 0.5) + theta shouldBe (1.754166138018281369 plusOrMinus 1e-12) + } + + @Test + fun eraGst00b() { + val theta = eraGst00b(2453736.0, 0.5) + theta shouldBe (1.754166136510680589 plusOrMinus 1e-12) + } + + @Test + fun eraEe00a() { + val ee = eraEe00a(2453736.0, 0.5) + ee shouldBe (-0.8834192459222588227e-5 plusOrMinus 1e-18) + } + + @Test + fun eraEe00b() { + val ee = eraEe00b(2453736.0, 0.5) + ee shouldBe (-0.8835700060003032831e-5 plusOrMinus 1e-18) + } + + @Test + fun eraPr00() { + val (dpsipr, depspr) = eraPr00(2453736.0, 0.5) + dpsipr shouldBe (-0.8716465172668347629e-7 plusOrMinus 1e-22) + depspr shouldBe (-0.7342018386722813087e-8 plusOrMinus 1e-22) + } + + @Test + fun eraObl80() { + val eps0 = eraObl80(2454388.0, 0.5) + eps0 shouldBe (0.4090751347643816218 plusOrMinus 1e-14) + } + + @Test + fun eraNut00b() { + val (dpsi, deps) = eraNut00b(2453736.0, 0.5) + dpsi shouldBe (-0.9632552291148362783e-5 plusOrMinus 1e-13) + deps shouldBe (0.4063197106621159367e-4 plusOrMinus 1e-13) + } + + @Test + fun eraC2tcio() { + val rc2i = Matrix3D( + 0.9999998323037164738, 0.5581526271714303683e-9, -0.5791308477073443903e-3, + -0.2384266227524722273e-7, 0.9999999991917404296, -0.4020594955030704125e-4, + 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, + ) + + val rpom = Matrix3D( + 0.9999999999999674705, -0.1367174580728847031e-10, 0.2550602379999972723e-6, + 0.1414624947957029721e-10, 0.9999999999982694954, -0.1860359246998866338e-5, + -0.2550602379741215275e-6, 0.1860359247002413923e-5, 0.9999999999982369658, + ) + + val rc2t = eraC2tcio(rc2i, 1.75283325530307.rad, rpom) + + rc2t[0] shouldBe (-0.1810332128307110439 plusOrMinus 1e-12) + rc2t[1] shouldBe (0.9834769806938470149 plusOrMinus 1e-12) + rc2t[2] shouldBe (0.6555535638685466874e-4 plusOrMinus 1e-12) + rc2t[3] shouldBe (-0.9834768134135996657 plusOrMinus 1e-12) + rc2t[4] shouldBe (-0.1810332203649448367 plusOrMinus 1e-12) + rc2t[5] shouldBe (0.5749801116141106528e-3 plusOrMinus 1e-12) + rc2t[6] shouldBe (0.5773474014081407076e-3 plusOrMinus 1e-12) + rc2t[7] shouldBe (0.3961832391772658944e-4 plusOrMinus 1e-12) + rc2t[8] shouldBe (0.9999998325501691969 plusOrMinus 1e-12) + } + + @Test + fun eraEcm06() { + val rm = eraEcm06(2456165.5, 0.401182685) + + rm[0, 0] shouldBe (0.9999952427708701137 plusOrMinus 1e-14) + rm[0, 1] shouldBe (-0.2829062057663042347e-2 plusOrMinus 1e-14) + rm[0, 2] shouldBe (-0.1229163741100017629e-2 plusOrMinus 1e-14) + rm[1, 0] shouldBe (0.3084546876908653562e-2 plusOrMinus 1e-14) + rm[1, 1] shouldBe (0.9174891871550392514 plusOrMinus 1e-14) + rm[1, 2] shouldBe (0.3977487611849338124 plusOrMinus 1e-14) + rm[2, 0] shouldBe (0.2488512951527405928e-5 plusOrMinus 1e-14) + rm[2, 1] shouldBe (-0.3977506604161195467 plusOrMinus 1e-14) + rm[2, 2] shouldBe (0.9174935488232863071 plusOrMinus 1e-14) + } + + @Test + fun eraPmat06() { + val rbp = eraPmat06(2400000.5, 50123.9999) + + rbp[0, 0] shouldBe (0.9999995505176007047 plusOrMinus 1e-12) + rbp[0, 1] shouldBe (0.8695404617348208406e-3 plusOrMinus 1e-14) + rbp[0, 2] shouldBe (0.3779735201865589104e-3 plusOrMinus 1e-14) + rbp[1, 0] shouldBe (-0.8695404723772031414e-3 plusOrMinus 1e-14) + rbp[1, 1] shouldBe (0.9999996219496027161 plusOrMinus 1e-12) + rbp[1, 2] shouldBe (-0.1361752497080270143e-6 plusOrMinus 1e-14) + rbp[2, 0] shouldBe (-0.3779734957034089490e-3 plusOrMinus 1e-14) + rbp[2, 1] shouldBe (-0.1924880847894457113e-6 plusOrMinus 1e-14) + rbp[2, 2] shouldBe (0.9999999285679971958 plusOrMinus 1e-12) + } + + @Test + fun eraP06e() { + val e = eraP06e(2452541.0, 0.5) + + e.eps0 shouldBe (0.4090926006005828715 plusOrMinus 1e-14) + e.psia shouldBe (0.6664369630191613431e-3 plusOrMinus 1e-14) + e.oma shouldBe (0.4090925973783255982 plusOrMinus 1e-14) + e.bpa shouldBe (0.5561149371265209445e-6 plusOrMinus 1e-14) + e.bqa shouldBe (-0.6191517193290621270e-5 plusOrMinus 1e-14) + e.pia shouldBe (0.6216441751884382923e-5 plusOrMinus 1e-14) + e.bpia shouldBe (3.052014180023779882 plusOrMinus 1e-14) + e.epsa shouldBe (0.4090864054922431688 plusOrMinus 1e-14) + e.chia shouldBe (0.1387703379530915364e-5 plusOrMinus 1e-14) + e.za shouldBe (0.2921789846651790546e-3 plusOrMinus 1e-14) + e.zetaa shouldBe (0.3178773290332009310e-3 plusOrMinus 1e-14) + e.thetaa shouldBe (0.2650932701657497181e-3 plusOrMinus 1e-14) + e.pa shouldBe (0.6651637681381016288e-3 plusOrMinus 1e-14) + e.gam shouldBe (0.1398077115963754987e-5 plusOrMinus 1e-14) + e.phi shouldBe (0.4090864090837462602 plusOrMinus 1e-14) + e.psi shouldBe (0.6664464807480920325e-3 plusOrMinus 1e-14) + } + + @Test + fun eraNumat() { + val rmatn = eraNumat(0.4090789763356509900.rad, (-0.9630909107115582393e-5).rad, 0.4063239174001678826e-4.rad) + + rmatn[0, 0] shouldBe (0.9999999999536227949 plusOrMinus 1e-12) + rmatn[0, 1] shouldBe (0.8836239320236250577e-5 plusOrMinus 1e-12) + rmatn[0, 2] shouldBe (0.3830833447458251908e-5 plusOrMinus 1e-12) + rmatn[1, 0] shouldBe (-0.8836083657016688588e-5 plusOrMinus 1e-12) + rmatn[1, 1] shouldBe (0.9999999991354654959 plusOrMinus 1e-12) + rmatn[1, 2] shouldBe (-0.4063240865361857698e-4 plusOrMinus 1e-12) + rmatn[2, 0] shouldBe (-0.3831192481833385226e-5 plusOrMinus 1e-12) + rmatn[2, 1] shouldBe (0.4063237480216934159e-4 plusOrMinus 1e-12) + rmatn[2, 2] shouldBe (0.9999999991671660407 plusOrMinus 1e-12) + } + + @Test + fun eraNum06a() { + val rmatn = eraNum06a(2453736.0, 0.5) + + rmatn[0, 0] shouldBe (0.9999999999536227668 plusOrMinus 1e-12) + rmatn[0, 1] shouldBe (0.8836241998111535233e-5 plusOrMinus 1e-12) + rmatn[0, 2] shouldBe (0.3830834608415287707e-5 plusOrMinus 1e-12) + rmatn[1, 0] shouldBe (-0.8836086334870740138e-5 plusOrMinus 1e-12) + rmatn[1, 1] shouldBe (0.9999999991354657474 plusOrMinus 1e-12) + rmatn[1, 2] shouldBe (-0.4063240188248455065e-4 plusOrMinus 1e-12) + rmatn[2, 0] shouldBe (-0.3831193642839398128e-5 plusOrMinus 1e-12) + rmatn[2, 1] shouldBe (0.4063236803101479770e-4 plusOrMinus 1e-12) + rmatn[2, 2] shouldBe (0.9999999991671663114 plusOrMinus 1e-12) + } + + @Test + fun eraC2teqx() { + val rbpn = Matrix3D( + 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, + 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, + 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, + ) + + val rpom = Matrix3D( + 0.9999999999999674705, -0.1367174580728847031e-10, 0.2550602379999972723e-6, + 0.1414624947957029721e-10, 0.9999999999982694954, -0.1860359246998866338e-5, + -0.2550602379741215275e-6, 0.1860359247002413923e-5, 0.9999999999982369658, + ) + + val rc2t = eraC2teqx(rbpn, 1.754166138040730516.rad, rpom) + + rc2t[0, 0] shouldBe (-0.1810332128528685730 plusOrMinus 1e-12) + rc2t[0, 1] shouldBe (0.9834769806897685071 plusOrMinus 1e-12) + rc2t[0, 2] shouldBe (0.6555535639982634449e-4 plusOrMinus 1e-12) + + rc2t[1, 0] shouldBe (-0.9834768134095211257 plusOrMinus 1e-12) + rc2t[1, 1] shouldBe (-0.1810332203871023800 plusOrMinus 1e-12) + rc2t[1, 2] shouldBe (0.5749801116126438962e-3 plusOrMinus 1e-12) + + rc2t[2, 0] shouldBe (0.5773474014081539467e-3 plusOrMinus 1e-12) + rc2t[2, 1] shouldBe (0.3961832391768640871e-4 plusOrMinus 1e-12) + rc2t[2, 2] shouldBe (0.9999998325501691969 plusOrMinus 1e-12) + } + + @Test + fun eraTpors() { + val (a, b) = eraTpors((-0.03).rad, 0.07.rad, 1.3.rad, 1.5.rad).shouldNotBeNull() + + a shouldBe (4.004971075806584490 plusOrMinus 1e-13) + b shouldBe (1.565084088476417917 plusOrMinus 1e-13) + } + + @Test + fun eraTpsts() { + val (ra, dec) = eraTpsts((-0.03).rad, 0.07.rad, 2.3.rad, 1.5.rad) + + ra shouldBe (0.7596127167359629775 plusOrMinus 1e-14) + dec shouldBe (1.540864645109263028 plusOrMinus 1e-13) + } + + @Test + fun eraTporv() { + val s = CartesianCoordinate.of(1.3.rad, 1.5.rad, 1.0.au) + val v = doubleArrayOf(s.x, s.y, s.z) + + val (a, b, c) = eraTporv((-0.03).rad, 0.07.rad, v).shouldNotBeNull() + + a shouldBe (-0.003712211763801968173 plusOrMinus 1e-16) + b shouldBe (-0.004341519956299836813 plusOrMinus 1e-16) + c shouldBe (0.9999836852110587012 plusOrMinus 1e-14) + } + + @Test + fun eraTpstv() { + val s = CartesianCoordinate.of(2.3.rad, 1.5.rad, 1.0.au) + val v = doubleArrayOf(s.x, s.y, s.z) + + val (a, b, c) = eraTpstv((-0.03).rad, 0.07.rad, v).shouldNotBeNull() + + a shouldBe (0.02170030454907376677 plusOrMinus 1e-15) + b shouldBe (0.02060909590535367447 plusOrMinus 1e-15) + c shouldBe (0.999552080658352380 plusOrMinus 1e-14) + } + + @Test + fun eraTpxes() { + val (xi, eta, j) = eraTpxes(1.3.rad, 1.55.rad, 2.3.rad, 1.5.rad) + + xi shouldBe (-0.01753200983236980595 plusOrMinus 1e-15) + eta shouldBe (0.05962940005778712891 plusOrMinus 1e-15) + j shouldBeExactly 0 + } + + @Test + fun eraTpxev() { + val s = CartesianCoordinate.of(1.3.rad, 1.55.rad, 1.0.au) + val v = doubleArrayOf(s.x, s.y, s.z) + + val s0 = CartesianCoordinate.of(2.3.rad, 1.5.rad, 1.0.au) + val v0 = doubleArrayOf(s0.x, s0.y, s0.z) + + val (xi, eta, j) = eraTpxev(v, v0) + + xi shouldBe (-0.01753200983236980595 plusOrMinus 1e-15) + eta shouldBe (0.05962940005778712891 plusOrMinus 1e-15) + j shouldBeExactly 0 + } + + @Test + fun eraPb06() { + val (zeta, z, theta) = eraPb06(2400000.5, 50123.9999) + + zeta shouldBe (-0.5092634016326478238e-3 plusOrMinus 1e-12) + z shouldBe (-0.3602772060566044413e-3 plusOrMinus 1e-12) + theta shouldBe (-0.3779735537167811177e-3 plusOrMinus 1e-12) + } + + @Test + fun eraJd2Cal() { + val (y, m, d, f) = eraJd2Cal(2400000.5, 50123.9999) + y shouldBeExactly 1996 + m shouldBeExactly 2 + d shouldBeExactly 10 + f shouldBe (0.9999 plusOrMinus 1e-7) + } + + @Test + fun eraCal2Jd() { + eraCal2Jd(2003, 6, 1) shouldBeExactly 52791.0 + } + + @Test + fun eraDat() { + eraDat(2003, 6, 1, 0.0) shouldBeExactly 32.0 + eraDat(2008, 1, 17, 0.0) shouldBeExactly 33.0 + eraDat(2017, 9, 1, 0.0) shouldBeExactly 37.0 + } + + @Test + fun eraUt1Utc() { + val (u1, u2) = eraUt1Utc(2453750.5, 0.892104561, 0.3341) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921006941018518519 plusOrMinus 1e-12) + } + + @Test + fun eraUtcTai() { + val (u1, u2) = eraUtcTai(2453750.5, 0.892100694) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8924826384444444444 plusOrMinus 1e-12) + } + + @Test + fun eraTaiUt1() { + val (u1, u2) = eraTaiUt1(2453750.5, 0.892482639, -32.6659) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921045614537037037 plusOrMinus 1e-12) + } + + @Test + fun eraUtcUt1() { + val (u1, u2) = eraUtcUt1(2453750.5, 0.892100694, 0.3341) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921045608981481481 plusOrMinus 1e-12) + } + + @Test + fun eraTaiUtc() { + val (u1, u2) = eraTaiUtc(2453750.5, 0.892482639) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921006945555555556 plusOrMinus 1e-12) + } + + @Test + fun eraTaiTt() { + val (u1, u2) = eraTaiTt(2453750.5, 0.892482639) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.892855139 plusOrMinus 1e-12) + } + + @Test + fun eraTtTai() { + val (u1, u2) = eraTtTai(2453750.5, 0.892482639) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.892110139 plusOrMinus 1e-12) + } + + @Test + fun eraTtTdb() { + val (u1, u2) = eraTtTdb(2453750.5, 0.892855139, -0.000201) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8928551366736111111 plusOrMinus 1e-12) + } + + @Test + fun eraTdbTt() { + val (u1, u2) = eraTdbTt(2453750.5, 0.892855137, -0.000201) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8928551393263888889 plusOrMinus 1e-12) + } + + @Test + fun eraUt1Tai() { + val (u1, u2) = eraUt1Tai(2453750.5, 0.892104561, -32.6659) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8924826385462962963 plusOrMinus 1e-12) + } + + @Test + fun eraTtUt1() { + val (u1, u2) = eraTtUt1(2453750.5, 0.892855139, 64.8499) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921045614537037037 plusOrMinus 1e-12) + } + + @Test + fun eraBp00() { + val (rb, rp, rbp) = eraBp00(2400000.5, 50123.9999) + + rb[0, 0] shouldBe (0.9999999999999942498 plusOrMinus 1e-12) + rb[0, 1] shouldBe (-0.7078279744199196626e-7 plusOrMinus 1e-16) + rb[0, 2] shouldBe (0.8056217146976134152e-7 plusOrMinus 1e-16) + rb[1, 0] shouldBe (0.7078279477857337206e-7 plusOrMinus 1e-16) + rb[1, 1] shouldBe (0.9999999999999969484 plusOrMinus 1e-12) + rb[1, 2] shouldBe (0.3306041454222136517e-7 plusOrMinus 1e-16) + rb[2, 0] shouldBe (-0.8056217380986972157e-7 plusOrMinus 1e-16) + rb[2, 1] shouldBe (-0.3306040883980552500e-7 plusOrMinus 1e-16) + rb[2, 2] shouldBe (0.9999999999999962084 plusOrMinus 1e-12) + rp[0, 0] shouldBe (0.9999995504864048241 plusOrMinus 1e-12) + rp[0, 1] shouldBe (0.8696113836207084411e-3 plusOrMinus 1e-14) + rp[0, 2] shouldBe (0.3778928813389333402e-3 plusOrMinus 1e-14) + rp[1, 0] shouldBe (-0.8696113818227265968e-3 plusOrMinus 1e-14) + rp[1, 1] shouldBe (0.9999996218879365258 plusOrMinus 1e-12) + rp[1, 2] shouldBe (-0.1690679263009242066e-6 plusOrMinus 1e-14) + rp[2, 0] shouldBe (-0.3778928854764695214e-3 plusOrMinus 1e-14) + rp[2, 1] shouldBe (-0.1595521004195286491e-6 plusOrMinus 1e-14) + rp[2, 2] shouldBe (0.9999999285984682756 plusOrMinus 1e-12) + rbp[0, 0] shouldBe (0.9999995505175087260 plusOrMinus 1e-12) + rbp[0, 1] shouldBe (0.8695405883617884705e-3 plusOrMinus 1e-14) + rbp[0, 2] shouldBe (0.3779734722239007105e-3 plusOrMinus 1e-14) + rbp[1, 0] shouldBe (-0.8695405990410863719e-3 plusOrMinus 1e-14) + rbp[1, 1] shouldBe (0.9999996219494925900 plusOrMinus 1e-12) + rbp[1, 2] shouldBe (-0.1360775820404982209e-6 plusOrMinus 1e-14) + rbp[2, 0] shouldBe (-0.3779734476558184991e-3 plusOrMinus 1e-14) + rbp[2, 1] shouldBe (-0.1925857585832024058e-6 plusOrMinus 1e-14) + rbp[2, 2] shouldBe (0.9999999285680153377 plusOrMinus 1e-12) + } + + @Test + fun eraPn00() { + val (_, _, epsa, rb, rp, rbp, rn, rbpn) = eraPn00(2400000.5, 53736.0, -0.9632552291149335877e-5, 0.4063197106621141414e-4) + + epsa shouldBe (0.4090791789404229916 plusOrMinus 1e-12) + + rb[0, 0] shouldBe (0.9999999999999942498 plusOrMinus 1e-12) + rb[0, 1] shouldBe (-0.7078279744199196626e-7 plusOrMinus 1e-18) + rb[0, 2] shouldBe (0.8056217146976134152e-7 plusOrMinus 1e-18) + + rb[1, 0] shouldBe (0.7078279477857337206e-7 plusOrMinus 1e-18) + rb[1, 1] shouldBe (0.9999999999999969484 plusOrMinus 1e-12) + rb[1, 2] shouldBe (0.3306041454222136517e-7 plusOrMinus 1e-18) + + rb[2, 0] shouldBe (-0.8056217380986972157e-7 plusOrMinus 1e-18) + rb[2, 1] shouldBe (-0.3306040883980552500e-7 plusOrMinus 1e-18) + rb[2, 2] shouldBe (0.9999999999999962084 plusOrMinus 1e-12) + + rp[0, 0] shouldBe (0.9999989300532289018 plusOrMinus 1e-12) + rp[0, 1] shouldBe (-0.1341647226791824349e-2 plusOrMinus 1e-14) + rp[0, 2] shouldBe (-0.5829880927190296547e-3 plusOrMinus 1e-14) + + rp[1, 0] shouldBe (0.1341647231069759008e-2 plusOrMinus 1e-14) + rp[1, 1] shouldBe (0.9999990999908750433 plusOrMinus 1e-12) + rp[1, 2] shouldBe (-0.3837444441583715468e-6 plusOrMinus 1e-14) + + rp[2, 0] shouldBe (0.5829880828740957684e-3 plusOrMinus 1e-14) + rp[2, 1] shouldBe (-0.3984203267708834759e-6 plusOrMinus 1e-14) + rp[2, 2] shouldBe (0.9999998300623538046 plusOrMinus 1e-12) + + rbp[0, 0] shouldBe (0.9999989300052243993 plusOrMinus 1e-12) + rbp[0, 1] shouldBe (-0.1341717990239703727e-2 plusOrMinus 1e-14) + rbp[0, 2] shouldBe (-0.5829075749891684053e-3 plusOrMinus 1e-14) + + rbp[1, 0] shouldBe (0.1341718013831739992e-2 plusOrMinus 1e-14) + rbp[1, 1] shouldBe (0.9999990998959191343 plusOrMinus 1e-12) + rbp[1, 2] shouldBe (-0.3505759733565421170e-6 plusOrMinus 1e-14) + + rbp[2, 0] shouldBe (0.5829075206857717883e-3 plusOrMinus 1e-14) + rbp[2, 1] shouldBe (-0.4315219955198608970e-6 plusOrMinus 1e-14) + rbp[2, 2] shouldBe (0.9999998301093036269 plusOrMinus 1e-12) + + rn[0, 0] shouldBe (0.9999999999536069682 plusOrMinus 1e-12) + rn[0, 1] shouldBe (0.8837746144872140812e-5 plusOrMinus 1e-16) + rn[0, 2] shouldBe (0.3831488838252590008e-5 plusOrMinus 1e-16) + + rn[1, 0] shouldBe (-0.8837590456633197506e-5 plusOrMinus 1e-16) + rn[1, 1] shouldBe (0.9999999991354692733 plusOrMinus 1e-12) + rn[1, 2] shouldBe (-0.4063198798559573702e-4 plusOrMinus 1e-16) + + rn[2, 0] shouldBe (-0.3831847930135328368e-5 plusOrMinus 1e-16) + rn[2, 1] shouldBe (0.4063195412258150427e-4 plusOrMinus 1e-16) + rn[2, 2] shouldBe (0.9999999991671806225 plusOrMinus 1e-12) + + rbpn[0, 0] shouldBe (0.9999989440499982806 plusOrMinus 1e-12) + rbpn[0, 1] shouldBe (-0.1332880253640848301e-2 plusOrMinus 1e-14) + rbpn[0, 2] shouldBe (-0.5790760898731087295e-3 plusOrMinus 1e-14) + + rbpn[1, 0] shouldBe (0.1332856746979948745e-2 plusOrMinus 1e-14) + rbpn[1, 1] shouldBe (0.9999991109064768883 plusOrMinus 1e-12) + rbpn[1, 2] shouldBe (-0.4097740555723063806e-4 plusOrMinus 1e-14) + + rbpn[2, 0] shouldBe (0.5791301929950205000e-3 plusOrMinus 1e-14) + rbpn[2, 1] shouldBe (0.4020553681373702931e-4 plusOrMinus 1e-14) + rbpn[2, 2] shouldBe (0.9999998314958529887 plusOrMinus 1e-12) + } + + @Test + fun eraC2tpe() { + val rc2t = + eraC2tpe(2400000.5, 53736.0, 2400000.5, 53736.0, -0.9630909107115582393e-5, 0.4090789763356509900, 2.55060238e-7, 1.860359247e-6) + + rc2t[0, 0] shouldBe (-0.1813677995763029394 plusOrMinus 1e-12) + rc2t[0, 1] shouldBe (0.9023482206891683275 plusOrMinus 1e-12) + rc2t[0, 2] shouldBe (-0.3909902938641085751 plusOrMinus 1e-12) + + rc2t[1, 0] shouldBe (-0.9834147641476804807 plusOrMinus 1e-12) + rc2t[1, 1] shouldBe (-0.1659883635434995121 plusOrMinus 1e-12) + rc2t[1, 2] shouldBe (0.7309763898042819705e-1 plusOrMinus 1e-12) + + rc2t[2, 0] shouldBe (0.1059685430673215247e-2 plusOrMinus 1e-12) + rc2t[2, 1] shouldBe (0.3977631855605078674 plusOrMinus 1e-12) + rc2t[2, 2] shouldBe (0.9174875068792735362 plusOrMinus 1e-12) + } + + @Test + fun eraS00() { + val s = eraS00(2400000.5, 53736.0, 0.5791308486706011000e-3, 0.4020579816732961219e-4) + s shouldBe (-0.1220036263270905693e-7 plusOrMinus 1e-18) + } + + @Test + fun eraS00b() { + val s = eraS00b(2400000.5, 52541.0) + s shouldBe (-0.1340695782951026584e-7 plusOrMinus 1e-18) + } + + @Test + fun eraS00a() { + val s = eraS00a(2400000.5, 52541.0) + s shouldBe (-0.1340684448919163584e-7 plusOrMinus 1e-18) + } + + @Test + fun eraApco13() { + val (astrom, eo) = eraApco13( + 2456384.5, 0.969254051, 0.1550675, + -0.527800806, -1.2345856, 2738.0, + 2.47230737e-7, 1.82640464e-6, + 731.0, 12.8, 0.59, 0.55 + ) + + astrom.pmt shouldBe (13.25248468622475727 plusOrMinus 1e-11) + astrom.eb.x shouldBe (-0.9741827107320875162 plusOrMinus 1e-12) + astrom.eb.y shouldBe (-0.2115130190489716682 plusOrMinus 1e-12) + astrom.eb.z shouldBe (-0.09179840189496755339 plusOrMinus 1e-12) + astrom.eh.x shouldBe (-0.9736425572586935247 plusOrMinus 1e-12) + astrom.eh.y shouldBe (-0.2092452121603336166 plusOrMinus 1e-12) + astrom.eh.z shouldBe (-0.09075578153885665295 plusOrMinus 1e-12) + astrom.em shouldBe (0.9998233240913898141 plusOrMinus 1e-12) + astrom.v.x shouldBe (0.2078704994520489246e-4 plusOrMinus 1e-16) + astrom.v.y shouldBe (-0.8955360133238868938e-4 plusOrMinus 1e-16) + astrom.v.z shouldBe (-0.3863338993055887398e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999950277561004 plusOrMinus 1e-12) + astrom.bpn[0, 0] shouldBe (0.9999991390295147999 plusOrMinus 1e-12) + astrom.bpn[1, 0] shouldBe (0.4978650075315529277e-7 plusOrMinus 1e-12) + astrom.bpn[2, 0] shouldBe (0.001312227200850293372 plusOrMinus 1e-12) + astrom.bpn[0, 1] shouldBe (-0.1136336652812486604e-7 plusOrMinus 1e-12) + astrom.bpn[1, 1] shouldBe (0.9999999995713154865 plusOrMinus 1e-12) + astrom.bpn[2, 1] shouldBe (-0.2928086230975367296e-4 plusOrMinus 1e-12) + astrom.bpn[0, 2] shouldBe (-0.001312227201745553566 plusOrMinus 1e-12) + astrom.bpn[1, 2] shouldBe (0.2928082218847679162e-4 plusOrMinus 1e-12) + astrom.bpn[2, 2] shouldBe (0.9999991386008312212 plusOrMinus 1e-12) + astrom.along shouldBe (-0.5278008060295995733 plusOrMinus 1e-12) + astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) + astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) + astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) + astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) + astrom.diurab shouldBeExactly 0.0 + astrom.eral shouldBe (2.617608909189664000 plusOrMinus 1e-12) + astrom.refa shouldBe (0.2014187785940396921e-3 plusOrMinus 1e-15) + astrom.refb shouldBe (-0.2361408314943696227e-6 plusOrMinus 1e-18) + eo shouldBe (-0.003020548354802412839 plusOrMinus 1e-14) + } + + @Test + fun eraPmpx() { + val pco = eraPmpx(1.234, 0.789, 1e-5, -2e-5, 1e-2.arcsec, 10.0.kms, 8.75, Vector3D(0.9, 0.4, 0.1)) + pco[0] shouldBe (0.2328137623960308438 plusOrMinus 1e-12) + pco[1] shouldBe (0.6651097085397855328 plusOrMinus 1e-12) + pco[2] shouldBe (0.7095257765896359837 plusOrMinus 1e-12) + } + + @Test + fun eraAtciq() { + val (astrom) = eraApci13(2456165.5, 0.401182685) + val (ri, di) = eraAtciq(2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, astrom) + ri shouldBe (2.710121572968696744 plusOrMinus 1e-12) + di shouldBe (0.1729371367219539137 plusOrMinus 1e-12) + } + + @Test + fun eraAtciqz() { + val (astrom) = eraApci13(2456165.5, 0.401182685) + val (ri, di) = eraAtciqz(2.71, 0.174, astrom) + ri shouldBe (2.709994899247256984 plusOrMinus 1e-12) + di shouldBe (0.1728740720984931891 plusOrMinus 1e-12) + } + + @Test + fun eraAtci13() { + val (ri, di, eo) = eraAtci13(2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, 2456165.5, 0.401182685) + ri shouldBe (2.710121572968696744 plusOrMinus 1e-12) + di shouldBe (0.1729371367219539137 plusOrMinus 1e-12) + eo shouldBe (-0.002900618712657375647 plusOrMinus 1e-14) + } + + @Test + fun eraApci13() { + val (astrom, eo) = eraApci13(2456165.5, 0.401182685) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb[0] shouldBe (0.9013108747340644755 plusOrMinus 1e-12) + astrom.eb[1] shouldBe (-0.4174026640406119957 plusOrMinus 1e-12) + astrom.eb[2] shouldBe (-0.1809822877867817771 plusOrMinus 1e-12) + astrom.eh[0] shouldBe (0.8940025429255499549 plusOrMinus 1e-12) + astrom.eh[1] shouldBe (-0.4110930268331896318 plusOrMinus 1e-12) + astrom.eh[2] shouldBe (-0.1782189006019749850 plusOrMinus 1e-12) + astrom.em shouldBe (1.010465295964664178 plusOrMinus 1e-12) + astrom.v[0] shouldBe (0.4289638912941341125e-4 plusOrMinus 1e-16) + astrom.v[1] shouldBe (0.8115034032405042132e-4 plusOrMinus 1e-16) + astrom.v[2] shouldBe (0.3517555135536470279e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999951686013142 plusOrMinus 1e-12) + astrom.bpn[0, 0] shouldBe (0.9999992060376761710 plusOrMinus 1e-12) + astrom.bpn[1, 0] shouldBe (0.4124244860106037157e-7 plusOrMinus 1e-12) + astrom.bpn[2, 0] shouldBe (0.1260128571051709670e-2 plusOrMinus 1e-12) + astrom.bpn[0, 1] shouldBe (-0.1282291987222130690e-7 plusOrMinus 1e-12) + astrom.bpn[1, 1] shouldBe (0.9999999997456835325 plusOrMinus 1e-12) + astrom.bpn[2, 1] shouldBe (-0.2255288829420524935e-4 plusOrMinus 1e-12) + astrom.bpn[0, 2] shouldBe (-0.1260128571661374559e-2 plusOrMinus 1e-12) + astrom.bpn[1, 2] shouldBe (0.2255285422953395494e-4 plusOrMinus 1e-12) + astrom.bpn[2, 2] shouldBe (0.9999992057833604343 plusOrMinus 1e-12) + eo shouldBe (-0.2900618712657375647e-2 plusOrMinus 1e-12) + } + + @Test + fun eraLdsun() { + val p = Vector3D(-0.763276255, -0.608633767, -0.216735543) + val e = Vector3D(-0.973644023, -0.20925523, -0.0907169552) + val p1 = eraLdsun(p, e, 0.999809214) + p1[0] shouldBe (-0.7632762580731413169 plusOrMinus 1e-12) + p1[1] shouldBe (-0.6086337635262647900 plusOrMinus 1e-12) + p1[2] shouldBe (-0.2167355419322321302 plusOrMinus 1e-12) + } + + @Test + fun eraLd() { + val p = Vector3D(-0.763276255, -0.608633767, -0.216735543) + val q = Vector3D(-0.763276255, -0.608633767, -0.216735543) + val e = Vector3D(0.76700421, 0.605629598, 0.211937094) + val p1 = eraLd(0.00028574, p, q, e, 8.91276983, 3e-10) + p1[0] shouldBe (-0.7632762548968159627 plusOrMinus 1e-12) + p1[1] shouldBe (-0.6086337670823762701 plusOrMinus 1e-12) + p1[2] shouldBe (-0.2167355431320546947 plusOrMinus 1e-12) + } + + @Test + fun eraApci() { + val ebp = Vector3D(0.901310875, -0.417402664, -0.180982288) + val ebv = Vector3D(0.00742727954, 0.0140507459, 0.00609045792) + val ehp = Vector3D(0.903358544, -0.415395237, -0.180084014) + val astrom = eraApci(2456165.5, 0.401182685, ebp, ebv, ehp, 0.0013122272, -2.92808623e-5, 3.05749468e-8) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb[0] shouldBe (0.901310875 plusOrMinus 1e-12) + astrom.eb[1] shouldBe (-0.417402664 plusOrMinus 1e-12) + astrom.eb[2] shouldBe (-0.180982288 plusOrMinus 1e-12) + astrom.eh[0] shouldBe (0.8940025429324143045 plusOrMinus 1e-12) + astrom.eh[1] shouldBe (-0.4110930268679817955 plusOrMinus 1e-12) + astrom.eh[2] shouldBe (-0.1782189004872870264 plusOrMinus 1e-12) + astrom.em shouldBe (1.010465295811013146 plusOrMinus 1e-12) + astrom.v[0] shouldBe (0.4289638913597693554e-4 plusOrMinus 1e-16) + astrom.v[1] shouldBe (0.8115034051581320575e-4 plusOrMinus 1e-16) + astrom.v[2] shouldBe (0.3517555136380563427e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999951686012981 plusOrMinus 1e-12) + astrom.bpn[0, 0] shouldBe (0.9999991390295159156 plusOrMinus 1e-12) + astrom.bpn[1, 0] shouldBe (0.4978650072505016932e-7 plusOrMinus 1e-12) + astrom.bpn[2, 0] shouldBe (0.1312227200000000000e-2 plusOrMinus 1e-12) + astrom.bpn[0, 1] shouldBe (-0.1136336653771609630e-7 plusOrMinus 1e-12) + astrom.bpn[1, 1] shouldBe (0.9999999995713154868 plusOrMinus 1e-12) + astrom.bpn[2, 1] shouldBe (-0.2928086230000000000e-4 plusOrMinus 1e-12) + astrom.bpn[0, 2] shouldBe (-0.1312227200895260194e-2 plusOrMinus 1e-12) + astrom.bpn[1, 2] shouldBe (0.2928082217872315680e-4 plusOrMinus 1e-12) + astrom.bpn[2, 2] shouldBe (0.9999991386008323373 plusOrMinus 1e-12) + } + + @Test + fun eraApcs13() { + val p = Vector3D(-6241497.16, 401346.896, -1251136.04) + val v = Vector3D(-29.264597, -455.021831, 0.0266151194) + val astrom = eraApcs13(2456165.5, 0.401182685, p, v) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb[0] shouldBe (0.9012691529025250644 plusOrMinus 1e-12) + astrom.eb[1] shouldBe (-0.4173999812023194317 plusOrMinus 1e-12) + astrom.eb[2] shouldBe (-0.1809906511146429670 plusOrMinus 1e-12) + astrom.eh[0] shouldBe (0.8939939101760130792 plusOrMinus 1e-12) + astrom.eh[1] shouldBe (-0.4111053891734021478 plusOrMinus 1e-12) + astrom.eh[2] shouldBe (-0.1782336880636997374 plusOrMinus 1e-12) + astrom.em shouldBe (1.010428384373491095 plusOrMinus 1e-12) + astrom.v[0] shouldBe (0.4279877294121697570e-4 plusOrMinus 1e-16) + astrom.v[1] shouldBe (0.7963255087052120678e-4 plusOrMinus 1e-16) + astrom.v[2] shouldBe (0.3517564013384691531e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999952947980978 plusOrMinus 1e-12) + astrom.bpn shouldBe Matrix3D.IDENTITY + } + + @Test + fun eraAtioq() { + val astrom = + eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) + val (aob, zob, hob, dob, rob) = eraAtioq(2.710121572969038991, 0.1729371367218230438, astrom) + + aob shouldBe (0.9233952224895122499e-1 plusOrMinus 1e-12) + zob shouldBe (1.407758704513549991 plusOrMinus 1e-12) + hob shouldBe (-0.9247619879881698140e-1 plusOrMinus 1e-12) + dob shouldBe (0.1717653435756234676 plusOrMinus 1e-12) + rob shouldBe (2.710085107988480746 plusOrMinus 1e-12) + } + + @Test + fun eraApio13() { + val astrom = + eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) + + astrom.along shouldBe (-0.5278008060295995733 plusOrMinus 1e-12) + astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) + astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) + astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) + astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) + astrom.diurab shouldBe (0.5135843661699913529e-6 plusOrMinus 1e-12) + astrom.eral shouldBe (2.617608909189664000 plusOrMinus 1e-12) + astrom.refa shouldBe (0.2014187785940396921e-3 plusOrMinus 1e-15) + astrom.refb shouldBe (-0.2361408314943696227e-6 plusOrMinus 1e-18) + } + + @Test + fun eraApio() { + val astrom = + eraApio(-3.01974337e-11, 3.14540971, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 0.000201418779, -2.36140831e-7) + + astrom.along shouldBe (-0.5278008060295995734 plusOrMinus 1e-12) + astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) + astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) + astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) + astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) + astrom.diurab shouldBe (0.5135843661699913529e-6 plusOrMinus 1e-12) + astrom.eral shouldBe (2.617608903970400427 plusOrMinus 1e-12) + astrom.refa shouldBe (0.2014187790000000000e-3 plusOrMinus 1e-15) + astrom.refb shouldBe (-0.2361408310000000000e-6 plusOrMinus 1e-18) + } + + @Test + fun eraAtco13() { + val (b, eo) = eraAtco13( + 2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, 2456384.5, 0.969254051, + 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, + 0.59, 0.55 + ) + + val (aob, zob, hob, dob, rob) = b + + aob shouldBe (0.9251774485485515207e-1 plusOrMinus 1e-12) + zob shouldBe (1.407661405256499357 plusOrMinus 1e-12) + hob shouldBe (-0.9265154431529724692e-1 plusOrMinus 1e-12) + dob shouldBe (0.1716626560072526200 plusOrMinus 1e-12) + rob shouldBe (2.710260453504961012 plusOrMinus 1e-12) + eo shouldBe (-0.003020548354802412839 plusOrMinus 1e-14) + } + + @Test + fun eraAticq() { + val (astrom) = eraApci13(2456165.5, 0.401182685) + val (ri, di) = eraAticq(2.710121572969038991, 0.1729371367218230438, astrom) + ri shouldBe (2.710126504531716819 plusOrMinus 1e-12) + di shouldBe (0.1740632537627034482 plusOrMinus 1e-12) + } + + @Test + fun eraAtic13() { + val (rc, dc, eo) = eraAtic13(2.710121572969038991, 0.1729371367218230438, 2456165.5, 0.401182685) + + rc shouldBe (2.710126504531716819 plusOrMinus 1e-12) + dc shouldBe (0.1740632537627034482 plusOrMinus 1e-12) + eo shouldBe (-0.002900618712657375647 plusOrMinus 1e-14) + } + + @Test + fun eraS2pv() { + val pv = eraS2pv(-3.21, 0.123, 0.456, -7.8e-6, 9.01e-6, -1.23e-5) + + pv.position[0] shouldBe (-0.4514964673880165228 plusOrMinus 1e-12) + pv.position[1] shouldBe (0.0309339427734258688 plusOrMinus 1e-12) + pv.position[2] shouldBe (0.0559466810510877933 plusOrMinus 1e-12) + + pv.velocity[0] shouldBe (0.1292270850663260170e-4 plusOrMinus 1e-16) + pv.velocity[1] shouldBe (0.2652814182060691422e-5 plusOrMinus 1e-16) + pv.velocity[2] shouldBe (0.2568431853930292259e-5 plusOrMinus 1e-16) + } + + @Test + fun eraStarpv() { + val pv = eraStarpv(0.01686756, -1.093989828, -1.78323516e-5, 2.336024047e-6, 0.74723.arcsec, (-21.6).kms) + + pv.position[0] shouldBe (126668.5912743160601 plusOrMinus 1e-10) + pv.position[1] shouldBe (2136.792716839935195 plusOrMinus 1e-12) + pv.position[2] shouldBe (-245251.2339876830091 plusOrMinus 1e-10) + + pv.velocity[0] shouldBe (-0.4051854008955659551e-2 plusOrMinus 1e-13) + pv.velocity[1] shouldBe (-0.6253919754414777970e-2 plusOrMinus 1e-15) + pv.velocity[2] shouldBe (0.1189353714588109341e-1 plusOrMinus 1e-13) + } + + @Test + fun eraAtoiq() { + val astrom = + eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) + + with(eraAtoiq('R', 2.710085107986886201, 0.1717653435758265198, astrom)) { + this[0] shouldBe (2.710121574447540810 plusOrMinus 1e-12) + this[1] shouldBe (0.17293718391166087785 plusOrMinus 1e-12) + } + with(eraAtoiq('H', -0.09247619879782006106, 0.1717653435758265198, astrom)) { + this[0] shouldBe (2.710121574448138676 plusOrMinus 1e-12) + this[1] shouldBe (0.1729371839116608778 plusOrMinus 1e-12) + } + with(eraAtoiq('A', 0.09233952224794989993, 1.407758704513722461, astrom)) { + this[0] shouldBe (2.710121574448138676 plusOrMinus 1e-12) + this[1] shouldBe (0.1729371839116608781 plusOrMinus 1e-12) + } + } - pv.velocity[0] shouldBe (-0.4051854008955659551e-2 plusOrMinus 1e-13) - pv.velocity[1] shouldBe (-0.6253919754414777970e-2 plusOrMinus 1e-15) - pv.velocity[2] shouldBe (0.1189353714588109341e-1 plusOrMinus 1e-13) + @Test + fun eraAtoc13() { + // @formatter:off + with(eraAtoc13('R', 2.710085107986886201, 0.1717653435758265198, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { + this[0] shouldBe (2.709956744659136129 plusOrMinus 1e-12) + this[1] shouldBe (0.1741696500898471362 plusOrMinus 1e-12) } - "eraAtoiq" { - val astrom = - eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) - - with(eraAtoiq('R', 2.710085107986886201, 0.1717653435758265198, astrom)) { - this[0] shouldBe (2.710121574447540810 plusOrMinus 1e-12) - this[1] shouldBe (0.17293718391166087785 plusOrMinus 1e-12) - } - with(eraAtoiq('H', -0.09247619879782006106, 0.1717653435758265198, astrom)) { - this[0] shouldBe (2.710121574448138676 plusOrMinus 1e-12) - this[1] shouldBe (0.1729371839116608778 plusOrMinus 1e-12) - } - with(eraAtoiq('A', 0.09233952224794989993, 1.407758704513722461, astrom)) { - this[0] shouldBe (2.710121574448138676 plusOrMinus 1e-12) - this[1] shouldBe (0.1729371839116608781 plusOrMinus 1e-12) - } + with(eraAtoc13('H', -0.09247619879782006106, 0.1717653435758265198, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { + this[0] shouldBe (2.709956744659734086 plusOrMinus 1e-12) + this[1] shouldBe (0.1741696500898471362 plusOrMinus 1e-12) } - "eraAtoc13" { - // @formatter:off - with(eraAtoc13('R', 2.710085107986886201, 0.1717653435758265198, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { - this[0] shouldBe (2.709956744659136129 plusOrMinus 1e-12) - this[1] shouldBe (0.1741696500898471362 plusOrMinus 1e-12) - } - with(eraAtoc13('H', -0.09247619879782006106, 0.1717653435758265198, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { - this[0] shouldBe (2.709956744659734086 plusOrMinus 1e-12) - this[1] shouldBe (0.1741696500898471362 plusOrMinus 1e-12) - } - with(eraAtoc13('A', 0.09233952224794989993, 1.407758704513722461, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { - this[0] shouldBe (2.709956744659734086 plusOrMinus 1e-12) - this[1] shouldBe (0.1741696500898471366 plusOrMinus 1e-12) - } - // @formatter:on + with(eraAtoc13('A', 0.09233952224794989993, 1.407758704513722461, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { + this[0] shouldBe (2.709956744659734086 plusOrMinus 1e-12) + this[1] shouldBe (0.1741696500898471366 plusOrMinus 1e-12) } + // @formatter:on } } diff --git a/nebulosa-fits/src/main/kotlin/nebulosa/fits/FitsFormat.kt b/nebulosa-fits/src/main/kotlin/nebulosa/fits/FitsFormat.kt index fef1fce0e..a17a61bf3 100644 --- a/nebulosa-fits/src/main/kotlin/nebulosa/fits/FitsFormat.kt +++ b/nebulosa-fits/src/main/kotlin/nebulosa/fits/FitsFormat.kt @@ -23,7 +23,7 @@ data object FitsFormat : ImageFormat { } @JvmStatic - fun BufferedSource.readSignature() = readString(6L, Charsets.US_ASCII) + fun BufferedSource.readSignature() = if (request(6L)) readString(6L, Charsets.US_ASCII) else "" fun isImageHdu(header: ReadableHeader) = header.getBoolean(FitsKeyword.SIMPLE) || header.getStringOrNull(FitsKeyword.XTENSION) == "IMAGE" @@ -71,11 +71,8 @@ data object FitsFormat : ImageFormat { val numberOfChannels = header.numberOfChannels val bitpix = header.bitpix val position = source.position - val rangeMin = header.getFloat(FitsKeyword.DATAMIN, 0f) - val rangeMax = header.getFloat(FitsKeyword.DATAMAX, 1f) - val range = rangeMin..rangeMax - val data = SeekableSourceImageData(source, position, width, height, numberOfChannels, bitpix, range) + val data = SeekableSourceImageData(source, position, width, height, numberOfChannels, bitpix) val skipBytes = computeRemainingBytesToSkip(data.totalSizeInBytes) if (skipBytes > 0L) source.seek(position + data.totalSizeInBytes + skipBytes) diff --git a/nebulosa-fits/src/main/kotlin/nebulosa/fits/SeekableSourceImageData.kt b/nebulosa-fits/src/main/kotlin/nebulosa/fits/SeekableSourceImageData.kt index 8733dde85..c514726a9 100644 --- a/nebulosa-fits/src/main/kotlin/nebulosa/fits/SeekableSourceImageData.kt +++ b/nebulosa-fits/src/main/kotlin/nebulosa/fits/SeekableSourceImageData.kt @@ -7,7 +7,6 @@ import nebulosa.io.SeekableSource import nebulosa.log.loggerFor import okio.Buffer import okio.Sink -import kotlin.math.max import kotlin.math.min @Suppress("NOTHING_TO_INLINE") @@ -18,7 +17,6 @@ internal data class SeekableSourceImageData( override val height: Int, override val numberOfChannels: Int, private val bitpix: Bitpix, - private val range: ClosedFloatingPointRange, ) : ImageData { @JvmField internal val channelSizeInBytes = (numberOfPixels * bitpix.byteLength).toLong() @@ -100,14 +98,12 @@ internal data class SeekableSourceImageData( } if (min < 0f || max > 1f) { - val rangeMin = min(range.start, min) - val rangeMax = max(range.endInclusive, max) - val rangeDelta = rangeMax - rangeMin + val rangeDelta = max - min - LOG.info("rescaling [{}, {}] to [0, 1]. channel={}, delta={}", rangeMin, rangeMax, channel, rangeDelta) + LOG.info("rescaling [{}, {}] to [0, 1]. channel={}, delta={}", min, max, channel, rangeDelta) for (i in output.indices) { - output[i] = (output[i] - rangeMin) / rangeDelta + output[i] = (output[i] - min) / rangeDelta } } } diff --git a/nebulosa-fits/src/test/kotlin/FitsFormatTest.kt b/nebulosa-fits/src/test/kotlin/FitsFormatTest.kt index 081321c31..17629276d 100644 --- a/nebulosa-fits/src/test/kotlin/FitsFormatTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsFormatTest.kt @@ -1,14 +1,17 @@ import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import nebulosa.fits.isFits -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.AbstractTest +import nebulosa.test.M82_COLOR_16_XISF +import nebulosa.test.NGC3344_COLOR_8_FITS +import org.junit.jupiter.api.Test -class FitsFormatTest : AbstractFitsAndXisfTest() { +class FitsFormatTest : AbstractTest() { - init { - "should be fits format" { - NGC3344_COLOR_8_FITS.isFits().shouldBeTrue() - M82_COLOR_16_XISF.isFits().shouldBeFalse() - } + @Test + fun shouldBeFitsFormat() { + NGC3344_COLOR_8_FITS.isFits().shouldBeTrue() + M82_COLOR_16_XISF.isFits().shouldBeFalse() + tempPath("empty-", ".fits").isFits().shouldBeFalse() } } diff --git a/nebulosa-fits/src/test/kotlin/FitsHeaderCardFormatterTest.kt b/nebulosa-fits/src/test/kotlin/FitsHeaderCardFormatterTest.kt index 54d099a1c..8a7ced33f 100644 --- a/nebulosa-fits/src/test/kotlin/FitsHeaderCardFormatterTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsHeaderCardFormatterTest.kt @@ -1,30 +1,37 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.fits.FitsHeaderCard import nebulosa.fits.FitsHeaderCardFormatter +import org.junit.jupiter.api.Test -class FitsHeaderCardFormatterTest : StringSpec() { +class FitsHeaderCardFormatterTest { - init { - "should format boolean value" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("SIMPLE", true, "Boolean Type")) - text shouldBe "SIMPLE = T / Boolean Type " - } - "should format integer value" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("NAXIS", 3, "Integer Type")) - text shouldBe "NAXIS = 3 / Integer Type " - } - "should format decimal value" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("EXPOSURE", 150.0, "Decimal Type")) - text shouldBe "EXPOSURE= 150.0 / Decimal Type " - } - "should format text value" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("INSTRUME", "Camera", "Text Type")) - text shouldBe "INSTRUME= 'Camera ' / Text Type " - } - "should format end key" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.END) - text shouldBe "END " - } + @Test + fun shouldFormatBooleanValue() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("SIMPLE", true, "Boolean Type")) + text shouldBe "SIMPLE = T / Boolean Type " + } + + @Test + fun shouldFormatIntegerValue() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("NAXIS", 3, "Integer Type")) + text shouldBe "NAXIS = 3 / Integer Type " + } + + @Test + fun shouldFormatDecimalValue() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("EXPOSURE", 150.0, "Decimal Type")) + text shouldBe "EXPOSURE= 150.0 / Decimal Type " + } + + @Test + fun shouldFormatTextValue() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("INSTRUME", "Camera", "Text Type")) + text shouldBe "INSTRUME= 'Camera ' / Text Type " + } + + @Test + fun shouldFormatEndKey() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.END) + text shouldBe "END " } } diff --git a/nebulosa-fits/src/test/kotlin/FitsHeaderCardParserTest.kt b/nebulosa-fits/src/test/kotlin/FitsHeaderCardParserTest.kt index 57ac0aabb..c2f522873 100644 --- a/nebulosa-fits/src/test/kotlin/FitsHeaderCardParserTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsHeaderCardParserTest.kt @@ -1,50 +1,57 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.fits.FitsHeaderCardParser import nebulosa.fits.FitsHeaderCardType +import org.junit.jupiter.api.Test -class FitsHeaderCardParserTest : StringSpec() { - - init { - "should parse boolean value" { - val parsed = FitsHeaderCardParser("SIMPLE = T / Boolean Type ") - - parsed.key shouldBe "SIMPLE" - parsed.value shouldBe "T" - parsed.type shouldBe FitsHeaderCardType.BOOLEAN - parsed.comment shouldBe "Boolean Type" - } - "should parse integer value" { - val parsed = FitsHeaderCardParser("NAXIS = 3 / Integer Type ") - - parsed.key shouldBe "NAXIS" - parsed.value shouldBe "3" - parsed.type shouldBe FitsHeaderCardType.INTEGER - parsed.comment shouldBe "Integer Type" - } - "should parse decimal value" { - val parsed = FitsHeaderCardParser("EXPOSURE= 150.0 / Decimal Type ") - - parsed.key shouldBe "EXPOSURE" - parsed.value shouldBe "150.0" - parsed.type shouldBe FitsHeaderCardType.DECIMAL - parsed.comment shouldBe "Decimal Type" - } - "should parse text value" { - val parsed = FitsHeaderCardParser("INSTRUME= 'Camera ' / Text Type ") - - parsed.key shouldBe "INSTRUME" - parsed.value shouldBe "Camera" - parsed.type shouldBe FitsHeaderCardType.TEXT - parsed.comment shouldBe "Text Type" - } - "should parse end key" { - val parsed = FitsHeaderCardParser("END") - - parsed.key shouldBe "END" - parsed.value shouldBe "" - parsed.type shouldBe FitsHeaderCardType.NONE - parsed.comment shouldBe "" - } +class FitsHeaderCardParserTest { + + @Test + fun shouldParseBooleanValue() { + val parsed = FitsHeaderCardParser("SIMPLE = T / Boolean Type ") + + parsed.key shouldBe "SIMPLE" + parsed.value shouldBe "T" + parsed.type shouldBe FitsHeaderCardType.BOOLEAN + parsed.comment shouldBe "Boolean Type" + } + + @Test + fun shouldParseIntegerValue() { + val parsed = FitsHeaderCardParser("NAXIS = 3 / Integer Type ") + + parsed.key shouldBe "NAXIS" + parsed.value shouldBe "3" + parsed.type shouldBe FitsHeaderCardType.INTEGER + parsed.comment shouldBe "Integer Type" + } + + @Test + fun shouldParseDecimalValue() { + val parsed = FitsHeaderCardParser("EXPOSURE= 150.0 / Decimal Type ") + + parsed.key shouldBe "EXPOSURE" + parsed.value shouldBe "150.0" + parsed.type shouldBe FitsHeaderCardType.DECIMAL + parsed.comment shouldBe "Decimal Type" + } + + @Test + fun shouldParseTextValue() { + val parsed = FitsHeaderCardParser("INSTRUME= 'Camera ' / Text Type ") + + parsed.key shouldBe "INSTRUME" + parsed.value shouldBe "Camera" + parsed.type shouldBe FitsHeaderCardType.TEXT + parsed.comment shouldBe "Text Type" + } + + @Test + fun shouldParseEndKey() { + val parsed = FitsHeaderCardParser("END") + + parsed.key shouldBe "END" + parsed.value shouldBe "" + parsed.type shouldBe FitsHeaderCardType.NONE + parsed.comment shouldBe "" } } diff --git a/nebulosa-fits/src/test/kotlin/FitsReadTest.kt b/nebulosa-fits/src/test/kotlin/FitsReadTest.kt index ca4e1d328..cac55b4e6 100644 --- a/nebulosa-fits/src/test/kotlin/FitsReadTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsReadTest.kt @@ -4,80 +4,98 @@ import nebulosa.fits.Bitpix import nebulosa.fits.bitpix import nebulosa.fits.fits import nebulosa.image.format.ImageHdu -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.* +import org.junit.jupiter.api.Test -class FitsReadTest : AbstractFitsAndXisfTest() { +class FitsReadTest { - init { - "mono:8-bit" { - val hdu = closeAfterEach(NGC3344_MONO_8_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.BYTE - } - "mono:16-bit" { - val hdu = closeAfterEach(NGC3344_MONO_16_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.SHORT - } - "mono:32-bit" { - val hdu = closeAfterEach(NGC3344_MONO_32_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.INTEGER - } - "mono:32-bit floating-point" { - val hdu = closeAfterEach(NGC3344_MONO_F32_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.FLOAT - } - "mono:64-bit floating-point" { - val hdu = closeAfterEach(NGC3344_MONO_F64_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.DOUBLE - } - "color:8-bit" { - val hdu = closeAfterEach(NGC3344_COLOR_8_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.BYTE - } - "color:16-bit" { - val hdu = closeAfterEach(NGC3344_COLOR_16_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.SHORT - } - "color:32-bit" { - val hdu = closeAfterEach(NGC3344_COLOR_32_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.INTEGER - } - "color:32-bit floating-point" { - val hdu = closeAfterEach(NGC3344_COLOR_F32_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.FLOAT - } - "color:64-bit floating-point" { - val hdu = closeAfterEach(NGC3344_COLOR_F64_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.DOUBLE - } + @Test + fun mono8Bit() { + val hdu = NGC3344_MONO_8_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.BYTE + } + + @Test + fun mono16Bit() { + val hdu = NGC3344_MONO_16_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.SHORT + } + + @Test + fun mono32Bit() { + val hdu = NGC3344_MONO_32_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.INTEGER + } + + @Test + fun monoFloat32Bit() { + val hdu = NGC3344_MONO_F32_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.FLOAT + } + + @Test + fun monoFloat64Bit() { + val hdu = NGC3344_MONO_F64_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.DOUBLE + } + + @Test + fun color8Bit() { + val hdu = NGC3344_COLOR_8_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.BYTE + } + + @Test + fun color16Bit() { + val hdu = NGC3344_COLOR_16_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.SHORT + } + + @Test + fun color32Bit() { + val hdu = NGC3344_COLOR_32_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.INTEGER + } + + @Test + fun colorFloat32Bit() { + val hdu = NGC3344_COLOR_F32_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.FLOAT + } + + @Test + fun colorFloat64Bit() { + val hdu = NGC3344_COLOR_F64_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.DOUBLE } } diff --git a/nebulosa-fits/src/test/kotlin/FitsWriteTest.kt b/nebulosa-fits/src/test/kotlin/FitsWriteTest.kt index 3f560ff36..d99ed215b 100644 --- a/nebulosa-fits/src/test/kotlin/FitsWriteTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsWriteTest.kt @@ -4,22 +4,23 @@ import nebulosa.fits.fits import nebulosa.image.format.ImageHdu import nebulosa.io.sink import nebulosa.io.source -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.AbstractTest +import nebulosa.test.NGC3344_MONO_8_FITS import okio.ByteString.Companion.toByteString +import org.junit.jupiter.api.Test -class FitsWriteTest : AbstractFitsAndXisfTest() { +class FitsWriteTest : AbstractTest() { - init { - "mono" { - val hdu0 = closeAfterEach(NGC3344_MONO_8_FITS.fits()).filterIsInstance().first() - val data = ByteArray(69120) - FitsFormat.write(data.sink(), listOf(hdu0)) - data.toByteString(2880, 66240).md5().hex() shouldBe "e1735e21c94dc49885fabc429406e573" + @Test + fun mono() { + val hdu0 = NGC3344_MONO_8_FITS.fits().autoClose().filterIsInstance().first() + val data = ByteArray(69120) + FitsFormat.write(data.sink(), listOf(hdu0)) + data.toByteString(2880, 66240).md5().hex() shouldBe "e1735e21c94dc49885fabc429406e573" - val fits = data.source().use { it.fits() } - val hdu1 = fits.filterIsInstance().first() + val fits = data.source().use { it.fits() } + val hdu1 = fits.filterIsInstance().first() - hdu0.header shouldBe hdu1.header - } + hdu0.header shouldBe hdu1.header } } diff --git a/nebulosa-hips2fits/src/main/kotlin/nebulosa/hips2fits/Hips2FitsService.kt b/nebulosa-hips2fits/src/main/kotlin/nebulosa/hips2fits/Hips2FitsService.kt index 888c00588..87deb6d54 100644 --- a/nebulosa-hips2fits/src/main/kotlin/nebulosa/hips2fits/Hips2FitsService.kt +++ b/nebulosa-hips2fits/src/main/kotlin/nebulosa/hips2fits/Hips2FitsService.kt @@ -35,6 +35,7 @@ class Hips2FitsService( coordSystem.name.lowercase(), rotation.toDegrees, format.name.lowercase(), ) + // https://alasky.cds.unistra.fr/MocServer/query?get=record&fmt=json&expr=ID%3DCDS*%20%26%26%20hips_service_url*%3D*alasky*%20%26%26%20dataproduct_type%3Dimage%20%26%26%20moc_sky_fraction%20%3E%3D%200.99%20%26%26%20obs_regime%3DOptical%2CInfrared%2CUV%2CRadio%2CX-ray%2CGamma-ray fun availableSurveys() = service .availableSurveys("ID=CDS* && hips_service_url*=*alasky* && dataproduct_type=image && moc_sky_fraction >= 0.99 && obs_regime=Optical,Infrared,UV,Radio,X-ray,Gamma-ray") diff --git a/nebulosa-hips2fits/src/test/kotlin/Hips2FitsServiceTest.kt b/nebulosa-hips2fits/src/test/kotlin/Hips2FitsServiceTest.kt index ce093b062..2d0101233 100644 --- a/nebulosa-hips2fits/src/test/kotlin/Hips2FitsServiceTest.kt +++ b/nebulosa-hips2fits/src/test/kotlin/Hips2FitsServiceTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldNotBeEmpty import io.kotest.matchers.doubles.shouldBeExactly @@ -11,31 +9,36 @@ import nebulosa.image.format.ImageHdu import nebulosa.io.source import nebulosa.math.deg import nebulosa.math.toDegrees -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.HTTP_CLIENT +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class Hips2FitsServiceTest : StringSpec() { +class Hips2FitsServiceTest { - init { - val service = Hips2FitsService() + @Test + fun query() { + val responseBody = SERVICE + .query("CDS/P/DSS2/red", 201.36506337683.deg, (-43.01911250808).deg) + .execute().body().shouldNotBeNull() + val fits = responseBody.use { it.bytes().source().fits() } + val hdu = fits.filterIsInstance().first().header + hdu.width shouldBeExactly 1200 + hdu.height shouldBeExactly 900 + hdu.rightAscension.toDegrees shouldBeExactly 201.36506337683 + hdu.declination.toDegrees shouldBeExactly -43.01911250808 + } + + @Test + @Disabled + fun availableSurveys() { + // Invalid UTF-8 start byte 0xb0. The API returns charset=ISO-8859-1. + val surveys = SERVICE.availableSurveys().execute().body().shouldNotBeNull() + surveys.shouldNotBeEmpty() + surveys shouldHaveSize 115 + } + + companion object { - "query" { - val responseBody = service - .query("CDS/P/DSS2/red", 201.36506337683.deg, (-43.01911250808).deg) - .execute() - .body() - .shouldNotBeNull() - val fits = responseBody.use { it.bytes().source().fits() } - val hdu = fits.filterIsInstance().first().header - hdu.width shouldBeExactly 1200 - hdu.height shouldBeExactly 900 - hdu.rightAscension.toDegrees shouldBeExactly 201.36506337683 - hdu.declination.toDegrees shouldBeExactly -43.01911250808 - } - "available surveys" { - val surveys = service.availableSurveys().execute().body().shouldNotBeNull() - surveys.shouldNotBeEmpty() - surveys shouldHaveSize 115 - } + @JvmStatic private val SERVICE = Hips2FitsService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt b/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt index 836dae134..66e890bb1 100644 --- a/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt +++ b/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt @@ -1,5 +1,4 @@ import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldNotBeEmpty import io.kotest.matchers.ints.shouldBeExactly @@ -16,110 +15,118 @@ import nebulosa.math.km import nebulosa.math.m import nebulosa.nasa.daf.SourceDaf import nebulosa.nasa.spk.Spk -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor +import nebulosa.test.HTTP_CLIENT import okio.ByteString.Companion.decodeBase64 +import org.junit.jupiter.api.Test import java.time.LocalDateTime -import java.util.concurrent.TimeUnit - -class HorizonsServiceTest : StringSpec() { - - init { - val httpClient = OkHttpClient.Builder() - .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) - .callTimeout(1, TimeUnit.MINUTES) - .connectTimeout(1, TimeUnit.MINUTES) - .readTimeout(1, TimeUnit.MINUTES) - .build() - - val service = HorizonsService(httpClient = httpClient) - - fun observe( - command: String, - startDate: LocalDateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0), - ) = service - .observer( - command, - 138.73119026648095.deg, 35.36276754848444.deg, 3776.m, - startDate, startDate.plusDays(1L), + +class HorizonsServiceTest { + + private fun observe( + command: String, + startDate: LocalDateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0), + ) = SERVICE.observer( + command, 138.73119026648095.deg, 35.36276754848444.deg, 3776.m, + startDate, startDate.plusDays(1L), extraPrecision = true, + ).execute().body().shouldNotBeNull().also { it.shouldNotBeEmpty() } + + @Test + fun spk() { + val start = LocalDateTime.of(2023, 1, 1, 0, 0) + val end = LocalDateTime.of(2023, 12, 31, 23, 59) + val spkFile = SERVICE.spk(1003517, start, end).execute().body().shouldNotBeNull() + spkFile.id shouldBeExactly 1003517 + val spkBytes = spkFile.spk.decodeBase64() + val spk = Spk(SourceDaf(spkBytes!!.asByteBuffer().source())) + spk.shouldHaveSize(1) + spk[10, 1003517].shouldNotBeNull() + } + + @Test + fun observerSun() { + val ephemeris = observe("10") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "274.11210" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "-23.38427" + } + + @Test + fun observerMoon() { + val ephemeris = observe("301") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "313.69977" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "-23.34363" + } + + @Test + fun observerMars() { + val ephemeris = observe("499") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "67.87667" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "24.65746" + ephemeris[dateTime]!![HorizonsQuantity.CONSTELLATION] shouldBe "Tau" + ephemeris[dateTime]!![HorizonsQuantity.ILLUMINATED_FRACTION] shouldBe "98.32712" + ephemeris[dateTime]!![HorizonsQuantity.VISUAL_MAGNITUDE] shouldBe "-1.426" + ephemeris[dateTime]!![HorizonsQuantity.SURFACE_BRIGHTNESS] shouldBe "4.239" + ephemeris[dateTime]!![HorizonsQuantity.APPARENT_AZ] shouldStartWith "317.88982" + ephemeris[dateTime]!![HorizonsQuantity.APPARENT_ALT] shouldStartWith "-16.31968" + ephemeris[dateTime]!![HorizonsQuantity.APPARENT_HOUR_ANGLE] shouldStartWith "8.99305" + } + + @Test + fun observerCeresBySpkId() { + val ephemeris = observe("DES=2000001;") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "185.92892" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "9.90348" + } + + @Test + fun observerCeresByIauNumber() { + val ephemeris = observe("1;") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "185.92892" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "9.90348" + } + + @Test + fun observerOsculatingElements() { + val ephemeris = SERVICE + .observerWithOsculationElements( + "(2023 GA2)", "2460049.5", ".6183399929327511", + "30.04427847488657", "30.56835826458952", "19.84449491210952", + ".3107780828530178", "2459989.479453452084", + longitude = 314.4173.deg, latitude = (-22.5354318).deg, elevation = 1.81754.km, + startTime = LocalDateTime.of(2023, 3, 11, 0, 0, 0), + endTime = LocalDateTime.of(2023, 4, 11, 0, 0, 0), extraPrecision = true, + stepSizeInMinutes = 1, ).execute().body().shouldNotBeNull() - .also { it.shouldNotBeEmpty() } - - "spk" { - val start = LocalDateTime.of(2023, 1, 1, 0, 0) - val end = LocalDateTime.of(2023, 12, 31, 23, 59) - val spkFile = service.spk(1003517, start, end).execute().body().shouldNotBeNull() - spkFile.id shouldBeExactly 1003517 - val spkBytes = spkFile.spk.decodeBase64() - val spk = Spk(SourceDaf(spkBytes!!.asByteBuffer().source())) - spk.shouldHaveSize(1) - spk[10, 1003517].shouldNotBeNull() - } - "observer: sun" { - val ephemeris = observe("10") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "274.11210" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "-23.38427" - } - "observer: moon" { - val ephemeris = observe("301") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "313.69977" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "-23.34363" - } - "observer: mars" { - val ephemeris = observe("499") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "67.87667" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "24.65746" - ephemeris[dateTime]!![HorizonsQuantity.CONSTELLATION] shouldBe "Tau" - ephemeris[dateTime]!![HorizonsQuantity.ILLUMINATED_FRACTION] shouldBe "98.32712" - ephemeris[dateTime]!![HorizonsQuantity.VISUAL_MAGNITUDE] shouldBe "-1.426" - ephemeris[dateTime]!![HorizonsQuantity.SURFACE_BRIGHTNESS] shouldBe "4.239" - ephemeris[dateTime]!![HorizonsQuantity.APPARENT_AZ] shouldStartWith "317.88982" - ephemeris[dateTime]!![HorizonsQuantity.APPARENT_ALT] shouldStartWith "-16.31968" - ephemeris[dateTime]!![HorizonsQuantity.APPARENT_HOUR_ANGLE] shouldStartWith "8.99305" - } - "observer: ceres by SPK ID" { - val ephemeris = observe("DES=2000001;") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "185.92892" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "9.90348" - } - "observer: ceres by IAU Number" { - val ephemeris = observe("1;") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "185.92892" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "9.90348" - } - "observer: osculating elements" { - val ephemeris = service - .observerWithOsculationElements( - "(2023 GA2)", "2460049.5", ".6183399929327511", - "30.04427847488657", "30.56835826458952", "19.84449491210952", - ".3107780828530178", "2459989.479453452084", - longitude = 314.4173.deg, latitude = (-22.5354318).deg, elevation = 1.81754.km, - startTime = LocalDateTime.of(2023, 3, 11, 0, 0, 0), - endTime = LocalDateTime.of(2023, 4, 11, 0, 0, 0), - extraPrecision = true, - stepSizeInMinutes = 1, - ).execute().body().shouldNotBeNull() - - ephemeris.shouldNotBeEmpty() - - val dateTime = LocalDateTime.of(2023, 3, 11, 0, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "344.45591" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "14.43086" - } - "cap & nofrag" { - observe("DES=1000041;CAP;NOFRAG", LocalDateTime.now().minusDays(2L)) - } - "non unique object" { - shouldThrow { observe("DES=1000041;") }.recordItems.shouldNotBeEmpty() - } - "no matches found" { - shouldThrow { observe("DES=1;CAP;NOFRAG") } - } + + ephemeris.shouldNotBeEmpty() + + val dateTime = LocalDateTime.of(2023, 3, 11, 0, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "344.45591" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "14.43086" + } + + @Test + fun capAndNofrag() { + observe("DES=1000041;CAP;NOFRAG", LocalDateTime.now().minusDays(2L)) + } + + @Test + fun nonUniqueObject() { + shouldThrow { observe("DES=1000041;") }.recordItems.shouldNotBeEmpty() + } + + @Test + fun noMatchesFound() { + shouldThrow { observe("DES=1;CAP;NOFRAG") } + } + + companion object { + + @JvmStatic private val SERVICE = HorizonsService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-image-format/src/main/kotlin/nebulosa/image/format/ImageHdu.kt b/nebulosa-image-format/src/main/kotlin/nebulosa/image/format/ImageHdu.kt index 140125c46..3c8275ddf 100644 --- a/nebulosa-image-format/src/main/kotlin/nebulosa/image/format/ImageHdu.kt +++ b/nebulosa-image-format/src/main/kotlin/nebulosa/image/format/ImageHdu.kt @@ -1,5 +1,11 @@ package nebulosa.image.format +import java.awt.image.BufferedImage +import java.awt.image.BufferedImage.TYPE_BYTE_GRAY +import java.awt.image.BufferedImage.TYPE_INT_RGB +import java.awt.image.DataBufferByte +import java.awt.image.DataBufferInt + interface ImageHdu : Hdu { val width: Int @@ -10,4 +16,33 @@ interface ImageHdu : Hdu { val isMono get() = numberOfChannels == 1 + + companion object { + + @JvmStatic + fun ImageHdu.makeImage(): BufferedImage { + val type = if (numberOfChannels == 1) TYPE_BYTE_GRAY else TYPE_INT_RGB + val image = BufferedImage(width, height, type) + val numberOfPixels = data.numberOfPixels + + if (numberOfChannels == 1) { + val buffer = (image.raster.dataBuffer as DataBufferByte).data + + repeat(numberOfPixels) { + buffer[it] = (data.red[it] * 255f).toInt().toByte() + } + } else { + val buffer = (image.raster.dataBuffer as DataBufferInt).data + + repeat(numberOfPixels) { + val red = (data.red[it] * 255f).toInt() and 0xFF + val green = (data.green[it] * 255f).toInt() and 0xFF + val blue = (data.blue[it] * 255f).toInt() and 0xFF + buffer[it] = blue or (green shl 8) or (red shl 16) + } + } + + return image + } + } } diff --git a/nebulosa-image/src/main/kotlin/nebulosa/image/Image.kt b/nebulosa-image/src/main/kotlin/nebulosa/image/Image.kt index eae28c6ec..600d4eb58 100644 --- a/nebulosa-image/src/main/kotlin/nebulosa/image/Image.kt +++ b/nebulosa-image/src/main/kotlin/nebulosa/image/Image.kt @@ -221,6 +221,9 @@ class Image internal constructor( companion object { + @JvmStatic + fun T.asImage(debayer: Boolean = true) where T : ImageRepresentation, T : AutoCloseable = use { open(it, debayer) } + @JvmStatic internal fun colorModel(mono: Boolean): ColorModel { val space = ColorSpace.getInstance(if (mono) ColorSpace.CS_GRAY else ColorSpace.CS_sRGB) diff --git a/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/computation/Histogram.kt b/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/computation/Histogram.kt index e439a8a82..e9974ff98 100644 --- a/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/computation/Histogram.kt +++ b/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/computation/Histogram.kt @@ -4,7 +4,6 @@ import nebulosa.image.Image import nebulosa.image.Image.Companion.forEach import nebulosa.image.algorithms.ComputationAlgorithm import nebulosa.image.format.ImageChannel -import kotlin.math.max import kotlin.math.min data class Histogram( @@ -17,11 +16,11 @@ data class Histogram( } override fun compute(source: Image): IntArray { - val data = IntArray(2 shl (bitLength - 1)) + val data = IntArray(1 shl bitLength) val maxValue = data.size - 1 source.forEach(channel) { - val value = max(0, min((it * maxValue).toInt(), maxValue)) + val value = min((it * maxValue).toInt(), maxValue) data[value]++ } diff --git a/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/transformation/AutoScreenTransformFunction.kt b/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/transformation/AutoScreenTransformFunction.kt index ddd031f63..4376998ff 100644 --- a/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/transformation/AutoScreenTransformFunction.kt +++ b/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/transformation/AutoScreenTransformFunction.kt @@ -5,6 +5,7 @@ import nebulosa.image.algorithms.ComputationAlgorithm import nebulosa.image.algorithms.TransformAlgorithm import nebulosa.image.algorithms.computation.Median import nebulosa.image.algorithms.computation.MedianAbsoluteDeviation +import nebulosa.log.debug import nebulosa.log.loggerFor import kotlin.math.max import kotlin.math.min @@ -35,7 +36,7 @@ data object AutoScreenTransformFunction : ComputationAlgorithm ((m - 1) * x) / ((2 * m - 1) * x - m) } - LOG.info("STF auto stretch. midtone={}, shadow={}, highlight={}", midtone, shadow, highlight) + LOG.debug { "STF auto stretch. midtone=$midtone, shadow=$shadow, highlight=$highlight" } return ScreenTransformFunction.Parameters(midtone, shadow, highlight) } diff --git a/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/transformation/ScreenTransformFunction.kt b/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/transformation/ScreenTransformFunction.kt index 8c9737a48..87c17d9bc 100644 --- a/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/transformation/ScreenTransformFunction.kt +++ b/nebulosa-image/src/main/kotlin/nebulosa/image/algorithms/transformation/ScreenTransformFunction.kt @@ -16,11 +16,13 @@ data class ScreenTransformFunction( ) : TransformAlgorithm { data class Parameters( - val midtone: Float = 0.5f, - val shadow: Float = 0f, - val highlight: Float = 1f, + @JvmField val midtone: Float = 0.5f, + @JvmField val shadow: Float = 0f, + @JvmField val highlight: Float = 1f, ) { + constructor(midtone: Int, shadow: Int, highlight: Int) : this(midtone / 65536f, shadow / 65536f, highlight / 65536f) + companion object { @JvmStatic val DEFAULT = Parameters() diff --git a/nebulosa-image/src/test/kotlin/ComputationAlgorithmTest.kt b/nebulosa-image/src/test/kotlin/ComputationAlgorithmTest.kt index 1ade07c95..a1d194c96 100644 --- a/nebulosa-image/src/test/kotlin/ComputationAlgorithmTest.kt +++ b/nebulosa-image/src/test/kotlin/ComputationAlgorithmTest.kt @@ -3,87 +3,94 @@ import io.kotest.matchers.floats.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.fits.fits -import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.computation.MedianAbsoluteDeviation import nebulosa.image.algorithms.computation.Statistics import nebulosa.image.format.ImageChannel -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.NGC3344_COLOR_F32_FITS +import nebulosa.test.NGC3344_MONO_F32_FITS +import org.junit.jupiter.api.Test -class ComputationAlgorithmTest : AbstractFitsAndXisfTest() { +class ComputationAlgorithmTest { - init { - "mono:median absolute deviation" { - val mImage = Image.open(NGC3344_MONO_F32_FITS.fits()) - mImage.compute(MedianAbsoluteDeviation()) shouldBe (0.0862f plusOrMinus 1e-4f) - } - "mono:statistics" { - val mImage = Image.open(NGC3344_MONO_F32_FITS.fits()) - val statistics = mImage.compute(Statistics.GRAY) + @Test + fun monoMedianAbsoluteDeviation() { + val mImage = NGC3344_MONO_F32_FITS.fits().asImage() + mImage.compute(MedianAbsoluteDeviation()) shouldBe (0.0862f plusOrMinus 1e-4f) + } + + @Test + fun monoStatistics() { + val mImage = NGC3344_MONO_F32_FITS.fits().asImage() + val statistics = mImage.compute(Statistics.GRAY) + + statistics.count shouldBeExactly 65536 + statistics.maxCount shouldBeExactly 926 + statistics.mean shouldBe (0.2848f plusOrMinus 1e-4f) + statistics.sumOfSquares shouldBe (6827.543f plusOrMinus 1e-3f) + statistics.median shouldBe (0.2470f plusOrMinus 1e-4f) + statistics.variance shouldBe (0.02302f plusOrMinus 1e-4f) + statistics.stdDev shouldBe (0.1517f plusOrMinus 1e-4f) + statistics.avgDev shouldBe (0.11553f plusOrMinus 1e-5f) + statistics.minimum shouldBeExactly 0.03529412f + statistics.maximum shouldBeExactly 1f + } + + @Test + fun colorMedianAbsoluteDeviation() { + val cImage = NGC3344_COLOR_F32_FITS.fits().asImage() + cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.RED)) shouldBe (0.0823f plusOrMinus 1e-4f) + cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.GREEN)) shouldBe (0.0745f plusOrMinus 1e-4f) + cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.BLUE)) shouldBe (0.0705f plusOrMinus 1e-4f) + } + + @Test + fun colorStatistics() { + val cImage = NGC3344_COLOR_F32_FITS.fits().asImage() + + run { + val statistics = cImage.compute(Statistics.RED) statistics.count shouldBeExactly 65536 - statistics.maxCount shouldBeExactly 926 - statistics.mean shouldBe (0.2848f plusOrMinus 1e-4f) - statistics.sumOfSquares shouldBe (6827.543f plusOrMinus 1e-3f) + statistics.maxCount shouldBeExactly 1027 + statistics.mean shouldBe (0.2843f plusOrMinus 1e-4f) + statistics.sumOfSquares shouldBe (6723.677f plusOrMinus 1e-3f) statistics.median shouldBe (0.2470f plusOrMinus 1e-4f) - statistics.variance shouldBe (0.02302f plusOrMinus 1e-4f) - statistics.stdDev shouldBe (0.1517f plusOrMinus 1e-4f) - statistics.avgDev shouldBe (0.11553f plusOrMinus 1e-5f) - statistics.minimum shouldBeExactly 0.03529412f + statistics.variance shouldBe (0.0217f plusOrMinus 1e-4f) + statistics.stdDev shouldBe (0.1474f plusOrMinus 1e-4f) + statistics.avgDev shouldBe (0.11034f plusOrMinus 1e-5f) + statistics.minimum shouldBeExactly 0.047058824f statistics.maximum shouldBeExactly 1f } - "color:median absolute deviation" { - val cImage = Image.open(NGC3344_COLOR_F32_FITS.fits()) - cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.RED)) shouldBe (0.0823f plusOrMinus 1e-4f) - cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.GREEN)) shouldBe (0.0745f plusOrMinus 1e-4f) - cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.BLUE)) shouldBe (0.0705f plusOrMinus 1e-4f) - } - "color:statistics" { - val cImage = Image.open(NGC3344_COLOR_F32_FITS.fits()) - - run { - val statistics = cImage.compute(Statistics.RED) - statistics.count shouldBeExactly 65536 - statistics.maxCount shouldBeExactly 1027 - statistics.mean shouldBe (0.2843f plusOrMinus 1e-4f) - statistics.sumOfSquares shouldBe (6723.677f plusOrMinus 1e-3f) - statistics.median shouldBe (0.2470f plusOrMinus 1e-4f) - statistics.variance shouldBe (0.0217f plusOrMinus 1e-4f) - statistics.stdDev shouldBe (0.1474f plusOrMinus 1e-4f) - statistics.avgDev shouldBe (0.11034f plusOrMinus 1e-5f) - statistics.minimum shouldBeExactly 0.047058824f - statistics.maximum shouldBeExactly 1f - } + run { + val statistics = cImage.compute(Statistics.GREEN) - run { - val statistics = cImage.compute(Statistics.GREEN) - - statistics.count shouldBeExactly 65536 - statistics.maxCount shouldBeExactly 1181 - statistics.mean shouldBe (0.2635f plusOrMinus 1e-4f) - statistics.sumOfSquares shouldBe (5845.3496f plusOrMinus 1e-3f) - statistics.median shouldBe (0.2235f plusOrMinus 1e-4f) - statistics.variance shouldBe (0.0197f plusOrMinus 1e-4f) - statistics.stdDev shouldBe (0.1404f plusOrMinus 1e-4f) - statistics.avgDev shouldBe (0.10392f plusOrMinus 1e-5f) - statistics.minimum shouldBeExactly 0.050980393f - statistics.maximum shouldBeExactly 1f - } + statistics.count shouldBeExactly 65536 + statistics.maxCount shouldBeExactly 1181 + statistics.mean shouldBe (0.2635f plusOrMinus 1e-4f) + statistics.sumOfSquares shouldBe (5845.3496f plusOrMinus 1e-3f) + statistics.median shouldBe (0.2235f plusOrMinus 1e-4f) + statistics.variance shouldBe (0.0197f plusOrMinus 1e-4f) + statistics.stdDev shouldBe (0.1404f plusOrMinus 1e-4f) + statistics.avgDev shouldBe (0.10392f plusOrMinus 1e-5f) + statistics.minimum shouldBeExactly 0.050980393f + statistics.maximum shouldBeExactly 1f + } - run { - val statistics = cImage.compute(Statistics.BLUE) + run { + val statistics = cImage.compute(Statistics.BLUE) - statistics.count shouldBeExactly 65536 - statistics.maxCount shouldBeExactly 1234 - statistics.mean shouldBe (0.2619f plusOrMinus 1e-4f) - statistics.sumOfSquares shouldBe (5615.849f plusOrMinus 1e-3f) - statistics.median shouldBe (0.2235f plusOrMinus 1e-4f) - statistics.variance shouldBe (0.0170f plusOrMinus 1e-4f) - statistics.stdDev shouldBe (0.1306f plusOrMinus 1e-4f) - statistics.avgDev shouldBe (0.09864f plusOrMinus 1e-5f) - statistics.minimum shouldBeExactly 0.050980393f - statistics.maximum shouldBeExactly 1f - } + statistics.count shouldBeExactly 65536 + statistics.maxCount shouldBeExactly 1234 + statistics.mean shouldBe (0.2619f plusOrMinus 1e-4f) + statistics.sumOfSquares shouldBe (5615.849f plusOrMinus 1e-3f) + statistics.median shouldBe (0.2235f plusOrMinus 1e-4f) + statistics.variance shouldBe (0.0170f plusOrMinus 1e-4f) + statistics.stdDev shouldBe (0.1306f plusOrMinus 1e-4f) + statistics.avgDev shouldBe (0.09864f plusOrMinus 1e-5f) + statistics.minimum shouldBeExactly 0.050980393f + statistics.maximum shouldBeExactly 1f } } } diff --git a/nebulosa-image/src/test/kotlin/FitsTransformAlgorithmTest.kt b/nebulosa-image/src/test/kotlin/FitsTransformAlgorithmTest.kt index 631ae94c8..629b6512d 100644 --- a/nebulosa-image/src/test/kotlin/FitsTransformAlgorithmTest.kt +++ b/nebulosa-image/src/test/kotlin/FitsTransformAlgorithmTest.kt @@ -3,278 +3,383 @@ import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.fits.fits -import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.transformation.* import nebulosa.image.algorithms.transformation.convolution.* import nebulosa.image.format.ImageChannel -import nebulosa.test.AbstractFitsAndXisfTest - -class FitsTransformAlgorithmTest : AbstractFitsAndXisfTest() { - - init { - "mono:raw" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.save("fits-mono-raw").second shouldBe "e17cfc29c3b343409cd8617b6913330e" - } - "mono:vertical flip" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(VerticalFlip) - mImage.save("fits-mono-vertical-flip").second shouldBe "262260dfe719726c0e7829a088279a21" - } - "mono:horizontal flip" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(HorizontalFlip) - mImage.save("fits-mono-horizontal-flip").second shouldBe "daf0f05db5de3750962f338527564b27" - } - "mono:vertical & horizontal flip" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(VerticalFlip, HorizontalFlip) - mImage.save("fits-mono-vertical-horizontal-flip").second shouldBe "3bc81f579a0e34ce9312c3b242209166" - } - "mono:subframe" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) - nImage.width shouldBeExactly 16 - nImage.height shouldBeExactly 16 - nImage.mono.shouldBeTrue() - nImage.save("fits-mono-subframe").second shouldBe "4d9984e778f82dde10b9aeeee7a29fe0" - } - "mono:sharpen" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(Sharpen) - mImage.save("fits-mono-sharpen").second shouldBe "0b162242a4e673f6480b5206cf49ca50" - } - "mono:mean" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(Mean) - mImage.save("fits-mono-mean").second shouldBe "cf866292f657c379ae3965931dd8eeea" - } - "mono:invert" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(Invert) - mImage.save("fits-mono-invert").second shouldBe "6e94463bb5b9561de1f0ee0a154db53e" - } - "mono:emboss" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(Emboss) - mImage.save("fits-mono-emboss").second shouldBe "94a8ef5e4573e392d087cf10c905ba12" - } - "mono:edges" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(Edges) - mImage.save("fits-mono-edges").second shouldBe "27ccd5f5e6098d0cae27e7495e18dd72" - } - "mono:blur" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(Blur) - mImage.save("fits-mono-blur").second shouldBe "f2c5466dccf71b5c4bee86c5fbbb95fc" - } - "mono:gaussian blur" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) - mImage.save("fits-mono-gaussian-blur").second shouldBe "69057b0c4461fb0d55b779da9e72fd69" - } - "mono:STF:midtone = 0.1, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.1f)) - mImage.save("fits-mono-stf-01-00-10").second shouldBe "22c0bd985e70a01330722d912869d6ee" - } - "mono:STF:midtone = 0.9, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.9f)) - mImage.save("fits-mono-stf-09-00-10").second shouldBe "553ccb7546dce3a8f742d5e8f7c58a3f" - } - "mono:STF:midtone = 0.1, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) - mImage.save("fits-mono-stf-01-05-10").second shouldBe "f31db854fab72033dce2f8c572ec6783" - } - "mono:STF:midtone = 0.9, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) - mImage.save("fits-mono-stf-09-05-10").second shouldBe "633b49c4a1dbb5ad8e6a9d74f330636d" - } - "mono:STF:midtone = 0.1, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) - mImage.save("fits-mono-stf-01-00-05").second shouldBe "26036937eb3e5f99cd6129f709ce4b31" - } - "mono:STF:midtone = 0.9, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) - mImage.save("fits-mono-stf-09-00-05").second shouldBe "e8f694dae666ac15ce2f8a169eb84024" - } - "mono:STF:midtone = 0.1, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) - mImage.save("fits-mono-stf-01-04-06").second shouldBe "5226aba21669a24f985703b3e7220568" - } - "mono:STF:midtone = 0.9, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) - mImage.save("fits-mono-stf-09-04-06").second shouldBe "c2acb25ef7be92a51f63e673ec9a850f" - } - "mono:auto STF" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) - mImage.transform(AutoScreenTransformFunction) - mImage.save("fits-mono-auto-stf").second shouldBe "e17cfc29c3b343409cd8617b6913330e" - } - "color:raw" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.save("fits-color-raw").second shouldBe "18fb83e240bc7a4cbafbc1aba2741db6" - } - "color:vertical flip" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(VerticalFlip) - mImage.save("fits-color-vertical-flip").second shouldBe "b717ecda5c5bba50cfa06304ef2bca88" - } - "color:horizontal flip" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(HorizontalFlip) - mImage.save("fits-color-horizontal-flip").second shouldBe "f70228600c77551473008ed4b9986439" - } - "color:vertical & horizontal flip" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(VerticalFlip, HorizontalFlip) - mImage.save("fits-color-vertical-horizontal-flip").second shouldBe "1237314044f20307b76203148af855e3" - } - "color:subframe" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) - nImage.width shouldBeExactly 16 - nImage.height shouldBeExactly 16 - nImage.mono.shouldBeFalse() - nImage.save("fits-color-subframe").second shouldBe "282fc4fdf9142fcb4b18e1df1eef4caa" - } - "color:sharpen" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(Sharpen) - mImage.save("fits-color-sharpen").second shouldBe "e562282bdafdeba6ce88981bb9c3ba61" - } - "color:mean" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(Mean) - mImage.save("fits-color-mean").second shouldBe "a8380d928aaa756e202ba43bd3a2f207" - } - "color:invert" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(Invert) - mImage.save("fits-color-invert").second shouldBe "decad269ec26450aebeaf7546867b5f8" - } - "color:emboss" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(Emboss) - mImage.save("fits-color-emboss").second shouldBe "58d69250f1233055aa33f9ec7ca40af1" - } - "color:edges" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(Edges) - mImage.save("fits-color-edges").second shouldBe "091f2955740a8edcd2401dc416d19d51" - } - "color:blur" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(Blur) - mImage.save("fits-color-blur").second shouldBe "0fca440b763de5380fa29de736f3c792" - } - "color:gaussian blur" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) - mImage.save("fits-color-gaussian-blur").second shouldBe "394d1a4f136f15c802dd73004c421d64" - } - "color:STF:midtone = 0.1, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.1f)) - mImage.save("fits-color-stf-01-00-10").second shouldBe "e952bd263df6fd275b9a80aca554cb4b" - } - "color:STF:midtone = 0.9, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.9f)) - mImage.save("fits-color-stf-09-00-10").second shouldBe "038809d7612018e2e5c19d5e1f551abd" - } - "color:STF:midtone = 0.1, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) - mImage.save("fits-color-stf-01-05-10").second shouldBe "70e812260f56f8621002327575611f31" - } - "color:STF:midtone = 0.9, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) - mImage.save("fits-color-stf-09-05-10").second shouldBe "6ca400f617f466a9eb02a3a6f2985d99" - } - "color:STF:midtone = 0.1, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) - mImage.save("fits-color-stf-01-00-05").second shouldBe "3cd98ee9a8949d5100295acccd77010b" - } - "color:STF:midtone = 0.9, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) - mImage.save("fits-color-stf-09-00-05").second shouldBe "2cfeffc88c893cc5883d8a2221f29b91" - } - "color:STF:midtone = 0.1, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) - mImage.save("fits-color-stf-01-04-06").second shouldBe "532a07a1a166eb007c2e40651aec2097" - } - "color:STF:midtone = 0.9, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) - mImage.save("fits-color-stf-09-04-06").second shouldBe "eb3d940d9fd2c8814e930715e89897c4" - } - "color:auto STF" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(AutoScreenTransformFunction) - mImage.save("fits-color-auto-stf").second shouldBe "a9c3657d8597b927607eb438e666d3a0" - } - "color:SCNR Maximum Mask" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_MASK)) - mImage.save("fits-color-scnr-maximum-mask").second shouldBe "e7d2155e18ff1e3172f4e849ae983145" - } - "color:SCNR Additive Mask" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.ADDITIVE_MASK)) - mImage.save("fits-color-scnr-additive-mask").second shouldBe "a458c44cedcda704de16d80053fd87eb" - } - "color:SCNR Average Neutral" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.AVERAGE_NEUTRAL)) - mImage.save("fits-color-scnr-average-neutral").second shouldBe "e07345ffc4982a62301c95c76d3efb35" - } - "color:SCNR Maximum Neutral" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_NEUTRAL)) - mImage.save("fits-color-scnr-maximum-neutral").second shouldBe "a1d4b04f57b001ba4a996bab0407fd7e" - } - "color:SCNR Minimum Neutral" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MINIMUM_NEUTRAL)) - mImage.save("fits-color-scnr-minimum-neutral").second shouldBe "8b7be57ff38da9c97b35d7888047c0f9" - } - "color:grayscale BT-709" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - val nImage = mImage.transform(Grayscale.BT709) - nImage.save("fits-color-grayscale-bt709").second shouldBe "cab675aa35390a2d58cd48555d91054f" - } - "color:grayscale RMY" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - val nImage = mImage.transform(Grayscale.RMY) - nImage.save("fits-color-grayscale-rmy").second shouldBe "e113627002a4178d1010a2f6246e325f" - } - "color:grayscale Y" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) - val nImage = mImage.transform(Grayscale.Y) - nImage.save("fits-color-grayscale-y").second shouldBe "24dd4a7e0fa9e4be34c53c924a78a940" - } - "color:debayer" { - val mImage = Image.open(DEBAYER_FITS.fits()) - val nImage = mImage.transform(AutoScreenTransformFunction) - nImage.save("fits-color-debayer").second shouldBe "86b5bdd67dfd6bbf5495afae4bf2bc04" - } - "color:no-debayer" { - val mImage = Image.open(DEBAYER_FITS.fits(), false) - val nImage = mImage.transform(AutoScreenTransformFunction) - nImage.save("fits-color-no-debayer").second shouldBe "958ccea020deec1f0c075042a9ba37c3" - } +import nebulosa.test.DEBAYER_FITS +import nebulosa.test.NGC3344_COLOR_32_FITS +import nebulosa.test.NGC3344_MONO_8_FITS +import nebulosa.test.save +import org.junit.jupiter.api.Test + +class FitsTransformAlgorithmTest { + + @Test + fun monoRaw() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.save("fits-mono-raw").second shouldBe "e17cfc29c3b343409cd8617b6913330e" + } + + @Test + fun monoVerticalFlip() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(VerticalFlip) + mImage.save("fits-mono-vertical-flip").second shouldBe "262260dfe719726c0e7829a088279a21" + } + + @Test + fun monoHorizontalFlip() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(HorizontalFlip) + mImage.save("fits-mono-horizontal-flip").second shouldBe "daf0f05db5de3750962f338527564b27" + } + + @Test + fun monoVerticalAndHorizontalFlip() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(VerticalFlip, HorizontalFlip) + mImage.save("fits-mono-vertical-horizontal-flip").second shouldBe "3bc81f579a0e34ce9312c3b242209166" + } + + @Test + fun monoSubframe() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) + nImage.width shouldBeExactly 16 + nImage.height shouldBeExactly 16 + nImage.mono.shouldBeTrue() + nImage.save("fits-mono-subframe").second shouldBe "4d9984e778f82dde10b9aeeee7a29fe0" + } + + @Test + fun monoSharpen() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(Sharpen) + mImage.save("fits-mono-sharpen").second shouldBe "0b162242a4e673f6480b5206cf49ca50" + } + + @Test + fun monoMean() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(Mean) + mImage.save("fits-mono-mean").second shouldBe "cf866292f657c379ae3965931dd8eeea" + } + + @Test + fun monoInvert() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(Invert) + mImage.save("fits-mono-invert").second shouldBe "6e94463bb5b9561de1f0ee0a154db53e" + } + + @Test + fun monoEmboss() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(Emboss) + mImage.save("fits-mono-emboss").second shouldBe "94a8ef5e4573e392d087cf10c905ba12" + } + + @Test + fun monoEdges() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(Edges) + mImage.save("fits-mono-edges").second shouldBe "27ccd5f5e6098d0cae27e7495e18dd72" + } + + @Test + fun monoBlur() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(Blur) + mImage.save("fits-mono-blur").second shouldBe "f2c5466dccf71b5c4bee86c5fbbb95fc" + } + + @Test + fun monoGaussianBlur() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) + mImage.save("fits-mono-gaussian-blur").second shouldBe "69057b0c4461fb0d55b779da9e72fd69" + } + + @Test + fun monoStfMidtone01Shadow00Highlight10() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.1f)) + mImage.save("fits-mono-stf-01-00-10").second shouldBe "22c0bd985e70a01330722d912869d6ee" + } + + @Test + fun monoStfMidtone09Shadow00Highlight10() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.9f)) + mImage.save("fits-mono-stf-09-00-10").second shouldBe "553ccb7546dce3a8f742d5e8f7c58a3f" + } + + @Test + fun monoStfMidtone01Shadow05Highlight10() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) + mImage.save("fits-mono-stf-01-05-10").second shouldBe "f31db854fab72033dce2f8c572ec6783" + } + + @Test + fun monoStfMidtone09Shadow05Highlight10() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) + mImage.save("fits-mono-stf-09-05-10").second shouldBe "633b49c4a1dbb5ad8e6a9d74f330636d" + } + + @Test + fun monoStfMidtone01Shadow00Highlight05() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) + mImage.save("fits-mono-stf-01-00-05").second shouldBe "26036937eb3e5f99cd6129f709ce4b31" + } + + @Test + fun monoStfMidtone09Shadow00Highlight05() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) + mImage.save("fits-mono-stf-09-00-05").second shouldBe "e8f694dae666ac15ce2f8a169eb84024" + } + + @Test + fun monoStfMidtone01Shadow04Highlight06() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) + mImage.save("fits-mono-stf-01-04-06").second shouldBe "5226aba21669a24f985703b3e7220568" + } + + @Test + fun monoStfMidtone09Shadow04Highlight06() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) + mImage.save("fits-mono-stf-09-04-06").second shouldBe "c2acb25ef7be92a51f63e673ec9a850f" + } + + @Test + fun monoAutoStf() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() + mImage.transform(AutoScreenTransformFunction) + mImage.save("fits-mono-auto-stf").second shouldBe "e17cfc29c3b343409cd8617b6913330e" + } + + @Test + fun colorRaw() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.save("fits-color-raw").second shouldBe "18fb83e240bc7a4cbafbc1aba2741db6" + } + + @Test + fun colorVerticalFlip() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(VerticalFlip) + mImage.save("fits-color-vertical-flip").second shouldBe "b717ecda5c5bba50cfa06304ef2bca88" + } + + @Test + fun colorHorizontalFlip() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(HorizontalFlip) + mImage.save("fits-color-horizontal-flip").second shouldBe "f70228600c77551473008ed4b9986439" + } + + @Test + fun colorVerticalAndHorizontalFlip() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(VerticalFlip, HorizontalFlip) + mImage.save("fits-color-vertical-horizontal-flip").second shouldBe "1237314044f20307b76203148af855e3" + } + + @Test + fun colorSubframe() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) + nImage.width shouldBeExactly 16 + nImage.height shouldBeExactly 16 + nImage.mono.shouldBeFalse() + nImage.save("fits-color-subframe").second shouldBe "282fc4fdf9142fcb4b18e1df1eef4caa" + } + + @Test + fun colorSharpen() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(Sharpen) + mImage.save("fits-color-sharpen").second shouldBe "e562282bdafdeba6ce88981bb9c3ba61" + } + + @Test + fun colorMean() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(Mean) + mImage.save("fits-color-mean").second shouldBe "a8380d928aaa756e202ba43bd3a2f207" + } + + @Test + fun colorInvert() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(Invert) + mImage.save("fits-color-invert").second shouldBe "decad269ec26450aebeaf7546867b5f8" + } + + @Test + fun colorEmboss() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(Emboss) + mImage.save("fits-color-emboss").second shouldBe "58d69250f1233055aa33f9ec7ca40af1" + } + + @Test + fun colorEdges() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(Edges) + mImage.save("fits-color-edges").second shouldBe "091f2955740a8edcd2401dc416d19d51" + } + + @Test + fun colorBlur() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(Blur) + mImage.save("fits-color-blur").second shouldBe "0fca440b763de5380fa29de736f3c792" + } + + @Test + fun colorGaussianBlur() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) + mImage.save("fits-color-gaussian-blur").second shouldBe "394d1a4f136f15c802dd73004c421d64" + } + + @Test + fun colorStfMidtone01Shadow00Highlight10() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.1f)) + mImage.save("fits-color-stf-01-00-10").second shouldBe "e952bd263df6fd275b9a80aca554cb4b" + } + + @Test + fun colorStfMidtone09Shadow00Highlight10() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.9f)) + mImage.save("fits-color-stf-09-00-10").second shouldBe "038809d7612018e2e5c19d5e1f551abd" + } + + @Test + fun colorStfMidtone01Shadow05Highlight10() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) + mImage.save("fits-color-stf-01-05-10").second shouldBe "70e812260f56f8621002327575611f31" + } + + @Test + fun colorStfMidtone09Shadow05Highlight10() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) + mImage.save("fits-color-stf-09-05-10").second shouldBe "6ca400f617f466a9eb02a3a6f2985d99" + } + + @Test + fun colorStfMidtone01Shadow00Highlight05() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) + mImage.save("fits-color-stf-01-00-05").second shouldBe "3cd98ee9a8949d5100295acccd77010b" + } + + @Test + fun colorStfMidtone09Shadow00Highlight05() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) + mImage.save("fits-color-stf-09-00-05").second shouldBe "2cfeffc88c893cc5883d8a2221f29b91" + } + + @Test + fun colorStfMidtone01Shadow04Highlight06() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) + mImage.save("fits-color-stf-01-04-06").second shouldBe "532a07a1a166eb007c2e40651aec2097" + } + + @Test + fun colorStfMidtone09Shadow04Highlight06() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) + mImage.save("fits-color-stf-09-04-06").second shouldBe "eb3d940d9fd2c8814e930715e89897c4" + } + + @Test + fun colorAutoStf() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(AutoScreenTransformFunction) + mImage.save("fits-color-auto-stf").second shouldBe "a9c3657d8597b927607eb438e666d3a0" + } + + @Test + fun colorScnrMaximumMask() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_MASK)) + mImage.save("fits-color-scnr-maximum-mask").second shouldBe "e7d2155e18ff1e3172f4e849ae983145" + } + + @Test + fun colorScnrAdditiveMask() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.ADDITIVE_MASK)) + mImage.save("fits-color-scnr-additive-mask").second shouldBe "a458c44cedcda704de16d80053fd87eb" + } + + @Test + fun colorScnrAverageNeutral() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.AVERAGE_NEUTRAL)) + mImage.save("fits-color-scnr-average-neutral").second shouldBe "e07345ffc4982a62301c95c76d3efb35" + } + + @Test + fun colorScnrMaximumNeutral() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_NEUTRAL)) + mImage.save("fits-color-scnr-maximum-neutral").second shouldBe "a1d4b04f57b001ba4a996bab0407fd7e" + } + + @Test + fun colorScnrMinimumNeutral() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MINIMUM_NEUTRAL)) + mImage.save("fits-color-scnr-minimum-neutral").second shouldBe "8b7be57ff38da9c97b35d7888047c0f9" + } + + @Test + fun colorGrayscaleBt709() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + val nImage = mImage.transform(Grayscale.BT709) + nImage.save("fits-color-grayscale-bt709").second shouldBe "cab675aa35390a2d58cd48555d91054f" + } + + @Test + fun colorGrayscaleRmy() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + val nImage = mImage.transform(Grayscale.RMY) + nImage.save("fits-color-grayscale-rmy").second shouldBe "e113627002a4178d1010a2f6246e325f" + } + + @Test + fun colorGrayscaleY() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() + val nImage = mImage.transform(Grayscale.Y) + nImage.save("fits-color-grayscale-y").second shouldBe "24dd4a7e0fa9e4be34c53c924a78a940" + } + + @Test + fun colorDebayer() { + val mImage = DEBAYER_FITS.fits().asImage() + val nImage = mImage.transform(AutoScreenTransformFunction) + nImage.save("fits-color-debayer").second shouldBe "86b5bdd67dfd6bbf5495afae4bf2bc04" + } + + @Test + fun colorNoDebayer() { + val mImage = DEBAYER_FITS.fits().asImage(false) + val nImage = mImage.transform(AutoScreenTransformFunction) + nImage.save("fits-color-no-debayer").second shouldBe "958ccea020deec1f0c075042a9ba37c3" } } diff --git a/nebulosa-image/src/test/kotlin/HFDTest.kt b/nebulosa-image/src/test/kotlin/HFDTest.kt index c5f4305a8..7092a5536 100644 --- a/nebulosa-image/src/test/kotlin/HFDTest.kt +++ b/nebulosa-image/src/test/kotlin/HFDTest.kt @@ -1,41 +1,41 @@ import io.kotest.matchers.floats.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.fits.fits -import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.computation.hfd.HFD -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.* +import org.junit.jupiter.api.Test -class HFDTest : AbstractFitsAndXisfTest() { +class HFDTest { - init { - "focus" { - val starFocus = arrayOf( - STAR_FOCUS_1 to floatArrayOf(7.0f, 77.2f, 11950.023f), - STAR_FOCUS_2 to floatArrayOf(7.3f, 114.1f, 26055.076f), - STAR_FOCUS_3 to floatArrayOf(7.7f, 185.1f, 68531.1f), - STAR_FOCUS_4 to floatArrayOf(6.9f, 363.4f, 264183.53f), - STAR_FOCUS_5 to floatArrayOf(6.2f, 382.5f, 292700.53f), - STAR_FOCUS_6 to floatArrayOf(6.1f, 410.0f, 336331.9f), - STAR_FOCUS_7 to floatArrayOf(5.7f, 422.7f, 357451.03f), - STAR_FOCUS_8 to floatArrayOf(5.1f, 430.5f, 370737.5f), - STAR_FOCUS_9 to floatArrayOf(4.8f, 435.4f, 379284.03f), - STAR_FOCUS_10 to floatArrayOf(4.3f, 444.6f, 395406.34f), - STAR_FOCUS_11 to floatArrayOf(3.79f, 442.9f, 392338.5f), - STAR_FOCUS_12 to floatArrayOf(3.71f, 443.3f, 393080.38f), - STAR_FOCUS_13 to floatArrayOf(4.9f, 443.6f, 393603.97f), - STAR_FOCUS_14 to floatArrayOf(6.1f, 368.8f, 272057.78f), - STAR_FOCUS_15 to floatArrayOf(6.9f, 249.0f, 124078.05f), - STAR_FOCUS_16 to floatArrayOf(6.5f, 188.2f, 70905.97f), - STAR_FOCUS_17 to floatArrayOf(6.9f, 164.3f, 53994.75f), - ) + @Test + fun focus() { + val starFocus = arrayOf( + STAR_FOCUS_1 to floatArrayOf(7.0f, 77.2f, 11950.023f), + STAR_FOCUS_2 to floatArrayOf(7.3f, 114.1f, 26055.076f), + STAR_FOCUS_3 to floatArrayOf(7.7f, 185.1f, 68531.1f), + STAR_FOCUS_4 to floatArrayOf(6.9f, 363.4f, 264183.53f), + STAR_FOCUS_5 to floatArrayOf(6.2f, 382.5f, 292700.53f), + STAR_FOCUS_6 to floatArrayOf(6.1f, 410.0f, 336331.9f), + STAR_FOCUS_7 to floatArrayOf(5.7f, 422.7f, 357451.03f), + STAR_FOCUS_8 to floatArrayOf(5.1f, 430.5f, 370737.5f), + STAR_FOCUS_9 to floatArrayOf(4.8f, 435.4f, 379284.03f), + STAR_FOCUS_10 to floatArrayOf(4.3f, 444.6f, 395406.34f), + STAR_FOCUS_11 to floatArrayOf(3.79f, 442.9f, 392338.5f), + STAR_FOCUS_12 to floatArrayOf(3.71f, 443.3f, 393080.38f), + STAR_FOCUS_13 to floatArrayOf(4.9f, 443.6f, 393603.97f), + STAR_FOCUS_14 to floatArrayOf(6.1f, 368.8f, 272057.78f), + STAR_FOCUS_15 to floatArrayOf(6.9f, 249.0f, 124078.05f), + STAR_FOCUS_16 to floatArrayOf(6.5f, 188.2f, 70905.97f), + STAR_FOCUS_17 to floatArrayOf(6.9f, 164.3f, 53994.75f), + ) - for ((first, second) in starFocus) { - val focusImage = Image.open(closeAfterEach(first.fits())) - val star = focusImage.compute(HFD(focusImage.width / 2, focusImage.height / 2, 50)) - star.hfd shouldBe (second[0] plusOrMinus 0.1f) - star.snr shouldBe (second[1] plusOrMinus 0.1f) - star.flux shouldBe (second[2] plusOrMinus 0.1f) - } + for ((first, second) in starFocus) { + val focusImage = first.fits().asImage() + val star = focusImage.compute(HFD(focusImage.width / 2, focusImage.height / 2, 50)) + star.hfd shouldBe (second[0] plusOrMinus 0.1f) + star.snr shouldBe (second[1] plusOrMinus 0.1f) + star.flux shouldBe (second[2] plusOrMinus 0.1f) } } } diff --git a/nebulosa-image/src/test/kotlin/XisfTransformAlgorithmTest.kt b/nebulosa-image/src/test/kotlin/XisfTransformAlgorithmTest.kt index d42c91837..3aa5aae07 100644 --- a/nebulosa-image/src/test/kotlin/XisfTransformAlgorithmTest.kt +++ b/nebulosa-image/src/test/kotlin/XisfTransformAlgorithmTest.kt @@ -2,279 +2,387 @@ import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe -import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.transformation.* import nebulosa.image.algorithms.transformation.convolution.* import nebulosa.image.format.ImageChannel -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.DEBAYER_FITS +import nebulosa.test.M82_COLOR_32_XISF +import nebulosa.test.M82_MONO_8_XISF +import nebulosa.test.save import nebulosa.xisf.xisf +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -class XisfTransformAlgorithmTest : AbstractFitsAndXisfTest() { - - init { - "mono:raw" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.save("xisf-mono-raw").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } - "mono:vertical flip" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(VerticalFlip) - mImage.save("xisf-mono-vertical-flip").second shouldBe "d8250e5ac45d9a6ec04bed2a583a70b2" - } - "mono:horizontal flip" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(HorizontalFlip) - mImage.save("xisf-mono-horizontal-flip").second shouldBe "9e2a05c331a5daeff20671a365e8b74c" - } - "mono:vertical & horizontal flip" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(VerticalFlip, HorizontalFlip) - mImage.save("xisf-mono-vertical-horizontal-flip").second shouldBe "c7d55e870850c10619f389e3f9ec1243" - } - "mono:subframe" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) - nImage.width shouldBeExactly 16 - nImage.height shouldBeExactly 16 - nImage.mono.shouldBeTrue() - nImage.save("xisf-mono-subframe").second shouldBe "dc0ca19c5d40f90e4daac87fbab8f449" - } - "mono:sharpen" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Sharpen) - mImage.save("xisf-mono-sharpen").second shouldBe "08421f6afef422cdaa9f464a860808ce" - } - "mono:mean" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Mean) - mImage.save("xisf-mono-mean").second shouldBe "4127027d467ce9537049dc5f25f23ede" - } - "mono:invert" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Invert) - mImage.save("xisf-mono-invert").second shouldBe "a6ec4a55225cb18f004e159a60137fe9" - } - "mono:emboss" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Emboss) - mImage.save("xisf-mono-emboss").second shouldBe "2f07b3964b52f6b141b8cc45639b2287" - } - "mono:edges" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Edges) - mImage.save("xisf-mono-edges").second shouldBe "897af72d536bca57cce77f09e396adb7" - } - "mono:blur" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Blur) - mImage.save("xisf-mono-blur").second shouldBe "5a850ef20c0ebd461e676523823ff6ea" - } - "mono:gaussian blur" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) - mImage.save("xisf-mono-gaussian-blur").second shouldBe "cb7489cb1948e802ea4d92bba24e0739" - } - "mono:STF:midtone = 0.1, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f)) - mImage.save("xisf-mono-stf-01-00-10").second shouldBe "91621dba5a58081347d156f2152bd8c6" - } - "mono:STF:midtone = 0.9, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f)) - mImage.save("xisf-mono-stf-09-00-10").second shouldBe "9a67728cfc4f871ea8ab6557f381949e" - } - "mono:STF:midtone = 0.1, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) - mImage.save("xisf-mono-stf-01-05-10").second shouldBe "c70b9caaaf88bba9c64891a2b3ba8033" - } - "mono:STF:midtone = 0.9, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) - mImage.save("xisf-mono-stf-09-05-10").second shouldBe "4f4c49b96bd9aa417f22c59b8224ea90" - } - "mono:STF:midtone = 0.1, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) - mImage.save("xisf-mono-stf-01-00-05").second shouldBe "b0c9598e3003d72a5fba617c30dc1821" - } - "mono:STF:midtone = 0.9, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) - mImage.save("xisf-mono-stf-09-00-05").second shouldBe "5824ac8c2d44c2c8ff8121e0904bb77b" - } - "mono:STF:midtone = 0.1, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) - mImage.save("xisf-mono-stf-01-04-06").second shouldBe "20417dfc43fc4c5cb425875614d8066c" - } - "mono:STF:midtone = 0.9, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) - mImage.save("xisf-mono-stf-09-04-06").second shouldBe "13d13ad164a952a29f7ad76e1e51e7d0" - } - "mono:auto STF" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(AutoScreenTransformFunction) - mImage.save("xisf-mono-auto-stf").second shouldBe "9204a71df3770e8fe5ca49e3420eed72" - } - "color:raw" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.save("xisf-color-raw").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } - "color:vertical flip" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(VerticalFlip) - mImage.save("xisf-color-vertical-flip").second shouldBe "595d8d3e46e91d7adf7591e591a7a98d" - } - "color:horizontal flip" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(HorizontalFlip) - mImage.save("xisf-color-horizontal-flip").second shouldBe "b3d9609ef88db9b9215b4e21fdd4992a" - } - "color:vertical & horizontal flip" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(VerticalFlip, HorizontalFlip) - mImage.save("xisf-color-vertical-horizontal-flip").second shouldBe "1c9be1a7e5a66a989f6d0715c7ff30e4" - } - "color:subframe" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) - nImage.width shouldBeExactly 16 - nImage.height shouldBeExactly 16 - nImage.mono.shouldBeFalse() - nImage.save("xisf-color-subframe").second shouldBe "dcf5bc8908ea6a32a5e98f14152f47b9" - } - "color:sharpen" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Sharpen) - mImage.save("xisf-color-sharpen").second shouldBe "1017a88ff2caeb83a650d94d96c59347" - } - "color:mean" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Mean) - mImage.save("xisf-color-mean").second shouldBe "eb948cab18442d008381f392bf43542a" - } - "color:invert" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Invert) - mImage.save("xisf-color-invert").second shouldBe "bbc97cdcde240873439bc29ff1adf169" - } - "color:emboss" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Emboss) - mImage.save("xisf-color-emboss").second shouldBe "fba6827f204df93f4aaf2f99b113a8f7" - } - "color:edges" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Edges) - mImage.save("xisf-color-edges").second shouldBe "71d6b5c936ab8165c7c1b63514d2da7b" - } - "color:blur" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Blur) - mImage.save("xisf-color-blur").second shouldBe "5c9f738a309fc86956d124f0f109eea2" - } - "color:gaussian blur" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) - mImage.save("xisf-color-gaussian-blur").second shouldBe "648beabcea0b486efc3f0c4ded632a06" - } - "color:STF:midtone = 0.1, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f)) - mImage.save("xisf-color-stf-01-00-10").second shouldBe "d84577785e44179f7af7c00807be5260" - } - "color:STF:midtone = 0.9, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f)) - mImage.save("xisf-color-stf-09-00-10").second shouldBe "11771bfe0e180e1efea4568902109013" - } - "color:STF:midtone = 0.1, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) - mImage.save("xisf-color-stf-01-05-10").second shouldBe "4e3b204d8a7ed06dfd05b7d010ef267b" - } - "color:STF:midtone = 0.9, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) - mImage.save("xisf-color-stf-09-05-10").second shouldBe "95c5b801978abdbb51afc9d0de12c218" - } - "color:STF:midtone = 0.1, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) - mImage.save("xisf-color-stf-01-00-05").second shouldBe "08322042c9e57102fec77a06b28c263d" - } - "color:STF:midtone = 0.9, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) - mImage.save("xisf-color-stf-09-00-05").second shouldBe "c99990d3a4bc6fc3aa7a8ac90d452419" - } - "color:STF:midtone = 0.1, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) - mImage.save("xisf-color-stf-01-04-06").second shouldBe "33bce2d7f7cde67d0ee618439f438ee7" - } - "color:STF:midtone = 0.9, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) - mImage.save("xisf-color-stf-09-04-06").second shouldBe "923e56840bcea4462160e36f2e801f5e" - } - "color:auto STF" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(AutoScreenTransformFunction) - mImage.save("xisf-color-auto-stf").second shouldBe "b1460451ad0f0580802d3d6d3a6750ba" - } - "color:SCNR Maximum Mask" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_MASK)) - mImage.save("xisf-color-scnr-maximum-mask").second shouldBe "e465a2fc0814055e089a3180c0235c8b" - } - "color:SCNR Additive Mask" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.ADDITIVE_MASK)) - mImage.save("xisf-color-scnr-additive-mask").second shouldBe "1016ec07253f869097d2d9c67194b3e3" - } - "color:SCNR Average Neutral" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.AVERAGE_NEUTRAL)) - mImage.save("xisf-color-scnr-average-neutral").second shouldBe "2f2583bfb2ae4a9e4441dc9b827196f7" - } - "color:SCNR Maximum Neutral" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_NEUTRAL)) - mImage.save("xisf-color-scnr-maximum-neutral").second shouldBe "dabd995db4fba052617b148b3700378d" - } - "color:SCNR Minimum Neutral" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MINIMUM_NEUTRAL)) - mImage.save("xisf-color-scnr-minimum-neutral").second shouldBe "b92e5130ec2a44c0afd1ca3782261e47" - } - "color:grayscale BT-709" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - val nImage = mImage.transform(Grayscale.BT709) - nImage.save("xisf-color-grayscale-bt709").second shouldBe "bf78482866a0b3a216fa5262f28b0e5e" - } - "color:grayscale RMY" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - val nImage = mImage.transform(Grayscale.RMY) - nImage.save("xisf-color-grayscale-rmy").second shouldBe "e75085eea073811aef41624aab318b48" - } - "color:grayscale Y" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - val nImage = mImage.transform(Grayscale.Y) - nImage.save("xisf-color-grayscale-y").second shouldBe "7a2bef966d460742533a1c8c3a74f1c5" - } - "!color:debayer" { - val mImage = Image.open(DEBAYER_FITS.xisf()) - val nImage = mImage.transform(AutoScreenTransformFunction) - nImage.save("xisf-color-debayer").second shouldBe "86b5bdd67dfd6bbf5495afae4bf2bc04" - } - "!color:no-debayer" { - val mImage = Image.open(DEBAYER_FITS.xisf(), false) - val nImage = mImage.transform(AutoScreenTransformFunction) - nImage.save("xisf-color-no-debayer").second shouldBe "958ccea020deec1f0c075042a9ba37c3" - } +class XisfTransformAlgorithmTest { + + @Test + fun monoRaw() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.save("xisf-mono-raw").second shouldBe "0dca7efedef5b3525f8037f401518b0b" + } + + @Test + fun monoVerticalFlip() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(VerticalFlip) + mImage.save("xisf-mono-vertical-flip").second shouldBe "d8250e5ac45d9a6ec04bed2a583a70b2" + } + + @Test + fun monoHorizontalFlip() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(HorizontalFlip) + mImage.save("xisf-mono-horizontal-flip").second shouldBe "9e2a05c331a5daeff20671a365e8b74c" + } + + @Test + fun monoVerticalAndHorizontalFlip() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(VerticalFlip, HorizontalFlip) + mImage.save("xisf-mono-vertical-horizontal-flip").second shouldBe "c7d55e870850c10619f389e3f9ec1243" + } + + @Test + fun monoSubframe() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) + nImage.width shouldBeExactly 16 + nImage.height shouldBeExactly 16 + nImage.mono.shouldBeTrue() + nImage.save("xisf-mono-subframe").second shouldBe "dc0ca19c5d40f90e4daac87fbab8f449" + } + + @Test + fun monoSharpen() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Sharpen) + mImage.save("xisf-mono-sharpen").second shouldBe "08421f6afef422cdaa9f464a860808ce" + } + + @Test + fun monoMean() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Mean) + mImage.save("xisf-mono-mean").second shouldBe "4127027d467ce9537049dc5f25f23ede" + } + + @Test + fun monoInvert() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Invert) + mImage.save("xisf-mono-invert").second shouldBe "a6ec4a55225cb18f004e159a60137fe9" + } + + @Test + fun monoEmboss() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Emboss) + mImage.save("xisf-mono-emboss").second shouldBe "2f07b3964b52f6b141b8cc45639b2287" + } + + @Test + fun monoEdges() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Edges) + mImage.save("xisf-mono-edges").second shouldBe "897af72d536bca57cce77f09e396adb7" + } + + @Test + fun monoBlur() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Blur) + mImage.save("xisf-mono-blur").second shouldBe "5a850ef20c0ebd461e676523823ff6ea" + } + + @Test + fun monoGaussianBlur() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) + mImage.save("xisf-mono-gaussian-blur").second shouldBe "cb7489cb1948e802ea4d92bba24e0739" + } + + @Test + fun monoStfMidTone01Shadow00Highlight10() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f)) + mImage.save("xisf-mono-stf-01-00-10").second shouldBe "91621dba5a58081347d156f2152bd8c6" + } + + @Test + fun monoStfMidTone09Shadow00Highlight10() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f)) + mImage.save("xisf-mono-stf-09-00-10").second shouldBe "9a67728cfc4f871ea8ab6557f381949e" + } + + @Test + fun monoStfMidTone01Shadow05Highlight10() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) + mImage.save("xisf-mono-stf-01-05-10").second shouldBe "c70b9caaaf88bba9c64891a2b3ba8033" + } + + @Test + fun monoStfMidTone09Shadow05Highlight10() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) + mImage.save("xisf-mono-stf-09-05-10").second shouldBe "4f4c49b96bd9aa417f22c59b8224ea90" + } + + @Test + fun monoStfMidTone01Shadow00Highlight05() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) + mImage.save("xisf-mono-stf-01-00-05").second shouldBe "b0c9598e3003d72a5fba617c30dc1821" + } + + @Test + fun monoStfMidTone09Shadow00Highlight05() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) + mImage.save("xisf-mono-stf-09-00-05").second shouldBe "5824ac8c2d44c2c8ff8121e0904bb77b" + } + + @Test + fun monoStfMidTone01Shadow04Highlight06() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) + mImage.save("xisf-mono-stf-01-04-06").second shouldBe "20417dfc43fc4c5cb425875614d8066c" + } + + @Test + fun monoStfMidTone09Shadow04Highlight06() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) + mImage.save("xisf-mono-stf-09-04-06").second shouldBe "13d13ad164a952a29f7ad76e1e51e7d0" + } + + @Test + fun monoAutoStf() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(AutoScreenTransformFunction) + mImage.save("xisf-mono-auto-stf").second shouldBe "9204a71df3770e8fe5ca49e3420eed72" + } + + @Test + fun colorRaw() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.save("xisf-color-raw").second shouldBe "764e326cc5260d81f3761112ad6a1969" + } + + @Test + fun colorVerticalFlip() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(VerticalFlip) + mImage.save("xisf-color-vertical-flip").second shouldBe "595d8d3e46e91d7adf7591e591a7a98d" + } + + @Test + fun colorHorizontalFlip() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(HorizontalFlip) + mImage.save("xisf-color-horizontal-flip").second shouldBe "b3d9609ef88db9b9215b4e21fdd4992a" + } + + @Test + fun colorVerticalAndHorizontalFlip() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(VerticalFlip, HorizontalFlip) + mImage.save("xisf-color-vertical-horizontal-flip").second shouldBe "1c9be1a7e5a66a989f6d0715c7ff30e4" + } + + @Test + fun colorSubframe() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) + nImage.width shouldBeExactly 16 + nImage.height shouldBeExactly 16 + nImage.mono.shouldBeFalse() + nImage.save("xisf-color-subframe").second shouldBe "dcf5bc8908ea6a32a5e98f14152f47b9" + } + + @Test + fun colorSharpen() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Sharpen) + mImage.save("xisf-color-sharpen").second shouldBe "1017a88ff2caeb83a650d94d96c59347" + } + + @Test + fun colorMean() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Mean) + mImage.save("xisf-color-mean").second shouldBe "eb948cab18442d008381f392bf43542a" + } + + @Test + fun colorInvert() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Invert) + mImage.save("xisf-color-invert").second shouldBe "bbc97cdcde240873439bc29ff1adf169" + } + + @Test + fun colorEmboss() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Emboss) + mImage.save("xisf-color-emboss").second shouldBe "fba6827f204df93f4aaf2f99b113a8f7" + } + + @Test + fun colorEdges() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Edges) + mImage.save("xisf-color-edges").second shouldBe "71d6b5c936ab8165c7c1b63514d2da7b" + } + + @Test + fun colorBlur() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Blur) + mImage.save("xisf-color-blur").second shouldBe "5c9f738a309fc86956d124f0f109eea2" + } + + @Test + fun colorGaussianBlur() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) + mImage.save("xisf-color-gaussian-blur").second shouldBe "648beabcea0b486efc3f0c4ded632a06" + } + + @Test + fun colorStfMidTone01Shadow00Highlight10() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f)) + mImage.save("xisf-color-stf-01-00-10").second shouldBe "d84577785e44179f7af7c00807be5260" + } + + @Test + fun colorStfMidTone09Shadow00Highlight10() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f)) + mImage.save("xisf-color-stf-09-00-10").second shouldBe "11771bfe0e180e1efea4568902109013" + } + + @Test + fun colorStfMidTone01Shadow05Highlight10() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) + mImage.save("xisf-color-stf-01-05-10").second shouldBe "4e3b204d8a7ed06dfd05b7d010ef267b" + } + + @Test + fun colorStfMidTone09Shadow05Highlight10() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) + mImage.save("xisf-color-stf-09-05-10").second shouldBe "95c5b801978abdbb51afc9d0de12c218" + } + + @Test + fun colorStfMidTone01Shadow00Highlight05() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) + mImage.save("xisf-color-stf-01-00-05").second shouldBe "08322042c9e57102fec77a06b28c263d" + } + + @Test + fun colorStfMidTone09Shadow00Highlight05() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) + mImage.save("xisf-color-stf-09-00-05").second shouldBe "c99990d3a4bc6fc3aa7a8ac90d452419" + } + + @Test + fun colorStfMidTone01Shadow04Highlight06() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) + mImage.save("xisf-color-stf-01-04-06").second shouldBe "33bce2d7f7cde67d0ee618439f438ee7" + } + + @Test + fun colorStfMidTone09Shadow04Highlight06() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) + mImage.save("xisf-color-stf-09-04-06").second shouldBe "923e56840bcea4462160e36f2e801f5e" + } + + @Test + fun colorAutoStf() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(AutoScreenTransformFunction) + mImage.save("xisf-color-auto-stf").second shouldBe "b1460451ad0f0580802d3d6d3a6750ba" + } + + @Test + fun colorScnrMaximumMask() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_MASK)) + mImage.save("xisf-color-scnr-maximum-mask").second shouldBe "e465a2fc0814055e089a3180c0235c8b" + } + + @Test + fun colorScnrAdditiveMask() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.ADDITIVE_MASK)) + mImage.save("xisf-color-scnr-additive-mask").second shouldBe "1016ec07253f869097d2d9c67194b3e3" + } + + @Test + fun colorScnrAverageNeutral() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.AVERAGE_NEUTRAL)) + mImage.save("xisf-color-scnr-average-neutral").second shouldBe "2f2583bfb2ae4a9e4441dc9b827196f7" + } + + @Test + fun colorScnrMaximumNeutral() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_NEUTRAL)) + mImage.save("xisf-color-scnr-maximum-neutral").second shouldBe "dabd995db4fba052617b148b3700378d" + } + + @Test + fun colorScnrMinimumNeutral() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MINIMUM_NEUTRAL)) + mImage.save("xisf-color-scnr-minimum-neutral").second shouldBe "b92e5130ec2a44c0afd1ca3782261e47" + } + + @Test + fun colorGrayscaleBt709() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + val nImage = mImage.transform(Grayscale.BT709) + nImage.save("xisf-color-grayscale-bt709").second shouldBe "bf78482866a0b3a216fa5262f28b0e5e" + } + + @Test + fun colorGrayscaleRmy() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + val nImage = mImage.transform(Grayscale.RMY) + nImage.save("xisf-color-grayscale-rmy").second shouldBe "e75085eea073811aef41624aab318b48" + } + + @Test + fun colorGrayscaleY() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + val nImage = mImage.transform(Grayscale.Y) + nImage.save("xisf-color-grayscale-y").second shouldBe "7a2bef966d460742533a1c8c3a74f1c5" + } + + @Test + @Disabled + fun colorDebayer() { + val mImage = DEBAYER_FITS.xisf().asImage() + val nImage = mImage.transform(AutoScreenTransformFunction) + nImage.save("xisf-color-debayer").second shouldBe "86b5bdd67dfd6bbf5495afae4bf2bc04" + } + + @Test + @Disabled + fun colorNoDebayer() { + val mImage = DEBAYER_FITS.xisf().asImage(false) + val nImage = mImage.transform(AutoScreenTransformFunction) + nImage.save("xisf-color-no-debayer").second shouldBe "958ccea020deec1f0c075042a9ba37c3" } } diff --git a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/GPSDevice.kt b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/GPSDevice.kt index e13d4d49f..c06331961 100644 --- a/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/GPSDevice.kt +++ b/nebulosa-indi-client/src/main/kotlin/nebulosa/indi/client/device/GPSDevice.kt @@ -1,6 +1,7 @@ package nebulosa.indi.client.device import nebulosa.indi.client.INDIClient +import nebulosa.indi.device.DeviceType import nebulosa.indi.device.gps.GPS import nebulosa.indi.device.gps.GPSCoordinateChanged import nebulosa.indi.device.gps.GPSTimeChanged @@ -17,6 +18,9 @@ internal open class GPSDevice( override val name: String, ) : INDIDevice(), GPS { + override val type + get() = DeviceType.GPS + @Volatile final override var hasGPS = true @Volatile final override var longitude = 0.0 @Volatile final override var latitude = 0.0 diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/Device.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/Device.kt index 60757ac91..edce9e775 100644 --- a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/Device.kt +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/Device.kt @@ -6,6 +6,8 @@ import java.io.Closeable interface Device : INDIProtocolHandler, Closeable, Comparable { + val type: DeviceType + val sender: MessageSender val id: String diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/DeviceType.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/DeviceType.kt new file mode 100644 index 000000000..c3fa6f460 --- /dev/null +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/DeviceType.kt @@ -0,0 +1,12 @@ +package nebulosa.indi.device + +enum class DeviceType { + CAMERA, + MOUNT, + WHEEL, + FOCUSER, + ROTATOR, + GPS, + DOME, + SWITCH +} diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/camera/Camera.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/camera/Camera.kt index 49e72e3b4..3430142b9 100644 --- a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/camera/Camera.kt +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/camera/Camera.kt @@ -2,6 +2,7 @@ package nebulosa.indi.device.camera import nebulosa.image.algorithms.transformation.CfaPattern import nebulosa.image.format.HeaderCard +import nebulosa.indi.device.DeviceType import nebulosa.indi.device.guide.GuideOutput import nebulosa.indi.device.thermometer.Thermometer import nebulosa.indi.protocol.PropertyState @@ -9,6 +10,9 @@ import java.time.Duration interface Camera : GuideOutput, Thermometer { + override val type + get() = DeviceType.CAMERA + val exposuring: Boolean val hasCoolerControl: Boolean diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/camera/FrameType.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/camera/FrameType.kt index f6bb25e2b..33e481bc7 100644 --- a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/camera/FrameType.kt +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/camera/FrameType.kt @@ -1,8 +1,23 @@ package nebulosa.indi.device.camera +import nebulosa.fits.frame +import nebulosa.image.format.ReadableHeader + enum class FrameType(@JvmField val description: String) { LIGHT("Light"), DARK("Dark"), FLAT("Flat"), - BIAS("Bias"), + BIAS("Bias"); + + companion object { + + @JvmStatic val ReadableHeader.frameType + get() = frame?.let { + if (it.contains("LIGHT", true)) LIGHT + else if (it.contains("DARK", true)) DARK + else if (it.contains("FLAT", true)) FLAT + else if (it.contains("BIAS", true)) BIAS + else null + } + } } diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/dome/Dome.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/dome/Dome.kt index 9331f44f2..e2641eb6e 100644 --- a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/dome/Dome.kt +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/dome/Dome.kt @@ -1,5 +1,10 @@ package nebulosa.indi.device.dome import nebulosa.indi.device.Device +import nebulosa.indi.device.DeviceType -interface Dome : Device +interface Dome : Device { + + override val type + get() = DeviceType.DOME +} diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/filterwheel/FilterWheel.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/filterwheel/FilterWheel.kt index 90c27c12f..92f9af5ef 100644 --- a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/filterwheel/FilterWheel.kt +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/filterwheel/FilterWheel.kt @@ -1,9 +1,13 @@ package nebulosa.indi.device.filterwheel import nebulosa.indi.device.Device +import nebulosa.indi.device.DeviceType interface FilterWheel : Device { + override val type + get() = DeviceType.WHEEL + val count: Int val position: Int diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/focuser/Focuser.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/focuser/Focuser.kt index 920702192..547a5e513 100644 --- a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/focuser/Focuser.kt +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/focuser/Focuser.kt @@ -1,10 +1,14 @@ package nebulosa.indi.device.focuser import nebulosa.indi.device.Device +import nebulosa.indi.device.DeviceType import nebulosa.indi.device.thermometer.Thermometer interface Focuser : Device, Thermometer { + override val type + get() = DeviceType.FOCUSER + val moving: Boolean val position: Int diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/mount/Mount.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/mount/Mount.kt index 847336c4e..b10d4b0d5 100644 --- a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/mount/Mount.kt +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/mount/Mount.kt @@ -1,5 +1,6 @@ package nebulosa.indi.device.mount +import nebulosa.indi.device.DeviceType import nebulosa.indi.device.Parkable import nebulosa.indi.device.gps.GPS import nebulosa.indi.device.guide.GuideOutput @@ -9,6 +10,9 @@ import java.time.OffsetDateTime interface Mount : GuideOutput, GPS, Parkable { + override val type + get() = DeviceType.MOUNT + val slewing: Boolean val tracking: Boolean diff --git a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/rotator/Rotator.kt b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/rotator/Rotator.kt index d22861982..a154381d9 100644 --- a/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/rotator/Rotator.kt +++ b/nebulosa-indi-device/src/main/kotlin/nebulosa/indi/device/rotator/Rotator.kt @@ -1,9 +1,13 @@ package nebulosa.indi.device.rotator import nebulosa.indi.device.Device +import nebulosa.indi.device.DeviceType interface Rotator : Device { + override val type + get() = DeviceType.ROTATOR + val moving: Boolean val canAbort: Boolean diff --git a/nebulosa-indi-protocol/src/test/kotlin/INDIXmlInputStreamTest.kt b/nebulosa-indi-protocol/src/test/kotlin/INDIXmlInputStreamTest.kt index 0b57e751f..c8f930824 100644 --- a/nebulosa-indi-protocol/src/test/kotlin/INDIXmlInputStreamTest.kt +++ b/nebulosa-indi-protocol/src/test/kotlin/INDIXmlInputStreamTest.kt @@ -1,294 +1,321 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.equality.shouldBeEqualToComparingFields import io.kotest.matchers.nulls.shouldNotBeNull import nebulosa.indi.protocol.* import nebulosa.indi.protocol.parser.INDIXmlInputStream +import org.junit.jupiter.api.Test import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream import java.io.PrintStream -class INDIXmlInputStreamTest : StringSpec() { - - init { - "def blob vector" { - DefBLOBVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.perm = PropertyPermission.RW - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - - with(DefBLOB()) { - name = "NAME" - label = "LABEL" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it +class INDIXmlInputStreamTest { + + @Test + fun defBlobVector() { + DefBLOBVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.perm = PropertyPermission.RW + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + + with(DefBLOB()) { + name = "NAME" + label = "LABEL" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "def switch vector" { - DefSwitchVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.perm = PropertyPermission.RW - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - - with(DefSwitch()) { - name = "NAME" - label = "LABEL" - value = false - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun defSwitchVector() { + DefSwitchVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.perm = PropertyPermission.RW + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + + with(DefSwitch()) { + name = "NAME" + label = "LABEL" + value = false + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "def number vector" { - DefNumberVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.perm = PropertyPermission.RW - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - - with(DefNumber()) { - name = "NAME" - label = "LABEL" - value = 0.0 - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun defNumberVector() { + DefNumberVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.perm = PropertyPermission.RW + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + + with(DefNumber()) { + name = "NAME" + label = "LABEL" + value = 0.0 + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "def light vector" { - DefLightVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.timestamp = "2023-02-01T12:59:52" - - with(DefLight()) { - name = "NAME" - label = "LABEL" - value = PropertyState.OK - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun defLightVector() { + DefLightVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.timestamp = "2023-02-01T12:59:52" + + with(DefLight()) { + name = "NAME" + label = "LABEL" + value = PropertyState.OK + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "def text vector" { - DefTextVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.perm = PropertyPermission.RW - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - - with(DefText()) { - name = "NAME" - label = "LABEL" - value = "VALUE" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun defTextVector() { + DefTextVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.perm = PropertyPermission.RW + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + + with(DefText()) { + name = "NAME" + label = "LABEL" + value = "VALUE" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new blob vector" { - NewBLOBVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneBLOB()) { - name = "NAME" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newBlobVector() { + NewBLOBVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneBLOB()) { + name = "NAME" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new switch vector" { - NewSwitchVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneSwitch()) { - name = "NAME" - value = false - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newSwitchVector() { + NewSwitchVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneSwitch()) { + name = "NAME" + value = false + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new number vector" { - NewNumberVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneNumber()) { - name = "NAME" - value = 0.0 - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newNumberVector() { + NewNumberVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneNumber()) { + name = "NAME" + value = 0.0 + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new light vector" { - NewLightVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneLight()) { - name = "NAME" - value = PropertyState.OK - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newLightVector() { + NewLightVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneLight()) { + name = "NAME" + value = PropertyState.OK + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new text vector" { - NewTextVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneText()) { - name = "NAME" - value = "VALUE" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newTextVector() { + NewTextVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneText()) { + name = "NAME" + value = "VALUE" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set blob vector" { - SetBLOBVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneBLOB()) { - name = "NAME" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setBlobVector() { + SetBLOBVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneBLOB()) { + name = "NAME" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set switch vector" { - SetSwitchVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneSwitch()) { - name = "NAME" - value = false - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setSwitchVector() { + SetSwitchVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneSwitch()) { + name = "NAME" + value = false + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set number vector" { - SetNumberVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneNumber()) { - name = "NAME" - value = 0.0 - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setNumberVector() { + SetNumberVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneNumber()) { + name = "NAME" + value = 0.0 + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set light vector" { - SetLightVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneLight()) { - name = "NAME" - value = PropertyState.OK - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setLightVector() { + SetLightVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneLight()) { + name = "NAME" + value = PropertyState.OK + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set text vector" { - SetTextVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneText()) { - name = "NAME" - value = "VALUE" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setTextVector() { + SetTextVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneText()) { + name = "NAME" + value = "VALUE" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } } diff --git a/nebulosa-io/src/test/kotlin/AbstractSeekableSinkAndSourceTest.kt b/nebulosa-io/src/test/kotlin/AbstractSeekableSinkAndSourceTest.kt index d7c219a92..4491cb013 100644 --- a/nebulosa-io/src/test/kotlin/AbstractSeekableSinkAndSourceTest.kt +++ b/nebulosa-io/src/test/kotlin/AbstractSeekableSinkAndSourceTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly @@ -6,288 +5,316 @@ import io.kotest.matchers.longs.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.io.SeekableSink import nebulosa.io.SeekableSource +import nebulosa.test.AbstractTest import okio.Buffer +import org.junit.jupiter.api.Test -abstract class AbstractSeekableSinkAndSourceTest : StringSpec() { +abstract class AbstractSeekableSinkAndSourceTest : AbstractTest() { abstract val sink: SeekableSink abstract val source: SeekableSource - init { - afterEach { - sink.seek(0) - source.seek(0) - } - - "byte" { - val buffer = Buffer() - buffer.writeByte(-12) - sink.seek(0) - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 1 - - source.seek(0) - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 1 - buffer.readByte().toInt() shouldBeExactly -12 - } - "short-le" { - val buffer = Buffer() - buffer.writeShortLe(-23975) - sink.seek(0) - sink.write(buffer, 2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 2 - - source.seek(0) - source.read(buffer, 2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 2 - buffer.readShortLe().toInt() shouldBeExactly -23975 - } - "short" { - val buffer = Buffer() - buffer.writeShort(-23975) - sink.seek(0) - sink.write(buffer, 2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 2 - - source.seek(0) - source.read(buffer, 2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 2 - buffer.readShort().toInt() shouldBeExactly -23975 - } - "int-le" { - val buffer = Buffer() - buffer.writeIntLe(-145983) - sink.seek(0) - sink.write(buffer, 4) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 4 - - source.seek(0) - source.read(buffer, 4) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 4 - buffer.readIntLe() shouldBeExactly -145983 - } - "int" { - val buffer = Buffer() - buffer.writeInt(-145983) - sink.seek(0) - sink.write(buffer, 4) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 4 - - source.seek(0) - source.read(buffer, 4) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 4 - buffer.readInt() shouldBeExactly -145983 - } - "long-le" { - val buffer = Buffer() - buffer.writeLongLe(-3534545345345) - sink.seek(0) - sink.write(buffer, 8) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.seek(0) - source.read(buffer, 8) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readLongLe() shouldBeExactly -3534545345345 - } - "long" { - val buffer = Buffer() - buffer.writeLong(-3534545345345) - sink.seek(0) - sink.write(buffer, 8) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.seek(0) - source.read(buffer, 8) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readLong() shouldBeExactly -3534545345345 - } - "ascii" { - val buffer = Buffer() - buffer.writeString("Gê", Charsets.ISO_8859_1) - sink.seek(0) - sink.write(buffer, 2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 2 - - source.seek(0) - source.read(buffer, 2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 2 - buffer.readString(Charsets.ISO_8859_1) shouldBe "Gê" - } - "utf-8" { - val buffer = Buffer() - buffer.writeUtf8("\uD83D\uDE0A") - sink.seek(0) - sink.write(buffer, 4) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 4 - - source.seek(0) - source.read(buffer, 4) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 4 - buffer.readUtf8() shouldBe "\uD83D\uDE0A" - } - "seek and exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.seek(7) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - sink.write(buffer, 1) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.seek(7) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - source.read(buffer, 1) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readByte() shouldBe 99 - } - "seek and not exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.seek(6) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 6 - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - - source.seek(6) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 6 - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - buffer.readByte() shouldBe 99 - } - "negative seek and exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.seek(-1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - sink.write(buffer, 1) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.seek(-1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - source.read(buffer, 1) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readByte() shouldBe 99 - } - "negative seek and not exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.seek(-2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 6 - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - - source.seek(-2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 6 - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - buffer.readByte() shouldBe 99 - } - "skip and exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.skip(7) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - sink.write(buffer, 1) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.skip(7) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - source.read(buffer, 1) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readByte() shouldBe 99 - } - "skip and not exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.skip(6) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 6 - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - - source.skip(6) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 6 - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - buffer.readByte() shouldBe 99 - } - "negative skip and exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.skip(-1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - sink.write(buffer, 1) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.skip(-1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - source.read(buffer, 1) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readByte() shouldBe 99 - } - "negative skip and not exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.skip(-2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 6 - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - - source.skip(-2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 6 - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - buffer.readByte() shouldBe 99 - } + @Test + fun byte() { + val buffer = Buffer() + buffer.writeByte(-12) + sink.seek(0) + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 1 + + source.seek(0) + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 1 + buffer.readByte().toInt() shouldBeExactly -12 + } + + @Test + fun shortLe() { + val buffer = Buffer() + buffer.writeShortLe(-23975) + sink.seek(0) + sink.write(buffer, 2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 2 + + source.seek(0) + source.read(buffer, 2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 2 + buffer.readShortLe().toInt() shouldBeExactly -23975 + } + + @Test + fun short() { + val buffer = Buffer() + buffer.writeShort(-23975) + sink.seek(0) + sink.write(buffer, 2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 2 + + source.seek(0) + source.read(buffer, 2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 2 + buffer.readShort().toInt() shouldBeExactly -23975 + } + + @Test + fun intLe() { + val buffer = Buffer() + buffer.writeIntLe(-145983) + sink.seek(0) + sink.write(buffer, 4) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 4 + + source.seek(0) + source.read(buffer, 4) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 4 + buffer.readIntLe() shouldBeExactly -145983 + } + + @Test + fun int() { + val buffer = Buffer() + buffer.writeInt(-145983) + sink.seek(0) + sink.write(buffer, 4) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 4 + + source.seek(0) + source.read(buffer, 4) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 4 + buffer.readInt() shouldBeExactly -145983 + } + + @Test + fun longLe() { + val buffer = Buffer() + buffer.writeLongLe(-3534545345345) + sink.seek(0) + sink.write(buffer, 8) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.seek(0) + source.read(buffer, 8) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readLongLe() shouldBeExactly -3534545345345 + } + + @Test + fun long() { + val buffer = Buffer() + buffer.writeLong(-3534545345345) + sink.seek(0) + sink.write(buffer, 8) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.seek(0) + source.read(buffer, 8) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readLong() shouldBeExactly -3534545345345 + } + + @Test + fun ascii() { + val buffer = Buffer() + buffer.writeString("Gê", Charsets.ISO_8859_1) + sink.seek(0) + sink.write(buffer, 2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 2 + + source.seek(0) + source.read(buffer, 2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 2 + buffer.readString(Charsets.ISO_8859_1) shouldBe "Gê" + } + + @Test + fun utf8() { + val buffer = Buffer() + buffer.writeUtf8("\uD83D\uDE0A") + sink.seek(0) + sink.write(buffer, 4) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 4 + + source.seek(0) + source.read(buffer, 4) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 4 + buffer.readUtf8() shouldBe "\uD83D\uDE0A" + } + + @Test + fun seekAndExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.seek(7) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + sink.write(buffer, 1) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.seek(7) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + source.read(buffer, 1) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readByte() shouldBe 99 + } + + @Test + fun seekAndNotExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.seek(6) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 6 + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + + source.seek(6) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 6 + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + buffer.readByte() shouldBe 99 + } + + @Test + fun negativeSeekAndExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.seek(-1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + sink.write(buffer, 1) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.seek(-1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + source.read(buffer, 1) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readByte() shouldBe 99 + } + + @Test + fun negativeSeekAndNotExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.seek(-2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 6 + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + + source.seek(-2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 6 + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + buffer.readByte() shouldBe 99 + } + + @Test + fun skipAndExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.skip(7) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + sink.write(buffer, 1) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.skip(7) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + source.read(buffer, 1) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readByte() shouldBe 99 + } + + @Test + fun skipAndNotExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.skip(6) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 6 + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + + source.skip(6) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 6 + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + buffer.readByte() shouldBe 99 + } + + @Test + fun negativeSkipAndExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.skip(-1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + sink.write(buffer, 1) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.skip(-1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + source.read(buffer, 1) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readByte() shouldBe 99 + } + + @Test + fun negativeSkipAndNotExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.skip(-2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 6 + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + + source.skip(-2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 6 + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + buffer.readByte() shouldBe 99 } } diff --git a/nebulosa-io/src/test/kotlin/Base64InputStreamTest.kt b/nebulosa-io/src/test/kotlin/Base64InputStreamTest.kt index 0b266fbda..86902ca3d 100644 --- a/nebulosa-io/src/test/kotlin/Base64InputStreamTest.kt +++ b/nebulosa-io/src/test/kotlin/Base64InputStreamTest.kt @@ -1,19 +1,18 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.io.Base64InputStream +import org.junit.jupiter.api.Test import java.util.* import kotlin.random.Random -class Base64InputStreamTest : StringSpec() { +class Base64InputStreamTest { - init { - "read" { - repeat(100) { - val bytes = Random.nextBytes(Random.nextInt(10, 100)) - val encoded = Base64.getEncoder().encodeToString(bytes) - val stream = Base64InputStream(encoded) - stream.readAllBytes() shouldBe bytes - } + @Test + fun read() { + repeat(100) { + val bytes = Random.nextBytes(Random.nextInt(10, 100)) + val encoded = Base64.getEncoder().encodeToString(bytes) + val stream = Base64InputStream(encoded) + stream.readAllBytes() shouldBe bytes } } } diff --git a/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetAndLengthTest.kt b/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetAndLengthTest.kt index 1fc9f0e40..aa068e7ab 100644 --- a/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetAndLengthTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetAndLengthTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach class ByteArrayWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { @@ -9,14 +10,13 @@ class ByteArrayWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { override val sink = data.sink(5, 8) override val source = data.source(5, 8) - init { - afterEach { - for (i in 0..4) { - data[i].toInt() shouldBeExactly i - } - for (i in 13..15) { - data[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..4) { + data[i].toInt() shouldBeExactly i + } + for (i in 13..15) { + data[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetTest.kt b/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetTest.kt index 62c2a0994..c60e28d67 100644 --- a/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach class ByteArrayWithOffsetTest : AbstractSeekableSinkAndSourceTest() { @@ -9,11 +10,10 @@ class ByteArrayWithOffsetTest : AbstractSeekableSinkAndSourceTest() { override val sink = data.sink(8) override val source = data.source(8) - init { - afterEach { - for (i in 0..7) { - data[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..7) { + data[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetAndLengthTest.kt b/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetAndLengthTest.kt index 02bc3f0c9..9c8f4024b 100644 --- a/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetAndLengthTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetAndLengthTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach import java.nio.ByteBuffer class ByteBufferWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { @@ -11,14 +12,13 @@ class ByteBufferWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { override val sink = data.sink(5, 8) override val source = data.source(5, 8) - init { - afterEach { - for (i in 0..4) { - bytes[i].toInt() shouldBeExactly i - } - for (i in 13..15) { - bytes[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..4) { + bytes[i].toInt() shouldBeExactly i + } + for (i in 13..15) { + bytes[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetTest.kt b/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetTest.kt index f501d8691..785a5fba1 100644 --- a/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach import java.nio.ByteBuffer class ByteBufferWithOffsetTest : AbstractSeekableSinkAndSourceTest() { @@ -11,11 +12,10 @@ class ByteBufferWithOffsetTest : AbstractSeekableSinkAndSourceTest() { override val sink = data.sink(8) override val source = data.source(8) - init { - afterEach { - for (i in 0..7) { - bytes[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..7) { + bytes[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/ByteBufferWrappedWithOffsetAndLengthTest.kt b/nebulosa-io/src/test/kotlin/ByteBufferWrappedWithOffsetAndLengthTest.kt index a273d0b20..9802ee646 100644 --- a/nebulosa-io/src/test/kotlin/ByteBufferWrappedWithOffsetAndLengthTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteBufferWrappedWithOffsetAndLengthTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach import java.nio.ByteBuffer class ByteBufferWrappedWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { @@ -11,14 +12,13 @@ class ByteBufferWrappedWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTe override val sink = data.sink() override val source = data.source() - init { - afterEach { - for (i in 0..4) { - bytes[i].toInt() shouldBeExactly i - } - for (i in 13..15) { - bytes[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..4) { + bytes[i].toInt() shouldBeExactly i + } + for (i in 13..15) { + bytes[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/RandomAccessFileTest.kt b/nebulosa-io/src/test/kotlin/RandomAccessFileTest.kt index 3e759064b..a516a32f9 100644 --- a/nebulosa-io/src/test/kotlin/RandomAccessFileTest.kt +++ b/nebulosa-io/src/test/kotlin/RandomAccessFileTest.kt @@ -1,10 +1,10 @@ -import io.kotest.engine.spec.tempfile import nebulosa.io.seekableSink import nebulosa.io.seekableSource +import kotlin.io.path.writeBytes class RandomAccessFileTest : AbstractSeekableSinkAndSourceTest() { - private val file = tempfile() + private val file = tempPath("raf-", ".dat") override val sink = file.seekableSink() override val source = file.seekableSource() diff --git a/nebulosa-io/src/test/kotlin/RandomSourceTest.kt b/nebulosa-io/src/test/kotlin/RandomSourceTest.kt index 742d24245..0da1cb088 100644 --- a/nebulosa-io/src/test/kotlin/RandomSourceTest.kt +++ b/nebulosa-io/src/test/kotlin/RandomSourceTest.kt @@ -1,38 +1,45 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import nebulosa.io.source import okio.buffer +import org.junit.jupiter.api.Test import java.util.* -class RandomSourceTest : StringSpec() { +class RandomSourceTest { - init { - "read full segment" { - val source = Random(0).source(8192L).buffer() - val bytes = source.readByteArray(8192L) - bytes.isRandom().shouldBeTrue() - } - "read few bytes" { - val source = Random(0).source(8192L).buffer() - val bytes = source.readByteArray(256) - bytes.isRandom().shouldBeTrue() - } - "read many bytes" { - val source = Random(0).source(8192L * 4L).buffer() - val bytes = source.readByteArray(8192L * 4L) - bytes.isRandom().shouldBeTrue() - } + @Test + fun readFullSegmen() { + val source = Random(0).source(8192L).buffer() + val bytes = source.readByteArray(8192L) + bytes.isRandom().shouldBeTrue() } - private fun ByteArray.isRandom(): Boolean { - val counter = IntArray(256) + @Test + fun readFewByte() { + val source = Random(0).source(8192L).buffer() + val bytes = source.readByteArray(256) + bytes.isRandom().shouldBeTrue() + } - for (byte in this) { - counter[byte.toInt() and 0xFF]++ - } + @Test + fun readManyByte() { + val source = Random(0).source(8192L * 4L).buffer() + val bytes = source.readByteArray(8192L * 4L) + bytes.isRandom().shouldBeTrue() + } + + companion object { + + @JvmStatic + private fun ByteArray.isRandom(): Boolean { + val counter = IntArray(256) - val average = counter.average() + for (byte in this) { + counter[byte.toInt() and 0xFF]++ + } - return size / 256 == average.toInt() + val average = counter.average() + + return size / 256 == average.toInt() + } } } diff --git a/nebulosa-json/build.gradle.kts b/nebulosa-json/build.gradle.kts new file mode 100644 index 000000000..7ab28945d --- /dev/null +++ b/nebulosa-json/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + kotlin("jvm") + id("maven-publish") +} + +dependencies { + api(libs.bundles.jackson) + testImplementation(project(":nebulosa-test")) +} + +publishing { + publications { + create("pluginMaven") { + from(components["java"]) + } + } +} diff --git a/nebulosa-common/src/main/kotlin/nebulosa/common/json/PathDeserializer.kt b/nebulosa-json/src/main/kotlin/nebulosa/json/PathDeserializer.kt similarity index 68% rename from nebulosa-common/src/main/kotlin/nebulosa/common/json/PathDeserializer.kt rename to nebulosa-json/src/main/kotlin/nebulosa/json/PathDeserializer.kt index 2d0fb4fda..77640facf 100644 --- a/nebulosa-common/src/main/kotlin/nebulosa/common/json/PathDeserializer.kt +++ b/nebulosa-json/src/main/kotlin/nebulosa/json/PathDeserializer.kt @@ -1,13 +1,11 @@ -package nebulosa.common.json +package nebulosa.json import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.deser.std.StdDeserializer import java.nio.file.Path -data object PathDeserializer : StdDeserializer(Path::class.java) { - - private fun readResolve(): Any = PathDeserializer +class PathDeserializer : StdDeserializer(Path::class.java) { override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Path? { return p.valueAsString?.let(Path::of) diff --git a/nebulosa-json/src/main/kotlin/nebulosa/json/PathModule.kt b/nebulosa-json/src/main/kotlin/nebulosa/json/PathModule.kt new file mode 100644 index 000000000..23e4a2d13 --- /dev/null +++ b/nebulosa-json/src/main/kotlin/nebulosa/json/PathModule.kt @@ -0,0 +1,24 @@ +package nebulosa.json + +import com.fasterxml.jackson.databind.module.SimpleDeserializers +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.databind.module.SimpleSerializers +import com.fasterxml.jackson.datatype.jsr310.PackageVersion +import java.nio.file.Path + +class PathModule : SimpleModule(PackageVersion.VERSION) { + + override fun setupModule(context: SetupContext) { + super.setupModule(context) + + with(SimpleDeserializers()) { + addDeserializer(Path::class.java, PathDeserializer()) + context.addDeserializers(this) + } + + with(SimpleSerializers()) { + addSerializer(PathSerializer()) + context.addSerializers(this) + } + } +} diff --git a/nebulosa-common/src/main/kotlin/nebulosa/common/json/PathSerializer.kt b/nebulosa-json/src/main/kotlin/nebulosa/json/PathSerializer.kt similarity index 70% rename from nebulosa-common/src/main/kotlin/nebulosa/common/json/PathSerializer.kt rename to nebulosa-json/src/main/kotlin/nebulosa/json/PathSerializer.kt index 199eb96b2..36c08fbbb 100644 --- a/nebulosa-common/src/main/kotlin/nebulosa/common/json/PathSerializer.kt +++ b/nebulosa-json/src/main/kotlin/nebulosa/json/PathSerializer.kt @@ -1,13 +1,11 @@ -package nebulosa.common.json +package nebulosa.json import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.ser.std.StdSerializer import java.nio.file.Path -data object PathSerializer : StdSerializer(Path::class.java) { - - private fun readResolve(): Any = PathSerializer +class PathSerializer : StdSerializer(Path::class.java) { override fun serialize(value: Path?, gen: JsonGenerator, provider: SerializerProvider) { value?.also { gen.writeString("$it") } ?: gen.writeNull() diff --git a/nebulosa-lx200-protocol/src/main/kotlin/nebulosa/lx200/protocol/LX200ProtocolHandler.kt b/nebulosa-lx200-protocol/src/main/kotlin/nebulosa/lx200/protocol/LX200ProtocolHandler.kt index 2e1907d8b..591009c4e 100644 --- a/nebulosa-lx200-protocol/src/main/kotlin/nebulosa/lx200/protocol/LX200ProtocolHandler.kt +++ b/nebulosa-lx200-protocol/src/main/kotlin/nebulosa/lx200/protocol/LX200ProtocolHandler.kt @@ -7,6 +7,7 @@ import nebulosa.log.debug import nebulosa.log.loggerFor import nebulosa.math.deg import nebulosa.math.hours +import nebulosa.time.SystemClock import java.time.* import java.util.concurrent.atomic.AtomicBoolean @@ -16,9 +17,9 @@ class LX200ProtocolHandler(private val server: LX200ProtocolServer) : ChannelInb @Volatile private var rightAscension = 0.0 @Volatile private var declination = 0.0 - @Volatile private var date = LocalDate.now() - @Volatile private var time = LocalTime.now() - @Volatile private var offset = ZoneId.systemDefault().rules.getOffset(Instant.now()) + @Volatile private var date = LocalDate.now(SystemClock) + @Volatile private var time = LocalTime.now(SystemClock) + @Volatile private var offset = SystemClock.zone.rules.getOffset(Instant.now()) override fun handlerAdded(ctx: ChannelHandlerContext) { LOG.info("client connected. address={}", ctx.channel().remoteAddress()) @@ -54,9 +55,9 @@ class LX200ProtocolHandler(private val server: LX200ProtocolServer) : ChannelInb "#:GD#" -> ctx.writeAndFlush(LX200ProtocolMessage.DECPosition(server.declination)) "#:Gg#" -> ctx.writeAndFlush(LX200ProtocolMessage.Longitude(server.longitude)) "#:Gt#" -> ctx.writeAndFlush(LX200ProtocolMessage.Latitude(server.latitude)) - "#:GC#" -> ctx.writeAndFlush(LX200ProtocolMessage.Date(LocalDate.now())) - "#:GL#" -> ctx.writeAndFlush(LX200ProtocolMessage.Time(LocalTime.now())) - "#:GG#" -> ctx.writeAndFlush(LX200ProtocolMessage.ZoneOffset(ZoneId.systemDefault().rules.getOffset(Instant.now()).totalSeconds / 3600.0)) + "#:GC#" -> ctx.writeAndFlush(LX200ProtocolMessage.Date(LocalDate.now(SystemClock))) + "#:GL#" -> ctx.writeAndFlush(LX200ProtocolMessage.Time(LocalTime.now(SystemClock))) + "#:GG#" -> ctx.writeAndFlush(LX200ProtocolMessage.ZoneOffset(SystemClock.zone.rules.getOffset(Instant.now()).totalSeconds / 3600.0)) "#:GW#" -> ctx.writeAndFlush(LX200ProtocolMessage.Status("G", server.tracking, server.parked)) "#:CM#" -> { ctx.writeAndFlush(LX200ProtocolMessage.Zero) @@ -101,14 +102,14 @@ class LX200ProtocolHandler(private val server: LX200ProtocolServer) : ChannelInb ctx.writeAndFlush(LX200ProtocolMessage.Ok) time = LocalTime.parse(command.substring(4, command.length - 1), LX200ProtocolEncoder.CALENDAR_TIME_FORMAT) val localTime = OffsetDateTime.of(date, time, offset) - val utcTime = localTime.minusSeconds(ZoneId.systemDefault().rules.getOffset(Instant.now()).totalSeconds.toLong()) + val utcTime = localTime.minusSeconds(SystemClock.zone.rules.getOffset(Instant.now()).totalSeconds.toLong()) server.time(utcTime) } command.startsWith("#:SC") -> { ctx.writeAndFlush(LX200ProtocolMessage.Text("1Updating planetary data # #")) date = LocalDate.parse(command.substring(4, command.length - 1), LX200ProtocolEncoder.CALENDAR_DATE_FORMAT) val localTime = OffsetDateTime.of(date, time, offset) - val utcTime = localTime.minusSeconds(ZoneId.systemDefault().rules.getOffset(Instant.now()).totalSeconds.toLong()) + val utcTime = localTime.minusSeconds(SystemClock.zone.rules.getOffset(Instant.now()).totalSeconds.toLong()) server.time(utcTime) } command.startsWith("#:SG") -> { @@ -116,7 +117,7 @@ class LX200ProtocolHandler(private val server: LX200ProtocolServer) : ChannelInb val offsetInHours = -command.substring(4, command.length - 1).toDouble() offset = ZoneOffset.ofTotalSeconds((offsetInHours * 3600.0).toInt()) val localTime = OffsetDateTime.of(date, time, offset) - val utcTime = localTime.minusSeconds(ZoneId.systemDefault().rules.getOffset(Instant.now()).totalSeconds.toLong()) + val utcTime = localTime.minusSeconds(SystemClock.zone.rules.getOffset(Instant.now()).totalSeconds.toLong()) server.time(utcTime) } command.startsWith("#:Sr") -> ctx.updateRA(command.substring(4)) diff --git a/nebulosa-math/src/main/kotlin/nebulosa/math/Unsafe.kt b/nebulosa-math/src/main/kotlin/nebulosa/math/Unsafe.kt index ffcb216f2..b7e91d355 100644 --- a/nebulosa-math/src/main/kotlin/nebulosa/math/Unsafe.kt +++ b/nebulosa-math/src/main/kotlin/nebulosa/math/Unsafe.kt @@ -2,8 +2,8 @@ package nebulosa.math import kotlin.annotation.AnnotationTarget.* -@RequiresOptIn(level = RequiresOptIn.Level.WARNING) @Retention(AnnotationRetention.BINARY) +@RequiresOptIn(level = RequiresOptIn.Level.WARNING) @Target(CLASS, ANNOTATION_CLASS, PROPERTY, FIELD, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, TYPEALIAS) @MustBeDocumented annotation class Unsafe diff --git a/nebulosa-math/src/test/kotlin/AngleTest.kt b/nebulosa-math/src/test/kotlin/AngleTest.kt index e648f136d..4bdfe2bb1 100644 --- a/nebulosa-math/src/test/kotlin/AngleTest.kt +++ b/nebulosa-math/src/test/kotlin/AngleTest.kt @@ -1,180 +1,209 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.math.* +import org.junit.jupiter.api.Test import java.util.* -class AngleTest : StringSpec() { - - init { - "mas" { - 37000.mas shouldBe (0.000179381 plusOrMinus 1e-8) - 37000.mas.toArcsec shouldBe (37.0 plusOrMinus 0.1) - 37000.mas.toArcmin shouldBe (0.616666667 plusOrMinus 1e-8) - 37000.mas.toDegrees shouldBe (0.01027778 plusOrMinus 1e-8) - 37000.mas.toHours shouldBe (0.00068518 plusOrMinus 1e-8) - } - "arcsec" { - 37.arcsec shouldBe (0.000179381 plusOrMinus 1e-8) - 37.arcsec.toMas shouldBe (37000.0 plusOrMinus 0.1) - 37.arcsec.toArcmin shouldBe (0.616666667 plusOrMinus 1e-8) - 37.arcsec.toDegrees shouldBe (0.01027778 plusOrMinus 1e-8) - 37.arcsec.toHours shouldBe (0.00068518 plusOrMinus 1e-8) - } - "arcmin" { - 45.arcmin shouldBe (0.01308997 plusOrMinus 1e-8) - 45.arcmin.toMas shouldBe (2700000.0 plusOrMinus 0.1) - 45.arcmin.toArcsec shouldBe (2700.0 plusOrMinus 0.1) - 45.arcmin.toDegrees shouldBe (0.75 plusOrMinus 1e-8) - 45.arcmin.toHours shouldBe (0.05 plusOrMinus 1e-8) - } - "degrees" { - 6.deg shouldBe (0.10471975 plusOrMinus 1e-8) - 6.deg.toMas shouldBe (21600000.0 plusOrMinus 0.1) - 6.deg.toArcsec shouldBe (21600.0 plusOrMinus 0.1) - 6.deg.toArcmin shouldBe (360.0 plusOrMinus 0.1) - 6.deg.toHours shouldBe (0.4 plusOrMinus 1e-8) - } - "hours" { - 4.hours shouldBe (1.04719755 plusOrMinus 1e-8) - 4.hours.toMas shouldBe (216000000.0 plusOrMinus 0.1) - 4.hours.toArcsec shouldBe (216000.0 plusOrMinus 0.1) - 4.hours.toArcmin shouldBe (3600.0 plusOrMinus 0.1) - 4.hours.toDegrees shouldBe (60.0 plusOrMinus 0.1) - } - "dms" { - DMS(6, 45, 8.0) shouldBe (0.1178485 plusOrMinus 1e-8) - } - "plus" { - (0.5.rad + 0.5.rad) shouldBeExactly 1.0 - (0.5.rad + 0.5) shouldBeExactly 1.0 - } - "minus" { - (0.8.rad - 0.5.rad) shouldBe (0.3 plusOrMinus 1e-2) - (0.8.rad - 0.5) shouldBe (0.3 plusOrMinus 1e-2) - } - "times" { - (0.5.rad * 5) shouldBeExactly 2.5 - } - "div" { - (5.0.rad / 5) shouldBeExactly 1.0 - 5.0.rad / 5.0.rad shouldBeExactly 1.0 - } - "rem" { - (5.0.rad % 5) shouldBeExactly 0.0 - (5.0.rad % 5.0.rad) shouldBeExactly 0.0 - } - "parse decimal coordinates" { - "23.5634453".deg.toDegrees shouldBe 23.5634453 - "23.5634453".hours.toDegrees shouldBe 353.4516795 - } - "parse sexagesimal coordinates" { - "23 33 48.40308".deg.toDegrees shouldBe 23.5634453 - "23h 33 48.40308".hours.toDegrees shouldBe 353.4516795 - "23 33m 48.40308".deg.toDegrees shouldBe 23.5634453 - "23 33 48.40308s".deg.toDegrees shouldBe 23.5634453 - "23h 33m 48.40308".hours.toDegrees shouldBe 353.4516795 - "23 33m 48.40308s".deg.toDegrees shouldBe 23.5634453 - "23h 33m 48.40308s".hours.toDegrees shouldBe 353.4516795 - "-23° 33m 48.40308s".deg.toDegrees shouldBe -23.5634453 - " -23 33m 48.40308s ".deg.toDegrees shouldBe -23.5634453 - "-23 33.806718m".deg.toDegrees shouldBe -23.5634453 - "+23".deg.toDegrees shouldBe 23.0 - "-23".deg.toDegrees shouldBe -23.0 - "23h33m48.40308s".hours.toDegrees shouldBe 353.4516795 - "23h33m 48.40308\"".hours.toDegrees shouldBe 353.4516795 - "23h33'48.40308\"".hours.toDegrees shouldBe 353.4516795 - "-23°33'48.40308\"".hours.toDegrees shouldBe -353.4516795 - "-23°33'48.40308s 67.99".deg.toDegrees shouldBe -23.5634453 - "- 23°33'48.40308s 67.99".deg.toDegrees shouldBe -23.5634453 - "".deg.isFinite().shouldBeFalse() - "kkk".deg.isFinite().shouldBeFalse() - } - "format" { - val angle = "12h 30 1".hours - - angle.toHours shouldBe (12.5003 plusOrMinus 1e-4) - - AngleFormatter.Builder() - .degrees() - .secondsDecimalPlaces(2) - .separators("°", "'", "\"") - .locale(Locale.ENGLISH) - .build() - .format(angle) shouldBe "+187°30'15.00\"" - - AngleFormatter.Builder() - .hours() - .secondsDecimalPlaces(1) - .separators("h", "m", "s") - .locale(Locale.ENGLISH) - .build() - .format(angle) shouldBe "+12h30m01.0s" - - AngleFormatter.Builder() - .hours() - .noSign() - .secondsDecimalPlaces(0) - .locale(Locale.ENGLISH) - .build() - .format(angle) shouldBe "12:30:01" - - AngleFormatter.Builder() - .hours() - .noSign() - .noSeconds() - .build() - .format(angle) shouldBe "12:30" - - AngleFormatter.Builder() - .hours() - .noSign() - .noSeconds() - .separators("h", "m") - .build() - .format(angle) shouldBe "12h30m" - - val negativeAngle = "-43 00 45".deg - - AngleFormatter.Builder() - .degreesFormat("%02d") - .separators("°", "'", "\"") - .secondsDecimalPlaces(2) - .build() - .format(negativeAngle) shouldBe "-43°00'45.00\"" - - AngleFormatter.HMS - .format(angle) shouldBe "12h30m01.0s" - - AngleFormatter.SIGNED_DMS - .format(negativeAngle) shouldBe "-043°00'45.0\"" - - AngleFormatter.DMS - .format(angle) shouldBe "187°30'15.0\"" - - AngleFormatter.SIGNED_DMS - .newBuilder() - .secondsDecimalPlaces(2) - .degreesFormat("%03d") - .minutesFormat("%04d") - .secondsFormat("%02.05f") - .whitespaced() - .build() - .format(negativeAngle) shouldBe "-043 0000 45.00000" - - AngleFormatter.HMS.format(0.0) shouldBe "00h00m00.0s" - AngleFormatter.HMS.format(CIRCLE) shouldBe "00h00m00.0s" - } - "bug on round seconds" { - "23h59m60.0s".hours.formatHMS() shouldBe "00h00m00.0s" - - AngleFormatter.HMS - .format(Radians(6.283182643402501)) shouldBe "23h59m59.9s" - } - "bug on parse Unicode negative sign U+2212" { - "−29 00 28.1".deg.toDegrees shouldBe -29.007805555555557 - } +class AngleTest { + + @Test + fun mas() { + 37000.mas shouldBe (0.000179381 plusOrMinus 1e-8) + 37000.mas.toArcsec shouldBe (37.0 plusOrMinus 0.1) + 37000.mas.toArcmin shouldBe (0.616666667 plusOrMinus 1e-8) + 37000.mas.toDegrees shouldBe (0.01027778 plusOrMinus 1e-8) + 37000.mas.toHours shouldBe (0.00068518 plusOrMinus 1e-8) + } + + @Test + fun arcsec() { + 37.arcsec shouldBe (0.000179381 plusOrMinus 1e-8) + 37.arcsec.toMas shouldBe (37000.0 plusOrMinus 0.1) + 37.arcsec.toArcmin shouldBe (0.616666667 plusOrMinus 1e-8) + 37.arcsec.toDegrees shouldBe (0.01027778 plusOrMinus 1e-8) + 37.arcsec.toHours shouldBe (0.00068518 plusOrMinus 1e-8) + } + + @Test + fun arcmin() { + 45.arcmin shouldBe (0.01308997 plusOrMinus 1e-8) + 45.arcmin.toMas shouldBe (2700000.0 plusOrMinus 0.1) + 45.arcmin.toArcsec shouldBe (2700.0 plusOrMinus 0.1) + 45.arcmin.toDegrees shouldBe (0.75 plusOrMinus 1e-8) + 45.arcmin.toHours shouldBe (0.05 plusOrMinus 1e-8) + } + + @Test + fun degrees() { + 6.deg shouldBe (0.10471975 plusOrMinus 1e-8) + 6.deg.toMas shouldBe (21600000.0 plusOrMinus 0.1) + 6.deg.toArcsec shouldBe (21600.0 plusOrMinus 0.1) + 6.deg.toArcmin shouldBe (360.0 plusOrMinus 0.1) + 6.deg.toHours shouldBe (0.4 plusOrMinus 1e-8) + } + + @Test + fun hours() { + 4.hours shouldBe (1.04719755 plusOrMinus 1e-8) + 4.hours.toMas shouldBe (216000000.0 plusOrMinus 0.1) + 4.hours.toArcsec shouldBe (216000.0 plusOrMinus 0.1) + 4.hours.toArcmin shouldBe (3600.0 plusOrMinus 0.1) + 4.hours.toDegrees shouldBe (60.0 plusOrMinus 0.1) + } + + @Test + fun dms() { + DMS(6, 45, 8.0) shouldBe (0.1178485 plusOrMinus 1e-8) + } + + @Test + fun plus() { + (0.5.rad + 0.5.rad) shouldBeExactly 1.0 + (0.5.rad + 0.5) shouldBeExactly 1.0 + } + + @Test + fun minus() { + (0.8.rad - 0.5.rad) shouldBe (0.3 plusOrMinus 1e-2) + (0.8.rad - 0.5) shouldBe (0.3 plusOrMinus 1e-2) + } + + @Test + fun times() { + (0.5.rad * 5) shouldBeExactly 2.5 + } + + @Test + fun div() { + (5.0.rad / 5) shouldBeExactly 1.0 + 5.0.rad / 5.0.rad shouldBeExactly 1.0 + } + + @Test + fun rem() { + (5.0.rad % 5) shouldBeExactly 0.0 + (5.0.rad % 5.0.rad) shouldBeExactly 0.0 + } + + @Test + fun parseDecimalCoordinates() { + "23.5634453".deg.toDegrees shouldBe 23.5634453 + "23.5634453".hours.toDegrees shouldBe 353.4516795 + } + + @Test + fun parseSexagesimalCoordinates() { + "23 33 48.40308".deg.toDegrees shouldBe 23.5634453 + "23h 33 48.40308".hours.toDegrees shouldBe 353.4516795 + "23 33m 48.40308".deg.toDegrees shouldBe 23.5634453 + "23 33 48.40308s".deg.toDegrees shouldBe 23.5634453 + "23h 33m 48.40308".hours.toDegrees shouldBe 353.4516795 + "23 33m 48.40308s".deg.toDegrees shouldBe 23.5634453 + "23h 33m 48.40308s".hours.toDegrees shouldBe 353.4516795 + "-23° 33m 48.40308s".deg.toDegrees shouldBe -23.5634453 + " -23 33m 48.40308s ".deg.toDegrees shouldBe -23.5634453 + "-23 33.806718m".deg.toDegrees shouldBe -23.5634453 + "+23".deg.toDegrees shouldBe 23.0 + "-23".deg.toDegrees shouldBe -23.0 + "23h33m48.40308s".hours.toDegrees shouldBe 353.4516795 + "23h33m 48.40308\"".hours.toDegrees shouldBe 353.4516795 + "23h33'48.40308\"".hours.toDegrees shouldBe 353.4516795 + "-23°33'48.40308\"".hours.toDegrees shouldBe -353.4516795 + "-23°33'48.40308s 67.99".deg.toDegrees shouldBe -23.5634453 + "- 23°33'48.40308s 67.99".deg.toDegrees shouldBe -23.5634453 + "".deg.isFinite().shouldBeFalse() + "kkk".deg.isFinite().shouldBeFalse() + } + + @Test + fun format() { + val angle = "12h 30 1".hours + + angle.toHours shouldBe (12.5003 plusOrMinus 1e-4) + + AngleFormatter.Builder() + .degrees() + .secondsDecimalPlaces(2) + .separators("°", "'", "\"") + .locale(Locale.ENGLISH) + .build() + .format(angle) shouldBe "+187°30'15.00\"" + + AngleFormatter.Builder() + .hours() + .secondsDecimalPlaces(1) + .separators("h", "m", "s") + .locale(Locale.ENGLISH) + .build() + .format(angle) shouldBe "+12h30m01.0s" + + AngleFormatter.Builder() + .hours() + .noSign() + .secondsDecimalPlaces(0) + .locale(Locale.ENGLISH) + .build() + .format(angle) shouldBe "12:30:01" + + AngleFormatter.Builder() + .hours() + .noSign() + .noSeconds() + .build() + .format(angle) shouldBe "12:30" + + AngleFormatter.Builder() + .hours() + .noSign() + .noSeconds() + .separators("h", "m") + .build() + .format(angle) shouldBe "12h30m" + + val negativeAngle = "-43 00 45".deg + + AngleFormatter.Builder() + .degreesFormat("%02d") + .separators("°", "'", "\"") + .secondsDecimalPlaces(2) + .build() + .format(negativeAngle) shouldBe "-43°00'45.00\"" + + AngleFormatter.HMS + .format(angle) shouldBe "12h30m01.0s" + + AngleFormatter.SIGNED_DMS + .format(negativeAngle) shouldBe "-043°00'45.0\"" + + AngleFormatter.DMS + .format(angle) shouldBe "187°30'15.0\"" + + AngleFormatter.SIGNED_DMS + .newBuilder() + .secondsDecimalPlaces(2) + .degreesFormat("%03d") + .minutesFormat("%04d") + .secondsFormat("%02.05f") + .whitespaced() + .build() + .format(negativeAngle) shouldBe "-043 0000 45.00000" + + AngleFormatter.HMS.format(0.0) shouldBe "00h00m00.0s" + AngleFormatter.HMS.format(CIRCLE) shouldBe "00h00m00.0s" + } + + @Test + fun bugOnRoundSeconds() { + "23h59m60.0s".hours.formatHMS() shouldBe "00h00m00.0s" + + AngleFormatter.HMS + .format(Radians(6.283182643402501)) shouldBe "23h59m59.9s" + } + + @Test + fun bugOnParseUnicodeNegativeSignU2212() { + "−29 00 28.1".deg.toDegrees shouldBe -29.007805555555557 } } diff --git a/nebulosa-math/src/test/kotlin/DistanceTest.kt b/nebulosa-math/src/test/kotlin/DistanceTest.kt index 79e9a2185..088da182b 100644 --- a/nebulosa-math/src/test/kotlin/DistanceTest.kt +++ b/nebulosa-math/src/test/kotlin/DistanceTest.kt @@ -1,43 +1,51 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.math.* +import org.junit.jupiter.api.Test -class DistanceTest : StringSpec() { - - init { - - timeout = 1000L - - "au" { - 1.au.toKilometers shouldBe (149597870.700 plusOrMinus 1e-8) - 1.au.toMeters shouldBe (149597870700.0 plusOrMinus 1e-8) - 1.au.toLightYears shouldBe (0.000015813 plusOrMinus 1e-8) - } - "km" { - 8000.km shouldBe (0.00005348 plusOrMinus 1e-8) - 8000.km.toMeters shouldBe (8000000.0 plusOrMinus 1e-8) - 8000.km.toLightYears shouldBe (0.0000000008456 plusOrMinus 1e-12) - } - "plus" { - (0.5.au + 0.5.au) shouldBeExactly 1.0 - (0.5.au + 0.5) shouldBeExactly 1.0 - } - "minus" { - (0.8.au - 0.5.au) shouldBe (0.3 plusOrMinus 1e-2) - (0.8.au - 0.5) shouldBe (0.3 plusOrMinus 1e-2) - } - "times" { - (0.5.au * 5) shouldBeExactly 2.5 - } - "div" { - (5.0.au / 5) shouldBeExactly 1.0 - 5.0.au / 5.0.au shouldBeExactly 1.0 - } - "rem" { - (5.0.au % 5) shouldBeExactly 0.0 - (5.0.au % 5.0.au) shouldBeExactly 0.0 - } +class DistanceTest { + + @Test + fun au() { + 1.au.toKilometers shouldBe (149597870.700 plusOrMinus 1e-8) + 1.au.toMeters shouldBe (149597870700.0 plusOrMinus 1e-8) + 1.au.toLightYears shouldBe (0.000015813 plusOrMinus 1e-8) + } + + @Test + fun km() { + 8000.km shouldBe (0.00005348 plusOrMinus 1e-8) + 8000.km.toMeters shouldBe (8000000.0 plusOrMinus 1e-8) + 8000.km.toLightYears shouldBe (0.0000000008456 plusOrMinus 1e-12) + } + + @Test + fun plus() { + (0.5.au + 0.5.au) shouldBeExactly 1.0 + (0.5.au + 0.5) shouldBeExactly 1.0 + } + + @Test + fun minus() { + (0.8.au - 0.5.au) shouldBe (0.3 plusOrMinus 1e-2) + (0.8.au - 0.5) shouldBe (0.3 plusOrMinus 1e-2) + } + + @Test + fun times() { + (0.5.au * 5) shouldBeExactly 2.5 + } + + @Test + fun div() { + (5.0.au / 5) shouldBeExactly 1.0 + 5.0.au / 5.0.au shouldBeExactly 1.0 + } + + @Test + fun rem() { + (5.0.au % 5) shouldBeExactly 0.0 + (5.0.au % 5.0.au) shouldBeExactly 0.0 } } diff --git a/nebulosa-math/src/test/kotlin/Matrix3DTest.kt b/nebulosa-math/src/test/kotlin/Matrix3DTest.kt index 24cf7f82c..cb67aed89 100644 --- a/nebulosa-math/src/test/kotlin/Matrix3DTest.kt +++ b/nebulosa-math/src/test/kotlin/Matrix3DTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.plusOrMinus @@ -8,152 +7,172 @@ import nebulosa.math.Matrix3D import nebulosa.math.SEMICIRCLE import nebulosa.math.Vector3D import nebulosa.math.rad +import org.junit.jupiter.api.Test @Suppress("FloatingPointLiteralPrecision") -class Matrix3DTest : StringSpec() { - - init { - "rotate x" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m.rotateX(0.3456789.rad) - r[0, 0] shouldBeExactly 2.0 - r[0, 1] shouldBeExactly 3.0 - r[0, 2] shouldBeExactly 2.0 - - r[1, 0] shouldBe (3.839043388235612460 plusOrMinus 1e-12) - r[1, 1] shouldBe (3.237033249594111899 plusOrMinus 1e-12) - r[1, 2] shouldBe (4.516714379005982719 plusOrMinus 1e-12) - - r[2, 0] shouldBe (1.806030415924501684 plusOrMinus 1e-12) - r[2, 1] shouldBe (3.085711545336372503 plusOrMinus 1e-12) - r[2, 2] shouldBe (3.687721683977873065 plusOrMinus 1e-12) - } - "rotate y" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m.rotateY(0.3456789.rad) - r[0, 0] shouldBe (0.8651847818978159930 plusOrMinus 1e-12) - r[0, 1] shouldBe (1.467194920539316554 plusOrMinus 1e-12) - r[0, 2] shouldBe (0.1875137911274457342 plusOrMinus 1e-12) - - r[1, 0] shouldBeExactly 3.0 - r[1, 1] shouldBeExactly 2.0 - r[1, 2] shouldBeExactly 3.0 - - r[2, 0] shouldBe (3.500207892850427330 plusOrMinus 1e-12) - r[2, 1] shouldBe (4.779889022262298150 plusOrMinus 1e-12) - r[2, 2] shouldBe (5.381899160903798712 plusOrMinus 1e-12) - } - "rotate z" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m.rotateZ(0.3456789.rad) - r[0, 0] shouldBe (2.898197754208926769 plusOrMinus 1e-12) - r[0, 1] shouldBe (3.500207892850427330 plusOrMinus 1e-12) - r[0, 2] shouldBe (2.898197754208926769 plusOrMinus 1e-12) - - r[1, 0] shouldBe (2.144865911309686813 plusOrMinus 1e-12) - r[1, 1] shouldBe (0.865184781897815993 plusOrMinus 1e-12) - r[1, 2] shouldBe (2.144865911309686813 plusOrMinus 1e-12) - - r[2, 0] shouldBeExactly 3.0 - r[2, 1] shouldBeExactly 4.0 - r[2, 2] shouldBeExactly 5.0 - } - "chain rotation" { - val m0 = Matrix3D.IDENTITY.rotateZ(SEMICIRCLE).rotateX(-SEMICIRCLE).rotateY(SEMICIRCLE) - val m1 = Matrix3D.rotZ(SEMICIRCLE).rotateX(-SEMICIRCLE).rotateY(SEMICIRCLE) - m0.matrix shouldBe m1.matrix - } - "plus matrix" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val n = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m + n - - r[0, 0] shouldBeExactly 4.0 - r[0, 1] shouldBeExactly 6.0 - r[0, 2] shouldBeExactly 4.0 - - r[1, 0] shouldBeExactly 6.0 - r[1, 1] shouldBeExactly 4.0 - r[1, 2] shouldBeExactly 6.0 - - r[2, 0] shouldBeExactly 6.0 - r[2, 1] shouldBeExactly 8.0 - r[2, 2] shouldBeExactly 10.0 - } - "minus matrix" { - val m = Matrix3D(2.0, 3.0, 2.0, 6.0, 9.0, 6.0, 3.0, 6.0, 5.0) - val n = Matrix3D(1.0, 1.0, 5.0, 4.0, 5.0, 9.0, 2.0, 4.0, 4.0) - val r = m - n - - r[0, 0] shouldBeExactly 1.0 - r[0, 1] shouldBeExactly 2.0 - r[0, 2] shouldBeExactly -3.0 - - r[1, 0] shouldBeExactly 2.0 - r[1, 1] shouldBeExactly 4.0 - r[1, 2] shouldBeExactly -3.0 - - r[2, 0] shouldBeExactly 1.0 - r[2, 1] shouldBeExactly 2.0 - r[2, 2] shouldBeExactly 1.0 - } - "times scalar" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m * 5.0 - - r[0, 0] shouldBeExactly 10.0 - r[0, 1] shouldBeExactly 15.0 - r[0, 2] shouldBeExactly 10.0 - - r[1, 0] shouldBeExactly 15.0 - r[1, 1] shouldBeExactly 10.0 - r[1, 2] shouldBeExactly 15.0 - - r[2, 0] shouldBeExactly 15.0 - r[2, 1] shouldBeExactly 20.0 - r[2, 2] shouldBeExactly 25.0 - } - "transpose" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m.transposed - - r[0, 0] shouldBeExactly 2.0 - r[0, 1] shouldBeExactly 3.0 - r[0, 2] shouldBeExactly 3.0 - - r[1, 0] shouldBeExactly 3.0 - r[1, 1] shouldBeExactly 2.0 - r[1, 2] shouldBeExactly 4.0 - - r[2, 0] shouldBeExactly 2.0 - r[2, 1] shouldBeExactly 3.0 - r[2, 2] shouldBeExactly 5.0 - } - "times vector" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val v = Vector3D(2.0, 3.0, 2.0) - val r = m * v - - r[0] shouldBeExactly 17.0 - r[1] shouldBeExactly 18.0 - r[2] shouldBeExactly 28.0 - } - "determinant" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - m.determinant shouldBeExactly -10.0 - } - "is empty" { - Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0).isEmpty().shouldBeFalse() - Matrix3D(2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeTrue() - } +class Matrix3DTest { + + @Test + fun rotateX() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m.rotateX(0.3456789.rad) + r[0, 0] shouldBeExactly 2.0 + r[0, 1] shouldBeExactly 3.0 + r[0, 2] shouldBeExactly 2.0 + + r[1, 0] shouldBe (3.839043388235612460 plusOrMinus 1e-12) + r[1, 1] shouldBe (3.237033249594111899 plusOrMinus 1e-12) + r[1, 2] shouldBe (4.516714379005982719 plusOrMinus 1e-12) + + r[2, 0] shouldBe (1.806030415924501684 plusOrMinus 1e-12) + r[2, 1] shouldBe (3.085711545336372503 plusOrMinus 1e-12) + r[2, 2] shouldBe (3.687721683977873065 plusOrMinus 1e-12) + } + + @Test + fun rotateY() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m.rotateY(0.3456789.rad) + r[0, 0] shouldBe (0.8651847818978159930 plusOrMinus 1e-12) + r[0, 1] shouldBe (1.467194920539316554 plusOrMinus 1e-12) + r[0, 2] shouldBe (0.1875137911274457342 plusOrMinus 1e-12) + + r[1, 0] shouldBeExactly 3.0 + r[1, 1] shouldBeExactly 2.0 + r[1, 2] shouldBeExactly 3.0 + + r[2, 0] shouldBe (3.500207892850427330 plusOrMinus 1e-12) + r[2, 1] shouldBe (4.779889022262298150 plusOrMinus 1e-12) + r[2, 2] shouldBe (5.381899160903798712 plusOrMinus 1e-12) + } + + @Test + fun rotateZ() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m.rotateZ(0.3456789.rad) + r[0, 0] shouldBe (2.898197754208926769 plusOrMinus 1e-12) + r[0, 1] shouldBe (3.500207892850427330 plusOrMinus 1e-12) + r[0, 2] shouldBe (2.898197754208926769 plusOrMinus 1e-12) + + r[1, 0] shouldBe (2.144865911309686813 plusOrMinus 1e-12) + r[1, 1] shouldBe (0.865184781897815993 plusOrMinus 1e-12) + r[1, 2] shouldBe (2.144865911309686813 plusOrMinus 1e-12) + + r[2, 0] shouldBeExactly 3.0 + r[2, 1] shouldBeExactly 4.0 + r[2, 2] shouldBeExactly 5.0 + } + + @Test + fun chainRotation() { + val m0 = Matrix3D.IDENTITY.rotateZ(SEMICIRCLE).rotateX(-SEMICIRCLE).rotateY(SEMICIRCLE) + val m1 = Matrix3D.rotZ(SEMICIRCLE).rotateX(-SEMICIRCLE).rotateY(SEMICIRCLE) + m0.matrix shouldBe m1.matrix + } + + @Test + fun plusMatrix() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val n = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m + n + + r[0, 0] shouldBeExactly 4.0 + r[0, 1] shouldBeExactly 6.0 + r[0, 2] shouldBeExactly 4.0 + + r[1, 0] shouldBeExactly 6.0 + r[1, 1] shouldBeExactly 4.0 + r[1, 2] shouldBeExactly 6.0 + + r[2, 0] shouldBeExactly 6.0 + r[2, 1] shouldBeExactly 8.0 + r[2, 2] shouldBeExactly 10.0 + } + + @Test + fun minusMatrix() { + val m = Matrix3D(2.0, 3.0, 2.0, 6.0, 9.0, 6.0, 3.0, 6.0, 5.0) + val n = Matrix3D(1.0, 1.0, 5.0, 4.0, 5.0, 9.0, 2.0, 4.0, 4.0) + val r = m - n + + r[0, 0] shouldBeExactly 1.0 + r[0, 1] shouldBeExactly 2.0 + r[0, 2] shouldBeExactly -3.0 + + r[1, 0] shouldBeExactly 2.0 + r[1, 1] shouldBeExactly 4.0 + r[1, 2] shouldBeExactly -3.0 + + r[2, 0] shouldBeExactly 1.0 + r[2, 1] shouldBeExactly 2.0 + r[2, 2] shouldBeExactly 1.0 + } + + @Test + fun timesScalar() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m * 5.0 + + r[0, 0] shouldBeExactly 10.0 + r[0, 1] shouldBeExactly 15.0 + r[0, 2] shouldBeExactly 10.0 + + r[1, 0] shouldBeExactly 15.0 + r[1, 1] shouldBeExactly 10.0 + r[1, 2] shouldBeExactly 15.0 + + r[2, 0] shouldBeExactly 15.0 + r[2, 1] shouldBeExactly 20.0 + r[2, 2] shouldBeExactly 25.0 + } + + @Test + fun transpose() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m.transposed + + r[0, 0] shouldBeExactly 2.0 + r[0, 1] shouldBeExactly 3.0 + r[0, 2] shouldBeExactly 3.0 + + r[1, 0] shouldBeExactly 3.0 + r[1, 1] shouldBeExactly 2.0 + r[1, 2] shouldBeExactly 4.0 + + r[2, 0] shouldBeExactly 2.0 + r[2, 1] shouldBeExactly 3.0 + r[2, 2] shouldBeExactly 5.0 + } + + @Test + fun timesVector() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val v = Vector3D(2.0, 3.0, 2.0) + val r = m * v + + r[0] shouldBeExactly 17.0 + r[1] shouldBeExactly 18.0 + r[2] shouldBeExactly 28.0 + } + + @Test + fun determinant() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + m.determinant shouldBeExactly -10.0 + } + + @Test + fun isEmpty() { + Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0).isEmpty().shouldBeFalse() + Matrix3D(2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeTrue() } } diff --git a/nebulosa-math/src/test/kotlin/Vector3DTest.kt b/nebulosa-math/src/test/kotlin/Vector3DTest.kt index 8a3cb9676..e1fc7357e 100644 --- a/nebulosa-math/src/test/kotlin/Vector3DTest.kt +++ b/nebulosa-math/src/test/kotlin/Vector3DTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.shouldBeExactly @@ -9,81 +8,113 @@ import nebulosa.math.Vector3D import nebulosa.math.deg import nebulosa.math.toDegrees import nebulosa.test.matchers.plusOrMinus +import org.junit.jupiter.api.Test -class Vector3DTest : StringSpec() { - - init { - "plus vector" { - Vector3D(2.0, 3.0, 2.0) + Vector3D(2.0, 3.0, 2.0) shouldBe Vector3D(4.0, 6.0, 4.0) - } - "minus vector" { - Vector3D(2.0, 3.0, 2.0) - Vector3D(1.0, 1.0, 5.0) shouldBe Vector3D(1.0, 2.0, -3.0) - } - "times scalar" { - Vector3D(2.0, 3.0, 2.0) * 5.0 shouldBe Vector3D(10.0, 15.0, 10.0) - } - "divide by scalar" { - Vector3D(2.0, 3.0, 2.0) / 2.0 shouldBe Vector3D(1.0, 1.5, 1.0) - } - "dot" { - val m = Vector3D(2.0, 3.0, 2.0) - val v = Vector3D(2.0, 3.0, 2.0) - m.dot(v) shouldBeExactly 17.0 - m.dot(-v) shouldBeExactly -17.0 - } - "cross" { - Vector3D(2.0, 3.0, 2.0).cross(Vector3D(3.0, 2.0, 3.0)) shouldBe Vector3D(5.0, 0.0, -5.0) - } - "is empty" { - Vector3D(2.0, 3.0, 2.0).isEmpty().shouldBeFalse() - Vector3D(2.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Vector3D(0.0, 3.0, 0.0).isEmpty().shouldBeFalse() - Vector3D(0.0, 0.0, 4.0).isEmpty().shouldBeFalse() - Vector3D(0.0, 0.0, 0.0).isEmpty().shouldBeTrue() - } - "right angle" { - Vector3D.X.angle(Vector3D.Y) shouldBeExactly PIOVERTWO - } - "opposite" { - Vector3D(1.0, 2.0, 3.0).angle(Vector3D(-1.0, -2.0, -3.0)) shouldBeExactly PI - } - "collinear" { - Vector3D(2.0, -3.0, 1.0).angle(Vector3D(4.0, -6.0, 2.0)) shouldBeExactly 0.0 - } - "general" { - Vector3D(3.0, 4.0, 5.0).angle(Vector3D(1.0, 2.0, 2.0)).toDegrees shouldBeExactly 8.130102354156005 - } - "rotate around x axis" { - Vector3D.X.rotate(Vector3D.X, 90.0.deg) shouldBe Vector3D.X - } - "rotate around y axis" { - Vector3D.Y.rotate(Vector3D.Y, 90.0.deg) shouldBe Vector3D.Y - } - "rotate around z axis" { - Vector3D.Z.rotate(Vector3D.Z, 90.0.deg) shouldBe Vector3D.Z - } - "rotate" { - val v = Vector3D(1.0, 2.0, 3.0) - v.rotate(Vector3D.X, PI / 4.0) shouldBe (Vector3D(1.0, -0.707107, 3.535534) plusOrMinus 1e-6) - v.rotate(Vector3D.Y, PI / 4.0) shouldBe (Vector3D(2.828427, 2.0, 1.414213) plusOrMinus 1e-6) - v.rotate(Vector3D.Z, PI / 4.0) shouldBe (Vector3D(-0.707107, 2.12132, 3.0) plusOrMinus 1e-6) - val axis = Vector3D(3.0, 4.0, 5.0) - v.rotate(axis, 29.6512852.deg) shouldBe (Vector3D(1.21325856, 1.73061994, 3.08754891) plusOrMinus 1e-8) - v.rotate(axis, 120.3053274.deg) shouldBe (Vector3D(2.08677229, 1.63198489, 2.64234871) plusOrMinus 1e-8) - v.rotate(axis, 230.6512852.deg) shouldBe (Vector3D(1.69633894, 2.56816842, 2.12766190) plusOrMinus 1e-8) - v.rotate(axis, 359.6139797.deg) shouldBe (Vector3D(0.99810712, 2.00381299, 2.99808533) plusOrMinus 1e-8) - } - "no rotation" { - Vector3D(1.0, 2.0, 3.0).rotate(Vector3D.Y, 0.0) shouldBe Vector3D(1.0, 2.0, 3.0) - } - "plane" { - val a = Vector3D(1.0, -2.0, 1.0) - val b = Vector3D(4.0, -2.0, -2.0) - val c = Vector3D(4.0, 1.0, 4.0) - val d = Vector3D.plane(a, b, c) - d.x shouldBeExactly 9.0 - d.y shouldBeExactly -18.0 - d.z shouldBeExactly 9.0 - } +class Vector3DTest { + + @Test + fun plusVector() { + Vector3D(2.0, 3.0, 2.0) + Vector3D(2.0, 3.0, 2.0) shouldBe Vector3D(4.0, 6.0, 4.0) + } + + @Test + fun minusVector() { + Vector3D(2.0, 3.0, 2.0) - Vector3D(1.0, 1.0, 5.0) shouldBe Vector3D(1.0, 2.0, -3.0) + } + + @Test + fun timesScalar() { + Vector3D(2.0, 3.0, 2.0) * 5.0 shouldBe Vector3D(10.0, 15.0, 10.0) + } + + @Test + fun divideByScalar() { + Vector3D(2.0, 3.0, 2.0) / 2.0 shouldBe Vector3D(1.0, 1.5, 1.0) + } + + @Test + fun dot() { + val m = Vector3D(2.0, 3.0, 2.0) + val v = Vector3D(2.0, 3.0, 2.0) + m.dot(v) shouldBeExactly 17.0 + m.dot(-v) shouldBeExactly -17.0 + } + + @Test + fun cross() { + Vector3D(2.0, 3.0, 2.0).cross(Vector3D(3.0, 2.0, 3.0)) shouldBe Vector3D(5.0, 0.0, -5.0) + } + + @Test + fun isEmpty() { + Vector3D(2.0, 3.0, 2.0).isEmpty().shouldBeFalse() + Vector3D(2.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Vector3D(0.0, 3.0, 0.0).isEmpty().shouldBeFalse() + Vector3D(0.0, 0.0, 4.0).isEmpty().shouldBeFalse() + Vector3D(0.0, 0.0, 0.0).isEmpty().shouldBeTrue() + } + + @Test + fun rightAngle() { + Vector3D.X.angle(Vector3D.Y) shouldBeExactly PIOVERTWO + } + + @Test + fun opposite() { + Vector3D(1.0, 2.0, 3.0).angle(Vector3D(-1.0, -2.0, -3.0)) shouldBeExactly PI + } + + @Test + fun collinear() { + Vector3D(2.0, -3.0, 1.0).angle(Vector3D(4.0, -6.0, 2.0)) shouldBeExactly 0.0 + } + + @Test + fun general() { + Vector3D(3.0, 4.0, 5.0).angle(Vector3D(1.0, 2.0, 2.0)).toDegrees shouldBeExactly 8.130102354156005 + } + + @Test + fun rotateAroundXAxis() { + Vector3D.X.rotate(Vector3D.X, 90.0.deg) shouldBe Vector3D.X + } + + @Test + fun rotateAroundYAxis() { + Vector3D.Y.rotate(Vector3D.Y, 90.0.deg) shouldBe Vector3D.Y + } + + @Test + fun rotateAroundZAxis() { + Vector3D.Z.rotate(Vector3D.Z, 90.0.deg) shouldBe Vector3D.Z + } + + @Test + fun rotate() { + val v = Vector3D(1.0, 2.0, 3.0) + v.rotate(Vector3D.X, PI / 4.0) shouldBe (Vector3D(1.0, -0.707107, 3.535534) plusOrMinus 1e-6) + v.rotate(Vector3D.Y, PI / 4.0) shouldBe (Vector3D(2.828427, 2.0, 1.414213) plusOrMinus 1e-6) + v.rotate(Vector3D.Z, PI / 4.0) shouldBe (Vector3D(-0.707107, 2.12132, 3.0) plusOrMinus 1e-6) + val axis = Vector3D(3.0, 4.0, 5.0) + v.rotate(axis, 29.6512852.deg) shouldBe (Vector3D(1.21325856, 1.73061994, 3.08754891) plusOrMinus 1e-8) + v.rotate(axis, 120.3053274.deg) shouldBe (Vector3D(2.08677229, 1.63198489, 2.64234871) plusOrMinus 1e-8) + v.rotate(axis, 230.6512852.deg) shouldBe (Vector3D(1.69633894, 2.56816842, 2.12766190) plusOrMinus 1e-8) + v.rotate(axis, 359.6139797.deg) shouldBe (Vector3D(0.99810712, 2.00381299, 2.99808533) plusOrMinus 1e-8) + } + + @Test + fun noRotation() { + Vector3D(1.0, 2.0, 3.0).rotate(Vector3D.Y, 0.0) shouldBe Vector3D(1.0, 2.0, 3.0) + } + + @Test + fun plane() { + val a = Vector3D(1.0, -2.0, 1.0) + val b = Vector3D(4.0, -2.0, -2.0) + val c = Vector3D(4.0, 1.0, 4.0) + val d = Vector3D.plane(a, b, c) + d.x shouldBeExactly 9.0 + d.y shouldBeExactly -18.0 + d.z shouldBeExactly 9.0 } } diff --git a/nebulosa-math/src/test/kotlin/VelocityTest.kt b/nebulosa-math/src/test/kotlin/VelocityTest.kt index 1d9ba7d72..028a4953e 100644 --- a/nebulosa-math/src/test/kotlin/VelocityTest.kt +++ b/nebulosa-math/src/test/kotlin/VelocityTest.kt @@ -1,45 +1,55 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.math.* +import org.junit.jupiter.api.Test -class VelocityTest : StringSpec() { - - init { - - timeout = 1000L - - "au/day" { - 1.auDay.toMetersPerSecond shouldBe (1731456.8368055555 plusOrMinus 1e-8) - 1.auDay.toKilometersPerSecond shouldBe (1731.4568368055554 plusOrMinus 1e-8) - } - "km/s" { - 8000.kms shouldBe (4.62038661891195 plusOrMinus 1e-8) - 8000.kms.toMetersPerSecond shouldBe (8000000.0 plusOrMinus 1e-8) - } - "m/s" { - 8000.ms shouldBe (0.00462038661891195 plusOrMinus 1e-8) - 8000.ms.toKilometersPerSecond shouldBe (8.0 plusOrMinus 1e-12) - } - "plus" { - (0.5.auDay + 0.5.auDay) shouldBeExactly 1.0 - (0.5.auDay + 0.5) shouldBeExactly 1.0 - } - "minus" { - (0.8.auDay - 0.5.auDay) shouldBe (0.3 plusOrMinus 1e-2) - (0.8.auDay - 0.5) shouldBe (0.3 plusOrMinus 1e-2) - } - "times" { - (0.5.auDay * 5) shouldBeExactly 2.5 - } - "div" { - (5.0.auDay / 5) shouldBeExactly 1.0 - 5.0.auDay / 5.0.auDay shouldBeExactly 1.0 - } - "rem" { - (5.0.auDay % 5) shouldBeExactly 0.0 - (5.0.auDay % 5.0.auDay) shouldBeExactly 0.0 - } +class VelocityTest { + + @Test + fun auDay() { + 1.auDay.toMetersPerSecond shouldBe (1731456.8368055555 plusOrMinus 1e-8) + 1.auDay.toKilometersPerSecond shouldBe (1731.4568368055554 plusOrMinus 1e-8) + } + + @Test + fun kms() { + 8000.kms shouldBe (4.62038661891195 plusOrMinus 1e-8) + 8000.kms.toMetersPerSecond shouldBe (8000000.0 plusOrMinus 1e-8) + } + + @Test + fun ms() { + 8000.ms shouldBe (0.00462038661891195 plusOrMinus 1e-8) + 8000.ms.toKilometersPerSecond shouldBe (8.0 plusOrMinus 1e-12) + } + + @Test + fun plus() { + (0.5.auDay + 0.5.auDay) shouldBeExactly 1.0 + (0.5.auDay + 0.5) shouldBeExactly 1.0 + } + + @Test + fun minus() { + (0.8.auDay - 0.5.auDay) shouldBe (0.3 plusOrMinus 1e-2) + (0.8.auDay - 0.5) shouldBe (0.3 plusOrMinus 1e-2) + } + + @Test + fun times() { + (0.5.auDay * 5) shouldBeExactly 2.5 + } + + @Test + fun div() { + (5.0.auDay / 5) shouldBeExactly 1.0 + 5.0.auDay / 5.0.auDay shouldBeExactly 1.0 + } + + @Test + fun rem() { + (5.0.auDay % 5) shouldBeExactly 0.0 + (5.0.auDay % 5.0.auDay) shouldBeExactly 0.0 } } diff --git a/nebulosa-nasa/src/test/kotlin/DafTest.kt b/nebulosa-nasa/src/test/kotlin/DafTest.kt index dbf37e9a0..84b0700e9 100644 --- a/nebulosa-nasa/src/test/kotlin/DafTest.kt +++ b/nebulosa-nasa/src/test/kotlin/DafTest.kt @@ -1,104 +1,107 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.matchers.shouldBe import nebulosa.nasa.daf.RemoteDaf +import org.junit.jupiter.api.Test -class DafTest : StringSpec() { +class DafTest { - init { - "NAIF/DAF" { - val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de405.bsp") - daf.read() - val summaries = daf.summaries + @Test + fun naifDaf() { + val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de405.bsp") + daf.read() + val summaries = daf.summaries - summaries.size shouldBeExactly 15 + summaries.size shouldBeExactly 15 - val data = arrayOf( - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 1, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 2, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 3, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 4, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 5, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 6, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 7, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 8, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 9, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 10, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 301, 3, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 399, 3, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 199, 1, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 299, 2, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 499, 4, 1, 2), - ) + val data = arrayOf( + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 1, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 2, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 3, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 4, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 5, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 6, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 7, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 8, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 9, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 10, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 301, 3, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 399, 3, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 199, 1, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 299, 2, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 499, 4, 1, 2), + ) - summaries.forEachIndexed { index, summary -> - summary.name shouldBe "DE-405" + summaries.forEachIndexed { index, summary -> + summary.name shouldBe "DE-405" - summary.doubleAt(0) shouldBe data[index][0] - summary.doubleAt(1) shouldBe data[index][1] - summary.intAt(0) shouldBe data[index][2] - summary.intAt(1) shouldBe data[index][3] - summary.intAt(2) shouldBe data[index][4] - summary.intAt(3) shouldBe data[index][5] - summary.intAt(4) shouldBeGreaterThan 1024 - summary.intAt(5) shouldBeGreaterThan 1024 - } + summary.doubleAt(0) shouldBe data[index][0] + summary.doubleAt(1) shouldBe data[index][1] + summary.intAt(0) shouldBe data[index][2] + summary.intAt(1) shouldBe data[index][3] + summary.intAt(2) shouldBe data[index][4] + summary.intAt(3) shouldBe data[index][5] + summary.intAt(4) shouldBeGreaterThan 1024 + summary.intAt(5) shouldBeGreaterThan 1024 } - "DAF/SPK" { - val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de421.bsp") - daf.read() - val summaries = daf.summaries + } + + @Test + fun dafSpk() { + val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de421.bsp") + daf.read() + val summaries = daf.summaries - summaries.size shouldBeExactly 15 + summaries.size shouldBeExactly 15 - val data = arrayOf( - arrayOf(-3.1691952E9, 1.6968528E9, 1, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 2, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 3, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 4, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 5, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 6, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 7, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 8, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 9, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 10, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 301, 3, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 399, 3, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 199, 1, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 299, 2, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 499, 4, 1, 2), - ) + val data = arrayOf( + arrayOf(-3.1691952E9, 1.6968528E9, 1, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 2, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 3, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 4, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 5, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 6, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 7, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 8, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 9, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 10, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 301, 3, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 399, 3, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 199, 1, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 299, 2, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 499, 4, 1, 2), + ) - summaries.forEachIndexed { index, summary -> - summary.name shouldBe "DE-0421LE-0421" + summaries.forEachIndexed { index, summary -> + summary.name shouldBe "DE-0421LE-0421" - summary.doubleAt(0) shouldBe data[index][0] - summary.doubleAt(1) shouldBe data[index][1] - summary.intAt(0) shouldBe data[index][2] - summary.intAt(1) shouldBe data[index][3] - summary.intAt(2) shouldBe data[index][4] - summary.intAt(3) shouldBe data[index][5] - summary.intAt(4) shouldBeGreaterThan 512 - summary.intAt(5) shouldBeGreaterThan 512 - } + summary.doubleAt(0) shouldBe data[index][0] + summary.doubleAt(1) shouldBe data[index][1] + summary.intAt(0) shouldBe data[index][2] + summary.intAt(1) shouldBe data[index][3] + summary.intAt(2) shouldBe data[index][4] + summary.intAt(3) shouldBe data[index][5] + summary.intAt(4) shouldBeGreaterThan 512 + summary.intAt(5) shouldBeGreaterThan 512 } - "DAF/PCK" { - val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/moon_pa_de421_1900-2050.bpc") - daf.read() - val summaries = daf.summaries + } - summaries.size shouldBeExactly 1 - summaries[0].name shouldBe "de421.nio" - val data = arrayOf(-3.1557168E9, 1.609416E9, 31006, 1, 2, 641, 221284) + @Test + fun dafPck() { + val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/moon_pa_de421_1900-2050.bpc") + daf.read() + val summaries = daf.summaries - summaries[0].doubleAt(0) shouldBe data[0] - summaries[0].doubleAt(1) shouldBe data[1] - summaries[0].intAt(0) shouldBe data[2] - summaries[0].intAt(1) shouldBe data[3] - summaries[0].intAt(2) shouldBe data[4] - summaries[0].intAt(3) shouldBe data[5] - summaries[0].intAt(4) shouldBe data[6] - } + summaries.size shouldBeExactly 1 + summaries[0].name shouldBe "de421.nio" + val data = arrayOf(-3.1557168E9, 1.609416E9, 31006, 1, 2, 641, 221284) + + summaries[0].doubleAt(0) shouldBe data[0] + summaries[0].doubleAt(1) shouldBe data[1] + summaries[0].intAt(0) shouldBe data[2] + summaries[0].intAt(1) shouldBe data[3] + summaries[0].intAt(2) shouldBe data[4] + summaries[0].intAt(3) shouldBe data[5] + summaries[0].intAt(4) shouldBe data[6] } } diff --git a/nebulosa-nasa/src/test/kotlin/RemoteDafTest.kt b/nebulosa-nasa/src/test/kotlin/RemoteDafTest.kt index 6eaa1a81a..268f1d620 100644 --- a/nebulosa-nasa/src/test/kotlin/RemoteDafTest.kt +++ b/nebulosa-nasa/src/test/kotlin/RemoteDafTest.kt @@ -1,26 +1,24 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import nebulosa.nasa.daf.RemoteDaf -import java.nio.file.Files +import nebulosa.test.AbstractTest +import org.junit.jupiter.api.Test import java.nio.file.Paths import kotlin.io.path.deleteIfExists import kotlin.io.path.exists -class RemoteDafTest : StringSpec() { - - init { - val cachePath = Files.createTempDirectory("nebulosa") +class RemoteDafTest : AbstractTest() { + @Test + fun cacheFileShouldBeCreated() { + val cachePath = tempDirectory("daf-") val daf = RemoteDaf("https://ssd.jpl.nasa.gov/ftp/eph/planets/bsp/de441.bsp", cachePath) - "cache file should be created" { - val cacheFile = Paths.get("$cachePath", "70fc746e6e9cffbcaaa5af7e5d2b169a-0-1023.cache") - cacheFile.deleteIfExists() - daf.read() - daf.summaries.size shouldBeExactly 28 - cacheFile.exists().shouldBeTrue() - cacheFile.deleteIfExists() - } + val cacheFile = Paths.get("$cachePath", "70fc746e6e9cffbcaaa5af7e5d2b169a-0-1023.cache") + cacheFile.deleteIfExists() + daf.read() + daf.summaries.size shouldBeExactly 28 + cacheFile.exists().shouldBeTrue() + cacheFile.deleteIfExists() } } diff --git a/nebulosa-nasa/src/test/kotlin/SpkTest.kt b/nebulosa-nasa/src/test/kotlin/SpkTest.kt index 8f74a9b85..97c1c6789 100644 --- a/nebulosa-nasa/src/test/kotlin/SpkTest.kt +++ b/nebulosa-nasa/src/test/kotlin/SpkTest.kt @@ -1,48 +1,53 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.io.seekableSource import nebulosa.nasa.daf.RemoteDaf import nebulosa.nasa.daf.SourceDaf import nebulosa.nasa.spk.Spk +import nebulosa.test.AbstractTest +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC -import java.nio.file.Path +import org.junit.jupiter.api.Test -class SpkTest : StringSpec() { +class SpkTest : AbstractTest() { - init { - "DE421: SSB - Earth Barycenter" { - val spk = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de421.bsp")) - val (p, v) = spk[0, 3]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) + @Test + fun de421SsbEarthBarycenter() { + val spk = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de421.bsp")) + val (p, v) = spk[0, 3]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) - p[0] shouldBe (2.226291206593103E-01 plusOrMinus 1e-6) - p[1] shouldBe (8.786267892743717E-01 plusOrMinus 1e-6) - p[2] shouldBe (3.811036725894850E-01 plusOrMinus 1e-6) - v[0] shouldBe (-1.700037773927917E-02 plusOrMinus 1e-6) - v[1] shouldBe (3.644822472013536E-03 plusOrMinus 1e-6) - v[2] shouldBe (1.580159029289274E-03 plusOrMinus 1e-6) - } - "DE405: SSB - Earth Barycenter" { - val spk = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de405.bsp")) - val (p, v) = spk[0, 3]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) + p[0] shouldBe (2.226291206593103E-01 plusOrMinus 1e-6) + p[1] shouldBe (8.786267892743717E-01 plusOrMinus 1e-6) + p[2] shouldBe (3.811036725894850E-01 plusOrMinus 1e-6) + v[0] shouldBe (-1.700037773927917E-02 plusOrMinus 1e-6) + v[1] shouldBe (3.644822472013536E-03 plusOrMinus 1e-6) + v[2] shouldBe (1.580159029289274E-03 plusOrMinus 1e-6) + } + + @Test + fun de405SsbEarthBarycenter() { + val spk = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de405.bsp")) + val (p, v) = spk[0, 3]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) + + p[0] shouldBe (2.226291206593103E-01 plusOrMinus 1e-6) + p[1] shouldBe (8.786267892743717E-01 plusOrMinus 1e-6) + p[2] shouldBe (3.811036725894850E-01 plusOrMinus 1e-6) + v[0] shouldBe (-1.700037773927917E-02 plusOrMinus 1e-6) + v[1] shouldBe (3.644822472013536E-03 plusOrMinus 1e-6) + v[2] shouldBe (1.580159029289274E-03 plusOrMinus 1e-6) + } - p[0] shouldBe (2.226291206593103E-01 plusOrMinus 1e-6) - p[1] shouldBe (8.786267892743717E-01 plusOrMinus 1e-6) - p[2] shouldBe (3.811036725894850E-01 plusOrMinus 1e-6) - v[0] shouldBe (-1.700037773927917E-02 plusOrMinus 1e-6) - v[1] shouldBe (3.644822472013536E-03 plusOrMinus 1e-6) - v[2] shouldBe (1.580159029289274E-03 plusOrMinus 1e-6) - } - "65803 Didymos (Type 21)" { - val spk = Spk(SourceDaf(Path.of("../data/65803 Didymos.bsp").seekableSource())) - val (p, v) = spk[10, 2065803]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) - p[0] shouldBe (1.231026319338612E-01 plusOrMinus 1e-2) - p[1] shouldBe (1.022833989843715E+00 plusOrMinus 1e-2) - p[2] shouldBe (4.567595812943146E-01 plusOrMinus 1e-2) - v[0] shouldBe (-1.739740083644565E-02 plusOrMinus 1e-2) - v[1] shouldBe (5.410812824810350E-03 plusOrMinus 1e-2) - v[2] shouldBe (3.549254153190032E-03 plusOrMinus 1e-2) - } + @Test + fun type2165803Didymos() { + val spk = Spk(SourceDaf(dataDirectory.concat("65803 Didymos.bsp").seekableSource().autoClose())) + val (p, v) = spk[10, 2065803]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) + p[0] shouldBe (1.231026319338612E-01 plusOrMinus 1e-2) + p[1] shouldBe (1.022833989843715E+00 plusOrMinus 1e-2) + p[2] shouldBe (4.567595812943146E-01 plusOrMinus 1e-2) + v[0] shouldBe (-1.739740083644565E-02 plusOrMinus 1e-2) + v[1] shouldBe (5.410812824810350E-03 plusOrMinus 1e-2) + v[2] shouldBe (3.549254153190032E-03 plusOrMinus 1e-2) } } diff --git a/nebulosa-nova/src/test/kotlin/AstrometryTest.kt b/nebulosa-nova/src/test/kotlin/AstrometryTest.kt index 77571fc4c..3a5ab4509 100644 --- a/nebulosa-nova/src/test/kotlin/AstrometryTest.kt +++ b/nebulosa-nova/src/test/kotlin/AstrometryTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.io.seekableSource @@ -11,98 +10,118 @@ import nebulosa.nasa.daf.SourceDaf import nebulosa.nasa.spk.Spk import nebulosa.nova.astrometry.* import nebulosa.nova.position.Barycentric +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.TDB import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC -import java.nio.file.Path +import org.junit.jupiter.api.Test -class AstrometryTest : StringSpec() { +class AstrometryTest { - init { - val de441 = Spk(RemoteDaf("https://ssd.jpl.nasa.gov/ftp/eph/planets/bsp/de441.bsp")) - val mar097 = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/mar097.bsp")) - val ura111 = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/ura111.bsp")) - val ceresSpk = Spk(SourceDaf(Path.of("../data/1 Ceres.bsp").seekableSource())) - val kernel = SpiceKernel(de441, mar097, ura111, ceresSpk) - val sun = kernel[10] - val moon = kernel[301] - val earth = kernel[399] - val mars = kernel[499] - val uranus = kernel[799] - val time = UTC(TimeYMDHMS(2022, 12, 25, 0, 0, 0.0)) + @Test + fun sunDE441() { + val astrometric = EARTH.at(TIME).observe(SUN) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (273.092531892 plusOrMinus 1e-9) + dec.toDegrees shouldBe (-23.406028751 plusOrMinus 1e-9) + } + + @Test + fun moonDE441() { + val astrometric = EARTH.at(TIME).observe(MOON) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-9) + dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-9) + } + + @Test + fun moonELPMPP02() { + val astrometric = EARTH.at(TIME).observe(EARTH + ELPMPP02) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-4) + dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-5) + } + + @Test + fun moonVSOP87EAndELPMPP02() { + val astrometric = VSOP87E.EARTH.at(TIME).observe(VSOP87E.EARTH + ELPMPP02) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-4) + dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-5) + } + + @Test + fun marsMAR097() { + val astrometric = EARTH.at(TIME).observe(MARS) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (68.127269738 plusOrMinus 1e-7) + dec.toDegrees shouldBe (24.681041544 plusOrMinus 1e-7) + } + + @Test + fun marsVSOP87E() { + val astrometric = VSOP87E.EARTH.at(TIME).observe(VSOP87E.MARS) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (68.127269738 plusOrMinus 1e-4) + dec.toDegrees shouldBe (24.681041544 plusOrMinus 1e-5) + } + + @Test + fun arielGUST86() { + val astrometric = EARTH.at(TIME).observe(URANUS + GUST86.ARIEL) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (42.621239755 plusOrMinus 1e-4) + dec.toDegrees shouldBe (15.978693112 plusOrMinus 1e-5) + } + + @Test + fun ceresAsteroid() { + val ceres = Asteroid( + semiMajorAxis = 2.769289292143484.au, + eccentricity = 0.07687465013145245, + inclination = 10.59127767086216.deg, + longitudeOfAscendingNode = 80.3011901917491.deg, // OM + argumentOfPerihelion = 73.80896808746482.deg, // W + meanAnomaly = 130.3159688200986.deg, + epoch = TDB(2458849.5), + ) + val astrometric = EARTH.at(TIME).observe(SUN + ceres) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (185.698485350 plusOrMinus 0.7) + dec.toDegrees shouldBe (9.929601380 plusOrMinus 0.3) + } + + @Test + fun ceresSPK() { + val ceres = KERNEL[2000001] + val astrometric = EARTH.at(TIME).observe(ceres) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (185.698485350 plusOrMinus 1e-7) + dec.toDegrees shouldBe (9.929601380 plusOrMinus 1e-7) + } + + companion object { - "sun: DE441" { - val astrometric = earth.at(time).observe(sun) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (273.092531892 plusOrMinus 1e-9) - dec.toDegrees shouldBe (-23.406028751 plusOrMinus 1e-9) - } - "moon: DE441" { - val astrometric = earth.at(time).observe(moon) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-9) - dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-9) - } - "moon: ELPMPP02" { - val astrometric = earth.at(time).observe(earth + ELPMPP02) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-4) - dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-5) - } - "moon: VSOP87E + ELPMPP02" { - val astrometric = VSOP87E.EARTH.at(time).observe(VSOP87E.EARTH + ELPMPP02) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-4) - dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-5) - } - "mars: MAR097" { - val astrometric = earth.at(time).observe(mars) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (68.127269738 plusOrMinus 1e-7) - dec.toDegrees shouldBe (24.681041544 plusOrMinus 1e-7) - } - "mars: VSOP87E" { - val astrometric = VSOP87E.EARTH.at(time).observe(VSOP87E.MARS) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (68.127269738 plusOrMinus 1e-4) - dec.toDegrees shouldBe (24.681041544 plusOrMinus 1e-5) - } - "ariel: GUST86" { - val astrometric = earth.at(time).observe(uranus + GUST86.ARIEL) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (42.621239755 plusOrMinus 1e-4) - dec.toDegrees shouldBe (15.978693112 plusOrMinus 1e-5) - } - "ceres: Asteroid" { - val ceres = Asteroid( - semiMajorAxis = 2.769289292143484.au, - eccentricity = 0.07687465013145245, - inclination = 10.59127767086216.deg, - longitudeOfAscendingNode = 80.3011901917491.deg, // OM - argumentOfPerihelion = 73.80896808746482.deg, // W - meanAnomaly = 130.3159688200986.deg, - epoch = TDB(2458849.5), - ) - val astrometric = earth.at(time).observe(sun + ceres) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (185.698485350 plusOrMinus 0.7) - dec.toDegrees shouldBe (9.929601380 plusOrMinus 0.3) - } - "ceres: SPK" { - val ceres = kernel[2000001] - val astrometric = earth.at(time).observe(ceres) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (185.698485350 plusOrMinus 1e-7) - dec.toDegrees shouldBe (9.929601380 plusOrMinus 1e-7) - } + @JvmStatic private val DE441 = Spk(RemoteDaf("https://ssd.jpl.nasa.gov/ftp/eph/planets/bsp/de441.bsp")) + @JvmStatic private val MAR097 = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/mar097.bsp")) + @JvmStatic private val URA111 = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/ura111.bsp")) + @JvmStatic private val CERES = Spk(SourceDaf(dataDirectory.concat("1 Ceres.bsp").seekableSource())) + @JvmStatic private val KERNEL = SpiceKernel(DE441, MAR097, URA111, CERES) + @JvmStatic private val SUN = KERNEL[10] + @JvmStatic private val MOON = KERNEL[301] + @JvmStatic private val EARTH = KERNEL[399] + @JvmStatic private val MARS = KERNEL[499] + @JvmStatic private val URANUS = KERNEL[799] + @JvmStatic private val TIME = UTC(TimeYMDHMS(2022, 12, 25, 0, 0, 0.0)) } } diff --git a/nebulosa-nova/src/test/kotlin/ConstellationTest.kt b/nebulosa-nova/src/test/kotlin/ConstellationTest.kt index af0bda414..dfaf33529 100644 --- a/nebulosa-nova/src/test/kotlin/ConstellationTest.kt +++ b/nebulosa-nova/src/test/kotlin/ConstellationTest.kt @@ -1,22 +1,21 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.io.resource import nebulosa.math.deg import nebulosa.math.hours import nebulosa.nova.astrometry.Constellation import nebulosa.nova.position.ICRF +import org.junit.jupiter.api.Test -class ConstellationTest : StringSpec() { +class ConstellationTest { - init { - "find" { - for (line in resource("CONSTELLATION_TEST.txt")!!.bufferedReader().lines()) { - val parts = line.split(",") - val ra = parts[0].toDouble() - val dec = parts[1].toDouble() - val name = parts[2] - Constellation.find(ICRF.equatorial(ra.hours, dec.deg)).name shouldBe name - } + @Test + fun find() { + for (line in resource("CONSTELLATION_TEST.txt")!!.bufferedReader().lines()) { + val parts = line.split(",") + val ra = parts[0].toDouble() + val dec = parts[1].toDouble() + val name = parts[2] + Constellation.find(ICRF.equatorial(ra.hours, dec.deg)).name shouldBe name } } } diff --git a/nebulosa-nova/src/test/kotlin/ELPMPP02Test.kt b/nebulosa-nova/src/test/kotlin/ELPMPP02Test.kt index 40294822f..029478f7d 100644 --- a/nebulosa-nova/src/test/kotlin/ELPMPP02Test.kt +++ b/nebulosa-nova/src/test/kotlin/ELPMPP02Test.kt @@ -1,24 +1,25 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.nova.astrometry.ELPMPP02 import nebulosa.time.TDB import nebulosa.time.TimeJD +import org.junit.jupiter.api.Test -class ELPMPP02Test : StringSpec() { +class ELPMPP02Test { - init { - val moon = ELPMPP02 - val time = TDB(TimeJD(2459938.0, 0.5)) + @Test + fun moon() { + val (p, v) = ELPMPP02.compute(TIME) + p[0] shouldBe (1.013355885727306E-03 plusOrMinus 1e-9) + p[1] shouldBe (-1.903485709903833E-03 plusOrMinus 1e-9) + p[2] shouldBe (-1.047798412089101E-03 plusOrMinus 1e-9) + v[0] shouldBe (5.762732121285166E-04 plusOrMinus 1e-9) + v[1] shouldBe (2.476878261262097E-04 plusOrMinus 1e-9) + v[2] shouldBe (8.902329774047208E-05 plusOrMinus 1e-9) + } + + companion object { - "moon" { - val (p, v) = moon.compute(time) - p[0] shouldBe (1.013355885727306E-03 plusOrMinus 1e-9) - p[1] shouldBe (-1.903485709903833E-03 plusOrMinus 1e-9) - p[2] shouldBe (-1.047798412089101E-03 plusOrMinus 1e-9) - v[0] shouldBe (5.762732121285166E-04 plusOrMinus 1e-9) - v[1] shouldBe (2.476878261262097E-04 plusOrMinus 1e-9) - v[2] shouldBe (8.902329774047208E-05 plusOrMinus 1e-9) - } + @JvmStatic private val TIME = TDB(TimeJD(2459938.0, 0.5)) } } diff --git a/nebulosa-nova/src/test/kotlin/FixedStarTest.kt b/nebulosa-nova/src/test/kotlin/FixedStarTest.kt index 4796e4084..4d73846d4 100644 --- a/nebulosa-nova/src/test/kotlin/FixedStarTest.kt +++ b/nebulosa-nova/src/test/kotlin/FixedStarTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe @@ -6,68 +5,80 @@ import nebulosa.math.* import nebulosa.nova.astrometry.FixedStar import nebulosa.nova.astrometry.VSOP87E import nebulosa.nova.position.Barycentric +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.IERS import nebulosa.time.IERSA import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC -import java.nio.file.Path +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream import kotlin.math.truncate -class FixedStarTest : StringSpec() { +class FixedStarTest { - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) - IERS.attach(iersa) + @Test + fun polaris() { + // https://api.noctuasky.com/api/v1/skysources/name/NAME%20Polaris + val star = FixedStar( + 37.95456067.deg, 89.26410897.deg, + (44.48).mas, (-11.85).mas, (7.54).mas, (-16.42).kms, + ) - "polaris" { - // https://api.noctuasky.com/api/v1/skysources/name/NAME%20Polaris - val star = FixedStar( - 37.95456067.deg, 89.26410897.deg, - (44.48).mas, (-11.85).mas, (7.54).mas, (-16.42).kms, - ) + val astrometric = VSOP87E.EARTH.at(UTC(TimeYMDHMS(2024, 2, 17, 12, 0, 0.0))) + .observe(star) - val astrometric = VSOP87E.EARTH.at(UTC(TimeYMDHMS(2024, 2, 17, 12, 0, 0.0))) - .observe(star) + val (ra, dec) = astrometric.equatorialAtDate() - val (ra, dec) = astrometric.equatorialAtDate() + with(ra.normalized.hms()) { + truncate(this[0]) shouldBeExactly 3.0 + truncate(this[1]) shouldBeExactly 2.0 + this[2] shouldBe (3.9 plusOrMinus 20.0) + } + + with(dec.dms()) { + truncate(this[0]) shouldBeExactly 89.0 + truncate(this[1]) shouldBe (22.0 plusOrMinus 1.0) + this[2] shouldBe (15.8 plusOrMinus 50.0) + } + } - with(ra.normalized.hms()) { - truncate(this[0]) shouldBeExactly 3.0 - truncate(this[1]) shouldBeExactly 2.0 - this[2] shouldBe (3.9 plusOrMinus 20.0) - } + @Test + fun barnardsStar() { + // https://api.noctuasky.com/api/v1/skysources/name/NAME%20Barnard's%20Star + val star = FixedStar( + 269.452082497514.deg, 4.6933642650633.deg, + (-802.803).mas, (10362.542).mas, (547.451).mas, (-110.51).kms, + ) - with(dec.dms()) { - truncate(this[0]) shouldBeExactly 89.0 - truncate(this[1]) shouldBe (22.0 plusOrMinus 1.0) - this[2] shouldBe (15.8 plusOrMinus 50.0) - } + val astrometric = VSOP87E.EARTH.at(UTC(TimeYMDHMS(2024, 2, 17, 12, 0, 0.0))) + .observe(star) + + val (ra, dec) = astrometric.equatorialAtDate() + + with(ra.normalized.hms()) { + truncate(this[0]) shouldBeExactly 17.0 + truncate(this[1]) shouldBeExactly 58.0 + this[2] shouldBe (57.8 plusOrMinus 1.0) } - "barnard's star" { - // https://api.noctuasky.com/api/v1/skysources/name/NAME%20Barnard's%20Star - val star = FixedStar( - 269.452082497514.deg, 4.6933642650633.deg, - (-802.803).mas, (10362.542).mas, (547.451).mas, (-110.51).kms, - ) - val astrometric = VSOP87E.EARTH.at(UTC(TimeYMDHMS(2024, 2, 17, 12, 0, 0.0))) - .observe(star) + with(dec.dms()) { + truncate(this[0]) shouldBeExactly 4.0 + truncate(this[1]) shouldBeExactly 45.0 + this[2] shouldBe (25.5 plusOrMinus 10.0) + } + } - val (ra, dec) = astrometric.equatorialAtDate() + companion object { - with(ra.normalized.hms()) { - truncate(this[0]) shouldBeExactly 17.0 - truncate(this[1]) shouldBeExactly 58.0 - this[2] shouldBe (57.8 plusOrMinus 1.0) - } + @JvmStatic private val IERSA = IERSA() - with(dec.dms()) { - truncate(this[0]) shouldBeExactly 4.0 - truncate(this[1]) shouldBeExactly 45.0 - this[2] shouldBe (25.5 plusOrMinus 10.0) - } + @JvmStatic + @BeforeAll + fun loadIERS() { + dataDirectory.concat("finals2000A.all").inputStream().use(IERSA::load) + IERS.attach(IERSA) } } } diff --git a/nebulosa-nova/src/test/kotlin/GUST86Test.kt b/nebulosa-nova/src/test/kotlin/GUST86Test.kt index 458aee5c2..42cda34e6 100644 --- a/nebulosa-nova/src/test/kotlin/GUST86Test.kt +++ b/nebulosa-nova/src/test/kotlin/GUST86Test.kt @@ -1,67 +1,74 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.nova.astrometry.GUST86 import nebulosa.time.TDB import nebulosa.time.TimeJD +import org.junit.jupiter.api.Test -class GUST86Test : StringSpec() { +class GUST86Test { - init { - "ariel" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.ARIEL.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-4.451100762924695E-04 plusOrMinus 1e-5) - p[1] shouldBe (4.127293811714091E-04 plusOrMinus 1e-5) - p[2] shouldBe (-1.122529644334287E-03 plusOrMinus 1e-5) - v[0] shouldBe (-2.905800702342619E-03 plusOrMinus 1e-5) - v[1] shouldBe (3.018674943703828E-04 plusOrMinus 1e-5) - v[2] shouldBe (1.261178539831990E-03 plusOrMinus 1e-5) - } - "umbriel" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.UMBRIEL.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-1.611384907091204E-03 plusOrMinus 1e-5) - p[1] shouldBe (1.606295364496704E-04 plusOrMinus 1e-5) - p[2] shouldBe (7.209771384443374E-04 plusOrMinus 1e-5) - v[0] shouldBe (9.751794785461074E-04 plusOrMinus 1e-5) - v[1] shouldBe (-8.767828036553259E-04 plusOrMinus 1e-5) - v[2] shouldBe (2.364500180196773E-03 plusOrMinus 1e-5) - } - "titania" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.TITANIA.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-2.232644660544111E-03 plusOrMinus 1e-5) - p[1] shouldBe (9.515348359690534E-04 plusOrMinus 1e-5) - p[2] shouldBe (-1.618326368294999E-03 plusOrMinus 1e-5) - v[0] shouldBe (-1.281512705822040E-03 plusOrMinus 1e-5) - v[1] shouldBe (-1.711924613002539E-04 plusOrMinus 1e-5) - v[2] shouldBe (1.660414235776199E-03 plusOrMinus 1e-5) - } - "oberon" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.OBERON.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (3.195547478166968E-03 plusOrMinus 1e-5) - p[1] shouldBe (-1.226712898705434E-03 plusOrMinus 1e-5) - p[2] shouldBe (1.866878239761929E-03 plusOrMinus 1e-5) - v[0] shouldBe (9.686636758969398E-04 plusOrMinus 1e-5) - v[1] shouldBe (2.027942751407152E-04 plusOrMinus 1e-5) - v[2] shouldBe (-1.527974319936715E-03 plusOrMinus 1e-5) - } - "miranda" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.MIRANDA.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-7.240222342711871E-04 plusOrMinus 1e-5) - p[1] shouldBe (7.574953585547300E-05 plusOrMinus 1e-5) - p[2] shouldBe (4.707549340071754E-04 plusOrMinus 1e-5) - v[0] shouldBe (1.941801978392934E-03 plusOrMinus 1e-5) - v[1] shouldBe (-1.068242211440700E-03 plusOrMinus 1e-5) - v[2] shouldBe (3.164065185998637E-03 plusOrMinus 1e-5) - } + @Test + fun ariel() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.ARIEL.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-4.451100762924695E-04 plusOrMinus 1e-5) + p[1] shouldBe (4.127293811714091E-04 plusOrMinus 1e-5) + p[2] shouldBe (-1.122529644334287E-03 plusOrMinus 1e-5) + v[0] shouldBe (-2.905800702342619E-03 plusOrMinus 1e-5) + v[1] shouldBe (3.018674943703828E-04 plusOrMinus 1e-5) + v[2] shouldBe (1.261178539831990E-03 plusOrMinus 1e-5) + } + + @Test + fun umbriel() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.UMBRIEL.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-1.611384907091204E-03 plusOrMinus 1e-5) + p[1] shouldBe (1.606295364496704E-04 plusOrMinus 1e-5) + p[2] shouldBe (7.209771384443374E-04 plusOrMinus 1e-5) + v[0] shouldBe (9.751794785461074E-04 plusOrMinus 1e-5) + v[1] shouldBe (-8.767828036553259E-04 plusOrMinus 1e-5) + v[2] shouldBe (2.364500180196773E-03 plusOrMinus 1e-5) + } + + @Test + fun titania() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.TITANIA.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-2.232644660544111E-03 plusOrMinus 1e-5) + p[1] shouldBe (9.515348359690534E-04 plusOrMinus 1e-5) + p[2] shouldBe (-1.618326368294999E-03 plusOrMinus 1e-5) + v[0] shouldBe (-1.281512705822040E-03 plusOrMinus 1e-5) + v[1] shouldBe (-1.711924613002539E-04 plusOrMinus 1e-5) + v[2] shouldBe (1.660414235776199E-03 plusOrMinus 1e-5) + } + + @Test + fun oberon() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.OBERON.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (3.195547478166968E-03 plusOrMinus 1e-5) + p[1] shouldBe (-1.226712898705434E-03 plusOrMinus 1e-5) + p[2] shouldBe (1.866878239761929E-03 plusOrMinus 1e-5) + v[0] shouldBe (9.686636758969398E-04 plusOrMinus 1e-5) + v[1] shouldBe (2.027942751407152E-04 plusOrMinus 1e-5) + v[2] shouldBe (-1.527974319936715E-03 plusOrMinus 1e-5) + } + + @Test + fun miranda() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.MIRANDA.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-7.240222342711871E-04 plusOrMinus 1e-5) + p[1] shouldBe (7.574953585547300E-05 plusOrMinus 1e-5) + p[2] shouldBe (4.707549340071754E-04 plusOrMinus 1e-5) + v[0] shouldBe (1.941801978392934E-03 plusOrMinus 1e-5) + v[1] shouldBe (-1.068242211440700E-03 plusOrMinus 1e-5) + v[2] shouldBe (3.164065185998637E-03 plusOrMinus 1e-5) } } diff --git a/nebulosa-nova/src/test/kotlin/GeographicPositionTest.kt b/nebulosa-nova/src/test/kotlin/GeographicPositionTest.kt index 30a74a811..62d5ddb9b 100644 --- a/nebulosa-nova/src/test/kotlin/GeographicPositionTest.kt +++ b/nebulosa-nova/src/test/kotlin/GeographicPositionTest.kt @@ -1,38 +1,48 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.math.deg import nebulosa.math.formatHMS import nebulosa.math.m import nebulosa.nova.position.Geoid +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.IERS import nebulosa.time.IERSA import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC -import java.nio.file.Path +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream -class GeographicPositionTest : StringSpec() { +class GeographicPositionTest { - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) - IERS.attach(iersa) + @Test + fun lst() { + val position = Geoid.IERS2010.lonLat((-45.4227).deg, 0.0) + position.lstAt(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h42m47.1s" + position.lstAt(UTC(TimeYMDHMS(2024, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h40m53.1s" + position.lstAt(UTC(TimeYMDHMS(2025, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h43m52.8s" + } - "lst" { - val position = Geoid.IERS2010.lonLat((-45.4227).deg, 0.0) - position.lstAt(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h42m47.1s" - position.lstAt(UTC(TimeYMDHMS(2024, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h40m53.1s" - position.lstAt(UTC(TimeYMDHMS(2025, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h43m52.8s" - } - "xyz" { - val latitude = "-23 32 51.00".deg - val longitude = "-46 38 10.00".deg - val position = Geoid.IERS2010.lonLat(longitude, latitude, 853.0.m) + @Test + fun xyz() { + val latitude = "-23 32 51.00".deg + val longitude = "-46 38 10.00".deg + val position = Geoid.IERS2010.lonLat(longitude, latitude, 853.0.m) + + position.x shouldBe (-2.8434040742871705E-5 plusOrMinus 1e-13) + position.y shouldBe (2.685480929038628E-5 plusOrMinus 1e-13) + position.z shouldBe (-1.693045603541487E-5 plusOrMinus 1e-13) + } + + companion object { - position.x shouldBe (-2.8434040742871705E-5 plusOrMinus 1e-13) - position.y shouldBe (2.685480929038628E-5 plusOrMinus 1e-13) - position.z shouldBe (-1.693045603541487E-5 plusOrMinus 1e-13) + @JvmStatic + @BeforeAll + fun loadIERS() { + val iersa = IERSA() + dataDirectory.concat("finals2000A.all").inputStream().use(iersa::load) + IERS.attach(iersa) } } } diff --git a/nebulosa-nova/src/test/kotlin/ICRFTest.kt b/nebulosa-nova/src/test/kotlin/ICRFTest.kt index 8725481cc..955b5ece0 100644 --- a/nebulosa-nova/src/test/kotlin/ICRFTest.kt +++ b/nebulosa-nova/src/test/kotlin/ICRFTest.kt @@ -1,53 +1,65 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.math.* import nebulosa.nova.position.Geoid import nebulosa.nova.position.ICRF +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.IERS import nebulosa.time.IERSA import nebulosa.time.TT import nebulosa.time.TimeYMDHMS -import java.nio.file.Path +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream -class ICRFTest : StringSpec() { +class ICRFTest { - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) - IERS.attach(iersa) + @Test + fun equatorialAtDateToEquatorialJ2000() { + val ra = 2.15105.deg + val dec = (-0.4493).deg + val (raNow, decNow) = ICRF.equatorial(ra, dec, epoch = TT(2459950.0, 0.24436)).equatorial() + raNow.toDegrees shouldBe (1.85881 plusOrMinus 1e-4) + decNow.toDegrees shouldBe (-0.5762 plusOrMinus 1e-4) + } - "equatorial at date to equatorial J2000" { - val ra = 2.15105.deg - val dec = (-0.4493).deg - val (raNow, decNow) = ICRF.equatorial(ra, dec, epoch = TT(2459950.0, 0.24436)).equatorial() - raNow.toDegrees shouldBe (1.85881 plusOrMinus 1e-4) - decNow.toDegrees shouldBe (-0.5762 plusOrMinus 1e-4) - } - "equatorial J2000 to equatorial at date" { - val ra = 1.85881.deg - val dec = (-0.5762).deg - val (raNow, decNow) = ICRF.equatorial(ra, dec).equatorialAtEpoch(TT(2459950.0, 0.24436)) - raNow.toDegrees shouldBe (2.15105 plusOrMinus 1e-4) - decNow.toDegrees shouldBe (-0.4493 plusOrMinus 1e-4) - } - "horizontal" { - // Sirius. - val ra = "06 45 08.91728".hours - val dec = "-16 42 58.0171".deg - - val latitude = (-23.547500000000003).deg - val longitude = (-46.63610833333333).deg - val elevation = 853.m - - val time = TimeYMDHMS(2023, 1, 30, 22) - val site = Geoid.IERS2010.lonLat(longitude, latitude, elevation) - - val icrf = ICRF.equatorial(ra, dec, time = time, center = site) - val azAlt = icrf.horizontal() - azAlt.longitude.normalized.toDegrees shouldBe (90.778 plusOrMinus 1e-1) - azAlt.latitude.toDegrees shouldBe (44.3538 plusOrMinus 1e-1) + @Test + fun equatorialJ2000ToEquatorialAtDate() { + val ra = 1.85881.deg + val dec = (-0.5762).deg + val (raNow, decNow) = ICRF.equatorial(ra, dec).equatorialAtEpoch(TT(2459950.0, 0.24436)) + raNow.toDegrees shouldBe (2.15105 plusOrMinus 1e-4) + decNow.toDegrees shouldBe (-0.4493 plusOrMinus 1e-4) + } + + @Test + fun horizontal() { + // Sirius. + val ra = "06 45 08.91728".hours + val dec = "-16 42 58.0171".deg + + val latitude = (-23.547500000000003).deg + val longitude = (-46.63610833333333).deg + val elevation = 853.m + + val time = TimeYMDHMS(2023, 1, 30, 22) + val site = Geoid.IERS2010.lonLat(longitude, latitude, elevation) + + val icrf = ICRF.equatorial(ra, dec, time = time, center = site) + val azAlt = icrf.horizontal() + azAlt.longitude.normalized.toDegrees shouldBe (90.778 plusOrMinus 1e-1) + azAlt.latitude.toDegrees shouldBe (44.3538 plusOrMinus 1e-1) + } + + companion object { + + @JvmStatic + @BeforeAll + fun loadIERS() { + val iersa = IERSA() + dataDirectory.concat("finals2000A.all").inputStream().use(iersa::load) + IERS.attach(iersa) } } } diff --git a/nebulosa-nova/src/test/kotlin/KeplerOrbitTest.kt b/nebulosa-nova/src/test/kotlin/KeplerOrbitTest.kt index 7c20f197d..186fce987 100644 --- a/nebulosa-nova/src/test/kotlin/KeplerOrbitTest.kt +++ b/nebulosa-nova/src/test/kotlin/KeplerOrbitTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeLessThan import io.kotest.matchers.shouldBe @@ -10,48 +9,50 @@ import nebulosa.nova.astrometry.Asteroid import nebulosa.nova.position.ICRF import nebulosa.time.TDB import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test + +class KeplerOrbitTest { + + @Test + fun asteroidMpc() { + val ceres = + Asteroid.parse("00001 3.33 0.15 K232P 17.21569 73.47045 80.26013 10.58634 0.0788175 0.21411523 2.7671817 0 MPO719049 7258 123 1801-2022 0.65 M-v 30l MPCLINUX 0000 (1) Ceres 20220916") + val time = TDB(TimeYMDHMS(2022, 12, 15, 22, 0, 0.0)) + val icrf = ceres.at(time) + + icrf.position[0] shouldBe (-2.2887358680299896 plusOrMinus 1e-6) + icrf.position[1] shouldBe (0.7613682864183494 plusOrMinus 1e-6) + icrf.position[2] shouldBe (0.8249889493185274 plusOrMinus 1e-6) + icrf.velocity[0] shouldBe (-0.004522105973639024 plusOrMinus 1e-6) + icrf.velocity[1] shouldBe (-0.009582289706142557 plusOrMinus 1e-6) + icrf.velocity[2] shouldBe (-0.003598833220781664 plusOrMinus 1e-6) + } + + @Test + fun asteroidMeanAnomaly() { + val time = TDB(2458886.5) + + val ceres = Asteroid( + semiMajorAxis = 2.768873850275102.au, + eccentricity = 7.705857791518426E-02, + inclination = 2.718528770987308E+01.deg, + argumentOfPerihelion = 1.328964361683606E+02.deg, // W + longitudeOfAscendingNode = 2.336112629072238E+01.deg, // OM + meanAnomaly = 1.382501360489816E+02.deg, + epoch = time, + rotation = null, + ) + + val (r) = ceres.at(time) + + val sun = Vector3D(-0.004105894975783999, 0.006739680703224941, 0.002956344702049446) + val horizons = Vector3D(1.334875927366032E+00, -2.239607658161781E+00, -1.328895183461897E+00) + + val s = r + sun - horizons + val epsilon = 0.001.m -class KeplerOrbitTest : StringSpec() { - - init { - "asteroid: MPC" { - val ceres = - Asteroid.parse("00001 3.33 0.15 K232P 17.21569 73.47045 80.26013 10.58634 0.0788175 0.21411523 2.7671817 0 MPO719049 7258 123 1801-2022 0.65 M-v 30l MPCLINUX 0000 (1) Ceres 20220916") - val time = TDB(TimeYMDHMS(2022, 12, 15, 22, 0, 0.0)) - val icrf = ceres.at(time) - - icrf.position[0] shouldBe (-2.2887358680299896 plusOrMinus 1e-6) - icrf.position[1] shouldBe (0.7613682864183494 plusOrMinus 1e-6) - icrf.position[2] shouldBe (0.8249889493185274 plusOrMinus 1e-6) - icrf.velocity[0] shouldBe (-0.004522105973639024 plusOrMinus 1e-6) - icrf.velocity[1] shouldBe (-0.009582289706142557 plusOrMinus 1e-6) - icrf.velocity[2] shouldBe (-0.003598833220781664 plusOrMinus 1e-6) - } - "asteroid: mean anomaly" { - val time = TDB(2458886.5) - - val ceres = Asteroid( - semiMajorAxis = 2.768873850275102.au, - eccentricity = 7.705857791518426E-02, - inclination = 2.718528770987308E+01.deg, - argumentOfPerihelion = 1.328964361683606E+02.deg, // W - longitudeOfAscendingNode = 2.336112629072238E+01.deg, // OM - meanAnomaly = 1.382501360489816E+02.deg, - epoch = time, - rotation = null, - ) - - val (r) = ceres.at(time) - - val sun = Vector3D(-0.004105894975783999, 0.006739680703224941, 0.002956344702049446) - val horizons = Vector3D(1.334875927366032E+00, -2.239607658161781E+00, -1.328895183461897E+00) - - val s = r + sun - horizons - val epsilon = 0.001.m - - s[0] shouldBeLessThan epsilon - s[1] shouldBeLessThan epsilon - s[2] shouldBeLessThan epsilon - } + s[0] shouldBeLessThan epsilon + s[1] shouldBeLessThan epsilon + s[2] shouldBeLessThan epsilon } } diff --git a/nebulosa-nova/src/test/kotlin/SpiceKernelTest.kt b/nebulosa-nova/src/test/kotlin/SpiceKernelTest.kt index 5add77ec4..fe1fe1173 100644 --- a/nebulosa-nova/src/test/kotlin/SpiceKernelTest.kt +++ b/nebulosa-nova/src/test/kotlin/SpiceKernelTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.nasa.daf.RemoteDaf @@ -8,55 +7,63 @@ import nebulosa.nova.position.Barycentric import nebulosa.nova.position.ICRF import nebulosa.time.TDB import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test -class SpiceKernelTest : StringSpec() { +class SpiceKernelTest { - init { - val time = TDB(TimeYMDHMS(2022, 11, 27, 22, 30, 0.0)) + @Test + fun ssbMarsBarycenter() { + val mars = MAR097[4] + val barycentric = mars.at(TIME) + barycentric.position[0] shouldBe (0.5001501370337544 plusOrMinus 1e-13) + barycentric.position[1] shouldBe (1.3081776387439241 plusOrMinus 1e-13) + barycentric.position[2] shouldBe (0.5865138637117445 plusOrMinus 1e-13) + barycentric.velocity[0] shouldBe (-0.012655073594243272 plusOrMinus 1e-13) + barycentric.velocity[1] shouldBe (0.005210405441841531 plusOrMinus 1e-13) + barycentric.velocity[2] shouldBe (0.0027316410426030074 plusOrMinus 1e-13) + } + + @Test + fun marsBarycenterMars() { + val mars = MAR097[499] - MAR097[4] + val icrf = mars.at(TIME) + icrf.position[0] shouldBe (-6.973204561568923e-13 plusOrMinus 1e-13) + icrf.position[1] shouldBe (-1.0364158902365925e-12 plusOrMinus 1e-13) + icrf.position[2] shouldBe (-1.3393210655347855e-13 plusOrMinus 1e-13) + icrf.velocity[0] shouldBe (1.7113987241274633e-11 plusOrMinus 1e-13) + icrf.velocity[1] shouldBe (-5.325181995164045e-12 plusOrMinus 1e-13) + icrf.velocity[2] shouldBe (-1.2236477437195075e-11 plusOrMinus 1e-13) + } + + @Test + fun positionOfMars() { + val mars = MAR097[499] + val barycentric = mars.at(TIME) + barycentric.position[0] shouldBe (0.5001501370330571 plusOrMinus 1e-13) + barycentric.position[1] shouldBe (1.3081776387428876 plusOrMinus 1e-13) + barycentric.position[2] shouldBe (0.5865138637116106 plusOrMinus 1e-13) + barycentric.velocity[0] shouldBe (-0.012655073577129285 plusOrMinus 1e-13) + barycentric.velocity[1] shouldBe (0.005210405436516349 plusOrMinus 1e-13) + barycentric.velocity[2] shouldBe (0.00273164103036653 plusOrMinus 1e-13) + } + + @Test + fun positionOfMarsViewedFromEarth() { + val earth = MAR097[399] + val mars = MAR097[499] + val barycentric = earth.at(TIME) + val astrometric = barycentric.observe(mars) + astrometric.position[0] shouldBe (0.09761625675629965 plusOrMinus 1e-13) + astrometric.position[1] shouldBe (0.48508891609539406 plusOrMinus 1e-13) + astrometric.position[2] shouldBe (0.22948292606924786 plusOrMinus 1e-13) + astrometric.velocity[0] shouldBe (0.003266059522699063 plusOrMinus 1e-13) + astrometric.velocity[1] shouldBe (-0.0013034648919245393 plusOrMinus 1e-13) + astrometric.velocity[2] shouldBe (-9.24056040658666e-05 plusOrMinus 1e-13) + } - val mar097 = SpiceKernel(Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/mar097.bsp"))) + companion object { - "SSB -> Mars barycenter" { - val mars = mar097[4] - val barycentric = mars.at(time) - barycentric.position[0] shouldBe (0.5001501370337544 plusOrMinus 1e-13) - barycentric.position[1] shouldBe (1.3081776387439241 plusOrMinus 1e-13) - barycentric.position[2] shouldBe (0.5865138637117445 plusOrMinus 1e-13) - barycentric.velocity[0] shouldBe (-0.012655073594243272 plusOrMinus 1e-13) - barycentric.velocity[1] shouldBe (0.005210405441841531 plusOrMinus 1e-13) - barycentric.velocity[2] shouldBe (0.0027316410426030074 plusOrMinus 1e-13) - } - "Mars Barycenter -> Mars" { - val mars = mar097[499] - mar097[4] - val icrf = mars.at(time) - icrf.position[0] shouldBe (-6.973204561568923e-13 plusOrMinus 1e-13) - icrf.position[1] shouldBe (-1.0364158902365925e-12 plusOrMinus 1e-13) - icrf.position[2] shouldBe (-1.3393210655347855e-13 plusOrMinus 1e-13) - icrf.velocity[0] shouldBe (1.7113987241274633e-11 plusOrMinus 1e-13) - icrf.velocity[1] shouldBe (-5.325181995164045e-12 plusOrMinus 1e-13) - icrf.velocity[2] shouldBe (-1.2236477437195075e-11 plusOrMinus 1e-13) - } - "position of mars" { - val mars = mar097[499] - val barycentric = mars.at(time) - barycentric.position[0] shouldBe (0.5001501370330571 plusOrMinus 1e-13) - barycentric.position[1] shouldBe (1.3081776387428876 plusOrMinus 1e-13) - barycentric.position[2] shouldBe (0.5865138637116106 plusOrMinus 1e-13) - barycentric.velocity[0] shouldBe (-0.012655073577129285 plusOrMinus 1e-13) - barycentric.velocity[1] shouldBe (0.005210405436516349 plusOrMinus 1e-13) - barycentric.velocity[2] shouldBe (0.00273164103036653 plusOrMinus 1e-13) - } - "position of mars viewed from earth" { - val earth = mar097[399] - val mars = mar097[499] - val barycentric = earth.at(time) - val astrometric = barycentric.observe(mars) - astrometric.position[0] shouldBe (0.09761625675629965 plusOrMinus 1e-13) - astrometric.position[1] shouldBe (0.48508891609539406 plusOrMinus 1e-13) - astrometric.position[2] shouldBe (0.22948292606924786 plusOrMinus 1e-13) - astrometric.velocity[0] shouldBe (0.003266059522699063 plusOrMinus 1e-13) - astrometric.velocity[1] shouldBe (-0.0013034648919245393 plusOrMinus 1e-13) - astrometric.velocity[2] shouldBe (-9.24056040658666e-05 plusOrMinus 1e-13) - } + @JvmStatic private val TIME = TDB(TimeYMDHMS(2022, 11, 27, 22, 30, 0.0)) + @JvmStatic private val MAR097 = SpiceKernel(Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/mar097.bsp"))) } } diff --git a/nebulosa-nova/src/test/kotlin/VSOP87ETest.kt b/nebulosa-nova/src/test/kotlin/VSOP87ETest.kt index 1d6119fa0..a8c2b1c55 100644 --- a/nebulosa-nova/src/test/kotlin/VSOP87ETest.kt +++ b/nebulosa-nova/src/test/kotlin/VSOP87ETest.kt @@ -1,111 +1,126 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.nova.astrometry.VSOP87E import nebulosa.time.TDB import nebulosa.time.TimeJD +import org.junit.jupiter.api.Test -class VSOP87ETest : StringSpec() { +class VSOP87ETest { - init { - "sun" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.SUN.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-9.061436632282236E-03 plusOrMinus 1e-5) - p[1] shouldBe (6.152584183942633E-05 plusOrMinus 1e-6) - p[2] shouldBe (2.553315997818977E-04 plusOrMinus 1e-6) - v[0] shouldBe (8.523732347654815E-07 plusOrMinus 1e-9) - v[1] shouldBe (-8.293160642939413E-06 plusOrMinus 1e-9) - v[2] shouldBe (-3.536408207768117E-06 plusOrMinus 1e-9) - } - "mercury" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.MERCURY.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (2.917749477051785E-01 plusOrMinus 1e-5) - p[1] shouldBe (1.307753573063683E-01 plusOrMinus 1e-5) - p[2] shouldBe (3.890188068703725E-02 plusOrMinus 1e-5) - v[0] shouldBe (-1.701508873456466E-02 plusOrMinus 1e-8) - v[1] shouldBe (2.317979796647154E-02 plusOrMinus 1e-8) - v[2] shouldBe (1.414717965470348E-02 plusOrMinus 1e-8) - } - "venus" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.VENUS.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (4.530231016612877E-01 plusOrMinus 1e-5) - p[1] shouldBe (-5.016891998928698E-01 plusOrMinus 1e-5) - p[2] shouldBe (-2.547479296682298E-01 plusOrMinus 1e-5) - v[0] shouldBe (1.548765426376424E-02 plusOrMinus 1e-7) - v[1] shouldBe (1.199990886845585E-02 plusOrMinus 1e-7) - v[2] shouldBe (4.419843673733974E-03 plusOrMinus 1e-7) - } - "earth" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.EARTH.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-5.774546457978428E-02 plusOrMinus 1e-5) - p[1] shouldBe (9.014209322230198E-01 plusOrMinus 1e-5) - p[2] shouldBe (3.909900074777803E-01 plusOrMinus 1e-5) - v[0] shouldBe (-1.746880754444190E-02 plusOrMinus 1e-7) - v[1] shouldBe (-8.523045001854702E-04 plusOrMinus 1e-7) - v[2] shouldBe (-3.690873763513220E-04 plusOrMinus 1e-7) - } - "mars" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.MARS.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (1.458124773200954E-01 plusOrMinus 1e-5) - p[1] shouldBe (1.408612236938829E+00 plusOrMinus 1e-5) - p[2] shouldBe (6.421493185701298E-01 plusOrMinus 1e-5) - v[0] shouldBe (-1.339320293960258E-02 plusOrMinus 1e-7) - v[1] shouldBe (2.208863862273841E-03 plusOrMinus 1e-7) - v[2] shouldBe (1.374808996076603E-03 plusOrMinus 1e-7) - } - "jupiter" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.JUPITER.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (4.840655361543647E+00 plusOrMinus 1e-5) - p[1] shouldBe (9.540894823613197E-01 plusOrMinus 1e-5) - p[2] shouldBe (2.911277391212874E-01 plusOrMinus 1e-5) - v[0] shouldBe (-1.599598915586027E-03 plusOrMinus 1e-7) - v[1] shouldBe (7.106125606377319E-03 plusOrMinus 1e-6) - v[2] shouldBe (3.084863409738924E-03 plusOrMinus 1e-7) - } - "saturn" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.SATURN.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (8.118575220877252E+00 plusOrMinus 1e-5) - p[1] shouldBe (-4.990053770638640E+00 plusOrMinus 1e-5) - p[2] shouldBe (-2.410825392716150E+00 plusOrMinus 1e-5) - v[0] shouldBe (2.830471313637587E-03 plusOrMinus 1e-6) - v[1] shouldBe (4.295334526830042E-03 plusOrMinus 1e-6) - v[2] shouldBe (1.652340036885691E-03 plusOrMinus 1e-6) - } - "uranus" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.URANUS.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (1.338107802611238E+01 plusOrMinus 1e-4) - p[1] shouldBe (1.326832905623722E+01 plusOrMinus 1e-4) - p[2] shouldBe (5.621910507435053E+00 plusOrMinus 1e-4) - v[0] shouldBe (-2.910912382282302E-03 plusOrMinus 1e-7) - v[1] shouldBe (2.268725951577151E-03 plusOrMinus 1e-7) - v[2] shouldBe (1.034738977414487E-03 plusOrMinus 1e-7) - } - "neptune" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.NEPTUNE.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (2.974978125776706E+01 plusOrMinus 1e-4) - p[1] shouldBe (-2.471636747869995E+00 plusOrMinus 1e-3) - p[2] shouldBe (-1.752319118165945E+00 plusOrMinus 1e-4) - v[0] shouldBe (2.908617762745452E-04 plusOrMinus 1e-6) - v[1] shouldBe (2.911615062634410E-03 plusOrMinus 1e-6) - v[2] shouldBe (1.184401587981311E-03 plusOrMinus 1e-6) - } + @Test + fun sun() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.SUN.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-9.061436632282236E-03 plusOrMinus 1e-5) + p[1] shouldBe (6.152584183942633E-05 plusOrMinus 1e-6) + p[2] shouldBe (2.553315997818977E-04 plusOrMinus 1e-6) + v[0] shouldBe (8.523732347654815E-07 plusOrMinus 1e-9) + v[1] shouldBe (-8.293160642939413E-06 plusOrMinus 1e-9) + v[2] shouldBe (-3.536408207768117E-06 plusOrMinus 1e-9) + } + + @Test + fun mercury() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.MERCURY.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (2.917749477051785E-01 plusOrMinus 1e-5) + p[1] shouldBe (1.307753573063683E-01 plusOrMinus 1e-5) + p[2] shouldBe (3.890188068703725E-02 plusOrMinus 1e-5) + v[0] shouldBe (-1.701508873456466E-02 plusOrMinus 1e-8) + v[1] shouldBe (2.317979796647154E-02 plusOrMinus 1e-8) + v[2] shouldBe (1.414717965470348E-02 plusOrMinus 1e-8) + } + + @Test + fun venus() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.VENUS.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (4.530231016612877E-01 plusOrMinus 1e-5) + p[1] shouldBe (-5.016891998928698E-01 plusOrMinus 1e-5) + p[2] shouldBe (-2.547479296682298E-01 plusOrMinus 1e-5) + v[0] shouldBe (1.548765426376424E-02 plusOrMinus 1e-7) + v[1] shouldBe (1.199990886845585E-02 plusOrMinus 1e-7) + v[2] shouldBe (4.419843673733974E-03 plusOrMinus 1e-7) + } + + @Test + fun earth() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.EARTH.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-5.774546457978428E-02 plusOrMinus 1e-5) + p[1] shouldBe (9.014209322230198E-01 plusOrMinus 1e-5) + p[2] shouldBe (3.909900074777803E-01 plusOrMinus 1e-5) + v[0] shouldBe (-1.746880754444190E-02 plusOrMinus 1e-7) + v[1] shouldBe (-8.523045001854702E-04 plusOrMinus 1e-7) + v[2] shouldBe (-3.690873763513220E-04 plusOrMinus 1e-7) + } + + @Test + fun mars() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.MARS.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (1.458124773200954E-01 plusOrMinus 1e-5) + p[1] shouldBe (1.408612236938829E+00 plusOrMinus 1e-5) + p[2] shouldBe (6.421493185701298E-01 plusOrMinus 1e-5) + v[0] shouldBe (-1.339320293960258E-02 plusOrMinus 1e-7) + v[1] shouldBe (2.208863862273841E-03 plusOrMinus 1e-7) + v[2] shouldBe (1.374808996076603E-03 plusOrMinus 1e-7) + } + + @Test + fun jupiter() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.JUPITER.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (4.840655361543647E+00 plusOrMinus 1e-5) + p[1] shouldBe (9.540894823613197E-01 plusOrMinus 1e-5) + p[2] shouldBe (2.911277391212874E-01 plusOrMinus 1e-5) + v[0] shouldBe (-1.599598915586027E-03 plusOrMinus 1e-7) + v[1] shouldBe (7.106125606377319E-03 plusOrMinus 1e-6) + v[2] shouldBe (3.084863409738924E-03 plusOrMinus 1e-7) + } + + @Test + fun saturn() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.SATURN.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (8.118575220877252E+00 plusOrMinus 1e-5) + p[1] shouldBe (-4.990053770638640E+00 plusOrMinus 1e-5) + p[2] shouldBe (-2.410825392716150E+00 plusOrMinus 1e-5) + v[0] shouldBe (2.830471313637587E-03 plusOrMinus 1e-6) + v[1] shouldBe (4.295334526830042E-03 plusOrMinus 1e-6) + v[2] shouldBe (1.652340036885691E-03 plusOrMinus 1e-6) + } + + @Test + fun uranus() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.URANUS.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (1.338107802611238E+01 plusOrMinus 1e-4) + p[1] shouldBe (1.326832905623722E+01 plusOrMinus 1e-4) + p[2] shouldBe (5.621910507435053E+00 plusOrMinus 1e-4) + v[0] shouldBe (-2.910912382282302E-03 plusOrMinus 1e-7) + v[1] shouldBe (2.268725951577151E-03 plusOrMinus 1e-7) + v[2] shouldBe (1.034738977414487E-03 plusOrMinus 1e-7) + } + + @Test + fun neptune() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.NEPTUNE.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (2.974978125776706E+01 plusOrMinus 1e-4) + p[1] shouldBe (-2.471636747869995E+00 plusOrMinus 1e-3) + p[2] shouldBe (-1.752319118165945E+00 plusOrMinus 1e-4) + v[0] shouldBe (2.908617762745452E-04 plusOrMinus 1e-6) + v[1] shouldBe (2.911615062634410E-03 plusOrMinus 1e-6) + v[2] shouldBe (1.184401587981311E-03 plusOrMinus 1e-6) } } diff --git a/nebulosa-phd2-client/build.gradle.kts b/nebulosa-phd2-client/build.gradle.kts index 6e7c20b3e..41aee57dc 100644 --- a/nebulosa-phd2-client/build.gradle.kts +++ b/nebulosa-phd2-client/build.gradle.kts @@ -7,7 +7,7 @@ dependencies { api(project(":nebulosa-common")) api(project(":nebulosa-netty")) api(project(":nebulosa-guiding")) - api(libs.bundles.jackson) + api(project(":nebulosa-json")) implementation(project(":nebulosa-log")) testImplementation(project(":nebulosa-test")) } diff --git a/nebulosa-phd2-client/src/main/kotlin/nebulosa/phd2/client/PHD2Client.kt b/nebulosa-phd2-client/src/main/kotlin/nebulosa/phd2/client/PHD2Client.kt index 9d3789923..e185fe988 100644 --- a/nebulosa-phd2-client/src/main/kotlin/nebulosa/phd2/client/PHD2Client.kt +++ b/nebulosa-phd2-client/src/main/kotlin/nebulosa/phd2/client/PHD2Client.kt @@ -7,15 +7,14 @@ import com.fasterxml.jackson.module.kotlin.jsonMapper import com.fasterxml.jackson.module.kotlin.kotlinModule import io.netty.channel.ChannelInitializer import io.netty.channel.socket.SocketChannel -import nebulosa.common.json.PathDeserializer import nebulosa.guiding.GuideState +import nebulosa.json.PathModule import nebulosa.log.loggerFor import nebulosa.netty.NettyClient import nebulosa.phd2.client.commands.CompletableCommand import nebulosa.phd2.client.commands.PHD2Command import nebulosa.phd2.client.events.GuideStateDeserializer import nebulosa.phd2.client.events.GuideStateSerializer -import java.nio.file.Path import java.util.* import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit @@ -72,7 +71,6 @@ class PHD2Client : NettyClient() { @JvmStatic private val LOG = loggerFor() @JvmStatic private val MODULE = kotlinModule().also { - it.addDeserializer(Path::class.java, PathDeserializer) it.addDeserializer(GuideState::class.java, GuideStateDeserializer) it.addSerializer(GuideStateSerializer) } @@ -81,6 +79,7 @@ class PHD2Client : NettyClient() { disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) serializationInclusion(JsonInclude.Include.NON_NULL) + addModule(PathModule()) addModule(MODULE) } } diff --git a/nebulosa-phd2-client/src/test/kotlin/PHD2ClientTest.kt b/nebulosa-phd2-client/src/test/kotlin/PHD2ClientTest.kt index 6505bd8f8..ceb9da26e 100644 --- a/nebulosa-phd2-client/src/test/kotlin/PHD2ClientTest.kt +++ b/nebulosa-phd2-client/src/test/kotlin/PHD2ClientTest.kt @@ -1,25 +1,22 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec -import kotlinx.coroutines.delay import nebulosa.phd2.client.PHD2Client import nebulosa.phd2.client.PHD2EventListener import nebulosa.phd2.client.commands.PHD2Command import nebulosa.phd2.client.events.PHD2Event -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class PHD2ClientTest : StringSpec(), PHD2EventListener { +@NonGitHubOnly +class PHD2ClientTest : PHD2EventListener { - init { - "start" { - val client = PHD2Client() - client.registerListener(this@PHD2ClientTest) - client.open("localhost", PHD2Client.DEFAULT_PORT) + @Test + fun start() { + val client = PHD2Client() + client.registerListener(this@PHD2ClientTest) + client.open("localhost", PHD2Client.DEFAULT_PORT) - delay(1000) + Thread.sleep(1000) - client.close() - } + client.close() } override fun onEventReceived(event: PHD2Event) { diff --git a/nebulosa-pixinsight/build.gradle.kts b/nebulosa-pixinsight/build.gradle.kts index a82b0f0a5..60cae2725 100644 --- a/nebulosa-pixinsight/build.gradle.kts +++ b/nebulosa-pixinsight/build.gradle.kts @@ -6,11 +6,14 @@ plugins { dependencies { api(project(":nebulosa-common")) api(project(":nebulosa-math")) + api(project(":nebulosa-platesolver")) api(project(":nebulosa-stardetector")) + api(project(":nebulosa-stacker")) api(project(":nebulosa-livestacker")) - api(libs.bundles.jackson) + api(project(":nebulosa-json")) api(libs.apache.codec) implementation(project(":nebulosa-log")) + testImplementation(project(":nebulosa-image")) testImplementation(project(":nebulosa-test")) } diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/livestacker/PixInsightLiveStacker.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/livestacker/PixInsightLiveStacker.kt index 3348915ff..47219c118 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/livestacker/PixInsightLiveStacker.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/livestacker/PixInsightLiveStacker.kt @@ -2,21 +2,23 @@ package nebulosa.pixinsight.livestacker import nebulosa.livestacker.LiveStacker import nebulosa.log.loggerFor -import nebulosa.pixinsight.script.* +import nebulosa.pixinsight.script.PixInsightIsRunning +import nebulosa.pixinsight.script.PixInsightScript +import nebulosa.pixinsight.script.PixInsightScriptRunner +import nebulosa.pixinsight.script.PixInsightStartup +import nebulosa.pixinsight.stacker.PixInsightStacker import java.nio.file.Path import java.util.concurrent.atomic.AtomicBoolean -import kotlin.io.path.copyTo import kotlin.io.path.deleteIfExists -import kotlin.io.path.moveTo data class PixInsightLiveStacker( private val runner: PixInsightScriptRunner, private val workingDirectory: Path, - private val dark: Path? = null, - private val flat: Path? = null, - private val bias: Path? = null, + private val darkPath: Path? = null, + private val flatPath: Path? = null, + private val biasPath: Path? = null, private val use32Bits: Boolean = false, - private val slot: Int = PixInsightScript.DEFAULT_SLOT, + private val slot: Int = PixInsightScript.UNSPECIFIED_SLOT, ) : LiveStacker { private val running = AtomicBoolean() @@ -30,21 +32,20 @@ data class PixInsightLiveStacker( @Volatile private var stackCount = 0 - private val referencePath = Path.of("$workingDirectory", "reference.fits") - private val calibratedPath = Path.of("$workingDirectory", "calibrated.fits") - private val alignedPath = Path.of("$workingDirectory", "aligned.fits") + private val stacker = PixInsightStacker(runner, workingDirectory, slot) + private val referencePath = Path.of("$workingDirectory", "reference.xisf") + private val calibratedPath = Path.of("$workingDirectory", "calibrated.xisf") + private val alignedPath = Path.of("$workingDirectory", "aligned.xisf") private val stackedPath = Path.of("$workingDirectory", "stacked.fits") @Synchronized override fun start() { if (!running.get()) { - val isPixInsightRunning = PixInsightIsRunning(slot).use { it.runSync(runner) } - - if (!isPixInsightRunning) { + if (!PixInsightIsRunning(slot).use { it.runSync(runner).success }) { try { - check(PixInsightStartup(slot).use { it.runSync(runner) }) + check(PixInsightStartup(slot).use { it.runSync(runner).success }) } catch (e: Throwable) { - throw IllegalStateException("unable to start PixInsight") + throw IllegalStateException("unable to start PixInsight", e) } } @@ -60,45 +61,27 @@ data class PixInsightLiveStacker( return if (running.get()) { stacking.set(true) - // Calibrate. - val calibrated = if (dark == null && flat == null && bias == null) false else { - PixInsightCalibrate(slot, workingDirectory, targetPath, dark, flat, if (dark == null) bias else null).use { s -> - val outputPath = s.runSync(runner).outputImage ?: return@use false - LOG.info("live stacking calibrated. count={}, output={}", stackCount, outputPath) - outputPath.moveTo(calibratedPath, true) - true - } - } - - if (calibrated) { + if (stacker.calibrate(targetPath, calibratedPath, darkPath, flatPath, biasPath)) { + LOG.info("live stacking calibrated. count={}, output={}", stackCount, calibratedPath) targetPath = calibratedPath } // TODO: Debayer, Resample? if (stackCount > 0) { - // Align. - val aligned = PixInsightAlign(slot, workingDirectory, referencePath, targetPath).use { s -> - val outputPath = s.runSync(runner).outputImage ?: return@use false - LOG.info("live stacking aligned. count={}, output={}", stackCount, outputPath) - outputPath.moveTo(alignedPath, true) - true - } - - if (aligned) { + if (stacker.align(referencePath, targetPath, alignedPath)) { + LOG.info("live stacking aligned. count={}, output={}", stackCount, alignedPath) targetPath = alignedPath - // Stack. - val expressionRK = "({{0}} * $stackCount + {{1}}) / ${stackCount + 1}" - PixInsightPixelMath(slot, listOf(stackedPath, targetPath), stackedPath, expressionRK).use { s -> - s.runSync(runner).stackedImage?.also { - LOG.info("live stacking finished. count={}, output={}", stackCount++, it) - } + if (stacker.integrate(stackCount, stackedPath, targetPath, stackedPath)) { + LOG.info("live stacking finished. count={}, output={}", stackCount, stackedPath) } + + stackCount++ } } else { - targetPath.copyTo(referencePath, true) - targetPath.copyTo(stackedPath, true) + stacker.saveAs(targetPath, referencePath) + stacker.saveAs(targetPath, stackedPath) LOG.info("live stacking started. target={}, reference={}, stacked={}", targetPath, referencePath, stackedPath) stackCount = 1 } diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/platesolver/PixInsightPlateSolver.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/platesolver/PixInsightPlateSolver.kt new file mode 100644 index 000000000..8625a6a65 --- /dev/null +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/platesolver/PixInsightPlateSolver.kt @@ -0,0 +1,66 @@ +package nebulosa.pixinsight.platesolver + +import nebulosa.common.concurrency.cancel.CancellationListener +import nebulosa.common.concurrency.cancel.CancellationToken +import nebulosa.image.Image +import nebulosa.math.Angle +import nebulosa.math.deg +import nebulosa.math.toArcsec +import nebulosa.pixinsight.script.PixInsightImageSolver +import nebulosa.pixinsight.script.PixInsightScript +import nebulosa.pixinsight.script.PixInsightScriptRunner +import nebulosa.platesolver.PlateSolution +import nebulosa.platesolver.PlateSolver +import java.nio.file.Path +import java.time.Duration + +data class PixInsightPlateSolver( + private val runner: PixInsightScriptRunner, + private val pixelSize: Double, // µm + private val resolution: Angle = 0.0, + private val focalLength: Double = 0.0, // mm + private val slot: Int = PixInsightScript.UNSPECIFIED_SLOT, +) : PlateSolver { + + init { + require(resolution > 0.0 || focalLength > 0.0) { "resolution or focalDistance are required" } + } + + override fun solve( + path: Path?, image: Image?, + centerRA: Angle, centerDEC: Angle, radius: Angle, + downsampleFactor: Int, timeout: Duration, + cancellationToken: CancellationToken + ): PlateSolution { + require(path != null) { "path must be provided" } + + val script = PixInsightImageSolver(slot, path, centerRA, centerDEC, pixelSize, resolution.toArcsec, focalLength, timeout, cancellationToken) + val cancellationListener = CancellationListener { runner.abort(script) } + + val solver = try { + cancellationToken.listen(cancellationListener) + script.use { it.runSync(runner) } + } finally { + cancellationToken.unlisten(cancellationListener) + } + + if (solver.success) { + val m = ROTATION_REGEX.find(solver.astrometricSolutionSummary) + val rotation = m?.groupValues?.get(1)?.toDoubleOrNull()?.deg ?: 0.0 + + return PlateSolution( + true, rotation, solver.resolution, + solver.rightAscension, solver.declination, + solver.width, solver.height, widthInPixels = solver.imageWidth, + heightInPixels = solver.imageHeight, + ) + } + + return PlateSolution.NO_SOLUTION + } + + companion object { + + @JvmStatic val ROTATION_REGEX = Regex("Rotation\\s*\\.+\\s*([-+\\d.]+)") + } +} diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/AbstractPixInsightScript.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/AbstractPixInsightScript.kt index ad5554d39..b49b79349 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/AbstractPixInsightScript.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/AbstractPixInsightScript.kt @@ -1,17 +1,17 @@ package nebulosa.pixinsight.script +import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.module.kotlin.jsonMapper -import com.fasterxml.jackson.module.kotlin.kotlinModule import nebulosa.common.exec.CommandLine import nebulosa.common.exec.CommandLineListener -import nebulosa.common.json.PathDeserializer -import nebulosa.common.json.PathSerializer +import nebulosa.json.PathModule import nebulosa.log.loggerFor import org.apache.commons.codec.binary.Hex import java.nio.file.Path import java.util.concurrent.CompletableFuture +import kotlin.io.path.readText -abstract class AbstractPixInsightScript : PixInsightScript, CommandLineListener, CompletableFuture() { +abstract class AbstractPixInsightScript : PixInsightScript, CommandLineListener, CompletableFuture() { override fun onLineRead(line: String) = Unit @@ -34,7 +34,10 @@ abstract class AbstractPixInsightScript : PixInsightScript, CommandLineLis if (isDone) return@whenComplete else if (exception != null) completeExceptionally(exception) - else complete(processOnComplete(exitCode).also { LOG.info("script processed. output={}", it) }) + else complete(processOnComplete(exitCode).also { LOG.info("{} script processed. output={}", this::class.simpleName, it) }) + } catch (e: Throwable) { + LOG.error("{} finished with fatal exception. message={}", this::class.simpleName, e.message) + completeExceptionally(e) } finally { commandLine.unregisterCommandLineListener(this) } @@ -52,16 +55,15 @@ abstract class AbstractPixInsightScript : PixInsightScript, CommandLineLis @JvmStatic private val LOG = loggerFor>() - @JvmStatic private val KOTLIN_MODULE = kotlinModule() - .addDeserializer(Path::class.java, PathDeserializer) - .addSerializer(PathSerializer) - @JvmStatic internal val OBJECT_MAPPER = jsonMapper { - addModule(KOTLIN_MODULE) + addModule(PathModule()) + disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) } @JvmStatic - internal fun execute(slot: Int, scriptPath: Path, data: Any?): String { + internal fun PixInsightScript<*>.execute(scriptPath: Path, data: Any?, slot: Int = this.slot): String { + LOG.info("{} will be executed. slot={}, script={}, data={}", this::class.simpleName, slot, scriptPath, data) + return buildString { if (slot > 0) append("$slot:") append("\"$scriptPath") @@ -79,5 +81,20 @@ abstract class AbstractPixInsightScript : PixInsightScript, CommandLineLis append('"') } } + + @JvmStatic + internal fun Path.parseStatus(type: Class): T? { + val text = readText() + + return if (text.startsWith(START_FILE) && text.endsWith(END_FILE)) { + OBJECT_MAPPER.readValue(text.substring(1, text.length - 1), type) + } else { + null + } + } + + internal inline fun Path.parseStatus(): T? { + return parseStatus(T::class.java) + } } } diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAlign.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAlign.kt index 2958d15b0..a96fedaac 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAlign.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAlign.kt @@ -6,10 +6,9 @@ import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream -import kotlin.io.path.readText data class PixInsightAlign( - private val slot: Int, + override val slot: Int, private val workingDirectory: Path, private val referencePath: Path, private val targetPath: Path, @@ -23,8 +22,8 @@ data class PixInsightAlign( ) data class Output( - @JvmField val success: Boolean = false, - @JvmField val errorMessage: String? = null, + override val success: Boolean = false, + override val errorMessage: String? = null, @JvmField val outputImage: Path? = null, @JvmField val outputMaskImage: Path? = null, @JvmField val totalPairMatches: Int = 0, @@ -45,7 +44,7 @@ data class PixInsightAlign( @JvmField val h31: Double = 0.0, @JvmField val h32: Double = 0.0, @JvmField val h33: Double = 0.0, - ) { + ) : PixInsightScript.Output { companion object { @@ -60,18 +59,12 @@ data class PixInsightAlign( resource("pixinsight/Align.js")!!.transferAndClose(scriptPath.outputStream()) } - override val arguments = listOf("-x=${execute(slot, scriptPath, Input(referencePath, targetPath, workingDirectory, statusPath))}") + override val arguments = listOf("-x=${execute(scriptPath, Input(referencePath, targetPath, workingDirectory, statusPath))}") override fun processOnComplete(exitCode: Int): Output { if (exitCode == 0) { repeat(30) { - val text = statusPath.readText() - - if (text.startsWith(START_FILE) && text.endsWith(END_FILE)) { - return OBJECT_MAPPER.readValue(text.substring(1, text.length - 1), Output::class.java) - } - - Thread.sleep(1000) + statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) } } diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAutomaticBackgroundExtractor.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAutomaticBackgroundExtractor.kt index a68dd5049..7bd437755 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAutomaticBackgroundExtractor.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAutomaticBackgroundExtractor.kt @@ -2,14 +2,14 @@ package nebulosa.pixinsight.script import nebulosa.io.resource import nebulosa.io.transferAndClose +import nebulosa.pixinsight.script.PixInsightImageSolver.Output import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream -import kotlin.io.path.readText data class PixInsightAutomaticBackgroundExtractor( - private val slot: Int, + override val slot: Int, private val targetPath: Path, private val outputPath: Path, ) : AbstractPixInsightScript() { @@ -21,10 +21,10 @@ data class PixInsightAutomaticBackgroundExtractor( ) data class Output( - @JvmField val success: Boolean = false, - @JvmField val errorMessage: String? = null, + override val success: Boolean = false, + override val errorMessage: String? = null, @JvmField val outputImage: Path? = null, - ) { + ) : PixInsightScript.Output { companion object { @@ -39,18 +39,12 @@ data class PixInsightAutomaticBackgroundExtractor( resource("pixinsight/ABE.js")!!.transferAndClose(scriptPath.outputStream()) } - override val arguments = listOf("-x=${execute(slot, scriptPath, Input(targetPath, outputPath, statusPath))}") + override val arguments = listOf("-x=${execute(scriptPath, Input(targetPath, outputPath, statusPath))}") override fun processOnComplete(exitCode: Int): Output { if (exitCode == 0) { repeat(30) { - val text = statusPath.readText() - - if (text.startsWith(START_FILE) && text.endsWith(END_FILE)) { - return OBJECT_MAPPER.readValue(text.substring(1, text.length - 1), Output::class.java) - } - - Thread.sleep(1000) + statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) } } diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightCalibrate.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightCalibrate.kt index 53cd83351..21a29ca21 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightCalibrate.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightCalibrate.kt @@ -6,15 +6,14 @@ import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream -import kotlin.io.path.readText data class PixInsightCalibrate( - private val slot: Int, + override val slot: Int, private val workingDirectory: Path, private val targetPath: Path, - private val dark: Path? = null, - private val flat: Path? = null, - private val bias: Path? = null, + private val darkPath: Path? = null, + private val flatPath: Path? = null, + private val biasPath: Path? = null, private val compress: Boolean = false, private val use32Bit: Boolean = false, ) : AbstractPixInsightScript() { @@ -31,10 +30,10 @@ data class PixInsightCalibrate( ) data class Output( - @JvmField val success: Boolean = false, - @JvmField val errorMessage: String? = null, + override val success: Boolean = false, + override val errorMessage: String? = null, @JvmField val outputImage: Path? = null, - ) { + ) : PixInsightScript.Output { companion object { @@ -49,19 +48,13 @@ data class PixInsightCalibrate( resource("pixinsight/Calibrate.js")!!.transferAndClose(scriptPath.outputStream()) } - override val arguments = - listOf("-x=${execute(slot, scriptPath, Input(targetPath, workingDirectory, statusPath, dark, flat, bias, compress, use32Bit))}") + private val input = Input(targetPath, workingDirectory, statusPath, darkPath, flatPath, biasPath, compress, use32Bit) + override val arguments = listOf("-x=${execute(scriptPath, input)}") override fun processOnComplete(exitCode: Int): Output { if (exitCode == 0) { repeat(30) { - val text = statusPath.readText() - - if (text.startsWith(START_FILE) && text.endsWith(END_FILE)) { - return OBJECT_MAPPER.readValue(text.substring(1, text.length - 1), Output::class.java) - } - - Thread.sleep(1000) + statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) } } diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightDetectStars.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightDetectStars.kt index bfb4952f1..6f45c2a92 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightDetectStars.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightDetectStars.kt @@ -11,7 +11,7 @@ import kotlin.io.path.outputStream import kotlin.io.path.readText data class PixInsightDetectStars( - private val slot: Int, + override val slot: Int, private val targetPath: Path, private val minSNR: Double = 0.0, private val invert: Boolean = false, @@ -26,10 +26,10 @@ data class PixInsightDetectStars( ) data class Output( - @JvmField val success: Boolean = false, - @JvmField val errorMessage: String? = null, + override val success: Boolean = false, + override val errorMessage: String? = null, @JvmField val stars: List = emptyList(), - ) { + ) : PixInsightScript.Output { override fun toString() = "Output(success=$success, errorMessage=$errorMessage, stars=${stars.size})" @@ -62,7 +62,7 @@ data class PixInsightDetectStars( resource("pixinsight/DetectStars.js")!!.transferAndClose(scriptPath.outputStream()) } - override val arguments = listOf("-x=${execute(slot, scriptPath, Input(targetPath, statusPath, minSNR, invert))}") + override val arguments = listOf("-x=${execute(scriptPath, Input(targetPath, statusPath, minSNR, invert))}") override fun processOnComplete(exitCode: Int): Output { val timeoutInMillis = timeout.toMillis() diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightFileFormatConversion.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightFileFormatConversion.kt new file mode 100644 index 000000000..9192893f0 --- /dev/null +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightFileFormatConversion.kt @@ -0,0 +1,57 @@ +package nebulosa.pixinsight.script + +import nebulosa.io.resource +import nebulosa.io.transferAndClose +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.deleteIfExists +import kotlin.io.path.outputStream + +data class PixInsightFileFormatConversion( + override val slot: Int, + private val inputPath: Path, + private val outputPath: Path, +) : AbstractPixInsightScript() { + + private data class Input( + @JvmField val inputPath: Path, + @JvmField val outputPath: Path, + @JvmField val statusPath: Path, + ) + + data class Output( + override val success: Boolean = false, + override val errorMessage: String? = null, + @JvmField val outputImage: Path? = null, + ) : PixInsightScript.Output { + + companion object { + + @JvmStatic val FAILED = Output() + } + } + + private val scriptPath = Files.createTempFile("pi-", ".js") + private val statusPath = Files.createTempFile("pi-", ".txt") + + init { + resource("pixinsight/FileFormatConversion.js")!!.transferAndClose(scriptPath.outputStream()) + } + + override val arguments = listOf("-x=${execute(scriptPath, Input(inputPath, outputPath, statusPath))}") + + override fun processOnComplete(exitCode: Int): Output { + if (exitCode == 0) { + repeat(30) { + statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + } + } + + return Output.FAILED + } + + override fun close() { + scriptPath.deleteIfExists() + statusPath.deleteIfExists() + } +} diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightHelper.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightHelper.kt new file mode 100644 index 000000000..1ab52282f --- /dev/null +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightHelper.kt @@ -0,0 +1,16 @@ +package nebulosa.pixinsight.script + +import java.nio.file.Path +import kotlin.math.max + +fun startPixInsight(executablePath: Path, slot: Int): PixInsightScriptRunner { + val runner = PixInsightScriptRunner(executablePath) + + if (!PixInsightIsRunning(slot).use { it.runSync(runner).success }) { + if (!PixInsightStartup(max(1, slot)).use { it.runSync(runner).success }) { + throw IllegalStateException("unable to start PixInsight") + } + } + + return runner +} diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightImageSolver.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightImageSolver.kt new file mode 100644 index 000000000..174ca044b --- /dev/null +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightImageSolver.kt @@ -0,0 +1,108 @@ +package nebulosa.pixinsight.script + +import com.sun.jna.Platform +import nebulosa.common.concurrency.cancel.CancellationToken +import nebulosa.io.resource +import nebulosa.io.transferAndClose +import nebulosa.math.Angle +import nebulosa.math.toDegrees +import java.nio.file.Files +import java.nio.file.Path +import java.time.Duration +import kotlin.io.path.deleteIfExists +import kotlin.io.path.inputStream +import kotlin.io.path.outputStream +import kotlin.math.max +import kotlin.math.min + +data class PixInsightImageSolver( + override val slot: Int, + private val targetPath: Path, + private val centerRA: Angle, + private val centerDEC: Angle, + private val pixelSize: Double = 0.0, // µm + private val resolution: Double = 0.0, // arcsec/px + private val focalLength: Double = 0.0, // mm + private val timeout: Duration = Duration.ZERO, + private val cancellationToken: CancellationToken = CancellationToken.NONE, +) : AbstractPixInsightScript() { + + private data class Input( + @JvmField val targetPath: Path, + @JvmField val statusPath: Path, + @JvmField val centerRA: Double, // deg + @JvmField val centerDEC: Double, // deg + @JvmField val pixelSize: Double = 0.0, + @JvmField val resolution: Double = 0.0, + @JvmField val focalLength: Double = 0.0, + ) + + data class Output( + override val success: Boolean = false, + override val errorMessage: String? = null, + @JvmField val rightAscension: Angle = 0.0, + @JvmField val declination: Angle = 0.0, + @JvmField val resolution: Angle = 0.0, + @JvmField val pixelSize: Double = 0.0, + @JvmField val focalLength: Double = 0.0, + @JvmField val width: Angle = 0.0, + @JvmField val height: Angle = 0.0, + // @JvmField val rotation: Angle = 0.0, + @JvmField val imageWidth: Double = 0.0, + @JvmField val imageHeight: Double = 0.0, + @JvmField val astrometricSolutionSummary: String = "", + ) : PixInsightScript.Output { + + companion object { + + @JvmStatic val FAILED = Output() + } + } + + private val scriptPath = Files.createTempFile("pi-", ".js") + private val statusPath = Files.createTempFile("pi-", ".txt") + private val includePaths = ArrayList(INCLUDE_PATHS.size) + + init { + val scriptsDir = if (Platform.isWindows()) WINDOWS_SCRIPTS_DIR else LINUX_SCRIPTS_DIR + + for (includePath in INCLUDE_PATHS) { + val inputPath = Path.of(scriptsDir, "AdP", includePath) + val outputPath = Path.of("${scriptPath.parent}", includePath) + inputPath.inputStream().transferAndClose(outputPath.outputStream()) + includePaths.add(outputPath) + } + + resource("pixinsight/ImageSolver.js")!!.transferAndClose(scriptPath.outputStream()) + } + + private val input = Input(targetPath, statusPath, centerRA.toDegrees, centerDEC.toDegrees, pixelSize, resolution, focalLength) + override val arguments = listOf("-x=${execute(scriptPath, input)}") + + override fun processOnComplete(exitCode: Int): Output { + if (exitCode == 0) { + val seconds = timeout.toSeconds().toInt() + + repeat(max(30, min(seconds, 300))) { + if (cancellationToken.isCancelled) return@repeat + statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + } + } + + return Output.FAILED + } + + override fun close() { + scriptPath.deleteIfExists() + statusPath.deleteIfExists() + includePaths.forEach { it.deleteIfExists() } + } + + companion object { + + private const val WINDOWS_SCRIPTS_DIR = "C:\\Program Files\\PixInsight\\src\\scripts" + private const val LINUX_SCRIPTS_DIR = "/opt/PixInsight/src/scripts" + + @JvmStatic private val INCLUDE_PATHS = listOf("Projections.js", "WCSmetadata.jsh", "AstronomicalCatalogs.jsh") + } +} diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightIsRunning.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightIsRunning.kt index 4ec4b78ad..2ea09cc5b 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightIsRunning.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightIsRunning.kt @@ -3,7 +3,19 @@ package nebulosa.pixinsight.script import nebulosa.log.debug import nebulosa.log.loggerFor -data class PixInsightIsRunning(private val slot: Int) : AbstractPixInsightScript() { +data class PixInsightIsRunning(override val slot: Int) : AbstractPixInsightScript() { + + data class Output( + override val success: Boolean, + override val errorMessage: String? = null, + ) : PixInsightScript.Output { + + companion object { + + @JvmStatic val SUCCESS = Output(true) + @JvmStatic val FAILED = Output(false) + } + } override val arguments = listOf(if (slot > 0) "-y=$slot" else "-y") @@ -20,16 +32,16 @@ data class PixInsightIsRunning(private val slot: Int) : AbstractPixInsightScript if (slot > 0) { if (line.contains(slotIsNotRunning, true) || line.contains(slotCrashed, true)) { - complete(false) + complete(Output.FAILED) } else if (line.contains(yieldedExecutionInstance, true)) { - complete(true) + complete(Output.SUCCESS) } else { return } } else if (line.contains(YIELDED_EXECUTION_INSTANCE, true)) { - complete(true) + complete(Output.SUCCESS) } else if (line.contains(NO_RUNNING_PROCESS, true)) { - complete(false) + complete(Output.FAILED) } else { return } @@ -37,7 +49,7 @@ data class PixInsightIsRunning(private val slot: Int) : AbstractPixInsightScript LOG.debug { line } } - override fun processOnComplete(exitCode: Int) = false + override fun processOnComplete(exitCode: Int) = Output.FAILED override fun close() = Unit diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLRGBCombination.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLRGBCombination.kt new file mode 100644 index 000000000..3af4274c8 --- /dev/null +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLRGBCombination.kt @@ -0,0 +1,74 @@ +package nebulosa.pixinsight.script + +import nebulosa.io.resource +import nebulosa.io.transferAndClose +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.deleteIfExists +import kotlin.io.path.outputStream + +@Suppress("ArrayInDataClass") +data class PixInsightLRGBCombination( + override val slot: Int, + private val outputPath: Path, + private val luminancePath: Path? = null, + private val redPath: Path? = null, + private val greenPath: Path? = null, + private val bluePath: Path? = null, + private val weights: DoubleArray = DEFAULT_CHANNEL_WEIGHTS, +) : AbstractPixInsightScript() { + + @Suppress("ArrayInDataClass") + private data class Input( + @JvmField val outputPath: Path, + @JvmField val statusPath: Path, + @JvmField val luminancePath: Path?, + @JvmField val redPath: Path?, + @JvmField val greenPath: Path?, + @JvmField val bluePath: Path?, + @JvmField val channelWeights: DoubleArray, + ) + + data class Output( + override val success: Boolean = false, + override val errorMessage: String? = null, + @JvmField val outputImage: Path? = null, + ) : PixInsightScript.Output { + + companion object { + + @JvmStatic val FAILED = Output() + } + } + + private val scriptPath = Files.createTempFile("pi-", ".js") + private val statusPath = Files.createTempFile("pi-", ".txt") + + init { + require(weights.size >= 4) { "invalid weights size: ${weights.size}" } + resource("pixinsight/LRGBCombination.js")!!.transferAndClose(scriptPath.outputStream()) + } + + private val input = Input(outputPath, statusPath, luminancePath, redPath, greenPath, bluePath, weights) + override val arguments = listOf("-x=${execute(scriptPath, input)}") + + override fun processOnComplete(exitCode: Int): Output { + if (exitCode == 0) { + repeat(30) { + statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + } + } + + return Output.FAILED + } + + override fun close() { + scriptPath.deleteIfExists() + statusPath.deleteIfExists() + } + + companion object { + + @JvmStatic private val DEFAULT_CHANNEL_WEIGHTS = doubleArrayOf(1.0, 1.0, 1.0, 1.0) + } +} diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLuminanceCombination.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLuminanceCombination.kt new file mode 100644 index 000000000..3ae858446 --- /dev/null +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLuminanceCombination.kt @@ -0,0 +1,61 @@ +package nebulosa.pixinsight.script + +import nebulosa.io.resource +import nebulosa.io.transferAndClose +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.deleteIfExists +import kotlin.io.path.outputStream + +data class PixInsightLuminanceCombination( + override val slot: Int, + private val outputPath: Path, + private val luminancePath: Path, + private val targetPath: Path, +) : AbstractPixInsightScript() { + + private data class Input( + @JvmField val outputPath: Path, + @JvmField val statusPath: Path, + @JvmField val luminancePath: Path, + @JvmField val targetPath: Path, + @JvmField val wWeight: Double, + ) + + data class Output( + override val success: Boolean = false, + override val errorMessage: String? = null, + @JvmField val outputImage: Path? = null, + ) : PixInsightScript.Output { + + companion object { + + @JvmStatic val FAILED = Output() + } + } + + private val scriptPath = Files.createTempFile("pi-", ".js") + private val statusPath = Files.createTempFile("pi-", ".txt") + + init { + resource("pixinsight/LuminanceCombination.js")!!.transferAndClose(scriptPath.outputStream()) + } + + private val input = Input(outputPath, statusPath, luminancePath, targetPath, 1.0) + override val arguments = listOf("-x=${execute(scriptPath, input)}") + + override fun processOnComplete(exitCode: Int): Output { + if (exitCode == 0) { + repeat(30) { + statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + } + } + + return Output.FAILED + } + + override fun close() { + scriptPath.deleteIfExists() + statusPath.deleteIfExists() + } +} diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightPixelMath.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightPixelMath.kt index a4cd13ad8..30b4820e3 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightPixelMath.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightPixelMath.kt @@ -2,14 +2,14 @@ package nebulosa.pixinsight.script import nebulosa.io.resource import nebulosa.io.transferAndClose +import nebulosa.pixinsight.script.PixInsightImageSolver.Output import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream -import kotlin.io.path.readText data class PixInsightPixelMath( - private val slot: Int, + override val slot: Int, private val inputPaths: List, private val outputPath: Path, private val expressionRK: String? = null, @@ -27,10 +27,10 @@ data class PixInsightPixelMath( ) data class Output( - @JvmField val success: Boolean = false, - @JvmField val errorMessage: String? = null, - @JvmField val stackedImage: Path? = null, - ) { + override val success: Boolean = false, + override val errorMessage: String? = null, + @JvmField val outputImage: Path? = null, + ) : PixInsightScript.Output { companion object { @@ -45,19 +45,13 @@ data class PixInsightPixelMath( resource("pixinsight/PixelMath.js")!!.transferAndClose(scriptPath.outputStream()) } - override val arguments = - listOf("-x=${execute(slot, scriptPath, Input(statusPath, inputPaths, outputPath, expressionRK, expressionG, expressionB))}") + private val input = Input(statusPath, inputPaths, outputPath, expressionRK, expressionG, expressionB) + override val arguments = listOf("-x=${execute(scriptPath, input)}") - override fun processOnComplete(exitCode: Int): Output? { + override fun processOnComplete(exitCode: Int): Output { if (exitCode == 0) { repeat(30) { - val text = statusPath.readText() - - if (text.startsWith(START_FILE) && text.endsWith(END_FILE)) { - return OBJECT_MAPPER.readValue(text.substring(1, text.length - 1), Output::class.java) - } - - Thread.sleep(1000) + statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) } } diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScript.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScript.kt index 58a39d50e..52811810d 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScript.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScript.kt @@ -4,7 +4,16 @@ import nebulosa.common.exec.CommandLine import java.io.Closeable import java.util.concurrent.Future -interface PixInsightScript : Future, Closeable { +sealed interface PixInsightScript : Future, Closeable { + + val slot: Int + + sealed interface Output { + + val success: Boolean + + val errorMessage: String? + } val arguments: Iterable diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptRunner.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptRunner.kt index 6becf6d2b..d7a6e7a7a 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptRunner.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptRunner.kt @@ -18,6 +18,8 @@ data class PixInsightScriptRunner(private val executablePath: Path) { script.startCommandLine(commandLine) } + fun abort(script: PixInsightScript<*>) = Unit + companion object { @JvmStatic private val DEFAULT_ARGS = arrayOf("--automation-mode", "--no-startup-scripts") diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightStartup.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightStartup.kt index e410a4ea9..713170b19 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightStartup.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightStartup.kt @@ -8,7 +8,19 @@ import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream import kotlin.io.path.readText -data class PixInsightStartup(private val slot: Int) : AbstractPixInsightScript() { +data class PixInsightStartup(override val slot: Int) : AbstractPixInsightScript() { + + data class Output( + override val success: Boolean, + override val errorMessage: String? = null, + ) : PixInsightScript.Output { + + companion object { + + @JvmStatic val SUCCESS = Output(true) + @JvmStatic val FAILED = Output(false) + } + } private val scriptPath = Files.createTempFile("pi-", ".js") private val outputPath = Files.createTempFile("pi-", ".txt") @@ -17,17 +29,17 @@ data class PixInsightStartup(private val slot: Int) : AbstractPixInsightScript 0) "-n=$slot" else "-n") + override val arguments = listOf("-r=${execute(scriptPath, outputPath, 0)}", if (slot > 0) "-n=$slot" else "-n") override fun beforeRun() { var count = 0 timer("PixInsight Startup Timer", true, 1000L, 500L) { if (outputPath.readText() == "STARTED") { - complete(true) + complete(Output.SUCCESS) cancel() } else if (count >= 60) { - complete(false) + complete(Output.FAILED) cancel() } @@ -35,7 +47,7 @@ data class PixInsightStartup(private val slot: Int) : AbstractPixInsightScript() + private val stacker = PixInsightStacker(runner, workingDirectory, slot) + + override fun stack(paths: Collection, outputPath: Path, referencePath: Path): Boolean { + if (paths.isEmpty()) return false + if (!cancellationToken.compareAndSet(null, CancellationToken())) return false + + val calibratedPath = Path.of("$workingDirectory", "calibrated.xisf") + val alignedPath = Path.of("$workingDirectory", "aligned.xisf") + + try { + var stackCount = 0 + + val realPaths = paths.map { it.toRealPath() } + val referenceRealPath = referencePath.toRealPath() + + realPaths.forEach { + var targetPath = it + + cancellationToken.get().throwIfCancelled() + + if (calibrate(targetPath, calibratedPath, darkPath, flatPath, biasPath)) { + targetPath = calibratedPath + } + + cancellationToken.get().throwIfCancelled() + + if (stackCount > 0) { + if (align(referenceRealPath, targetPath, alignedPath)) { + cancellationToken.get().throwIfCancelled() + integrate(stackCount, outputPath, alignedPath, outputPath) + stackCount++ + } + } else { + if (referenceRealPath != it) { + if (align(referenceRealPath, targetPath, alignedPath)) { + cancellationToken.get().throwIfCancelled() + saveAs(alignedPath, outputPath) + cancellationToken.get().throwIfCancelled() + integrate(0, outputPath, alignedPath, outputPath) + } else { + saveAs(targetPath, outputPath) + } + } else { + saveAs(targetPath, outputPath) + } + + stackCount = 1 + } + + cancellationToken.get().throwIfCancelled() + } + } catch (e: CancellationException) { + return false + } finally { + calibratedPath.deleteIfExists() + alignedPath.deleteIfExists() + + cancellationToken.getAndSet(null) + } + + return true + } + + override fun calibrate(targetPath: Path, outputPath: Path, darkPath: Path?, flatPath: Path?, biasPath: Path?): Boolean { + return stacker.calibrate(targetPath, outputPath, darkPath, flatPath, biasPath) + } + + override fun align(referencePath: Path, targetPath: Path, outputPath: Path): Boolean { + return stacker.align(referencePath, targetPath, outputPath) + } + + override fun integrate(stackCount: Int, stackedPath: Path, targetPath: Path, outputPath: Path): Boolean { + return stacker.integrate(stackCount, stackedPath, targetPath, outputPath) + } + + override fun combineLRGB(outputPath: Path, luminancePath: Path?, redPath: Path?, greenPath: Path?, bluePath: Path?): Boolean { + return stacker.combineLRGB(outputPath, luminancePath, redPath, greenPath, bluePath) + } + + override fun combineLuminance(outputPath: Path, luminancePath: Path, targetPath: Path, mono: Boolean): Boolean { + return stacker.combineLuminance(outputPath, luminancePath, targetPath, mono) + } + + override fun saveAs(inputPath: Path, outputPath: Path): Boolean { + return stacker.saveAs(inputPath, outputPath) + } + + override fun stop() { + cancellationToken.get()?.cancel() + } +} diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightStacker.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightStacker.kt new file mode 100644 index 000000000..53863b774 --- /dev/null +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightStacker.kt @@ -0,0 +1,55 @@ +package nebulosa.pixinsight.stacker + +import nebulosa.pixinsight.script.* +import nebulosa.stacker.Stacker +import java.nio.file.Path + +data class PixInsightStacker( + private val runner: PixInsightScriptRunner, + private val workingDirectory: Path, + private val slot: Int = PixInsightScript.UNSPECIFIED_SLOT, +) : Stacker { + + override fun calibrate( + targetPath: Path, outputPath: Path, + darkPath: Path?, flatPath: Path?, biasPath: Path?, + ) = if (darkPath != null || flatPath != null || biasPath != null) { + PixInsightCalibrate(slot, workingDirectory, targetPath, darkPath, flatPath, if (darkPath == null) biasPath else null) + .use { calibrate -> calibrate.runSync(runner).outputImage?.let { saveAs(it, outputPath) } ?: false } + } else { + false + } + + override fun align(referencePath: Path, targetPath: Path, outputPath: Path): Boolean { + return PixInsightAlign(slot, workingDirectory, referencePath, targetPath) + .use { align -> align.runSync(runner).outputImage?.let { saveAs(it, outputPath) } ?: false } + } + + override fun integrate(stackCount: Int, stackedPath: Path, targetPath: Path, outputPath: Path): Boolean { + val expressionRK = "({{0}} * $stackCount + {{1}}) / ${stackCount + 1}" + return PixInsightPixelMath(slot, listOf(stackedPath, targetPath), outputPath, expressionRK) + .use { it.runSync(runner).outputImage != null } + } + + override fun combineLRGB(outputPath: Path, luminancePath: Path?, redPath: Path?, greenPath: Path?, bluePath: Path?): Boolean { + if (luminancePath == null && redPath == null && greenPath == null && bluePath == null) return false + + return PixInsightLRGBCombination(slot, outputPath, luminancePath, redPath, greenPath, bluePath) + .use { it.runSync(runner).outputImage != null } + } + + override fun combineLuminance(outputPath: Path, luminancePath: Path, targetPath: Path, mono: Boolean): Boolean { + return if (mono) { + PixInsightPixelMath(slot, listOf(luminancePath, targetPath), outputPath, "{{0}} + (1 - {{0}}) * {{1}}") + .use { it.runSync(runner).outputImage != null } + } else { + PixInsightLuminanceCombination(slot, outputPath, luminancePath, targetPath) + .use { it.runSync(runner).outputImage != null } + } + } + + override fun saveAs(inputPath: Path, outputPath: Path): Boolean { + return PixInsightFileFormatConversion(slot, inputPath, outputPath) + .use { it.runSync(runner).outputImage != null } + } +} diff --git a/nebulosa-pixinsight/src/main/resources/pixinsight/ABE.js b/nebulosa-pixinsight/src/main/resources/pixinsight/ABE.js index 9ffb6e97c..b4e4455ba 100644 --- a/nebulosa-pixinsight/src/main/resources/pixinsight/ABE.js +++ b/nebulosa-pixinsight/src/main/resources/pixinsight/ABE.js @@ -22,6 +22,7 @@ function abe() { const outputPath = input.outputPath const statusPath = input.statusPath + console.writeln("abe started") console.writeln("targetPath=" + targetPath) console.writeln("outputPath=" + outputPath) console.writeln("statusPath=" + statusPath) @@ -65,7 +66,7 @@ function abe() { } catch (e) { data.success = false data.errorMessage = e.message - console.writeln(data.errorMessage) + console.criticalln(data.errorMessage) } finally { File.writeTextFile(statusPath, "@" + JSON.stringify(data) + "#") } diff --git a/nebulosa-pixinsight/src/main/resources/pixinsight/Align.js b/nebulosa-pixinsight/src/main/resources/pixinsight/Align.js index f8f93f38c..bd2414576 100644 --- a/nebulosa-pixinsight/src/main/resources/pixinsight/Align.js +++ b/nebulosa-pixinsight/src/main/resources/pixinsight/Align.js @@ -42,6 +42,7 @@ function alignment() { const outputDirectory = input.outputDirectory const statusPath = input.statusPath + console.writeln("alignment started") console.writeln("referencePath=" + referencePath) console.writeln("targetPath=" + targetPath) console.writeln("outputDirectory=" + outputDirectory) @@ -103,7 +104,7 @@ function alignment() { P.pixelInterpolation = StarAlignment.prototype.Auto P.clampingThreshold = 0.30 P.outputDirectory = outputDirectory - P.outputExtension = ".fits" + P.outputExtension = ".xisf" P.outputPrefix = "" P.outputPostfix = "_a" P.maskPostfix = "_m" @@ -146,7 +147,7 @@ function alignment() { } catch (e) { data.success = false data.errorMessage = e.message - console.writeln(data.errorMessage) + console.criticalln(data.errorMessage) } finally { File.writeTextFile(statusPath, "@" + JSON.stringify(data) + "#") } diff --git a/nebulosa-pixinsight/src/main/resources/pixinsight/Calibrate.js b/nebulosa-pixinsight/src/main/resources/pixinsight/Calibrate.js index 814586b86..4668ce5cf 100644 --- a/nebulosa-pixinsight/src/main/resources/pixinsight/Calibrate.js +++ b/nebulosa-pixinsight/src/main/resources/pixinsight/Calibrate.js @@ -27,6 +27,7 @@ function calibrate() { const compress = input.compress const use32Bit = input.use32Bit + console.writeln("calibration started") console.writeln("targetPath=" + targetPath) console.writeln("outputDirectory=" + outputDirectory) console.writeln("statusPath=" + statusPath) @@ -89,7 +90,7 @@ function calibrate() { P.psfGrowth = 1.00 P.maxStars = 24576 P.outputDirectory = outputDirectory - P.outputExtension = ".fits" + P.outputExtension = ".xisf" P.outputPrefix = "" P.outputPostfix = "_c" P.outputSampleFormat = use32Bit ? ImageCalibration.prototype.f32 : ImageCalibration.prototype.i16 @@ -112,7 +113,7 @@ function calibrate() { } catch (e) { data.success = false data.errorMessage = e.message - console.writeln(data.errorMessage) + console.criticalln(data.errorMessage) } finally { File.writeTextFile(statusPath, "@" + JSON.stringify(data) + "#") } diff --git a/nebulosa-pixinsight/src/main/resources/pixinsight/DetectStars.js b/nebulosa-pixinsight/src/main/resources/pixinsight/DetectStars.js index 45b04f3e6..651c0573a 100644 --- a/nebulosa-pixinsight/src/main/resources/pixinsight/DetectStars.js +++ b/nebulosa-pixinsight/src/main/resources/pixinsight/DetectStars.js @@ -832,7 +832,7 @@ function detectStars() { } catch (e) { data.success = false data.errorMessage = e.message - console.writeln(data.errorMessage) + console.criticalln(data.errorMessage) } finally { File.writeTextFile(statusPath, "@" + JSON.stringify(data) + "#") } diff --git a/nebulosa-pixinsight/src/main/resources/pixinsight/FileFormatConversion.js b/nebulosa-pixinsight/src/main/resources/pixinsight/FileFormatConversion.js new file mode 100644 index 000000000..172144fe1 --- /dev/null +++ b/nebulosa-pixinsight/src/main/resources/pixinsight/FileFormatConversion.js @@ -0,0 +1,46 @@ +function decodeParams(hex) { + const buffer = new Uint8Array(hex.length / 4) + + for (let i = 0; i < hex.length; i += 4) { + buffer[i / 4] = parseInt(hex.substr(i, 4), 16) + } + + return JSON.parse(String.fromCharCode.apply(null, buffer)) +} + +function fileFormatConversion() { + const data = { + success: true, + errorMessage: null, + outputImage: null, + } + + try { + const input = decodeParams(jsArguments[0]) + + const outputPath = input.outputPath + const statusPath = input.statusPath + const inputPath = input.inputPath + + console.writeln("Format conversion started") + console.writeln("outputPath=" + outputPath) + console.writeln("statusPath=" + statusPath) + console.writeln("inputPath=" + inputPath) + + const window = ImageWindow.open(inputPath)[0] + window.saveAs(outputPath, false, false, false, false) + window.forceClose() + + data.outputImage = outputPath + + console.writeln("Format conversion finished") + } catch (e) { + data.success = false + data.errorMessage = e.message + console.criticalln(data.errorMessage) + } finally { + File.writeTextFile(statusPath, "@" + JSON.stringify(data) + "#") + } +} + +fileFormatConversion() diff --git a/nebulosa-pixinsight/src/main/resources/pixinsight/ImageSolver.js b/nebulosa-pixinsight/src/main/resources/pixinsight/ImageSolver.js new file mode 100644 index 000000000..6ab08f84b --- /dev/null +++ b/nebulosa-pixinsight/src/main/resources/pixinsight/ImageSolver.js @@ -0,0 +1,1458 @@ +/* + * Image Plate Solver + * + * Plate solving of astronomical images. + * + * Copyright (C) 2012-2024, Andres del Pozo + * Copyright (C) 2019-2024, Juan Conejero (PTeam) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#define __PJSR_USE_STAR_DETECTOR_V2 + +#include +#include +#include +#include +#include +#include + +#define SOLVERVERSION "6.2.7" +#define STAR_CSV_FILE (File.systemTempDirectory + format("/stars-%03d.csv", CoreApplication.instance)) +#define SETTINGS_MODULE "SOLVER" +#define SETTINGS_MODULE_SCRIPT "SOLVER" + +#include "WCSmetadata.jsh" +#include "AstronomicalCatalogs.jsh" + +function CatalogMode() { } +CatalogMode.prototype.LocalText = 0 +CatalogMode.prototype.Online = 1 +CatalogMode.prototype.Automatic = 2 +CatalogMode.prototype.LocalXPSDServer = 3 + +function SolverConfiguration(module) { + this.__base__ = ObjectWithSettings + this.__base__( + module, + "solver", + new Array( + ["version", DataType_UCString], + ["magnitude", DataType_Float], + ["autoMagnitude", DataType_Boolean], + ["databasePath", DataType_UCString], + ["generateErrorImg", DataType_Boolean], + ["structureLayers", DataType_UInt8], + ["minStructureSize", DataType_UInt8], + ["hotPixelFilterRadius", DataType_UInt8], + ["noiseReductionFilterRadius", DataType_UInt8], + ["sensitivity", DataType_Double], + ["peakResponse", DataType_Double], + ["brightThreshold", DataType_Double], + ["maxStarDistortion", DataType_Double], + ["autoPSF", DataType_Boolean], + ["catalogMode", DataType_UInt8], + ["vizierServer", DataType_UCString], + ["showStars", DataType_Boolean], + ["showStarMatches", DataType_Boolean], + ["showSimplifiedSurfaces", DataType_Boolean], + ["showDistortion", DataType_Boolean], + ["generateDistortModel", DataType_Boolean], + ["catalog", DataType_UCString], + ["distortionCorrection", DataType_Boolean], + ["splineOrder", DataType_UInt8], + ["splineSmoothing", DataType_Float], + ["enableSimplifier", DataType_Boolean], + ["simplifierRejectFraction", DataType_Float], + ["outlierDetectionRadius", DataType_Int32], + ["outlierDetectionMinThreshold", DataType_Float], + ["outlierDetectionSigma", DataType_Float], + ["useDistortionModel", DataType_Boolean], + ["distortionModelPath", DataType_UCString], + ["useActive", DataType_Boolean], + ["outSuffix", DataType_UCString], + ["files", Ext_DataType_StringArray], + ["projection", DataType_UInt8], + ["projectionOriginMode", DataType_UInt8], + ["restrictToHQStars", DataType_Boolean], + ["tryApparentCoordinates", DataType_Boolean] + ) + ) + + this.version = SOLVERVERSION + this.useActive = true + this.files = [] + this.catalogMode = CatalogMode.prototype.Automatic + this.availableCatalogs = [ + new PPMXLCatalog(), + new TychoCatalog(), + new HR_Catalog(), + new GaiaDR2_Catalog() + ] + this.availableXPSDServers = [ + new GaiaDR3XPSDCatalog(), + new GaiaEDR3XPSDCatalog(), + new GaiaDR2XPSDCatalog() + ] + this.vizierServer = "https://vizier.cds.unistra.fr/" + this.magnitude = 12 + this.maxIterations = 100 + this.structureLayers = 5 + this.minStructureSize = 0 + this.hotPixelFilterRadius = 1 + this.noiseReductionFilterRadius = 0 + this.sensitivity = 0.5 + this.peakResponse = 0.5 + this.brightThreshold = 3.0 + this.maxStarDistortion = 0.6 + this.autoPSF = false + this.generateErrorImg = false + this.showStars = false + this.catalog = "PPMXL" + this.autoMagnitude = true + this.showStarMatches = false + this.showSimplifiedSurfaces = false + this.showDistortion = false + this.distortionCorrection = true + this.splineOrder = 2 + this.splineSmoothing = 0.005 + this.enableSimplifier = true + this.simplifierRejectFraction = 0.10 + this.outlierDetectionRadius = 160 + this.outlierDetectionMinThreshold = 4.0 + this.outlierDetectionSigma = 5.0 + this.generateDistortModel = false + this.useDistortionModel = false + this.distortionModelPath = null + this.outSuffix = "_ast" + this.projection = 0 + this.projectionOriginMode = 0 + this.restrictToHQStars = false + this.tryApparentCoordinates = true + + this.ResetSettings = function () { + Settings.remove(SETTINGS_MODULE) + } +} + +SolverConfiguration.prototype = new ObjectWithSettings + +function ImageSolver() { + this.config = new SolverConfiguration(SETTINGS_MODULE_SCRIPT) + this.metadata = new ImageMetadata(SETTINGS_MODULE_SCRIPT) + + this.config.catalogMode = 2 + this.config.vizierServer = "https://vizier.cds.unistra.fr/" + this.config.magnitude = 12 + this.config.maxIterations = 100 + this.config.structureLayers = 5 + this.config.minStructureSize = 0 + this.config.hotPixelFilterRadius = 1 + this.config.noiseReductionFilterRadius = 0 + this.config.sensitivity = 0.5 + this.config.peakResponse = 0.5 + this.config.brightThreshold = 3 + this.config.maxStarDistortion = 0.6 + this.config.autoPSF = false + this.config.generateErrorImg = false + this.config.showStars = false + this.config.catalog = "PPMXL" + this.config.autoMagnitude = true + this.config.showStarMatches = false + this.config.showSimplifiedSurfaces = false + this.config.showDistortion = false + this.config.distortionCorrection = true + this.config.splineOrder = 2 + this.config.splineSmoothing = 0.005 + this.config.enableSimplifier = true + this.config.simplifierRejectFraction = 0.10 + this.config.outlierDetectionRadius = 160 + this.config.outlierDetectionMinThreshold = 4 + this.config.outlierDetectionSigma = 5 + this.config.generateDistortModel = false + this.config.useDistortionModel = false + this.config.distortionModelPath = null + this.config.outSuffix = "_ast" + this.config.projection = 0 + this.config.projectionOriginMode = 0 + this.config.restrictToHQStars = false + this.config.tryApparentCoordinates = true + + this.finished = false + + let listener = new MessageListener + listener.onMessage = function (instance, uniqueId, message) { + console.writeln("Received message: " + message) + + if (message === "ABORTED") { + this.finished = true + } + } + + /* + * Initializes the image solver. If the parameter prioritizeSettings is + * defined and is true, the solver will use the values stored in preferences + * instead of the values obtained from the image. + */ + this.Init = function (window, prioritizeSettings) { + if (prioritizeSettings) + if (window && window.isWindow) + this.metadata.ExtractMetadata(window) + + this.metadata.LoadSettings() + this.metadata.LoadParameters() + + if (!prioritizeSettings) + if (window && window.isWindow) + this.metadata.ExtractMetadata(window) + + this.metadata.ensureValidReferenceSystemForSolution() + } + + this.InitialAlignment = function (window) { + const SA = new StarAlignment + SA.referenceImage = STAR_CSV_FILE + SA.referenceIsFile = true + SA.mode = StarAlignment.prototype.OutputMatrix + SA.writeKeywords = false + SA.structureLayers = this.config.structureLayers + SA.minStructureSize = this.config.minStructureSize + SA.hotPixelFilterRadius = this.config.hotPixelFilterRadius + SA.noiseReductionFilterRadius = this.config.noiseReductionFilterRadius + SA.sensitivity = this.config.sensitivity + SA.peakResponse = this.config.peakResponse + SA.brightThreshold = this.config.brightThreshold + SA.maxStarDistortion = this.config.maxStarDistortion + SA.allowClusteredSources = true; // because we want it to match as many stars as possible at this stage + SA.ransacTolerance = 2 + SA.ransacMaxIterations = 5000 + SA.ransacMaximizeInliers = 1 + SA.ransacMaximizeOverlapping = 0 + SA.ransacMaximizeRegularity = 0 + SA.ransacMinimizeError = 0 + SA.useTriangles = false + SA.polygonSides = 7 + SA.descriptorsPerStar = 100 + SA.restrictToPreviews = false + + if (this.config.useDistortionModel) { + SA.distortionModel = this.config.distortionModelPath + SA.undistortedReference = true + } + + if (!SA.executeOn(window.currentView, false)) { + /* + * If we are using polygonal descriptors, try again using triangle + * similarity, just in case we have a mirrored image. + */ + console.noteln("
* Previous attempt with polygonal descriptors failed - trying with triangle similarity.") + SA.useTriangles = true + if (!SA.executeOn(window.currentView, false)) + return null + } + + let numPairs = Math.min(SA.outputData[0][2], 4000) + let pairs = { + pS: new Array(numPairs), + pI: new Array(numPairs) + } + + for (let i = 0; i < numPairs; ++i) { + pairs.pS[i] = new Point(SA.outputData[0][29][i], SA.outputData[0][30][i]) + pairs.pI[i] = new Point(SA.outputData[0][31][i] + 0.5, SA.outputData[0][32][i] + 0.5) + } + + return pairs + } + + this.GenerateTemplate = function (metadata, templateGeom) { + if (this.config.catalogMode == CatalogMode.prototype.LocalText) { + this.catalog = new CustomCatalog(this.config.databasePath) + } else { + this.catalog = __catalogRegister__.GetCatalog(this.catalogName) + this.catalog.magMax = this.limitMagnitude + this.catalog.restrictToHQStars = this.config.restrictToHQStars + } + + this.catalog.Load(metadata, this.config.vizierServer) + + if (this.catalog.objects == null) + throw "Catalog error: " + this.catalogName + + let ref_G_S = templateGeom.ref_S_G.inverse() + + let file = File.createFileForWriting(STAR_CSV_FILE) + file.outTextLn(templateGeom.width + "," + templateGeom.height) + let elements = this.catalog.objects + let numStars = 0 + let clipRectS = templateGeom.clipRectS || new Rect(0, 0, templateGeom.width, templateGeom.height) + + for (let i = 0; i < elements.length; ++i) + if (elements[i]) { + let flux = (elements[i].magnitude == null) ? 0 : Math.pow(2.512, -1.5 - elements[i].magnitude) + let pos_G = templateGeom.projection.Direct(elements[i].posRD) + + if (pos_G) { + let pos_S = ref_G_S.apply(pos_G) + + if (pos_S.x > clipRectS.left + && pos_S.x < clipRectS.right + && pos_S.y > clipRectS.top + && pos_S.y < clipRectS.bottom) { + file.outTextLn(format("%.4f,%.4f,%.3e", pos_S.x, pos_S.y, flux)) + numStars++ + } + } + } + + file.close() + + if (numStars < 8) + throw "Found too few stars. The magnitude filter could be too strict, or the catalog server could be malfunctioning." + } + + this.DoIterationSA = function (window, metadata) { + try { + /* + * Render a star field around the original coordinates. + */ + let templateSize = Math.max(metadata.width, metadata.height) + let templateGeom = { + ref_S_G: new Matrix(-metadata.resolution, 0, metadata.resolution * templateSize / 2, + 0, -metadata.resolution, metadata.resolution * templateSize / 2, + 0, 0, 1), + projection: ProjectionFactory(this.config, metadata.ra, metadata.dec), + width: templateSize, + height: templateSize, + clipRectS: null + } + + this.GenerateTemplate(metadata, templateGeom) + + /* + * Perform the initial image registration using a projective + * transformation. + */ + let pairs = this.InitialAlignment(window) + + if (pairs === null) { + /* + * Some image acquisition applications store apparent or 'of the + * date' coordinates in image metadata without providing the + * required metadata items to let us know. If the corresponding + * option is enabled, make a second attempt assuming that the + * center coordinates are apparent. + */ + if (this.config.tryApparentCoordinates) + if (metadata.mightBeApparent) { + console.noteln("
* Previous attempt failed - trying again assuming apparent coordinates.") + metadata.convertRADecFromApparentToAstrometric() + metadata.mightBeApparent = false + templateGeom.projection = ProjectionFactory(this.config, metadata.ra, metadata.dec) + this.GenerateTemplate(metadata, templateGeom) + pairs = this.InitialAlignment(window) + + if (pairs === null) { + if (!console.isAborted) { + console.criticalln("
*** Error: The image could not be aligned with the reference star field.") + console.writeln( + "" + + "

Please check the following items:

" + + "
    " + + "
  • The initial center coordinates should be inside the image.
  • " + + "
  • The initial image resolution should be within a factor of 2 from the correct value.
  • " + + "
  • If you use an online star catalog through the VizieR service, consider using " + + "the Gaia DR3 catalog with local XPSD databases instead.
  • " + + "
  • If the image has extreme noise levels, bad tracking, or is poorly focused, you may " + + "need to adjust some star detection parameters.
  • " + + "
" + + "") + } + throw "" + } + else { + console.warningln("
** Warning: The image provides apparent or 'of the date' coordinates in " + + "image metadata but does not include the appropriate Observation:CelestialReferenceSystem XISF property " + + "or RADESYS FITS keyword. We suggest you inform the authors of your image acquisition application about " + + "this error, which they should fix.") + } + } + } + + /* + * Adjust to a projection with the origin at the center of the image. + */ + let pG = pairs.pS.map(p => templateGeom.ref_S_G.apply(p)) + let ref_S_G = Math.homography(pairs.pI, pG) + let centerRD = templateGeom.projection.Inverse(ref_S_G.apply(new Point(metadata.width / 2, metadata.height / 2))) + let newProjection = ProjectionFactory(this.config, centerRD.x, centerRD.y) + pairs.pG = pG.map(p => newProjection.Direct(templateGeom.projection.Inverse(p))) + templateGeom.projection = newProjection + + /* + * Initialize a new metadata structure appropriate for the selected + * working mode. + */ + let newMetadata = metadata.Clone() + newMetadata.projection = templateGeom.projection + + if (this.config.distortionCorrection) { + // Using surface splines. + newMetadata.ref_I_G_linear = Math.homography(pairs.pI, pairs.pG) + + newMetadata.ref_I_G = new ReferSpline(pairs.pI, pairs.pG, + this.config.splineOrder, + this.config.splineSmoothing, + this.config.enableSimplifier, + this.config.simplifierRejectFraction) + processEvents() + + newMetadata.ref_G_I = new ReferSpline(pairs.pG, pairs.pI, + this.config.splineOrder, + this.config.splineSmoothing, + this.config.enableSimplifier, + this.config.simplifierRejectFraction) + processEvents() + + newMetadata.controlPoints = { + pI: pairs.pI, + pG: pairs.pG + } + } + else { + // Using a linear solution. + newMetadata.ref_I_G = Math.homography(pairs.pI, pairs.pG) + newMetadata.ref_I_G_linear = newMetadata.ref_I_G + newMetadata.ref_G_I = newMetadata.ref_I_G.inverse() + newMetadata.controlPoints = null + } + + /* + * Find the celestial coordinates (RD) of the center of the original + * image. First transform from I to G and then unproject the native + * projection coordinates (G) to celestial (RD). + */ + let centerI = new Point(metadata.width / 2, metadata.height / 2) + let centerG = newMetadata.ref_I_G.apply(centerI) + centerRD = newMetadata.projection.Inverse(centerG) + while (centerRD.x < 0) + centerRD.x += 360 + while (centerRD.x >= 360) + centerRD.x -= 360 + newMetadata.ra = centerRD.x + newMetadata.dec = centerRD.y + let ref = newMetadata.ref_I_G_linear + let resx = Math.sqrt(ref.at(0, 0) * ref.at(0, 0) + ref.at(0, 1) * ref.at(0, 1)) + let resy = Math.sqrt(ref.at(1, 0) * ref.at(1, 0) + ref.at(1, 1) * ref.at(1, 1)) + newMetadata.resolution = (resx + resy) / 2 + newMetadata.focal = newMetadata.FocalFromResolution(newMetadata.resolution) + newMetadata.useFocal = false + + return newMetadata + } + catch (ex) { + if (!console.isAborted) + if (ex.length === undefined || ex.length > 0) + console.criticalln("*** Error: ", ex.toString()) + return null + } + finally { + try { + if (File.exists(STAR_CSV_FILE)) + File.remove(STAR_CSV_FILE) + } + catch (x) { + // Propagate no further filesystem exceptions here. + } + } + } + + this.MatchStars = function (window, predictedCoords) { + /* + * Putative point matches by proximity search. + */ + let actualCoords = new Array(predictedCoords.length) + for (let i = 0; i < predictedCoords.length; ++i) { + let p = predictedCoords[i] + if (p) { + let s = this.starTree.search({ + x0: p.x - this.psfSearchRadius, + y0: p.y - this.psfSearchRadius, + x1: p.x + this.psfSearchRadius, + y1: p.y + this.psfSearchRadius + }) + if (s.length > 0) { + let j = 0 + if (s.length > 1) { + let star = this.starTree.objects[s[0]] + let dx = star.x - p.x + let dy = star.y - p.y + let d2 = dx * dx + dy * dy + for (let i = 1; i < s.length; ++i) { + let star = this.starTree.objects[s[i]] + let dx = star.x - p.x + let dy = star.y - p.y + let d2i = dx * dx + dy * dy + if (d2i < d2) { + j = i + d2 = d2i + } + } + } + let star = this.starTree.objects[s[j]] + actualCoords[i] = new Point(star.x, star.y) + } + } + } + + if (!this.config.distortionCorrection) + return { matchedPoints: actualCoords, meanSparsity: 0, sigmaSparsity: 0, rejectionThreshold: 0 } + + /* + * Adaptive spline outlier rejection based on local sparsity estimation. + * In this context, outliers are wrongly extrapolated points that can + * prevent modeling non-convex surfaces and regions of strongly varying + * distortion by stalling surface spline generation in subsequent + * iterations. + */ + let P = [] + for (let i = 0; i < actualCoords.length; ++i) { + let p = actualCoords[i] + if (p) + P.push({ + x: p.x, y: p.y, + rect: { + x0: p.x - 0.5, y0: p.y - 0.5, + x1: p.x + 0.5, y1: p.y + 0.5 + }, + idx: i + }) + } + let T = new BRQuadTree(P, 256/*bucketSize*/) + let S = new Float32Array(P.length) + for (let i = 0; i < P.length; ++i) { + let p = P[i] + let r = { + x0: p.x - this.config.outlierDetectionRadius, + y0: p.y - this.config.outlierDetectionRadius, + x1: p.x + this.config.outlierDetectionRadius, + y1: p.y + this.config.outlierDetectionRadius + } + let s1 = this.starTree.search(r) + let s2 = T.search(r) + S[i] = s1.length / s2.length + } + let m = Math.median(S) + let s = 1.1926 * Math.Sn(S) + let d = Math.max(this.config.outlierDetectionMinThreshold, + m + this.config.outlierDetectionSigma * s) + for (let i = 0; i < P.length; ++i) + if (S[i] > d) + P[i] = null; // outlier removed + + /* + * Output coordinates. + */ + let Q = new Array(predictedCoords.length) + for (let i = 0; i < P.length; ++i) { + let p = P[i] + if (p) + Q[p.idx] = actualCoords[p.idx] + } + return { matchedPoints: Q, meanSparsity: m, sigmaSparsity: s, rejectionThreshold: d } + } + + // This warning is now silenced. + this.showedWarningOnTruncatedInputSet = true //false + + this.DetectStars = function (window, metadata) { + /* + * Load reference stars. + */ + if (!this.catalog) + if (this.config.catalogMode == CatalogMode.prototype.LocalText) { + this.catalog = new CustomCatalog(this.config.databasePath) + } + else { + this.catalog = __catalogRegister__.GetCatalog(this.catalogName) + this.catalog.magMax = this.limitMagnitude + this.catalog.restrictToHQStars = this.config.restrictToHQStars + } + this.catalog.Load(metadata, this.config.vizierServer) + let catalogObjects = this.catalog.objects + if (catalogObjects == null) + throw "Catalog error: " + this.catalogName + if (catalogObjects.length < 10) + throw "Insufficient stars found in catalog: " + this.catalogName + if (catalogObjects.length > WCS_MAX_STARS_IN_SOLUTION) + if (!this.showedWarningOnTruncatedInputSet) { + console.warningln("** Warning: Exceeded the maximum number of stars allowed. " + + "Truncating the input set to the ", WCS_MAX_STARS_IN_SOLUTION, " brightest stars.") + this.showedWarningOnTruncatedInputSet = true + } + + /* + * Sort reference stars by magnitude in ascending order (brighter stars + * first). Possible objects with undefined magnitudes are packed at the + * tail of the array. + */ + catalogObjects.sort((a, b) => a.magnitude ? (b.magnitude ? a.magnitude - b.magnitude : -1) : (b.magnitude ? +1 : 0)) + + /* + * Calculate image coordinates of catalog stars with the current + * transformation. + */ + let result = { + projection: ProjectionFactory(this.config, metadata.ra, metadata.dec), + starCoords: [], + coordsG: [], + magnitudes: [], + actualCoords: null + } + let predictedCoords = [] + { + let posRD = [], magnitudes = [] + for (let i = 0, n = Math.min(WCS_MAX_STARS_IN_SOLUTION, catalogObjects.length); i < n; ++i) + if (catalogObjects[i]) { + posRD.push(catalogObjects[i].posRD) + magnitudes.push(catalogObjects[i].magnitude) + } + let posI = metadata.Convert_RD_I_Points(posRD, true/*unscaled*/) + + for (let i = 0; i < posI.length; ++i) { + let pI = posI[i] + if (pI && + pI.x >= 0 && + pI.y >= 0 && + pI.x <= metadata.width && + pI.y <= metadata.height) { + let pG = result.projection.Direct(posRD[i]) + if (pG) { + result.coordsG.push(pG) + result.starCoords.push(posRD[i]) + result.magnitudes.push(magnitudes[i]) + predictedCoords.push(pI) + } + } + } + } + + if (predictedCoords.length < 4) + throw "Unable to define a valid set of reference star positions." + + /* + * Find the stars in the image using predictedCoords as starting point. + */ + let matches = this.MatchStars(window, predictedCoords) + + result.actualCoords = matches.matchedPoints + + /* + * Remove control points with identical coordinates. + */ + { + let A = [] + for (let i = 0; i < result.actualCoords.length; ++i) + if (result.actualCoords[i]) + A.push({ i: i, x: result.actualCoords[i].x, y: result.actualCoords[i].y }) + A.sort((a, b) => (a.x != b.x) ? a.x - b.x : a.y - b.y) + for (let i = 1; i < A.length; ++i) + if (A[i].x == A[i - 1].x) + if (A[i].y == A[i - 1].y) { + result.actualCoords[A[i].i] = null + result.coordsG[A[i].i] = null + } + A = [] + for (let i = 0; i < result.coordsG.length; ++i) + if (result.coordsG[i]) + A.push({ i: i, x: result.coordsG[i].x, y: result.coordsG[i].y }) + A.sort((a, b) => (a.x != b.x) ? a.x - b.x : a.y - b.y) + for (let i = 1; i < A.length; ++i) + if (A[i].x == A[i - 1].x) + if (A[i].y == A[i - 1].y) { + result.actualCoords[A[i].i] = null + result.coordsG[A[i].i] = null + } + } + + /* + * Gather information on matching errors. + */ + result.errors = new Array(predictedCoords.length) + result.numValid = 0 + let meanError, sigmaError, peakError = 0, sum2 = 0 + { + let E = [] + for (let i = 0; i < predictedCoords.length; ++i) + if (result.actualCoords[i]) { + let ex = predictedCoords[i].x - result.actualCoords[i].x + let ey = predictedCoords[i].y - result.actualCoords[i].y + let e = Math.sqrt(ex * ex + ey * ey) + result.errors[i] = e + E.push(e) + if (e > peakError) + peakError = e + result.numValid++ + sum2 += e * e + } + + meanError = Math.median(E) + sigmaError = Math.sqrt(Math.biweightMidvariance(E, meanError)) + } + result.rms = (result.numValid > 0) ? Math.sqrt(sum2 / result.numValid) : 0 + result.score = Math.roundTo(result.numValid / (1 + result.rms), 3) + + if (this.config.distortionCorrection) + console.writeln(format("Surface sparsity : median = %.2f, sigma = %.2f, threshold = %.2f", + matches.meanSparsity, matches.sigmaSparsity, matches.rejectionThreshold)) + console.writeln(format("Matching errors : median = %.2f px, sigma = %.2f px, peak = %.2f px", + meanError, sigmaError, peakError)) + console.writeln(format("Matched stars : %d (%.2f%%)", + result.numValid, 100.0 * result.numValid / predictedCoords.length)) + console.flush() + + return result + } + + this.DoIterationLinear = function (metadata, stars) { + console.flush() + processEvents() + + /* + * Find linear transformations. + */ + let newMetadata = metadata.Clone() + newMetadata.projection = stars.projection + newMetadata.ref_I_G = Math.homography(stars.actualCoords, stars.coordsG) + newMetadata.ref_I_G_linear = newMetadata.ref_I_G + newMetadata.ref_G_I = newMetadata.ref_I_G.inverse() + newMetadata.controlPoints = null + + /* + * Find the celestial coordinates (RD) of the center of the original + * image. First transform from I to G and then unproject from native + * projection coordinates (G) to celestial (RD). + */ + let centerI = new Point(metadata.width / 2, metadata.height / 2) + let centerG = newMetadata.ref_I_G.apply(centerI) + let centerRD = newMetadata.projection.Inverse(centerG) + while (centerRD.x < 0) + centerRD.x += 360 + while (centerRD.x >= 360) + centerRD.x -= 360 + newMetadata.ra = (Math.abs(metadata.ra - centerRD.x) < 1) ? (metadata.ra + centerRD.x * 2) / 3 : centerRD.x + newMetadata.dec = (Math.abs(metadata.dec - centerRD.y) < 1) ? (metadata.dec + centerRD.y * 2) / 3 : centerRD.y + let ref = newMetadata.ref_I_G_linear + let resx = Math.sqrt(ref.at(0, 0) * ref.at(0, 0) + ref.at(0, 1) * ref.at(0, 1)) + let resy = Math.sqrt(ref.at(1, 0) * ref.at(1, 0) + ref.at(1, 1) * ref.at(1, 1)) + newMetadata.resolution = (resx + resy) / 2 + newMetadata.focal = newMetadata.FocalFromResolution(newMetadata.resolution) + newMetadata.useFocal = false + + return newMetadata + } + + this.DoIterationSpline = function (metadata, stars) { + console.flush() + processEvents() + + /* + * Build surface splines. + */ + let newMetadata = metadata.Clone() + newMetadata.projection = stars.projection + newMetadata.ref_I_G_linear = Math.homography(stars.actualCoords, stars.coordsG) + + newMetadata.ref_I_G = new ReferSpline(stars.actualCoords, stars.coordsG, + this.config.splineOrder, + this.config.splineSmoothing, + this.config.enableSimplifier, + this.config.simplifierRejectFraction) + processEvents() + + newMetadata.ref_G_I = new ReferSpline(stars.coordsG, stars.actualCoords, + this.config.splineOrder, + this.config.splineSmoothing, + this.config.enableSimplifier, + this.config.simplifierRejectFraction) + processEvents() + + newMetadata.controlPoints = { + pI: stars.actualCoords, + pG: stars.coordsG, + weights: null + } + + /* + * Find the celestial coordinates (RD) of the center of the original + * image. First transform from I to G and then unproject from native + * projection coordinates (G) to celestial (RD). + */ + let centerI = new Point(metadata.width / 2, metadata.height / 2) + let centerG = newMetadata.ref_I_G.apply(centerI) + let centerRD = newMetadata.projection.Inverse(centerG) + while (centerRD.x < 0) + centerRD.x += 360 + while (centerRD.x >= 360) + centerRD.x -= 360 + newMetadata.ra = (Math.abs(metadata.ra - centerRD.x) < 1) ? (metadata.ra + centerRD.x * 2) / 3 : centerRD.x + newMetadata.dec = (Math.abs(metadata.dec - centerRD.y) < 1) ? (metadata.dec + centerRD.y * 2) / 3 : centerRD.y + let ref = newMetadata.ref_I_G_linear + let resx = Math.sqrt(ref.at(0, 0) * ref.at(0, 0) + ref.at(0, 1) * ref.at(0, 1)) + let resy = Math.sqrt(ref.at(1, 0) * ref.at(1, 0) + ref.at(1, 1) * ref.at(1, 1)) + newMetadata.resolution = (resx + resy) / 2 + newMetadata.focal = newMetadata.FocalFromResolution(newMetadata.resolution) + newMetadata.useFocal = false + + return newMetadata + } + + this.GenerateWorkingImage = function (targetWindow) { + // Convert the image to grayscale. + // The chrominance is not necessary for the astrometry. + let grayscaleImage = new Image + grayscaleImage.assign(targetWindow.mainView.image) + grayscaleImage.colorSpace = ColorSpace_HSI + grayscaleImage.selectedChannel = 2 // intensity component + + let workingWindow = new ImageWindow(grayscaleImage.width, grayscaleImage.height, + 1/*channels*/, 32/*bits*/, true/*float*/, false/*color*/, + targetWindow.mainView.id + "_working") + workingWindow.mainView.beginProcess(UndoFlag_NoSwapFile) + workingWindow.mainView.image.apply(grayscaleImage) + workingWindow.mainView.endProcess() + + // Deallocate now, don't wait for garbage collection. + grayscaleImage.free() + + return workingWindow + } + + this.MetadataDelta = function (metadata1, metadata2, pI) { + /* + * Calculate the difference between the last two iterations using the + * displacement between the center and the given point pI. + */ + let pRD2 = metadata2.Convert_I_RD(pI) + let pRD1 = metadata1.ref_I_G ? metadata1.Convert_I_RD(pI) : pRD2 + let delta1 = 0 + if (pRD1) + delta1 = Math.sqrt(Math.pow((pRD1.x - pRD2.x) * Math.cos(Math.rad(pRD2.y)), 2) + + Math.pow(pRD1.y - pRD2.y, 2)) * 3600 + let delta2 = Math.sqrt(Math.pow((metadata2.ra - metadata1.ra) * Math.cos(Math.rad(metadata2.dec)), 2) + + Math.pow(metadata2.dec - metadata1.dec, 2)) * 3600 + return Math.max(delta1, delta2) + } + + this.OptimizeSolution = function (workingWindow, currentMetadata, stars) { + let iteration = 1 + let numItersWithoutImprovement = 0 + let maxItersWithoutImprovement = 4 + let bestMetadata = currentMetadata + let bestScore = stars.score + let bestRMS = stars.rms + let bestStarCount = stars.numValid + let converged = false + + if (this.finished) { + return + } + + do { + console.abortEnabled = true + + let result + try { + if (this.config.distortionCorrection) + result = this.DoIterationSpline(currentMetadata, stars) + else + result = this.DoIterationLinear(currentMetadata, stars) + + if (result == null) + throw "" + } catch (ex) { + let haveException = !console.isAborted && (ex.length === undefined || ex.length > 0) + if (haveException) + console.criticalln("
*** Error: " + ex.toString()) + console.criticalln("" + + (haveException ? "" : "
*** Error: ") + + "The image could not be fully solved. We have tagged it with the latest known valid solution.") + console.abortEnabled = false + break + } + + stars = this.DetectStars(workingWindow, result) + + processEvents() + + if (console.abortRequested || this.finished) { + this.finished = true + console.criticalln("*** User requested abort ***") + break + } + + /* + * Calculate the difference between the current and previous + * iterations using the displacements between the center and eight + * points located on the image borders. Report the maximum difference. + */ + let delta = Math.max(this.MetadataDelta(currentMetadata, result, new Point(0, 0)), + this.MetadataDelta(currentMetadata, result, new Point(result.width, 0)), + this.MetadataDelta(currentMetadata, result, new Point(0, result.height)), + this.MetadataDelta(currentMetadata, result, new Point(result.width, result.height)), + this.MetadataDelta(currentMetadata, result, new Point(result.width / 2, 0)), + this.MetadataDelta(currentMetadata, result, new Point(result.width / 2, result.height)), + this.MetadataDelta(currentMetadata, result, new Point(0, result.height / 2)), + this.MetadataDelta(currentMetadata, result, new Point(result.width, result.height / 2))) + let deltaPx = delta / (result.resolution * 3600) + + console.writeln("
*****") + console.writeln(format("Iteration %d, delta = %.3f as (%.2f px)", iteration, delta, deltaPx)) + console.writeln("Image center ... RA: ", DMSangle.FromAngle(result.ra / 15).ToString(true), + " Dec: ", DMSangle.FromAngle(result.dec).ToString()) + console.writeln(format("Resolution ..... %.2f as/px", result.resolution * 3600)) + console.writeln(format("RMS error ...... %.3f px (%d stars)", stars.rms, stars.numValid)) + + converged = deltaPx < 0.005 && Math.abs(stars.rms - bestRMS) < 0.01 + if (converged || stars.numValid > bestStarCount && (stars.rms <= bestRMS || stars.rms - bestRMS < 0.01)) + stars.score = Math.max(stars.score, bestScore + 1) + + if (stars.score > bestScore) + console.writeln(format("Score .......... \x1b[38;2;128;255;128m%.3f\x1b[0m", stars.score)) + else + console.writeln(format("Score .......... %.3f", stars.score)) + console.writeln("*****") + + /* + * Prevent degenerate cases where we cannot match any stars. This + * happens, among other causes, when projection systems are used + * beyond their capabilities. + */ + if (stars.numValid < 4) { + console.criticalln("*** Error: Unable to find a valid set of star pair matches.") + break + } + + currentMetadata = result + + // Store the best model so far + if (stars.score > bestScore) { + numItersWithoutImprovement = 0 + bestMetadata = result + bestScore = stars.score + bestRMS = stars.rms + bestStarCount = stars.numValid + } + else { + if (iteration == 1) + bestMetadata = result + numItersWithoutImprovement++ + } + + // Finish condition + this.finished = true + + if (converged || numItersWithoutImprovement > maxItersWithoutImprovement) { + if (this.distortModel) { + converged = false + numItersWithoutImprovement = 0 + this.finished = false + this.distortModel = null + console.noteln("* The solution with distortion model has converged. Trying to optimize it without the model.") + } + else if (converged) + console.noteln(format("* Convergence reached after %d iterations.", iteration)) + else + console.warningln(format("** Warning: Process stalled after %d iterations.", iteration)) + } + else if (iteration > this.config.maxIterations) + console.warningln("** Warning: Reached maximum number of iterations without convergence.") + else + this.finished = false + + ++iteration + + console.abortEnabled = true + + processEvents() + + if (console.abortRequested) { + this.finished = true + console.criticalln("*** User requested abort ***") + } + + gc(true) + } while (!this.finished) + + if (converged) + console.noteln(format("* Successful astrometry optimization. Score = %.3f", bestScore)) + else + console.warningln(format("** Partial astrometry optimization. Score = %.3f", bestScore)) + + return bestMetadata + } + + this.SolveImage = function (targetWindow) { + this.error = false + + let abortableBackup = jsAbortable + jsAbortable = true + let auxWindow = null + + try { + console.show() + console.abortEnabled = true + + let workingWindow = targetWindow + if (targetWindow.mainView.image.isColor) + auxWindow = workingWindow = this.GenerateWorkingImage(targetWindow) + + /* + * Build a bucket region quadtree structure with all detected stars in + * the image for fast star matching. + */ + try { + /* + * Step 1 - Star detection + */ + let D = new StarDetector + D.structureLayers = this.config.structureLayers + D.hotPixelFilterRadius = this.config.hotPixelFilterRadius + D.noiseReductionFilterRadius = this.config.noiseReductionFilterRadius + D.sensitivity = this.config.sensitivity + D.peakResponse = this.config.peakResponse + D.allowClusteredSources = false + D.maxDistortion = this.config.maxStarDistortion + D.brightThreshold = this.config.brightThreshold + D.minStructureSize = this.config.minStructureSize + let lastProgressPc = 0 + D.progressCallback = + (count, total) => { + if (count == 0) { + console.write("Detecting stars: 0%") + lastProgressPc = 0 + processEvents() + } + else { + let pc = Math.round(100 * count / total) + if (pc > lastProgressPc) { + console.write(format("\b\b\b\b%3d%%", pc)) + lastProgressPc = pc + processEvents() + } + } + return true + } + + let S = D.stars(workingWindow.mainView.image) + this.numberOfDetectedStars = S.length + if (this.numberOfDetectedStars < 6) + throw "Insufficient stars detected: found " + this.numberOfDetectedStars.toString() + ", at least 6 are required." + + + console.writeln(format("%d stars found ", this.numberOfDetectedStars)) + console.flush() + + /* + * Step 2 - PSF fitting + */ + let stars = [] + let minStructSize = Number.POSITIVE_INFINITY + for (let i = 0; i < S.length; ++i) { + let p = S[i].pos + let r = S[i].rect + stars.push([0, 0, DynamicPSF.prototype.Star_DetectedOk, + r.x0, r.y0, r.x1, r.y1, + p.x, p.y]) + let m = Math.max(r.x1 - r.x0, r.y1 - r.y0) + if (m < minStructSize) + minStructSize = m + } + + let P = new DynamicPSF + P.views = [[workingWindow.mainView.id]] + P.stars = stars + P.astrometry = false + P.autoAperture = true + P.searchRadius = minStructSize + P.circularPSF = false + P.autoPSF = this.config.autoPSF + P.gaussianPSF = true + P.moffatPSF = P.moffat10PSF = P.moffat8PSF = + P.moffat6PSF = P.moffat4PSF = P.moffat25PSF = + P.moffat15PSF = P.lorentzianPSF = this.config.autoPSF + P.variableShapePSF = false + if (!P.executeGlobal()) + throw "Unable to execute DynamicPSF process." + + console.flush() + + stars = [] + for (let psf = P.psf, i = 0; i < psf.length; ++i) { + let p = psf[i] + if (p[3] == DynamicPSF.prototype.PSF_FittedOk) { + let x = p[6] + let y = p[7] + let rx = p[8] / 2 + let ry = p[9] / 2 + stars.push({ + x: x, y: y, + rect: { + x0: x - rx, y0: y - ry, + x1: x + rx, y1: y + ry + } + }) + } + } + + /* + * Step 3 - Remove potential duplicate objects + */ + this.starTree = new BRQuadTree(stars.slice(), 256/*bucketSize*/) + stars = [] + for (let i = 0; i < this.starTree.objects.length; ++i) { + let o = this.starTree.objects[i] + let s = this.starTree.search({ + x0: o.x - 1, y0: o.y - 1, + x1: o.x + 1, y1: o.y + 1 + }) + if (s.length == 1) + stars.push(o) + } + if (stars.length < 6) + throw "Insufficient number of objects: found " + stars.length.toString() + ", at least 6 are required." + + console.write(format("* Removed %d conflicting sources (%.2f %%)", + this.starTree.objects.length - stars.length, 100 * (this.starTree.objects.length - stars.length) / stars.length)) + + /* + * Step 4 - Quadtree generation + */ + this.starTree.build(stars.slice(), 256/*bucketSize*/) + console.write(format("* Search quadtree generated with %d objects, %d node(s), height = %d", + this.starTree.objects.length, this.starTree.numberOfNodes(), this.starTree.height())) + + /* + * Step 5 - Calculate search and matching tolerances + */ + this.psfMinimumDistance = Math.min(stars[0].rect.x1 - stars[0].rect.x0, + stars[0].rect.y1 - stars[0].rect.y0) + for (let i = 1; i < stars.length; ++i) { + let s = stars[i] + let d = Math.min(stars[i].rect.x1 - stars[i].rect.x0, + stars[i].rect.y1 - stars[i].rect.y0) + if (d < this.psfMinimumDistance) + this.psfMinimumDistance = d + } + this.psfMinimumDistance = Math.max(2, Math.trunc(0.75 * (this.psfMinimumDistance - 2))) // StarDetector inflates detection regions + this.psfSearchRadius = 1.0 * this.psfMinimumDistance + console.writeln(format("* Star matching tolerance: %d px", this.psfMinimumDistance)) + console.flush() + } + catch (ex) { + this.starTree = null + gc() + throw ex + } + + /* + * Find limit magnitude. + */ + if (this.config.autoMagnitude || this.config.catalogMode == CatalogMode.prototype.Automatic) { + let fov = this.metadata.resolution * Math.max(this.metadata.width, this.metadata.height) + // Empiric formula for 1000 stars at 20 deg of galactic latitude + let m = 14.5 * Math.pow(fov, -0.179) + m = Math.round(100 * Math.min(20, Math.max(7, m))) / 100 + + /* + * Identify a local XPSD server and use it if available to find an + * optimal magnitude limit adaptively. + */ + let xpsd = ((typeof Gaia) != 'undefined') ? (new Gaia) : null + if (xpsd) { + xpsd.command = "get-info" + xpsd.dataRelease = Gaia.prototype.DataRelease_BestAvailable + xpsd.executeGlobal() + if (xpsd.isValid) { + if (this.config.autoMagnitude) { + let radiusPx = Math.SQRT2 * Math.sqrt(this.metadata.width * this.metadata.height) / 2 + let targetStarCount = this.numberOfDetectedStars * 1.25 + + console.writeln(format("
Searching for optimal magnitude limit. Target: %u stars", targetStarCount)) + + xpsd.command = "search" + xpsd.centerRA = this.metadata.ra + xpsd.centerDec = this.metadata.dec + xpsd.radius = this.metadata.resolution * radiusPx + xpsd.magnitudeLow = -1.5 + xpsd.sourceLimit = 0 // do not retrieve objects, just count them. + xpsd.exclusionFlags = GaiaFlag_NoPM + xpsd.inclusionFlags = this.config.restrictToHQStars ? GaiaFlag_GoodAstrometry : 0 + xpsd.verbosity = 0 // work quietly + xpsd.generateTextOutput = false + + const MAX_AUTOMAG_ITER = 100 // prevent a hypothetical case where the loop might stall + for (let m0 = 7, m1 = xpsd.databaseMagnitudeHigh, i = 0; i < MAX_AUTOMAG_ITER; ++i) { + xpsd.magnitudeHigh = m + xpsd.executeGlobal() + console.writeln(format("m = %.2f, %u stars", m, xpsd.excessCount)) + if (xpsd.excessCount < targetStarCount) { + if (m1 - m < 0.05) + break + m0 = m + m += (m1 - m) / 2 + } + else if (xpsd.excessCount > 1.05 * targetStarCount) { + if (m - m0 < 0.05) + break + m1 = m + m -= (m - m0) / 2 + } + else + break + } + } + } + else { + /* + * We have a local XPSD server, but either it is not well + * configured, or there are no database files available. + */ + xpsd = null + } + } + + if (this.config.autoMagnitude) { + this.limitMagnitude = m + console.noteln("
* Using an automatically calculated limit magnitude of " + format("%.2f", m) + ".") + } + else + this.limitMagnitude = this.config.magnitude + + if (this.config.catalogMode == CatalogMode.prototype.Automatic) { + /* + * - For magnitude limits below 8, use the Bright Stars catalog. + * - Otherwise: + * - Use a local XPSD server when available. + * - Otherwise: + * - Use the online Gaia DR2 catalog if FOV <= 3 deg. + * - Use the online TYCHO-2 catalog if FOV > 3 deg. + */ + if (this.limitMagnitude < 8) + this.catalogName = "Bright Stars" + else if (fov > 3 && !xpsd) + this.catalogName = "TYCHO-2" + else if (xpsd) { + switch (xpsd.outputDataRelease) { + default: + case Gaia.prototype.DataRelease_3: + this.catalogName = "GaiaDR3_XPSD" + break + case Gaia.prototype.DataRelease_E3: + this.catalogName = "GaiaEDR3_XPSD" + break + case Gaia.prototype.DataRelease_2: + this.catalogName = "GaiaDR2_XPSD" + break + } + } + else + this.catalogName = "GaiaDR2" + + console.noteln("* Using the automatically selected " + this.catalogName + " catalog.") + } + else + this.catalogName = this.config.catalog + } + else { + this.limitMagnitude = this.config.magnitude + this.catalogName = this.config.catalog + } + + console.writeln("Seed parameters for plate solving:") + console.writeln(" Center coordinates: RA = ", + DMSangle.FromAngle(this.metadata.ra / 15).ToString(true), ", Dec = ", + DMSangle.FromAngle(this.metadata.dec).ToString()) + console.writeln(format(" Resolution: %.3f as/px", this.metadata.resolution * 3600)) + console.writeln() + + let stars = null + + this.distortModel = null + + /* + * Initial Alignment. + */ + try { + let result = this.DoIterationSA(targetWindow, this.metadata) + if (!result) + throw "" + this.metadata = result + + stars = this.DetectStars(workingWindow, this.metadata) + + console.writeln("
*****") + console.writeln("Initial alignment") + console.writeln("Image center ... RA: ", DMSangle.FromAngle(this.metadata.ra / 15).ToString(true), + " Dec: ", DMSangle.FromAngle(this.metadata.dec).ToString()) + console.writeln(format("Resolution ..... %.2f as/px", this.metadata.resolution * 3600)) + console.writeln(format("RMS error ...... %.3f px (%d stars)", stars.rms, stars.numValid)) + console.writeln(format("Score .......... %.3f", stars.score)) + console.writeln("*****") + } + catch (ex) { + if (!console.isAborted) + if (ex.length === undefined || ex.length > 0) + console.criticalln("
*** Error: " + ex.toString()) + this.error = true + return false + } + + /* + * Optimize the solution. + */ + this.metadata = this.OptimizeSolution(workingWindow, this.metadata, stars) + + /* + * Update metadata and regenerate the astrometric solution. + */ + targetWindow.mainView.beginProcess(UndoFlag_Keywords | UndoFlag_AstrometricSolution) + this.metadata.SaveKeywords(targetWindow, false/*beginProcess*/) + this.metadata.SaveProperties(targetWindow, "ImageSolver " + SOLVERVERSION, this.catalog.name) + targetWindow.regenerateAstrometricSolution() + targetWindow.mainView.endProcess() + + return true + } + catch (ex) { + this.error = true + throw ex + } + finally { + jsAbortable = abortableBackup + + if (auxWindow) + auxWindow.forceClose() + } + } +} + +function decodeParams(hex) { + const buffer = new Uint8Array(hex.length / 4) + + for (let i = 0; i < hex.length; i += 4) { + buffer[i / 4] = parseInt(hex.substr(i, 4), 16) + } + + return JSON.parse(String.fromCharCode.apply(null, buffer)) +} + +function imageSolver() { + const data = { + success: true, + errorMessage: null, + outputImage: null, + } + + try { + const input = decodeParams(jsArguments[0]) + + const targetPath = input.targetPath + const statusPath = input.statusPath + const centerRA = input.centerRA + const centerDEC = input.centerDEC + const pixelSize = input.pixelSize + const resolution = input.resolution // arcsec/px + const focalLength = input.focalLength + + console.writeln("image solver started") + console.writeln("targetPath=" + targetPath) + console.writeln("statusPath=" + statusPath) + console.writeln("centerRA=" + centerRA) + console.writeln("centerDEC=" + centerDEC) + console.writeln("pixelSize=" + pixelSize) + console.writeln("resolution=" + resolution) + console.writeln("focalLength=" + focalLength) + + const P = new ImageSolver + + const targetWindow = ImageWindow.open(targetPath)[0] + + P.Init(targetWindow) + + P.metadata.topocentric = false + P.metadata.referenceSystem = "ICRS" + P.metadata.ra = centerRA + P.metadata.dec = centerDEC + + if (focalLength > 0) { + P.metadata.useFocal = false + P.metadata.focal = focalLength + P.metadata.resolution = pixelSize / focalLength * 0.18 / Math.PI + } else { + P.metadata.useFocal = false + P.metadata.resolution = resolution / 3600 // deg? + } + + P.metadata.xpixsz = pixelSize + + P.metadata.width = targetWindow.mainView.image.width + P.metadata.height = targetWindow.mainView.image.height + + if (P.SolveImage(targetWindow, false)) { + console.writeln(targetWindow.astrometricSolutionSummary()) + data.resolution = P.metadata.resolution * Math.PI / 180.0 + data.pixelSize = P.metadata.xpixsz + data.focalLength = P.metadata.focal + data.rightAscension = P.metadata.ra * Math.PI / 180.0 + data.declination = P.metadata.dec * Math.PI / 180.0 + data.imageWidth = P.metadata.width + data.imageHeight = P.metadata.height + data.width = data.resolution * data.imageWidth + data.height = data.resolution * data.imageHeight + data.rotation = 0 + data.astrometricSolutionSummary = targetWindow.astrometricSolutionSummary() + } else { + data.success = false + data.errorMessage = "the image could not be plate solved" + console.criticalln(data.errorMessage) + } + + data.success = !this.finished + + console.writeln("image solver finished") + } catch (e) { + data.success = false + data.errorMessage = e.message + console.criticalln(data.errorMessage) + } finally { + if (targetWindow) + targetWindow.forceClose() + + File.writeTextFile(statusPath, "@" + JSON.stringify(data) + "#") + gc(true) + } +} + +imageSolver() diff --git a/nebulosa-pixinsight/src/main/resources/pixinsight/LRGBCombination.js b/nebulosa-pixinsight/src/main/resources/pixinsight/LRGBCombination.js new file mode 100644 index 000000000..e069cf329 --- /dev/null +++ b/nebulosa-pixinsight/src/main/resources/pixinsight/LRGBCombination.js @@ -0,0 +1,81 @@ +function decodeParams(hex) { + const buffer = new Uint8Array(hex.length / 4) + + for (let i = 0; i < hex.length; i += 4) { + buffer[i / 4] = parseInt(hex.substr(i, 4), 16) + } + + return JSON.parse(String.fromCharCode.apply(null, buffer)) +} + +function lrgbCombination() { + const data = { + success: true, + errorMessage: null, + outputImage: null, + } + + try { + const input = decodeParams(jsArguments[0]) + + const outputPath = input.outputPath + const statusPath = input.statusPath + const channelWeights = input.channelWeights + const luminancePath = input.luminancePath + const redPath = input.redPath + const greenPath = input.greenPath + const bluePath = input.bluePath + + console.writeln("LRGB combination started") + console.writeln("outputPath=" + outputPath) + console.writeln("statusPath=" + statusPath) + console.writeln("channelWeights=" + channelWeights) + console.writeln("luminancePath=" + luminancePath) + console.writeln("redPath=" + redPath) + console.writeln("greenPath=" + greenPath) + console.writeln("bluePath=" + bluePath) + + const luminanceWindow = luminancePath ? ImageWindow.open(luminancePath)[0] : undefined + const redWindow = redPath ? ImageWindow.open(redPath)[0] : undefined + const greenWindow = greenPath ? ImageWindow.open(greenPath)[0] : undefined + const blueWindow = bluePath ? ImageWindow.open(bluePath)[0] : undefined + + var P = new LRGBCombination + P.channels = [ // enabled, id, k + [!!redPath, redWindow ? redWindow.mainView.id : "", channelWeights[1]], + [!!greenPath, greenWindow ? greenWindow.mainView.id : "", channelWeights[2]], + [!!bluePath, blueWindow ? blueWindow.mainView.id : "", channelWeights[3]], + [!!luminancePath, luminanceWindow ? luminanceWindow.mainView.id : "", channelWeights[0]] + ] + P.mL = 0.500 + P.mc = 0.500 + P.clipHighlights = true + P.noiseReduction = false + P.layersRemoved = 4 + P.layersProtected = 2 + P.inheritAstrometricSolution = true + + P.executeGlobal() + + const window = ImageWindow.windows[ImageWindow.windows.length - 1] + window.saveAs(outputPath, false, false, false, false) + window.forceClose() + + if (luminanceWindow) luminanceWindow.forceClose() + if (redWindow) redWindow.forceClose() + if (greenWindow) greenWindow.forceClose() + if (blueWindow) blueWindow.forceClose() + + data.outputImage = outputPath + + console.writeln("LRGB combination finished") + } catch (e) { + data.success = false + data.errorMessage = e.message + console.criticalln(data.errorMessage) + } finally { + File.writeTextFile(statusPath, "@" + JSON.stringify(data) + "#") + } +} + +lrgbCombination() diff --git a/nebulosa-pixinsight/src/main/resources/pixinsight/LuminanceCombination.js b/nebulosa-pixinsight/src/main/resources/pixinsight/LuminanceCombination.js new file mode 100644 index 000000000..4dc36858a --- /dev/null +++ b/nebulosa-pixinsight/src/main/resources/pixinsight/LuminanceCombination.js @@ -0,0 +1,71 @@ +function decodeParams(hex) { + const buffer = new Uint8Array(hex.length / 4) + + for (let i = 0; i < hex.length; i += 4) { + buffer[i / 4] = parseInt(hex.substr(i, 4), 16) + } + + return JSON.parse(String.fromCharCode.apply(null, buffer)) +} + +function luminanceCombination() { + const data = { + success: true, + errorMessage: null, + outputImage: null, + } + + try { + const input = decodeParams(jsArguments[0]) + + const outputPath = input.outputPath + const statusPath = input.statusPath + const weight = input.weight + const luminancePath = input.luminancePath + const targetPath = input.targetPath + + console.writeln("Luminance combination started") + console.writeln("outputPath=" + outputPath) + console.writeln("statusPath=" + statusPath) + console.writeln("weight=" + weight) + console.writeln("luminancePath=" + luminancePath) + console.writeln("targetPath=" + targetPath) + + const luminanceWindow = luminancePath ? ImageWindow.open(luminancePath)[0] : undefined + const targetWindow = targetPath ? ImageWindow.open(targetPath)[0] : undefined + + var P = new LRGBCombination + P.channels = [ // enabled, id, k + [false, "", 1.0], + [false, "", 1.0], + [false, "", 1.0], + [true, luminanceWindow.mainView.id, weight] + ] + P.mL = 0.500 + P.mc = 0.500 + P.clipHighlights = true + P.noiseReduction = false + P.layersRemoved = 4 + P.layersProtected = 2 + P.inheritAstrometricSolution = true + + P.executeOn(targetWindow.mainView) + + targetWindow.saveAs(outputPath, false, false, false, false) + window.forceClose() + + luminanceWindow.forceClose() + + data.outputImage = outputPath + + console.writeln("Luminance combination finished") + } catch (e) { + data.success = false + data.errorMessage = e.message + console.criticalln(data.errorMessage) + } finally { + File.writeTextFile(statusPath, "@" + JSON.stringify(data) + "#") + } +} + +luminanceCombination() diff --git a/nebulosa-pixinsight/src/main/resources/pixinsight/PixelMath.js b/nebulosa-pixinsight/src/main/resources/pixinsight/PixelMath.js index 7c335ff77..048aa1813 100644 --- a/nebulosa-pixinsight/src/main/resources/pixinsight/PixelMath.js +++ b/nebulosa-pixinsight/src/main/resources/pixinsight/PixelMath.js @@ -12,7 +12,7 @@ function pixelMath() { const data = { success: true, errorMessage: null, - stackedImage: null, + outputImage: null, } try { @@ -47,6 +47,7 @@ function pixelMath() { } } + console.writeln("pixel math started") console.writeln("expressionRK=" + expressionRK) console.writeln("expressionG=" + expressionG) console.writeln("expressionB=" + expressionB) @@ -87,13 +88,13 @@ function pixelMath() { windows[i].forceClose() } - data.stackedImage = outputPath + data.outputImage = outputPath - console.writeln("stacking finished") + console.writeln("pixel math finished") } catch (e) { data.success = false data.errorMessage = e.message - console.writeln(data.errorMessage) + console.criticalln(data.errorMessage) } finally { File.writeTextFile(statusPath, "@" + JSON.stringify(data) + "#") } diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt new file mode 100644 index 000000000..75fc0736a --- /dev/null +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt @@ -0,0 +1,40 @@ +import PixInsightScriptTest.Companion.RUNNER +import PixInsightScriptTest.Companion.openAsImage +import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.shouldBe +import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction +import nebulosa.pixinsight.stacker.PixInsightAutoStacker +import nebulosa.test.* +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +@NonGitHubOnly +class PixInsightAutoStackerTest : AbstractTest() { + + @Test + fun stack() { + val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) + val workingDirectory = tempDirectory("pi-") + val outputPath = tempPath("pi-", ".fits") + + val stacker = PixInsightAutoStacker(RUNNER, workingDirectory) + stacker.stack(files, outputPath).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-auto-stacked").second shouldBe "a107143dff3d43c4b56c872da869f89b" + } + + @Test + @Disabled + fun calibratedStack() { + val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) + val workingDirectory = tempDirectory("pi-") + val outputPath = tempPath("pi-", ".fits") + + val stacker = PixInsightAutoStacker(RUNNER, workingDirectory, PI_DARK, PI_FLAT, PI_BIAS) + stacker.stack(files, outputPath).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-calibrated-auto-stacked").second shouldBe "" + } +} diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt new file mode 100644 index 000000000..7c968728f --- /dev/null +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt @@ -0,0 +1,45 @@ +import PixInsightScriptTest.Companion.RUNNER +import PixInsightScriptTest.Companion.openAsImage +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction +import nebulosa.pixinsight.livestacker.PixInsightLiveStacker +import nebulosa.test.AbstractTest +import nebulosa.test.NonGitHubOnly +import nebulosa.test.PI_01_LIGHT +import nebulosa.test.PI_02_LIGHT +import nebulosa.test.PI_03_LIGHT +import nebulosa.test.PI_04_LIGHT +import nebulosa.test.PI_05_LIGHT +import nebulosa.test.PI_06_LIGHT +import nebulosa.test.PI_07_LIGHT +import nebulosa.test.PI_08_LIGHT +import nebulosa.test.save +import org.junit.jupiter.api.Test +import java.nio.file.Path + +@NonGitHubOnly +class PixInsightLiveStackerTest : AbstractTest() { + + @Test + fun stack() { + val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) + val workingDirectory = tempDirectory("pi-") + var outputPath: Path? = null + + val stacker = PixInsightLiveStacker(RUNNER, workingDirectory) + + try { + stacker.start() + + for (file in files) { + outputPath = stacker.add(file) + } + } finally { + stacker.stop() + } + + outputPath.shouldNotBeNull().openAsImage().transform(AutoScreenTransformFunction) + .save("pi-live-stacked").second shouldBe "a107143dff3d43c4b56c872da869f89b" + } +} diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightPlateSolverTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightPlateSolverTest.kt new file mode 100644 index 000000000..52e4a22b6 --- /dev/null +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightPlateSolverTest.kt @@ -0,0 +1,35 @@ +import PixInsightScriptTest.Companion.RUNNER +import io.kotest.matchers.doubles.plusOrMinus +import io.kotest.matchers.ints.shouldBeExactly +import io.kotest.matchers.shouldBe +import nebulosa.math.* +import nebulosa.pixinsight.platesolver.PixInsightPlateSolver +import nebulosa.test.AbstractTest +import nebulosa.test.NonGitHubOnly +import nebulosa.test.download +import org.junit.jupiter.api.Test +import kotlin.math.roundToInt + +@NonGitHubOnly +class PixInsightPlateSolverTest : AbstractTest() { + + @Test + fun solver() { + val pixelSize = 6.58 + val resolution = 6.78.arcsec + val solver = PixInsightPlateSolver(RUNNER, pixelSize, resolution = resolution) + val path = download("https://nova.astrometry.net/image/14603", "jpg") + val centerRA = "06 40 51".hours + val centerDEC = "09 49 53".deg + + val solution = solver.solve(path, null, centerRA, centerDEC) + + solution.scale.toArcsec shouldBe (6.774 plusOrMinus 1e-3) + solution.rightAscension.formatHMS() shouldBe "06h40m51.8s" + solution.declination.formatSignedDMS() shouldBe "+009°49'53.6\"" + solution.width.toArcmin shouldBe (90.321 plusOrMinus 1e-3) + solution.height.toArcmin shouldBe (59.386 plusOrMinus 1e-3) + solution.widthInPixels.roundToInt() shouldBeExactly 800 + solution.heightInPixels.roundToInt() shouldBeExactly 526 + } +} diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt index 2bc11d4d2..3098a6b02 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt @@ -1,65 +1,198 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.engine.spec.tempdir -import io.kotest.engine.spec.tempfile import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.plusOrMinus +import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.nulls.shouldNotBeNull -import io.kotest.matchers.paths.shouldExist import io.kotest.matchers.shouldBe +import nebulosa.fits.fits +import nebulosa.fits.isFits +import nebulosa.image.Image.Companion.asImage +import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction +import nebulosa.math.* import nebulosa.pixinsight.script.* -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition -import java.nio.file.Files +import nebulosa.pixinsight.script.PixInsightScript.Companion.UNSPECIFIED_SLOT +import nebulosa.test.* +import nebulosa.xisf.isXisf +import nebulosa.xisf.xisf +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test import java.nio.file.Path +import kotlin.math.roundToInt -@EnabledIf(NonGitHubOnlyCondition::class) -class PixInsightScriptTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class PixInsightScriptTest : AbstractTest() { - init { - val runner = PixInsightScriptRunner(Path.of("PixInsight")) - val workingDirectory = tempdir("pi-").toPath() + @Test + @Disabled + fun startup() { + PixInsightStartup(PixInsightScript.DEFAULT_SLOT) + .use { it.runSync(RUNNER).success.shouldBeTrue() } + } - "startup" { - PixInsightStartup(PixInsightScript.DEFAULT_SLOT) - .use { it.runSync(runner).shouldBeTrue() } - } - "is running" { - PixInsightIsRunning(PixInsightScript.DEFAULT_SLOT) - .use { it.runSync(runner).shouldBeTrue() } - } - "calibrate" { - PixInsightCalibrate(PixInsightScript.UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_DARK, PI_FLAT, PI_BIAS) - .use { it.runSync(runner).also(::println).outputImage.shouldNotBeNull().shouldExist() } - } - "align" { - PixInsightAlign(PixInsightScript.UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_02_LIGHT) - .use { it.runSync(runner).also(::println).outputImage.shouldNotBeNull().shouldExist() } - } - "detect stars" { - PixInsightDetectStars(PixInsightScript.UNSPECIFIED_SLOT, PI_FOCUS_0) - .use { it.runSync(runner).also(::println).stars } - .map { it.hfd } - .average() shouldBe (8.43 plusOrMinus 1e-2) - - PixInsightDetectStars(PixInsightScript.UNSPECIFIED_SLOT, PI_FOCUS_30000) - .use { it.runSync(runner).also(::println).stars } - .map { it.hfd } - .average() shouldBe (1.85 plusOrMinus 1e-2) - - PixInsightDetectStars(PixInsightScript.UNSPECIFIED_SLOT, PI_FOCUS_100000) - .use { it.runSync(runner).also(::println).stars } - .map { it.hfd } - .average() shouldBe (18.35 plusOrMinus 1e-2) - } - "pixel math" { - val outputPath = Files.createTempFile("pi-stacked-", ".fits") - PixInsightPixelMath(PixInsightScript.UNSPECIFIED_SLOT, listOf(PI_01_LIGHT, PI_02_LIGHT), outputPath, "{{0}} + {{1}}") - .use { it.runSync(runner).also(::println).stackedImage.shouldNotBeNull().shouldExist() } + @Test + @Disabled + fun isRunning() { + PixInsightIsRunning(PixInsightScript.DEFAULT_SLOT) + .use { it.runSync(RUNNER).success.shouldBeTrue() } + } + + @Test + fun calibrate() { + val workingDirectory = tempDirectory("pi-") + PixInsightCalibrate(UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_DARK, PI_FLAT, PI_BIAS) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-calibrate").second shouldBe "731562ee12f45bf7c1095f4773f70e71" + } + + @Test + fun align() { + val workingDirectory = tempDirectory("pi-") + PixInsightAlign(UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_02_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-align").second shouldBe "483ebaf15afa5957fe099f3ee2beff78" + } + + @Test + fun detectStars() { + PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_0) + .use { it.runSync(RUNNER).stars } + .map { it.hfd } + .average() shouldBe (8.43 plusOrMinus 1e-2) + + PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_30000) + .use { it.runSync(RUNNER).stars } + .map { it.hfd } + .average() shouldBe (1.85 plusOrMinus 1e-2) + + PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_100000) + .use { it.runSync(RUNNER).stars } + .map { it.hfd } + .average() shouldBe (18.35 plusOrMinus 1e-2) + } + + @Test + fun pixelMath() { + val outputPath = tempPath("pi-stacked-", ".fits") + PixInsightPixelMath(UNSPECIFIED_SLOT, listOf(PI_01_LIGHT, PI_02_LIGHT), outputPath, "{{0}} + {{1}}") + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-pixelmath").second shouldBe "cafc8138e2ce17614dcfa10edf410b07" + } + + @Test + fun abe() { + val outputPath = tempPath("pi-abe-", ".fits") + PixInsightAutomaticBackgroundExtractor(UNSPECIFIED_SLOT, PI_01_LIGHT, outputPath) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-abe").second shouldBe "bf62207dc17190009ba215da7c011297" + } + + @Test + fun lrgbCombination() { + val outputPath = tempPath("pi-lrgb-", ".fits") + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lrgb").second shouldBe "99db35d78f7b360e7592217f4179b189" + + val weights = doubleArrayOf(1.0, 0.2470588, 0.31764705, 0.709803921) // LRGB #3F51B5 + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, weights) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-weighted-lrgb").second shouldBe "1148ee222fbfb382ad2d708df5b0f79f" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, null, null) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lr").second shouldBe "9100d3ce892f05f4b832b2fb5f35b5a1" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, PI_01_LIGHT, null) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lg").second shouldBe "b4e8d8f7e289db60b41ba2bbe0035344" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, null, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lb").second shouldBe "1760e7cb1d139b63022dd975fe84897d" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, PI_01_LIGHT, PI_01_LIGHT, null) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-rg").second shouldBe "8c59307b5943932aefdf2dedfe1c8178" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, PI_01_LIGHT, null, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-rb").second shouldBe "1bdf9cada6a33f76dceaccdaacf30fef" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, null, PI_01_LIGHT, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-bg").second shouldBe "4a9c81c71fd37546fd300d1037742fa2" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, null) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lrg").second shouldBe "06c32c8679d409302423baa3a07fb241" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, null, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lrb").second shouldBe "f6d026cb63f7a58fc325e422c277ff89" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, PI_01_LIGHT, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lbg").second shouldBe "67f961110fb4b9f0033b3b8dbc8b1638" + } + + @Test + fun fileFormatConversion() { + val xisfPath = tempPath("pi-ffc", ".xisf") + PixInsightFileFormatConversion(UNSPECIFIED_SLOT, PI_01_LIGHT, xisfPath) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().isXisf().shouldBeTrue() } + + val fitsPath = tempPath("pi-ffc", ".fits") + PixInsightFileFormatConversion(UNSPECIFIED_SLOT, xisfPath, fitsPath) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().isFits().shouldBeTrue() } + } + + @Test + fun imageSolver() { + // https://nova.astrometry.net/user_images/10373761 + val path = download("https://nova.astrometry.net/image/14603", "jpg") + val resolution = 6.78 // arcsec/px + val centerRA = "06 40 51".hours + val centerDEC = "09 49 53".deg + + // Estimated to match resolution = (pixelSize / focalDistance) * 206.265 + val focalDistance = 200.0 // mm + val pixelSize = 6.58 + + with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, resolution = resolution) + .use { it.runSync(RUNNER) }) { + success.shouldBeTrue() + this.focalLength shouldBe (200.355 plusOrMinus 1e-5) + this.pixelSize shouldBe (6.58 plusOrMinus 1e-2) + this.resolution.toArcsec shouldBe (6.774 plusOrMinus 1e-3) + rightAscension.formatHMS() shouldBe "06h40m51.8s" + declination.formatSignedDMS() shouldBe "+009°49'53.6\"" + width.toArcmin shouldBe (90.321 plusOrMinus 1e-3) + height.toArcmin shouldBe (59.386 plusOrMinus 1e-3) + imageWidth.roundToInt() shouldBeExactly 800 + imageHeight.roundToInt() shouldBeExactly 526 } - "abe" { - val outputPath = tempfile("pi-", ".fits").toPath() - PixInsightAutomaticBackgroundExtractor(PixInsightScript.UNSPECIFIED_SLOT, PI_01_LIGHT, outputPath) - .use { it.runSync(runner).also(::println).outputImage.shouldNotBeNull() } + + with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, focalLength = focalDistance) + .use { it.runSync(RUNNER) }) { + success.shouldBeTrue() + this.focalLength shouldBe (200.355 plusOrMinus 1e-5) + this.pixelSize shouldBe (6.58 plusOrMinus 1e-2) + this.resolution.toArcsec shouldBe (6.774 plusOrMinus 1e-3) + rightAscension.formatHMS() shouldBe "06h40m51.8s" + declination.formatSignedDMS() shouldBe "+009°49'53.6\"" + width.toArcmin shouldBe (90.321 plusOrMinus 1e-3) + height.toArcmin shouldBe (59.386 plusOrMinus 1e-3) + imageWidth.roundToInt() shouldBeExactly 800 + imageHeight.roundToInt() shouldBeExactly 526 } } + + companion object { + + @JvmStatic val RUNNER = PixInsightScriptRunner(Path.of("PixInsight")) + + @JvmStatic + internal fun Path.openAsImage() = if (isFits()) fits().asImage() + else if (isXisf()) xisf().asImage() + else throw IllegalArgumentException("the path at $this is not an image") + } } diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt new file mode 100644 index 000000000..c04e0a364 --- /dev/null +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt @@ -0,0 +1,67 @@ +import PixInsightScriptTest.Companion.RUNNER +import PixInsightScriptTest.Companion.openAsImage +import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.shouldBe +import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction +import nebulosa.pixinsight.stacker.PixInsightStacker +import nebulosa.test.* +import org.junit.jupiter.api.Test + +@NonGitHubOnly +class PixInsightStackerTest : AbstractTest() { + + @Test + fun align() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.align(PI_01_LIGHT, PI_03_LIGHT, outputPath).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-aligned").second shouldBe "106651a7c1e640852384284ec12e0977" + } + + @Test + fun calibrate() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.calibrate(PI_01_LIGHT, outputPath, PI_DARK).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-calibrated").second shouldBe "8f5a2632c701680b41fcfe170c9cf468" + } + + @Test + fun integrate() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.integrate(1, PI_01_LIGHT, PI_01_LIGHT, outputPath).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-integrated").second shouldBe "bf62207dc17190009ba215da7c011297" + } + + @Test + fun combineLrgb() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.combineLRGB(outputPath, PI_01_LIGHT, PI_01_LIGHT).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-lrgb-combined").second shouldBe "9100d3ce892f05f4b832b2fb5f35b5a1" + } + + @Test + fun combineMonoLuminance() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.combineLuminance(outputPath, PI_01_LIGHT, PI_01_LIGHT, true).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-mono-luminance-combined").second shouldBe "85de365a9895234222acdc6e9feb7009" + } +} diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightStarDetectorTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightStarDetectorTest.kt index 75d38714e..40fb96d0f 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightStarDetectorTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightStarDetectorTest.kt @@ -1,19 +1,16 @@ -import io.kotest.core.annotation.EnabledIf +import PixInsightScriptTest.Companion.RUNNER import io.kotest.matchers.collections.shouldHaveSize -import nebulosa.pixinsight.script.PixInsightScriptRunner import nebulosa.pixinsight.stardetector.PixInsightStarDetector -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition -import java.nio.file.Path +import nebulosa.test.NGC3344_MONO_8_FITS +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class PixInsightStarDetectorTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class PixInsightStarDetectorTest { - init { - "detect stars" { - val runner = PixInsightScriptRunner(Path.of("PixInsight")) - val detectedStars = PixInsightStarDetector(runner, 0).detect(NGC3344_MONO_8_FITS) - detectedStars shouldHaveSize 15 - } + @Test + fun detectStars() { + val detectedStars = PixInsightStarDetector(RUNNER, 0).detect(NGC3344_MONO_8_FITS) + detectedStars shouldHaveSize 15 } } diff --git a/nebulosa-platesolver/src/main/kotlin/nebulosa/platesolver/PlateSolution.kt b/nebulosa-platesolver/src/main/kotlin/nebulosa/platesolver/PlateSolution.kt index 04a6d68df..cdf0f583f 100644 --- a/nebulosa-platesolver/src/main/kotlin/nebulosa/platesolver/PlateSolution.kt +++ b/nebulosa-platesolver/src/main/kotlin/nebulosa/platesolver/PlateSolution.kt @@ -4,6 +4,7 @@ import nebulosa.fits.FitsHeader import nebulosa.fits.FitsKeyword import nebulosa.image.format.HeaderCard import nebulosa.image.format.ReadableHeader +import nebulosa.log.debug import nebulosa.log.loggerFor import nebulosa.math.* import nebulosa.wcs.computeCdMatrix @@ -44,11 +45,11 @@ data class PlateSolution( val width = header.getIntOrNull(FitsKeyword.NAXIS1) ?: header.getInt("IMAGEW", 0) val height = header.getIntOrNull(FitsKeyword.NAXIS2) ?: header.getInt("IMAGEH", 0) - LOG.info( - "solution from {}: ORIE={}, SCALE={}, RA={}, DEC={}", - header, crota2.formatSignedDMS(), cdelt2.toArcsec, - crval1.formatHMS(), crval2.formatSignedDMS(), - ) + LOG.debug { + "solution from %s: ORIE=%s, SCALE=%f, RA=%s, DEC=%s".format( + header, crota2.formatSignedDMS(), cdelt2.toArcsec, crval1.formatHMS(), crval2.formatSignedDMS(), + ) + } return PlateSolution( true, crota2, cdelt2, crval1, crval2, abs(cdelt1 * width), abs(cdelt2 * height), diff --git a/nebulosa-platesolver/src/test/kotlin/PlateSolutionTest.kt b/nebulosa-platesolver/src/test/kotlin/PlateSolutionTest.kt index 9ef2a59f2..f329df2e4 100644 --- a/nebulosa-platesolver/src/test/kotlin/PlateSolutionTest.kt +++ b/nebulosa-platesolver/src/test/kotlin/PlateSolutionTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe @@ -8,12 +7,26 @@ import nebulosa.math.formatSignedDMS import nebulosa.math.toArcsec import nebulosa.math.toDegrees import nebulosa.platesolver.PlateSolution +import org.junit.jupiter.api.Test -class PlateSolutionTest : StringSpec() { +class PlateSolutionTest { + + @Test + fun astrometryNet() { + val header = FitsHeader.from(ASTROMETRY_NET_FITS_HEADER) + val solution = PlateSolution.from(header).shouldNotBeNull() + + solution.rightAscension.formatHMS() shouldBe "03h19m07.7s" + solution.declination.formatSignedDMS() shouldBe "-066°30'12.2\"" + solution.orientation.toDegrees shouldBe (-136.9 plusOrMinus 1e-1) + solution.scale.toArcsec shouldBe (1.37 plusOrMinus 1e-2) + solution.radius.toDegrees shouldBe (0.476 plusOrMinus 1e-3) + } + + companion object { - init { // Don't have CDELT and CROTA - val astrometryNet = "SIMPLE = T / Standard FITS file " + + @JvmStatic private val ASTROMETRY_NET_FITS_HEADER = "SIMPLE = T / Standard FITS file " + "BITPIX = 8 / ASCII or bytes array " + "NAXIS = 0 / Minimal header " + "EXTEND = T / There may be FITS ext " + @@ -36,16 +49,5 @@ class PlateSolutionTest : StringSpec() { "IMAGEW = 2072 / Image width, in pixels. " + "IMAGEH = 1410 / Image height, in pixels. " + "END " - - "astrometry.net" { - val header = FitsHeader.from(astrometryNet) - val solution = PlateSolution.from(header).shouldNotBeNull() - - solution.rightAscension.formatHMS() shouldBe "03h19m07.7s" - solution.declination.formatSignedDMS() shouldBe "-066°30'12.2\"" - solution.orientation.toDegrees shouldBe (-136.9 plusOrMinus 1e-1) - solution.scale.toArcsec shouldBe (1.37 plusOrMinus 1e-2) - solution.radius.toDegrees shouldBe (0.476 plusOrMinus 1e-3) - } } } diff --git a/nebulosa-retrofit/build.gradle.kts b/nebulosa-retrofit/build.gradle.kts index b17fb04ba..d68a28ef0 100644 --- a/nebulosa-retrofit/build.gradle.kts +++ b/nebulosa-retrofit/build.gradle.kts @@ -4,11 +4,11 @@ plugins { } dependencies { + api(project(":nebulosa-json")) api(libs.retrofit) api(libs.retrofit.jackson) api(libs.okhttp) api(libs.okhttp.logging) - api(libs.bundles.jackson) compileOnly(libs.csv) implementation(project(":nebulosa-log")) testImplementation(project(":nebulosa-test")) diff --git a/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RawAsByteArray.kt b/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RawAsByteArray.kt index e80406a3f..a288d02ac 100644 --- a/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RawAsByteArray.kt +++ b/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RawAsByteArray.kt @@ -1,5 +1,4 @@ package nebulosa.retrofit @Target(AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.RUNTIME) annotation class RawAsByteArray diff --git a/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RawAsString.kt b/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RawAsString.kt index 52a40b028..b5fb95376 100644 --- a/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RawAsString.kt +++ b/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RawAsString.kt @@ -1,5 +1,4 @@ package nebulosa.retrofit @Target(AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.RUNTIME) annotation class RawAsString diff --git a/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RetrofitService.kt b/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RetrofitService.kt index 34bd3b82f..4d0565dc3 100644 --- a/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RetrofitService.kt +++ b/nebulosa-retrofit/src/main/kotlin/nebulosa/retrofit/RetrofitService.kt @@ -4,8 +4,9 @@ import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.MapperFeature import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import com.fasterxml.jackson.module.kotlin.jsonMapper +import nebulosa.json.PathModule import okhttp3.ConnectionPool import okhttp3.OkHttpClient import retrofit2.CallAdapter @@ -22,9 +23,11 @@ abstract class RetrofitService( protected val jsonMapper: ObjectMapper by lazy { mapper ?: DEFAULT_MAPPER.copy() } - protected open val converterFactory = emptyList() + protected open val converterFactory: Iterable + get() = emptyList() - protected open val callAdaptorFactory: CallAdapter.Factory? = null + protected open val callAdaptorFactory: CallAdapter.Factory? + get() = null protected open fun withOkHttpClientBuilder(builder: OkHttpClient.Builder) = Unit @@ -61,11 +64,12 @@ abstract class RetrofitService( .callTimeout(60L, TimeUnit.SECONDS) .build() - @JvmStatic private val DEFAULT_MAPPER = JsonMapper.builder() - .addModule(JavaTimeModule()) - .serializationInclusion(JsonInclude.Include.NON_NULL) - .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) - .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) - .build()!! + @JvmStatic private val DEFAULT_MAPPER = jsonMapper { + addModule(PathModule()) + addModule(JavaTimeModule()) + enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) + disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + serializationInclusion(JsonInclude.Include.NON_NULL) + } } } diff --git a/nebulosa-sbd/src/test/kotlin/SmallBodyCloseApprochServiceTest.kt b/nebulosa-sbd/src/test/kotlin/SmallBodyCloseApprochServiceTest.kt index b725a05ac..80d8a634a 100644 --- a/nebulosa-sbd/src/test/kotlin/SmallBodyCloseApprochServiceTest.kt +++ b/nebulosa-sbd/src/test/kotlin/SmallBodyCloseApprochServiceTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainAll import io.kotest.matchers.collections.shouldHaveAtLeastSize import io.kotest.matchers.collections.shouldHaveSize @@ -8,51 +7,55 @@ import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import nebulosa.constants.AU_KM import nebulosa.sbd.SmallBodyDatabaseService +import nebulosa.test.HTTP_CLIENT +import org.junit.jupiter.api.Test import java.time.LocalDate -class SmallBodyCloseApprochServiceTest : StringSpec() { - - init { - val service = SmallBodyDatabaseService() - - "search" { - val data = service.closeApproaches(distance = 10.0, date = LocalDate.of(2024, 3, 13)).execute().body() - - data.shouldNotBeNull() - data.count shouldBeGreaterThanOrEqual 1 - - data.fields shouldHaveSize 14 - data.data shouldHaveAtLeastSize 10 - - data.data[0][0] shouldBe "2024 EC3" - data.data[0][3] shouldBe "2024-Mar-13 07:22" - data.data[0][4].toDouble() shouldBe (2.29 * 384400 / AU_KM plusOrMinus 1e-2) - - data.data.map { it[0] } shouldContainAll listOf( - "2024 EC3", "2024 EL1", "2024 EH3", - "2024 EK3", "2024 EQ", "2024 EK", "2024 EX2", "2024 ED3", - "2024 EN", "2020 FD" - ) - - // https://minorplanetcenter.net/ - // ############# Close Approaches ############ - // 2024 EC3 Mar 13 07:22 2.29 4-11 - // 2024 EL1 Mar 13 13:10 8.48 9-30 - // 2024 EH3 Mar 14 16:16 6.5 16-50 - // 2024 EK3 Mar 15 03:10 1.62 3-11 - // 2020 FU Mar 15 21:29 14.84 10-31 - // 2024 CJ8 Mar 16 01:25 17.21 43-140 - // 2024 EQ Mar 16 06:31 6.92 12-37 - // 2022 EK1 Mar 16 09:08 45.25 22-69 - // 2024 EK Mar 16 15:29 6.96 8-26 - // 2024 EX2 Mar 16 16:03 5.82 10-32 - // 2004 FK2 Mar 17 23:18 24.01 24-75 - // 2024 EW Mar 18 07:55 32.45 14-43 - // 2024 ED3 Mar 18 08:37 3.5 17-54 - // 2024 CH6 Mar 18 08:42 47.08 61-190 - // 2024 EN Mar 18 18:34 3.87 23-74 - // 2020 FD Mar 18 19:11 4.52 5-17 - // 2011 BY24 Mar 18 22:50 26.14 56-180 - } +class SmallBodyCloseApprochServiceTest { + + @Test + fun search() { + val data = SERVICE.closeApproaches(distance = 10.0, date = LocalDate.of(2024, 3, 13)).execute().body() + + data.shouldNotBeNull() + data.count shouldBeGreaterThanOrEqual 1 + + data.fields shouldHaveSize 14 + data.data shouldHaveAtLeastSize 10 + + data.data[0][0] shouldBe "2024 EC3" + data.data[0][3] shouldBe "2024-Mar-13 07:22" + data.data[0][4].toDouble() shouldBe (2.29 * 384400 / AU_KM plusOrMinus 1e-2) + + data.data.map { it[0] } shouldContainAll listOf( + "2024 EC3", "2024 EL1", "2024 EH3", + "2024 EK3", "2024 EQ", "2024 EK", "2024 EX2", "2024 ED3", + "2024 EN", "2020 FD" + ) + + // https://minorplanetcenter.net/ + // ############# Close Approaches ############ + // 2024 EC3 Mar 13 07:22 2.29 4-11 + // 2024 EL1 Mar 13 13:10 8.48 9-30 + // 2024 EH3 Mar 14 16:16 6.5 16-50 + // 2024 EK3 Mar 15 03:10 1.62 3-11 + // 2020 FU Mar 15 21:29 14.84 10-31 + // 2024 CJ8 Mar 16 01:25 17.21 43-140 + // 2024 EQ Mar 16 06:31 6.92 12-37 + // 2022 EK1 Mar 16 09:08 45.25 22-69 + // 2024 EK Mar 16 15:29 6.96 8-26 + // 2024 EX2 Mar 16 16:03 5.82 10-32 + // 2004 FK2 Mar 17 23:18 24.01 24-75 + // 2024 EW Mar 18 07:55 32.45 14-43 + // 2024 ED3 Mar 18 08:37 3.5 17-54 + // 2024 CH6 Mar 18 08:42 47.08 61-190 + // 2024 EN Mar 18 18:34 3.87 23-74 + // 2020 FD Mar 18 19:11 4.52 5-17 + // 2011 BY24 Mar 18 22:50 26.14 56-180 + } + + companion object { + + @JvmStatic private val SERVICE = SmallBodyDatabaseService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-sbd/src/test/kotlin/SmallBodyDatabaseLookupServiceTest.kt b/nebulosa-sbd/src/test/kotlin/SmallBodyDatabaseLookupServiceTest.kt index a01795101..0cface85f 100644 --- a/nebulosa-sbd/src/test/kotlin/SmallBodyDatabaseLookupServiceTest.kt +++ b/nebulosa-sbd/src/test/kotlin/SmallBodyDatabaseLookupServiceTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual @@ -7,41 +5,49 @@ import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import nebulosa.sbd.SmallBodyDatabaseService -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.HTTP_CLIENT +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class SmallBodyDatabaseLookupServiceTest : StringSpec() { +class SmallBodyDatabaseLookupServiceTest { - init { - val service = SmallBodyDatabaseService() + @Test + fun searchMatchesSingleRecord() { + val body = SERVICE.search("C/2017 K2").execute().body().shouldNotBeNull() + body.list.shouldBeNull() + body.orbit!!.equinox shouldBe "J2000" + body.body!!.fullname shouldBe "C/2017 K2 (PANSTARRS)" + body.body!!.spkId shouldBeExactly 1003517 + } + + @Test + fun searchNeo() { + val body = SERVICE.search("2023 GA2").execute().body().shouldNotBeNull() + body.list.shouldBeNull() + body.orbit!!.equinox shouldBe "J2000" + body.body!!.fullname shouldBe "(2023 GA2)" + body.body!!.spkId shouldBeExactly 54354395 + } + + @Test + fun searchMatchesMultipleRecords() { + val body = SERVICE.search("PANSTARRS").execute().body().shouldNotBeNull() + val list = body.list.shouldNotBeNull() + list.size shouldBeGreaterThanOrEqual 257 + list.any { it.name == "253P/PANSTARRS" }.shouldBeTrue() + } + + @Test + fun searchFailed() { + val body = SERVICE.search("ggdgdfgdfgdg").execute().body().shouldNotBeNull() + body.list.shouldBeNull() + body.orbit.shouldBeNull() + body.body.shouldBeNull() + body.physical.shouldBeNull() + body.message shouldBe "specified object was not found" + } + + companion object { - "search matches single record" { - val body = service.search("C/2017 K2").execute().body().shouldNotBeNull() - body.list.shouldBeNull() - body.orbit!!.equinox shouldBe "J2000" - body.body!!.fullname shouldBe "C/2017 K2 (PANSTARRS)" - body.body!!.spkId shouldBeExactly 1003517 - } - "search NEO" { - val body = service.search("2023 GA2").execute().body().shouldNotBeNull() - body.list.shouldBeNull() - body.orbit!!.equinox shouldBe "J2000" - body.body!!.fullname shouldBe "(2023 GA2)" - body.body!!.spkId shouldBeExactly 54354395 - } - "search matches multiple records" { - val body = service.search("PANSTARRS").execute().body().shouldNotBeNull() - val list = body.list.shouldNotBeNull() - list.size shouldBeGreaterThanOrEqual 257 - list.any { it.name == "253P/PANSTARRS" }.shouldBeTrue() - } - "search failed" { - val body = service.search("ggdgdfgdfgdg").execute().body().shouldNotBeNull() - body.list.shouldBeNull() - body.orbit.shouldBeNull() - body.body.shouldBeNull() - body.physical.shouldBeNull() - body.message shouldBe "specified object was not found" - } + @JvmStatic private val SERVICE = SmallBodyDatabaseService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-sbd/src/test/kotlin/SmallBodyIdentificationServiceTest.kt b/nebulosa-sbd/src/test/kotlin/SmallBodyIdentificationServiceTest.kt index 2b65c35db..cc3b9cb36 100644 --- a/nebulosa-sbd/src/test/kotlin/SmallBodyIdentificationServiceTest.kt +++ b/nebulosa-sbd/src/test/kotlin/SmallBodyIdentificationServiceTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.ints.shouldBeExactly @@ -8,36 +7,42 @@ import nebulosa.math.deg import nebulosa.math.hours import nebulosa.math.km import nebulosa.sbd.SmallBodyDatabaseService +import nebulosa.test.HTTP_CLIENT +import org.junit.jupiter.api.Test import java.time.LocalDateTime -class SmallBodyIdentificationServiceTest : StringSpec() { +class SmallBodyIdentificationServiceTest { - init { - val service = SmallBodyDatabaseService() + @Test + fun searchAroundCeres() { + val data = SERVICE.identify( + LocalDateTime.of(2023, 8, 21, 0, 0, 0, 0), + // Observatorio do Pico dos Dias, Itajuba (observatory) [code: 874] + (-22.5354318).deg, (-45.5827).deg, 1.81754.km, + "13 21 16.50".hours, "-01 57 06.5".deg, 1.0.deg, + ).execute().body() - "search around Ceres" { - val data = service.identify( - LocalDateTime.of(2023, 8, 21, 0, 0, 0, 0), - // Observatorio do Pico dos Dias, Itajuba (observatory) [code: 874] - (-22.5354318).deg, (-45.5827).deg, 1.81754.km, - "13 21 16.50".hours, "-01 57 06.5".deg, 1.0.deg, - ).execute().body() + data.shouldNotBeNull() + data.count shouldBeGreaterThanOrEqual 1 + data.data.any { "Ceres" in it[0] }.shouldBeTrue() + } + + @Test + fun noMatchingRecords() { + val data = SERVICE.identify( + LocalDateTime.of(2023, 1, 15, 1, 38, 15, 0), + // Observatorio do Pico dos Dias, Itajuba (observatory) [code: 874] + (-22.5354318).deg, (-45.5827).deg, 1.81754.km, + "10 44 02".hours, "-59 36 04".deg, 1.0.deg, + ).execute().body() + + data.shouldNotBeNull() + data.count shouldBeExactly 0 + data.data.shouldBeEmpty() + } - data.shouldNotBeNull() - data.count shouldBeGreaterThanOrEqual 1 - data.data.any { "Ceres" in it[0] }.shouldBeTrue() - } - "no matching records" { - val data = service.identify( - LocalDateTime.of(2023, 1, 15, 1, 38, 15, 0), - // Observatorio do Pico dos Dias, Itajuba (observatory) [code: 874] - (-22.5354318).deg, (-45.5827).deg, 1.81754.km, - "10 44 02".hours, "-59 36 04".deg, 1.0.deg, - ).execute().body() + companion object { - data.shouldNotBeNull() - data.count shouldBeExactly 0 - data.data.shouldBeEmpty() - } + @JvmStatic private val SERVICE = SmallBodyDatabaseService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-simbad/src/test/kotlin/SimbadServiceTest.kt b/nebulosa-simbad/src/test/kotlin/SimbadServiceTest.kt index 23bde8e13..e913824cf 100644 --- a/nebulosa-simbad/src/test/kotlin/SimbadServiceTest.kt +++ b/nebulosa-simbad/src/test/kotlin/SimbadServiceTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveAtLeastSize import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe @@ -9,120 +7,120 @@ import nebulosa.math.arcmin import nebulosa.math.deg import nebulosa.math.hours import nebulosa.simbad.SimbadService -import nebulosa.test.NonGitHubOnlyCondition -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import java.util.concurrent.TimeUnit - -@EnabledIf(NonGitHubOnlyCondition::class) -class SimbadServiceTest : StringSpec() { - - init { - val httpClient = OkHttpClient.Builder() - .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) - .callTimeout(1, TimeUnit.MINUTES) - .connectTimeout(1, TimeUnit.MINUTES) - .readTimeout(1, TimeUnit.MINUTES) - .build() - - val service = SimbadService(httpClient = httpClient) - val basicTable = From("basic").alias("b") - val identTable = From("ident").alias("i") - val otypesTable = From("otypes").alias("o") - val fluxTable = From("allfluxes").alias("f") - - val oidColumn = basicTable.column("oid") - val mainIdColumn = basicTable.column("main_id") - val otypeColumn = basicTable.column("otype") - val raColumn = basicTable.column("ra") - val decColumn = basicTable.column("dec") - val pmRAColumn = basicTable.column("pmra") - val pmDECColumn = basicTable.column("pmdec") - val plxColumn = basicTable.column("plx_value") - val radVelColumn = basicTable.column("rvz_radvel") - val redshiftColumn = basicTable.column("rvz_redshift") - val nameColumn = identTable.column("id") - val magColumn = fluxTable.column("V") - - "identifier query" { - val query = QueryBuilder() - query.addAll(listOf(oidColumn, otypeColumn, raColumn, decColumn, pmRAColumn, pmDECColumn)) - query.addAll(listOf(plxColumn, radVelColumn, redshiftColumn, mainIdColumn)) - val join = InnerJoin(basicTable, identTable, arrayOf(oidColumn equal Column("i.oidref"))) - query.add(join) - query.add(nameColumn equal "ngc5128") - - val call = service.query(query.build()) - val rows = call.execute().body().shouldNotBeNull() - - with(rows[0]) { - getField("oid") shouldBe "3392496" - getField("ra") shouldStartWith "201.3650" - getField("dec") shouldStartWith "-43.0191" - } +import nebulosa.test.HTTP_CLIENT +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test + +@NonGitHubOnly +class SimbadServiceTest { + + @Test + fun identifierQuery() { + val query = QueryBuilder() + query.addAll(listOf(OID_COLUMN, OTYPE_COLUMN, RA_COLUMN, DEC_COLUMN, PM_RA_COLUMN, PM_DEC_COLUMN)) + query.addAll(listOf(PLX_COLUMN, RAD_VAL_COLUMN, REDSHIFT_COLUMN, MAIN_ID_COLUMN)) + val join = InnerJoin(BASIC_TABLE, IDENT_TABLE, arrayOf(OID_COLUMN equal Column("i.oidref"))) + query.add(join) + query.add(NAME_COLUMN equal "ngc5128") + + val call = SERVICE.query(query.build()) + val rows = call.execute().body().shouldNotBeNull() + + with(rows[0]) { + getField("oid") shouldBe "3392496" + getField("ra") shouldStartWith "201.3650" + getField("dec") shouldStartWith "-43.0191" } - "coordinate query" { - val query = QueryBuilder() - query.addAll(listOf(oidColumn, otypeColumn, raColumn, decColumn, pmRAColumn, pmDECColumn)) - query.addAll(listOf(plxColumn, radVelColumn, redshiftColumn, mainIdColumn)) - query.add(basicTable) - query.add(SkyPoint(raColumn, decColumn) contains Circle("20 54 05.689".hours, "+37 01 17.38".deg, 2.0.arcmin)) - - val call = service.query(query.build()) - val rows = call.execute().body().shouldNotBeNull() - - rows shouldHaveAtLeastSize 4 - - with(rows.first { it.getField("main_id") == "TYC 2700-2084-1" }) { - getField("oid") shouldBe "6742437" - getField("ra") shouldStartWith "313.5013" - getField("dec") shouldStartWith "37.0192" - } + } + + @Test + fun coordinateQuery() { + val query = QueryBuilder() + query.addAll(listOf(OID_COLUMN, OTYPE_COLUMN, RA_COLUMN, DEC_COLUMN, PM_RA_COLUMN, PM_DEC_COLUMN)) + query.addAll(listOf(PLX_COLUMN, RAD_VAL_COLUMN, REDSHIFT_COLUMN, MAIN_ID_COLUMN)) + query.add(BASIC_TABLE) + query.add(SkyPoint(RA_COLUMN, DEC_COLUMN) contains Circle("20 54 05.689".hours, "+37 01 17.38".deg, 2.0.arcmin)) + + val call = SERVICE.query(query.build()) + val rows = call.execute().body().shouldNotBeNull() + + rows shouldHaveAtLeastSize 4 + + with(rows.first { it.getField("main_id") == "TYC 2700-2084-1" }) { + getField("oid") shouldBe "6742437" + getField("ra") shouldStartWith "313.5013" + getField("dec") shouldStartWith "37.0192" } - "object type query" { - val query = QueryBuilder() - query.add(Limit(100)) - query.addAll(listOf(oidColumn, otypeColumn, raColumn, decColumn, pmRAColumn, pmDECColumn)) - query.addAll(listOf(plxColumn, radVelColumn, redshiftColumn, mainIdColumn)) - val join = InnerJoin(basicTable, otypesTable, arrayOf(oidColumn equal otypesTable.column("oidref"))) - query.add(join) - query.add(otypesTable.column("otype") equal "*") - - val call = service.query(query.build()) - val rows = call.execute().body().shouldNotBeNull() - - rows shouldHaveAtLeastSize 100 - - with(rows.first { it.getField("main_id") == "TYC 2713-2426-1" }) { - getField("oid") shouldBe "86" - getField("ra") shouldStartWith "316.7063" - getField("dec") shouldStartWith "36.7207" - getField("otype") shouldBe "PM*" - } + } + + @Test + fun objectTypeQuery() { + val query = QueryBuilder() + query.add(Limit(100)) + query.addAll(listOf(OID_COLUMN, OTYPE_COLUMN, RA_COLUMN, DEC_COLUMN, PM_RA_COLUMN, PM_DEC_COLUMN)) + query.addAll(listOf(PLX_COLUMN, RAD_VAL_COLUMN, REDSHIFT_COLUMN, MAIN_ID_COLUMN)) + val join = InnerJoin(BASIC_TABLE, OTYPES_TABLE, arrayOf(OID_COLUMN equal OTYPES_TABLE.column("oidref"))) + query.add(join) + query.add(OTYPES_TABLE.column("otype") equal "*") + + val call = SERVICE.query(query.build()) + val rows = call.execute().body().shouldNotBeNull() + + rows shouldHaveAtLeastSize 100 + + with(rows.first { it.getField("main_id") == "TYC 2713-2426-1" }) { + getField("oid") shouldBe "86" + getField("ra") shouldStartWith "316.7063" + getField("dec") shouldStartWith "36.7207" + getField("otype") shouldBe "PM*" } - "constellation query" { - val query = QueryBuilder() - query.add(Limit(100)) - query.addAll(listOf(oidColumn, otypeColumn, raColumn, decColumn, pmRAColumn, pmDECColumn)) - query.addAll(listOf(plxColumn, radVelColumn, redshiftColumn, mainIdColumn, magColumn)) - val join = InnerJoin(basicTable, fluxTable, arrayOf(oidColumn equal fluxTable.column("oidref"))) - query.add(join) - query.add(raColumn.isNotNull) - query.add(SkyPoint(raColumn, decColumn) contains ConstellationBoundary("CRU")) - query.add(SortBy(magColumn)) - - val call = service.query(query.build()) - val rows = call.execute().body().shouldNotBeNull() - - rows shouldHaveAtLeastSize 100 - - with(rows.first { it.getField("main_id") == "Cl Collinder 258" }) { - getField("oid") shouldBe "3297061" - getField("otype") shouldBe "OpC" - getField("ra") shouldStartWith "186.798" - getField("dec") shouldStartWith "-60.767" - getField("V") shouldBe "7.1" - } + } + + @Test + fun constellationQuery() { + val query = QueryBuilder() + query.add(Limit(100)) + query.addAll(listOf(OID_COLUMN, OTYPE_COLUMN, RA_COLUMN, DEC_COLUMN, PM_RA_COLUMN, PM_DEC_COLUMN)) + query.addAll(listOf(PLX_COLUMN, RAD_VAL_COLUMN, REDSHIFT_COLUMN, MAIN_ID_COLUMN, MAG_COLUMN)) + val join = InnerJoin(BASIC_TABLE, FLUX_TABLE, arrayOf(OID_COLUMN equal FLUX_TABLE.column("oidref"))) + query.add(join) + query.add(RA_COLUMN.isNotNull) + query.add(SkyPoint(RA_COLUMN, DEC_COLUMN) contains ConstellationBoundary("CRU")) + query.add(SortBy(MAG_COLUMN)) + + val call = SERVICE.query(query.build()) + val rows = call.execute().body().shouldNotBeNull() + + rows shouldHaveAtLeastSize 100 + + with(rows.first { it.getField("main_id") == "Cl Collinder 258" }) { + getField("oid") shouldBe "3297061" + getField("otype") shouldBe "OpC" + getField("ra") shouldStartWith "186.798" + getField("dec") shouldStartWith "-60.767" + getField("V") shouldBe "7.1" } } + + companion object { + + @JvmStatic private val SERVICE = SimbadService(httpClient = HTTP_CLIENT) + @JvmStatic private val BASIC_TABLE = From("basic").alias("b") + @JvmStatic private val IDENT_TABLE = From("ident").alias("i") + @JvmStatic private val OTYPES_TABLE = From("otypes").alias("o") + @JvmStatic private val FLUX_TABLE = From("allfluxes").alias("f") + + @JvmStatic private val OID_COLUMN = BASIC_TABLE.column("oid") + @JvmStatic private val MAIN_ID_COLUMN = BASIC_TABLE.column("main_id") + @JvmStatic private val OTYPE_COLUMN = BASIC_TABLE.column("otype") + @JvmStatic private val RA_COLUMN = BASIC_TABLE.column("ra") + @JvmStatic private val DEC_COLUMN = BASIC_TABLE.column("dec") + @JvmStatic private val PM_RA_COLUMN = BASIC_TABLE.column("pmra") + @JvmStatic private val PM_DEC_COLUMN = BASIC_TABLE.column("pmdec") + @JvmStatic private val PLX_COLUMN = BASIC_TABLE.column("plx_value") + @JvmStatic private val RAD_VAL_COLUMN = BASIC_TABLE.column("rvz_radvel") + @JvmStatic private val REDSHIFT_COLUMN = BASIC_TABLE.column("rvz_redshift") + @JvmStatic private val NAME_COLUMN = IDENT_TABLE.column("id") + @JvmStatic private val MAG_COLUMN = FLUX_TABLE.column("V") + } } diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/PlateSolve.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/PlateSolve.kt index df72d7bd3..20b39c4b5 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/PlateSolve.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/PlateSolve.kt @@ -104,7 +104,7 @@ data class PlateSolve( } private fun String.matchesOrientation(): Boolean { - val m = ORIENTATION_REGEX.matchEntire(this) ?: return false + val m = ORIENTATION_REGEX.find(this) ?: return false orientation = m.groupValues[1].toDouble().deg parity = if ("flipped" in this) Parity.FLIPPED else Parity.NORMAL return true @@ -120,13 +120,13 @@ data class PlateSolve( } private fun String.matchesResolution(): Boolean { - val m = RESOLUTION_REGEX.matchEntire(this) ?: return false + val m = RESOLUTION_REGEX.find(this) ?: return false resolution = m.groupValues[1].toDouble().arcsec return true } private fun String.matchesFOV(): Boolean { - val m = FOV_REGEX.matchEntire(this) ?: return false + val m = FOV_REGEX.find(this) ?: return false val (_, width, height) = m.groupValues fovWidth = width.parseFovInDHMS() fovHeight = height.parseFovInDHMS() @@ -134,7 +134,7 @@ data class PlateSolve( } private fun String.matchesImageCenter(): Boolean { - val m = IMAGE_CENTER_REGEX.matchEntire(this) ?: return false + val m = IMAGE_CENTER_REGEX.find(this) ?: return false val (_, alpha, delta) = m.groupValues imageCenterRA = alpha.hours imageCenterDEC = delta.deg diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/StartLs.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/StartLs.kt index 716b47754..85afd66af 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/StartLs.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/StartLs.kt @@ -12,16 +12,16 @@ import kotlin.io.path.isRegularFile * Initializes a livestacking session. */ data class StartLs( - @JvmField val dark: Path? = null, - @JvmField val flat: Path? = null, + @JvmField val darkPath: Path? = null, + @JvmField val flatPath: Path? = null, @JvmField val use32Bits: Boolean = false, ) : SirilCommand, CommandLineListener { private val command by lazy { buildString(256) { append("start_ls") - if (dark != null && dark.exists() && dark.isRegularFile()) append(" \"-dark=$dark\"") - if (flat != null && flat.exists() && flat.isRegularFile()) append(" \"-flat=$flat\"") + if (darkPath != null && darkPath.exists() && darkPath.isRegularFile()) append(" \"-dark=$darkPath\"") + if (flatPath != null && flatPath.exists() && flatPath.isRegularFile()) append(" \"-flat=$flatPath\"") if (use32Bits) append(" -32bits") } } diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/livestacker/SirilLiveStacker.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/livestacker/SirilLiveStacker.kt index f8617f95b..a1e421fef 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/livestacker/SirilLiveStacker.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/livestacker/SirilLiveStacker.kt @@ -14,8 +14,8 @@ import kotlin.io.path.name data class SirilLiveStacker( private val executablePath: Path, private val workingDirectory: Path, - private val dark: Path? = null, - private val flat: Path? = null, + private val darkPath: Path? = null, + private val flatPath: Path? = null, private val use32Bits: Boolean = false, ) : LiveStacker, CommandLineListener { @@ -38,7 +38,7 @@ data class SirilLiveStacker( try { check(commandLine.execute(Cd(workingDirectory))) { "failed to run cd command" } - check(commandLine.execute(StartLs(dark, flat, use32Bits))) { "failed to start livestacking" } + check(commandLine.execute(StartLs(darkPath, flatPath, use32Bits))) { "failed to start livestacking" } } catch (e: Throwable) { commandLine.close() throw e diff --git a/nebulosa-siril/src/test/kotlin/SirilTest.kt b/nebulosa-siril/src/test/kotlin/SirilTest.kt index 7da243822..26d489523 100644 --- a/nebulosa-siril/src/test/kotlin/SirilTest.kt +++ b/nebulosa-siril/src/test/kotlin/SirilTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.engine.spec.tempdir import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.plusOrMinus @@ -11,72 +9,78 @@ import nebulosa.platesolver.Parity import nebulosa.siril.livestacker.SirilLiveStacker import nebulosa.siril.platesolver.SirilPlateSolver import nebulosa.siril.stardetector.SirilStarDetector -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.* +import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.io.path.copyTo import kotlin.io.path.listDirectoryEntries -@EnabledIf(NonGitHubOnlyCondition::class) -class SirilTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class SirilTest : AbstractTest() { - init { - val executablePath = Path.of("siril-cli") + @Test + fun liveStacking() { + val workingDirectory = dataDirectory.concat("/siril") - "live stacking" { - val workingDirectory = Path.of("/home/tiagohm/Git/nebulosa/data/siril") + SirilLiveStacker(EXECUTABLE_PATH, workingDirectory).use { + it.start() - SirilLiveStacker(executablePath, workingDirectory).use { - it.start() + val inputDir = tempDirectory("ls-") - val fitsDir = tempdir().toPath() + PI_01_LIGHT.copyTo(inputDir.concat("01.fits")) + PI_02_LIGHT.copyTo(inputDir.concat("02.fits")) + PI_03_LIGHT.copyTo(inputDir.concat("03.fits")) + PI_04_LIGHT.copyTo(inputDir.concat("04.fits")) - PI_01_LIGHT.copyTo(Path.of("$fitsDir", "01.fits")) - PI_02_LIGHT.copyTo(Path.of("$fitsDir", "02.fits")) - PI_03_LIGHT.copyTo(Path.of("$fitsDir", "03.fits")) - PI_04_LIGHT.copyTo(Path.of("$fitsDir", "04.fits")) + for (fits in inputDir.listDirectoryEntries().shouldHaveSize(4).sorted()) { + it.add(fits).shouldNotBeNull() + } - for (fits in fitsDir.listDirectoryEntries().shouldHaveSize(4).sorted()) { - it.add(fits).shouldNotBeNull() - } + workingDirectory.listDirectoryEntries().shouldHaveSize(5) + } - workingDirectory.listDirectoryEntries().shouldHaveSize(5) - } + workingDirectory.listDirectoryEntries().shouldHaveSize(1) + } + + @Test + fun plateSolver() { + val solution = SOLVER.solve(PI_01_LIGHT, null) + + solution.solved.shouldBeTrue() + solution.orientation.toDegrees shouldBe (-90.02 plusOrMinus 1e-2) + solution.rightAscension.formatHMS() shouldBe "00h06m46.0s" + solution.declination.formatSignedDMS() shouldBe "+089°51'42.0\"" + solution.scale.toArcsec shouldBe (3.575 plusOrMinus 1e-3) + solution.width.formatDMS() shouldBe "001°16'16.3\"" + solution.height.formatDMS() shouldBe "001°01'01.1\"" + solution.parity shouldBe Parity.FLIPPED + solution.widthInPixels shouldBeExactly 1280.0 + solution.heightInPixels shouldBeExactly 1024.0 + } + + @Test + fun starDetector() { + val detector = SirilStarDetector(EXECUTABLE_PATH) - workingDirectory.listDirectoryEntries().shouldHaveSize(1) + with(detector.detect(PI_FOCUS_0)) { + this shouldHaveSize 307 + map { it.hfd }.average() shouldBe (7.9 plusOrMinus 1e-1) } - "plate solver" { - val solver = SirilPlateSolver(executablePath) - val solution = solver.solve(PI_01_LIGHT, null) - - solution.solved.shouldBeTrue() - solution.orientation.toDegrees shouldBe (-90.02 plusOrMinus 1e-2) - solution.rightAscension.formatHMS() shouldBe "00h06m46.0s" - solution.declination.formatSignedDMS() shouldBe "+089°51'42.0\"" - solution.scale.toArcsec shouldBe (3.575 plusOrMinus 1e-3) - solution.width.formatDMS() shouldBe "001°16'16.3\"" - solution.height.formatDMS() shouldBe "001°01'01.1\"" - solution.parity shouldBe Parity.FLIPPED - solution.widthInPixels shouldBeExactly 1280.0 - solution.heightInPixels shouldBeExactly 1024.0 + + with(detector.detect(PI_FOCUS_30000)) { + this shouldHaveSize 258 + map { it.hfd }.average() shouldBe (1.1 plusOrMinus 1e-1) } - "star detector" { - val detector = SirilStarDetector(executablePath) - with(detector.detect(PI_FOCUS_0)) { - this shouldHaveSize 307 - map { it.hfd }.average() shouldBe (7.9 plusOrMinus 1e-1) - } + with(detector.detect(PI_FOCUS_100000)) { + this shouldHaveSize 82 + map { it.hfd }.average() shouldBe (22.4 plusOrMinus 1e-1) + } + } - with(detector.detect(PI_FOCUS_30000)) { - this shouldHaveSize 258 - map { it.hfd }.average() shouldBe (1.1 plusOrMinus 1e-1) - } + companion object { - with(detector.detect(PI_FOCUS_100000)) { - this shouldHaveSize 82 - map { it.hfd }.average() shouldBe (22.4 plusOrMinus 1e-1) - } - } + @JvmStatic private val EXECUTABLE_PATH = Path.of("siril-cli") + @JvmStatic private val SOLVER = SirilPlateSolver(EXECUTABLE_PATH) } } diff --git a/nebulosa-skycatalog-hyg/src/test/kotlin/HygDatabaseTest.kt b/nebulosa-skycatalog-hyg/src/test/kotlin/HygDatabaseTest.kt index a0e405c83..18b1c0e13 100644 --- a/nebulosa-skycatalog-hyg/src/test/kotlin/HygDatabaseTest.kt +++ b/nebulosa-skycatalog-hyg/src/test/kotlin/HygDatabaseTest.kt @@ -1,20 +1,21 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe -import nebulosa.io.resource import nebulosa.skycatalog.hyg.HygDatabase -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.download +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import kotlin.io.path.inputStream -@EnabledIf(NonGitHubOnlyCondition::class) -class HygDatabaseTest : StringSpec() { +@Disabled +class HygDatabaseTest { - init { - "load" { - val database = HygDatabase() - resource("hyg_v35.csv")!!.use(database::load) - database.size shouldBe 118002 - database.withText("Alp Psc") shouldHaveSize 1 - } + @Test + fun load() { + val database = HygDatabase() + // Typo at constellation name 119628 "Eco" + val hygCsv = download("https://github.com/astronexus/HYG-Database/raw/main/hyg/CURRENT/hygdata_v41.csv") + hygCsv.inputStream().use(database::load) + database.size shouldBe 118002 + database.withText("Alp Psc") shouldHaveSize 1 } } diff --git a/nebulosa-skycatalog-hyg/src/test/resources/.gitignore b/nebulosa-skycatalog-hyg/src/test/resources/.gitignore deleted file mode 100644 index afed0735d..000000000 --- a/nebulosa-skycatalog-hyg/src/test/resources/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.csv diff --git a/nebulosa-skycatalog-sao/src/test/kotlin/SaoCatalogTest.kt b/nebulosa-skycatalog-sao/src/test/kotlin/SaoCatalogTest.kt index 6dab8d788..8b5516d2d 100644 --- a/nebulosa-skycatalog-sao/src/test/kotlin/SaoCatalogTest.kt +++ b/nebulosa-skycatalog-sao/src/test/kotlin/SaoCatalogTest.kt @@ -1,34 +1,34 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe -import nebulosa.io.bufferedResource import nebulosa.math.deg import nebulosa.math.hours import nebulosa.skycatalog.sao.SaoCatalog -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.concat +import nebulosa.test.dataDirectory +import okio.source +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class SaoCatalogTest : StringSpec() { +@Disabled +class SaoCatalogTest { - init { - "load" { - val catalog = SaoCatalog() - catalog.load(bufferedResource("SAO.pc")!!) - catalog shouldHaveSize 258997 + @Test + fun load() { + val catalog = SaoCatalog() + dataDirectory.concat("SAO.pc").source().use(catalog::load) + catalog shouldHaveSize 258997 - catalog.first().id shouldBe 1 - catalog.first().rightAscensionJ2000 shouldBe ("0 00 05.097".hours plusOrMinus 1e-14) - catalog.first().declinationJ2000 shouldBe ("82 41 41.82".deg plusOrMinus 1e-14) - catalog.first().magnitude shouldBe 7.2 - catalog.first().spType shouldBe "A0" + catalog.first().id shouldBe 1 + catalog.first().rightAscensionJ2000 shouldBe ("0 00 05.097".hours plusOrMinus 1e-14) + catalog.first().declinationJ2000 shouldBe ("82 41 41.82".deg plusOrMinus 1e-14) + catalog.first().magnitude shouldBe 7.2 + catalog.first().spType shouldBe "A0" - catalog.last().id shouldBe 258997 - catalog.last().rightAscensionJ2000 shouldBe ("23 58 52.487".hours plusOrMinus 1e-14) - catalog.last().declinationJ2000 shouldBe ("-83 48 05.02".deg plusOrMinus 1e-14) - catalog.last().magnitude shouldBe 8.9 - catalog.last().spType shouldBe "K0" - } + catalog.last().id shouldBe 258997 + catalog.last().rightAscensionJ2000 shouldBe ("23 58 52.487".hours plusOrMinus 1e-14) + catalog.last().declinationJ2000 shouldBe ("-83 48 05.02".deg plusOrMinus 1e-14) + catalog.last().magnitude shouldBe 8.9 + catalog.last().spType shouldBe "K0" } } diff --git a/nebulosa-skycatalog-sao/src/test/resources/.gitignore b/nebulosa-skycatalog-sao/src/test/resources/.gitignore deleted file mode 100644 index 6fd0ef029..000000000 --- a/nebulosa-skycatalog-sao/src/test/resources/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.pc diff --git a/nebulosa-skycatalog-stellarium/src/main/kotlin/nebulosa/skycatalog/stellarium/Nebula.kt b/nebulosa-skycatalog-stellarium/src/main/kotlin/nebulosa/skycatalog/stellarium/Nebula.kt index db16bb05e..7969817ad 100644 --- a/nebulosa-skycatalog-stellarium/src/main/kotlin/nebulosa/skycatalog/stellarium/Nebula.kt +++ b/nebulosa-skycatalog-stellarium/src/main/kotlin/nebulosa/skycatalog/stellarium/Nebula.kt @@ -132,7 +132,7 @@ class Nebula : SkyCatalog(94661) { companion object { - @JvmStatic private val DSO_NAME_REGEX = Regex(".*_\\(\"(.*?)\"\\).*") + @JvmStatic private val DSO_NAME_REGEX = Regex("_\\(\"(.*?)\"\\)") @Suppress("NOTHING_TO_INLINE") private inline fun BufferedSource.readString(): String { @@ -154,7 +154,7 @@ class Nebula : SkyCatalog(94661) { val prefix = prefixFor(line.substring(0..4).trim()) val id = line.substring(5..19).trim() - val name = DSO_NAME_REGEX.matchEntire(line.substring(20))?.groupValues?.get(1) ?: continue + val name = DSO_NAME_REGEX.find(line.substring(20))?.groupValues?.get(1) ?: continue val key = if (prefix.isEmpty()) id else "$prefix$id" names.add(Name(key, name)) } diff --git a/nebulosa-skycatalog-stellarium/src/test/kotlin/NebulaTest.kt b/nebulosa-skycatalog-stellarium/src/test/kotlin/NebulaTest.kt index ad244ed64..5fec73507 100644 --- a/nebulosa-skycatalog-stellarium/src/test/kotlin/NebulaTest.kt +++ b/nebulosa-skycatalog-stellarium/src/test/kotlin/NebulaTest.kt @@ -1,43 +1,46 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainAll import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.ints.shouldBeExactly import nebulosa.math.deg import nebulosa.math.hours import nebulosa.skycatalog.stellarium.Nebula -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.AbstractTest +import nebulosa.test.download import okio.gzip import okio.source -import java.nio.file.Path - -@EnabledIf(NonGitHubOnlyCondition::class) -class NebulaTest : StringSpec() { - - init { - "load" { - val catalog = Path.of("../data/catalog.dat").source().gzip() - val names = Path.of("../data/names.dat").source() - - val nebula = Nebula() - nebula.load(catalog, names) - - nebula.size shouldBeExactly 94661 - - nebula - .searchAround("05 35 16.8".hours, "-05 23 24".deg, 1.0.deg) - .onEach { println(it) } - .size shouldBeExactly 11 - - nebula - .searchAround("18 02 42.0".hours, "-22 58 18".deg, 1.0.deg) - .onEach { println(it) } - .size shouldBeExactly 19 - } - "names" { - val names = Path.of("../data/names.dat").source().use(Nebula::namesFor) - val thorHelmet = names.filter { it.id == "NGC 2359" } shouldHaveSize 5 - thorHelmet.map { it.name }.shouldContainAll("Thor's Helmet", "Duck Head Nebula", "Flying Eye Nebula", "Duck Nebula", "Whistle Nebula") - } +import org.junit.jupiter.api.Test + +class NebulaTest : AbstractTest() { + + @Test + fun load() { + val catalogPath = download("https://github.com/Stellarium/stellarium/raw/master/nebulae/default/catalog.dat") + val catalog = catalogPath.source().gzip().autoClose() + + val namesPath = download("https://github.com/Stellarium/stellarium/raw/master/nebulae/default/names.dat") + val names = namesPath.source().autoClose() + + val nebula = Nebula() + nebula.load(catalog, names) + + nebula.size shouldBeExactly 94660 + + nebula + .searchAround("05 35 16.8".hours, "-05 23 24".deg, 1.0.deg) + .onEach { println(it) } + .size shouldBeExactly 11 + + nebula + .searchAround("18 02 42.0".hours, "-22 58 18".deg, 1.0.deg) + .onEach { println(it) } + .size shouldBeExactly 19 + } + + @Test + fun names() { + val namesPath = download("https://github.com/Stellarium/stellarium/raw/master/nebulae/default/names.dat") + val names = namesPath.source().use(Nebula::namesFor) + val thorHelmet = names.filter { it.id == "NGC 2359" } shouldHaveSize 5 + thorHelmet.map { it.name }.shouldContainAll("Thor's Helmet", "Duck Head Nebula", "Flying Eye Nebula", "Duck Nebula", "Whistle Nebula") } } diff --git a/nebulosa-skycatalog/src/test/kotlin/GeodesicGridTest.kt b/nebulosa-skycatalog/src/test/kotlin/GeodesicGridTest.kt index 99b2a0565..39bc7404b 100644 --- a/nebulosa-skycatalog/src/test/kotlin/GeodesicGridTest.kt +++ b/nebulosa-skycatalog/src/test/kotlin/GeodesicGridTest.kt @@ -1,16 +1,15 @@ -import io.kotest.core.annotation.Ignored -import io.kotest.core.spec.style.StringSpec import nebulosa.math.Vector3D import nebulosa.skycatalog.GeodesicGrid +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -@Ignored -class GeodesicGridTest : StringSpec(), GeodesicGrid.Traverser { +@Disabled +class GeodesicGridTest : GeodesicGrid.Traverser { - init { - "visit" { - val grid = GeodesicGrid(2) - grid.visitTriangles(2, this@GeodesicGridTest) - } + @Test + fun visit() { + val grid = GeodesicGrid(2) + grid.visitTriangles(2, this@GeodesicGridTest) } override fun traverse(level: Int, index: Int, c0: Vector3D, c1: Vector3D, c2: Vector3D) { diff --git a/nebulosa-stacker/build.gradle.kts b/nebulosa-stacker/build.gradle.kts new file mode 100644 index 000000000..4d1b2976b --- /dev/null +++ b/nebulosa-stacker/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + kotlin("jvm") + id("maven-publish") +} + +dependencies { + api(libs.logback) +} + +publishing { + publications { + create("pluginMaven") { + from(components["java"]) + } + } +} diff --git a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt new file mode 100644 index 000000000..d13fa3fc5 --- /dev/null +++ b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt @@ -0,0 +1,10 @@ +package nebulosa.stacker + +import java.nio.file.Path + +interface AutoStacker : Stacker { + + fun stack(paths: Collection, outputPath: Path, referencePath: Path = paths.first()): Boolean + + fun stop() +} diff --git a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt new file mode 100644 index 000000000..31cf7f719 --- /dev/null +++ b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt @@ -0,0 +1,21 @@ +package nebulosa.stacker + +import java.nio.file.Path + +interface Stacker { + + fun calibrate( + targetPath: Path, outputPath: Path, + darkPath: Path? = null, flatPath: Path? = null, biasPath: Path? = null, + ): Boolean + + fun align(referencePath: Path, targetPath: Path, outputPath: Path): Boolean + + fun integrate(stackCount: Int, stackedPath: Path, targetPath: Path, outputPath: Path): Boolean + + fun combineLRGB(outputPath: Path, luminancePath: Path? = null, redPath: Path? = null, greenPath: Path? = null, bluePath: Path? = null): Boolean + + fun combineLuminance(outputPath: Path, luminancePath: Path, targetPath: Path, mono: Boolean): Boolean + + fun saveAs(inputPath: Path, outputPath: Path): Boolean +} diff --git a/nebulosa-test/build.gradle.kts b/nebulosa-test/build.gradle.kts index 6ab8f1936..c866db4e8 100644 --- a/nebulosa-test/build.gradle.kts +++ b/nebulosa-test/build.gradle.kts @@ -9,7 +9,9 @@ dependencies { api(project(":nebulosa-fits")) api(project(":nebulosa-xisf")) api(libs.okhttp) - api(libs.bundles.kotest) + api(libs.kotest) + api(libs.junit.api) + runtimeOnly(libs.junit.engine) } publishing { diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractFitsAndXisfTest.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractFitsAndXisfTest.kt deleted file mode 100644 index f14f90738..000000000 --- a/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractFitsAndXisfTest.kt +++ /dev/null @@ -1,253 +0,0 @@ -package nebulosa.test - -import io.kotest.core.listeners.TestListener -import io.kotest.core.spec.style.StringSpec -import io.kotest.core.test.TestCase -import io.kotest.core.test.TestResult -import io.kotest.core.test.TestScope -import nebulosa.hips2fits.Hips2FitsService -import nebulosa.hips2fits.HipsSurvey -import nebulosa.image.format.ImageHdu -import nebulosa.io.transferAndCloseOutput -import nebulosa.math.Angle -import nebulosa.math.deg -import nebulosa.math.hours -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.logging.HttpLoggingInterceptor -import okio.ByteString.Companion.toByteString -import java.awt.image.BufferedImage -import java.awt.image.DataBufferByte -import java.awt.image.DataBufferInt -import java.io.Closeable -import java.nio.file.Path -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ConcurrentLinkedDeque -import java.util.concurrent.TimeUnit -import javax.imageio.ImageIO -import kotlin.io.path.* - -@Suppress("PropertyName") -abstract class AbstractFitsAndXisfTest : StringSpec() { - - protected val M82_MONO_8_LZ4_XISF by lazy { download("M82.Mono.8.LZ4.xisf", GITHUB_XISF_URL) } - protected val M82_MONO_8_LZ4_HC_XISF by lazy { download("M82.Mono.8.LZ4-HC.xisf", GITHUB_XISF_URL) } - protected val M82_MONO_8_XISF by lazy { download("M82.Mono.8.xisf", GITHUB_XISF_URL) } - protected val M82_MONO_8_ZLIB_XISF by lazy { download("M82.Mono.8.ZLib.xisf", GITHUB_XISF_URL) } - protected val M82_MONO_8_ZSTANDARD_XISF by lazy { download("M82.Mono.8.ZStandard.xisf", GITHUB_XISF_URL) } - protected val M82_MONO_16_XISF by lazy { download("M82.Mono.16.xisf", GITHUB_XISF_URL) } - protected val M82_MONO_32_XISF by lazy { download("M82.Mono.32.xisf", GITHUB_XISF_URL) } - protected val M82_MONO_F32_XISF by lazy { download("M82.Mono.F32.xisf", GITHUB_XISF_URL) } - protected val M82_MONO_F64_XISF by lazy { download("M82.Mono.F64.xisf", GITHUB_XISF_URL) } - - protected val M82_COLOR_8_LZ4_XISF by lazy { download("M82.Color.8.LZ4.xisf", GITHUB_XISF_URL) } - protected val M82_COLOR_8_LZ4_HC_XISF by lazy { download("M82.Color.8.LZ4-HC.xisf", GITHUB_XISF_URL) } - protected val M82_COLOR_8_XISF by lazy { download("M82.Color.8.xisf", GITHUB_XISF_URL) } - protected val M82_COLOR_8_ZLIB_XISF by lazy { download("M82.Color.8.ZLib.xisf", GITHUB_XISF_URL) } - protected val M82_COLOR_8_ZSTANDARD_XISF by lazy { download("M82.Color.8.ZStandard.xisf", GITHUB_XISF_URL) } - protected val M82_COLOR_16_XISF by lazy { download("M82.Color.16.xisf", GITHUB_XISF_URL) } - protected val M82_COLOR_32_XISF by lazy { download("M82.Color.32.xisf", GITHUB_XISF_URL) } - protected val M82_COLOR_F32_XISF by lazy { download("M82.Color.F32.xisf", GITHUB_XISF_URL) } - protected val M82_COLOR_F64_XISF by lazy { download("M82.Color.F64.xisf", GITHUB_XISF_URL) } - protected val DEBAYER_XISF_PATH by lazy { download("Debayer.xisf", GITHUB_XISF_URL) } - - protected val NGC3344_MONO_8_FITS by lazy { download("NGC3344.Mono.8.fits", GITHUB_FITS_URL) } - protected val NGC3344_MONO_16_FITS by lazy { download("NGC3344.Mono.16.fits", GITHUB_FITS_URL) } - protected val NGC3344_MONO_32_FITS by lazy { download("NGC3344.Mono.32.fits", GITHUB_FITS_URL) } - protected val NGC3344_MONO_F32_FITS by lazy { download("NGC3344.Mono.F32.fits", GITHUB_FITS_URL) } - protected val NGC3344_MONO_F64_FITS by lazy { download("NGC3344.Mono.F64.fits", GITHUB_FITS_URL) } - - protected val NGC3344_COLOR_8_FITS by lazy { download("NGC3344.Color.8.fits", GITHUB_FITS_URL) } - protected val NGC3344_COLOR_16_FITS by lazy { download("NGC3344.Color.16.fits", GITHUB_FITS_URL) } - protected val NGC3344_COLOR_32_FITS by lazy { download("NGC3344.Color.32.fits", GITHUB_FITS_URL) } - protected val NGC3344_COLOR_F32_FITS by lazy { download("NGC3344.Color.F32.fits", GITHUB_FITS_URL) } - protected val NGC3344_COLOR_F64_FITS by lazy { download("NGC3344.Color.F64.fits", GITHUB_FITS_URL) } - - protected val PALETTE_MONO_8_FITS by lazy { download("PALETTE.Mono.8.fits", GITHUB_FITS_URL) } - protected val PALETTE_MONO_16_FITS by lazy { download("PALETTE.Mono.16.fits", GITHUB_FITS_URL) } - protected val PALETTE_MONO_32_FITS by lazy { download("PALETTE.Mono.32.fits", GITHUB_FITS_URL) } - protected val PALETTE_MONO_F32_FITS by lazy { download("PALETTE.Mono.F32.fits", GITHUB_FITS_URL) } - protected val PALETTE_MONO_F64_FITS by lazy { download("PALETTE.Mono.F64.fits", GITHUB_FITS_URL) } - - protected val PALETTE_COLOR_8_FITS by lazy { download("PALETTE.Color.8.fits", GITHUB_FITS_URL) } - protected val PALETTE_COLOR_16_FITS by lazy { download("PALETTE.Color.16.fits", GITHUB_FITS_URL) } - protected val PALETTE_COLOR_32_FITS by lazy { download("PALETTE.Color.32.fits", GITHUB_FITS_URL) } - protected val PALETTE_COLOR_F32_FITS by lazy { download("PALETTE.Color.F32.fits", GITHUB_FITS_URL) } - protected val PALETTE_COLOR_F64_FITS by lazy { download("PALETTE.Color.F64.fits", GITHUB_FITS_URL) } - - protected val PALETTE_MONO_8_XISF by lazy { download("PALETTE.Mono.8.xisf", GITHUB_XISF_URL) } - protected val PALETTE_MONO_16_XISF by lazy { download("PALETTE.Mono.16.xisf", GITHUB_XISF_URL) } - protected val PALETTE_MONO_32_XISF by lazy { download("PALETTE.Mono.32.xisf", GITHUB_XISF_URL) } - protected val PALETTE_MONO_F32_XISF by lazy { download("PALETTE.Mono.F32.xisf", GITHUB_XISF_URL) } - protected val PALETTE_MONO_F64_XISF by lazy { download("PALETTE.Mono.F64.xisf", GITHUB_XISF_URL) } - - protected val PALETTE_COLOR_8_XISF by lazy { download("PALETTE.Color.8.xisf", GITHUB_XISF_URL) } - protected val PALETTE_COLOR_16_XISF by lazy { download("PALETTE.Color.16.xisf", GITHUB_XISF_URL) } - protected val PALETTE_COLOR_32_XISF by lazy { download("PALETTE.Color.32.xisf", GITHUB_XISF_URL) } - protected val PALETTE_COLOR_F32_XISF by lazy { download("PALETTE.Color.F32.xisf", GITHUB_XISF_URL) } - protected val PALETTE_COLOR_F64_XISF by lazy { download("PALETTE.Color.F64.xisf", GITHUB_XISF_URL) } - - protected val DEBAYER_FITS by lazy { download("Debayer.fits", GITHUB_FITS_URL) } - protected val M6707HH by lazy { download("M6707HH.fits", ASTROPY_PHOTOMETRY_URL) } - protected val M31_FITS by lazy { download("00 42 44.3".hours, "41 16 9".deg, 3.deg) } - - protected val STAR_FOCUS_1 by lazy { download("STAR.FOCUS.1.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_2 by lazy { download("STAR.FOCUS.2.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_3 by lazy { download("STAR.FOCUS.3.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_4 by lazy { download("STAR.FOCUS.4.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_5 by lazy { download("STAR.FOCUS.5.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_6 by lazy { download("STAR.FOCUS.6.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_7 by lazy { download("STAR.FOCUS.7.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_8 by lazy { download("STAR.FOCUS.8.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_9 by lazy { download("STAR.FOCUS.9.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_10 by lazy { download("STAR.FOCUS.10.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_11 by lazy { download("STAR.FOCUS.11.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_12 by lazy { download("STAR.FOCUS.12.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_13 by lazy { download("STAR.FOCUS.13.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_14 by lazy { download("STAR.FOCUS.14.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_15 by lazy { download("STAR.FOCUS.15.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_16 by lazy { download("STAR.FOCUS.16.fits", GITHUB_FITS_URL) } - protected val STAR_FOCUS_17 by lazy { download("STAR.FOCUS.17.fits", GITHUB_FITS_URL) } - - protected val PI_01_LIGHT by lazy { download("PI.01.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_02_LIGHT by lazy { download("PI.02.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_03_LIGHT by lazy { download("PI.03.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_04_LIGHT by lazy { download("PI.04.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_05_LIGHT by lazy { download("PI.05.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_06_LIGHT by lazy { download("PI.06.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_07_LIGHT by lazy { download("PI.07.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_08_LIGHT by lazy { download("PI.08.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_09_LIGHT by lazy { download("PI.09.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_10_LIGHT by lazy { download("PI.10.LIGHT.fits", GITHUB_FITS_URL) } - protected val PI_BIAS by lazy { download("PI.BIAS.fits", GITHUB_FITS_URL) } - protected val PI_DARK by lazy { download("PI.DARK.fits", GITHUB_FITS_URL) } - protected val PI_FLAT by lazy { download("PI.FLAT.fits", GITHUB_FITS_URL) } - - protected val PI_FOCUS_0 by lazy { download("PI.FOCUS.0.fits", GITHUB_FITS_URL) } - protected val PI_FOCUS_10000 by lazy { download("PI.FOCUS.10000.fits", GITHUB_FITS_URL) } - protected val PI_FOCUS_20000 by lazy { download("PI.FOCUS.20000.fits", GITHUB_FITS_URL) } - protected val PI_FOCUS_30000 by lazy { download("PI.FOCUS.30000.fits", GITHUB_FITS_URL) } - protected val PI_FOCUS_100000 by lazy { download("PI.FOCUS.100000.fits", GITHUB_FITS_URL) } - - private val afterEach = AfterEach() - - init { - prependExtension(afterEach) - } - - protected fun TestScope.closeAfterEach(closeable: T) = closeable.apply { - afterEach.add(testCase to this) - } - - protected fun BufferedImage.save(name: String): Pair { - val path = Path.of("..", "data", "test", "$name.png").createParentDirectories() - ImageIO.write(this, "PNG", path.toFile()) - val md5 = path.md5() - println("$name: $md5") - return path to md5 - } - - protected fun ByteArray.md5(): String { - return toByteString().md5().hex() - } - - protected fun Path.md5(): String { - return readBytes().md5() - } - - protected fun download(name: String, baseUrl: String): Path { - return synchronize(name) { - val path = Path.of(System.getProperty("java.io.tmpdir"), name) - - if (!path.exists() || path.fileSize() <= 0L) { - val request = Request.Builder() - .get() - .url("$baseUrl/$name") - .build() - - val call = HTTP_CLIENT.newCall(request) - - call.execute().use { - it.body?.byteStream()?.transferAndCloseOutput(path.outputStream()) - } - } - - path - } - } - - protected fun download(centerRA: Angle, centerDEC: Angle, fov: Angle): Path { - val name = "$centerRA@$centerDEC@$fov".toByteArray().md5() - - return synchronize(name) { - val path = Path.of(System.getProperty("java.io.tmpdir"), name) - - if (!path.exists() || path.fileSize() <= 0L) { - HIPS_SERVICE - .query(CDS_P_DSS2_NIR.id, centerRA, centerDEC, 1280, 720, 0.0, fov) - .execute() - .body()!! - .use { it.byteStream().transferAndCloseOutput(path.outputStream()) } - } - - path - } - } - - protected fun ImageHdu.makeImage(): BufferedImage { - val type = if (numberOfChannels == 1) BufferedImage.TYPE_BYTE_GRAY else BufferedImage.TYPE_INT_RGB - val image = BufferedImage(width, height, type) - val numberOfPixels = data.numberOfPixels - - if (numberOfChannels == 1) { - val buffer = (image.raster.dataBuffer as DataBufferByte).data - - repeat(numberOfPixels) { - buffer[it] = (data.red[it] * 255f).toInt().toByte() - } - } else { - val buffer = (image.raster.dataBuffer as DataBufferInt).data - - repeat(numberOfPixels) { - val red = (data.red[it] * 255f).toInt() and 0xFF - val green = (data.green[it] * 255f).toInt() and 0xFF - val blue = (data.blue[it] * 255f).toInt() and 0xFF - buffer[it] = blue or (green shl 8) or (red shl 16) - } - } - - return image - } - - @Suppress("BlockingMethodInNonBlockingContext") - private class AfterEach : ConcurrentLinkedDeque>(), TestListener { - - override suspend fun afterEach(testCase: TestCase, result: TestResult) { - find { it.first === testCase }?.second?.close() ?: return - } - } - - companion object { - - const val ASTROPY_PHOTOMETRY_URL = "https://www.astropy.org/astropy-data/photometry" - const val GITHUB_FITS_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/fits" - const val GITHUB_XISF_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/xisf" - - @JvmStatic val HTTP_CLIENT = OkHttpClient.Builder() - .readTimeout(60L, TimeUnit.SECONDS) - .writeTimeout(60L, TimeUnit.SECONDS) - .connectTimeout(60L, TimeUnit.SECONDS) - .callTimeout(60L, TimeUnit.SECONDS) - .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) - .build() - - @JvmStatic val HIPS_SERVICE = Hips2FitsService(httpClient = HTTP_CLIENT) - @JvmStatic val CDS_P_DSS2_NIR = HipsSurvey("CDS/P/DSS2/NIR") - @JvmStatic @PublishedApi internal val SYNC_KEYS = ConcurrentHashMap() - - inline fun synchronize(key: String, block: () -> R): R { - val lock = synchronized(SYNC_KEYS) { SYNC_KEYS.getOrPut(key) { Any() } } - return synchronized(lock, block) - } - } -} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractTest.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractTest.kt new file mode 100644 index 000000000..de32a5719 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractTest.kt @@ -0,0 +1,46 @@ +package nebulosa.test + +import org.junit.jupiter.api.AfterEach +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.deleteIfExists +import kotlin.io.path.deleteRecursively +import kotlin.io.path.isDirectory + +abstract class AbstractTest { + + private val autoCloseables = HashSet(2) + private val deletablePaths = ArrayList(2) + + @AfterEach + fun closeResourcesAfterEach() { + autoCloseables.forEach(AutoCloseable::close) + autoCloseables.clear() + } + + @AfterEach + fun deletePathsAfterEach() { + deletablePaths.forEach { it.deleteRecursivelyOrIfExists() } + deletablePaths.clear() + } + + protected fun T.autoClose(): T { + return apply(autoCloseables::add) + } + + protected fun Path.deleteRecursivelyOrIfExists() { + if (isDirectory()) deleteRecursively() else deleteIfExists() + } + + protected fun Path.deleteAfterEach(): Path { + return apply(deletablePaths::add) + } + + protected fun tempPath(prefix: String, suffix: String): Path { + return Files.createTempFile(prefix, suffix).deleteAfterEach() + } + + protected fun tempDirectory(prefix: String): Path { + return Files.createTempDirectory(prefix).deleteAfterEach() + } +} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Extensions.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Extensions.kt new file mode 100644 index 000000000..708cf0d2b --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Extensions.kt @@ -0,0 +1,20 @@ +@file:JvmName("Extensions") +@file:Suppress("NOTHING_TO_INLINE") + +package nebulosa.test + +import okio.ByteString.Companion.toByteString +import java.awt.image.BufferedImage +import java.nio.file.Path +import javax.imageio.ImageIO +import kotlin.io.path.createParentDirectories +import kotlin.io.path.readBytes + +inline fun ByteArray.md5() = toByteString().md5().hex() +inline fun Path.md5() = readBytes().md5() // TODO: Improve it. Remove readBytes() + +fun BufferedImage.save(name: String): Pair { + val path = dataDirectory.concat("test", "$name.png").createParentDirectories() + ImageIO.write(this, "PNG", path.toFile()) + return path to path.md5().also { println("$name: $it") } +} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt new file mode 100644 index 000000000..ce19039cf --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt @@ -0,0 +1,109 @@ +@file:JvmName("Files") + +package nebulosa.test + +import nebulosa.math.deg +import nebulosa.math.hours + +const val ASTROPY_PHOTOMETRY_URL = "https://www.astropy.org/astropy-data/photometry" +const val GITHUB_FITS_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/fits" +const val GITHUB_XISF_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/xisf" + +val M82_MONO_8_LZ4_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.LZ4.xisf") } +val M82_MONO_8_LZ4_HC_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.LZ4-HC.xisf") } +val M82_MONO_8_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.xisf") } +val M82_MONO_8_ZLIB_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.ZLib.xisf") } +val M82_MONO_8_ZSTANDARD_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.ZStandard.xisf") } +val M82_MONO_16_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.16.xisf") } +val M82_MONO_32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.32.xisf") } +val M82_MONO_F32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.F32.xisf") } +val M82_MONO_F64_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.F64.xisf") } + +val M82_COLOR_8_LZ4_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.LZ4.xisf") } +val M82_COLOR_8_LZ4_HC_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.LZ4-HC.xisf") } +val M82_COLOR_8_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.xisf") } +val M82_COLOR_8_ZLIB_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.ZLib.xisf") } +val M82_COLOR_8_ZSTANDARD_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.ZStandard.xisf") } +val M82_COLOR_16_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.16.xisf") } +val M82_COLOR_32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.32.xisf") } +val M82_COLOR_F32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.F32.xisf") } +val M82_COLOR_F64_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.F64.xisf") } +val DEBAYER_XISF_PATH by lazy { download("$GITHUB_XISF_URL/Debayer.xisf") } + +val NGC3344_MONO_8_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.8.fits") } +val NGC3344_MONO_16_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.16.fits") } +val NGC3344_MONO_32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.32.fits") } +val NGC3344_MONO_F32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.F32.fits") } +val NGC3344_MONO_F64_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.F64.fits") } + +val NGC3344_COLOR_8_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.8.fits") } +val NGC3344_COLOR_16_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.16.fits") } +val NGC3344_COLOR_32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.32.fits") } +val NGC3344_COLOR_F32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.F32.fits") } +val NGC3344_COLOR_F64_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.F64.fits") } + +val PALETTE_MONO_8_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.8.fits") } +val PALETTE_MONO_16_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.16.fits") } +val PALETTE_MONO_32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.32.fits") } +val PALETTE_MONO_F32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.F32.fits") } +val PALETTE_MONO_F64_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.F64.fits") } + +val PALETTE_COLOR_8_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.8.fits") } +val PALETTE_COLOR_16_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.16.fits") } +val PALETTE_COLOR_32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.32.fits") } +val PALETTE_COLOR_F32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.F32.fits") } +val PALETTE_COLOR_F64_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.F64.fits") } + +val PALETTE_MONO_8_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.8.xisf") } +val PALETTE_MONO_16_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.16.xisf") } +val PALETTE_MONO_32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.32.xisf") } +val PALETTE_MONO_F32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.F32.xisf") } +val PALETTE_MONO_F64_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.F64.xisf") } + +val PALETTE_COLOR_8_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.8.xisf") } +val PALETTE_COLOR_16_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.16.xisf") } +val PALETTE_COLOR_32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.32.xisf") } +val PALETTE_COLOR_F32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.F32.xisf") } +val PALETTE_COLOR_F64_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.F64.xisf") } + +val DEBAYER_FITS by lazy { download("$GITHUB_FITS_URL/Debayer.fits") } +val M6707HH by lazy { download("$ASTROPY_PHOTOMETRY_URL/M6707HH.fits") } +val M31_FITS by lazy { downloadFits("00 42 44.3".hours, "41 16 9".deg, 3.deg) } + +val STAR_FOCUS_1 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.1.fits") } +val STAR_FOCUS_2 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.2.fits") } +val STAR_FOCUS_3 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.3.fits") } +val STAR_FOCUS_4 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.4.fits") } +val STAR_FOCUS_5 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.5.fits") } +val STAR_FOCUS_6 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.6.fits") } +val STAR_FOCUS_7 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.7.fits") } +val STAR_FOCUS_8 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.8.fits") } +val STAR_FOCUS_9 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.9.fits") } +val STAR_FOCUS_10 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.10.fits") } +val STAR_FOCUS_11 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.11.fits") } +val STAR_FOCUS_12 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.12.fits") } +val STAR_FOCUS_13 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.13.fits") } +val STAR_FOCUS_14 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.14.fits") } +val STAR_FOCUS_15 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.15.fits") } +val STAR_FOCUS_16 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.16.fits") } +val STAR_FOCUS_17 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.17.fits") } + +val PI_01_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.01.LIGHT.fits") } +val PI_02_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.02.LIGHT.fits") } +val PI_03_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.03.LIGHT.fits") } +val PI_04_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.04.LIGHT.fits") } +val PI_05_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.05.LIGHT.fits") } +val PI_06_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.06.LIGHT.fits") } +val PI_07_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.07.LIGHT.fits") } +val PI_08_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.08.LIGHT.fits") } +val PI_09_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.09.LIGHT.fits") } +val PI_10_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.10.LIGHT.fits") } +val PI_BIAS by lazy { download("$GITHUB_FITS_URL/PI.BIAS.fits") } +val PI_DARK by lazy { download("$GITHUB_FITS_URL/PI.DARK.fits") } +val PI_FLAT by lazy { download("$GITHUB_FITS_URL/PI.FLAT.fits") } + +val PI_FOCUS_0 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.0.fits") } +val PI_FOCUS_10000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.10000.fits") } +val PI_FOCUS_20000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.20000.fits") } +val PI_FOCUS_30000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.30000.fits") } +val PI_FOCUS_100000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.100000.fits") } diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Http.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Http.kt new file mode 100644 index 000000000..bf45e0490 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Http.kt @@ -0,0 +1,66 @@ +@file:JvmName("Http") +@file:Suppress("NOTHING_TO_INLINE") + +package nebulosa.test + +import nebulosa.hips2fits.Hips2FitsService +import nebulosa.hips2fits.HipsSurvey +import nebulosa.io.transferAndCloseOutput +import nebulosa.math.Angle +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.logging.HttpLoggingInterceptor +import java.nio.file.Path +import java.util.concurrent.TimeUnit +import kotlin.io.path.exists +import kotlin.io.path.fileSize +import kotlin.io.path.outputStream + +val HTTP_CLIENT = OkHttpClient.Builder() + .readTimeout(60L, TimeUnit.SECONDS) + .writeTimeout(60L, TimeUnit.SECONDS) + .connectTimeout(60L, TimeUnit.SECONDS) + .callTimeout(60L, TimeUnit.SECONDS) + .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) + .build() + +private val String.extensionFromUrl + get() = lastIndexOf('.').let { if (it >= 0) substring(it + 1) else this } + +fun download(url: String, extension: String = url.extensionFromUrl): Path { + require(extension.isNotBlank()) + + return synchronized(url) { + val name = url.encodeToByteArray().md5() + val path = cacheDirectory.concat("$name.$extension") + + if (!path.exists() || path.fileSize() <= 0L) { + val request = Request.Builder().get().url(url).build() + val call = HTTP_CLIENT.newCall(request) + + call.execute().use { + it.body?.byteStream()?.transferAndCloseOutput(path.outputStream()) + } + } + + path + } +} + +private val HIPS_SERVICE = Hips2FitsService(httpClient = HTTP_CLIENT) +private val CDS_P_DSS2_NIR = HipsSurvey("CDS/P/DSS2/NIR") + +fun downloadFits(centerRA: Angle, centerDEC: Angle, fov: Angle): Path { + val name = "$centerRA@$centerDEC@$fov".toByteArray().md5() + val path = cacheDirectory.concat(name) + + if (!path.exists() || path.fileSize() <= 0L) { + HIPS_SERVICE + .query(CDS_P_DSS2_NIR.id, centerRA, centerDEC, 1280, 720, 0.0, fov) + .execute() + .body()!! + .use { it.byteStream().transferAndCloseOutput(path.outputStream()) } + } + + return path +} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/LinuxOnly.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/LinuxOnly.kt new file mode 100644 index 000000000..e7a06c76a --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/LinuxOnly.kt @@ -0,0 +1,10 @@ +package nebulosa.test + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledOnOs +import org.junit.jupiter.api.condition.OS + +@Test +@EnabledOnOs(OS.LINUX) +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +annotation class LinuxOnly diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Matchers.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Matchers.kt new file mode 100644 index 000000000..0bfadb132 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Matchers.kt @@ -0,0 +1,4 @@ +@file:JvmName("Matchers") +@file:Suppress("NOTHING_TO_INLINE") + +package nebulosa.test diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnly.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnly.kt new file mode 100644 index 000000000..ec8b329bf --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnly.kt @@ -0,0 +1,9 @@ +package nebulosa.test + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledIfSystemProperty + +@Test +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@EnabledIfSystemProperty(named = "github", matches = "false") +annotation class NonGitHubOnly diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnlyCondition.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnlyCondition.kt deleted file mode 100644 index c5a598bd0..000000000 --- a/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnlyCondition.kt +++ /dev/null @@ -1,12 +0,0 @@ -package nebulosa.test - -import io.kotest.core.annotation.EnabledCondition -import io.kotest.core.spec.Spec -import kotlin.reflect.KClass - -class NonGitHubOnlyCondition : EnabledCondition { - - override fun enabled(kclass: KClass): Boolean { - return System.getProperty("github", "false") == "false" - } -} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Paths.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Paths.kt new file mode 100644 index 000000000..2b61ecb5c --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Paths.kt @@ -0,0 +1,25 @@ +@file:Suppress("NOTHING_TO_INLINE") +@file:JvmName("Paths") + +package nebulosa.test + +import java.nio.file.Path + +val homeDirectory: Path + get() = Path.of(System.getProperty("user.home")) + +val rootDirectory: Path + get() = Path.of(System.getProperty("root.dir")) + +val projectDirectory: Path + get() = Path.of(System.getProperty("project.dir")) + +val dataDirectory: Path + get() = Path.of("$rootDirectory", "data") + +val cacheDirectory: Path + get() = Path.of("$rootDirectory", ".cache") + +inline fun Path.concat(path: String): Path = Path.of("$this", path) + +inline fun Path.concat(vararg path: String): Path = Path.of("$this", *path) diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/WindowsOnly.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/WindowsOnly.kt new file mode 100644 index 000000000..4fb102bc7 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/WindowsOnly.kt @@ -0,0 +1,10 @@ +package nebulosa.test + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledOnOs +import org.junit.jupiter.api.condition.OS + +@Test +@EnabledOnOs(OS.WINDOWS) +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +annotation class WindowsOnly diff --git a/nebulosa-test/src/test/kotlin/PathsTest.kt b/nebulosa-test/src/test/kotlin/PathsTest.kt new file mode 100644 index 000000000..40ee672f9 --- /dev/null +++ b/nebulosa-test/src/test/kotlin/PathsTest.kt @@ -0,0 +1,27 @@ +import io.kotest.matchers.string.shouldEndWith +import nebulosa.test.* +import org.junit.jupiter.api.Test + +@LinuxOnly +class PathsTest { + + @Test + fun rootDirectory() { + "$rootDirectory" shouldEndWith "/nebulosa" + } + + @Test + fun projectDirectory() { + "$projectDirectory" shouldEndWith "/nebulosa-test" + } + + @Test + fun cacheDirectory() { + "$cacheDirectory" shouldEndWith "/.cache" + } + + @Test + fun dataDirectory() { + "$dataDirectory" shouldEndWith "/data" + } +} diff --git a/nebulosa-test/src/test/kotlin/SystemPropertyTest.kt b/nebulosa-test/src/test/kotlin/SystemPropertyTest.kt new file mode 100644 index 000000000..1d3218006 --- /dev/null +++ b/nebulosa-test/src/test/kotlin/SystemPropertyTest.kt @@ -0,0 +1,23 @@ +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.string.shouldEndWith +import nebulosa.test.LinuxOnly +import org.junit.jupiter.api.Test + +@LinuxOnly +class SystemPropertyTest { + + @Test + fun rootDir() { + System.getProperty("root.dir").shouldNotBeNull() shouldEndWith ("/nebulosa") + } + + @Test + fun projectDir() { + System.getProperty("project.dir").shouldNotBeNull() shouldEndWith ("/nebulosa-test") + } + + @Test + fun github() { + System.getProperty("github").shouldNotBeNull() + } +} diff --git a/nebulosa-time/src/main/kotlin/nebulosa/time/SystemClock.kt b/nebulosa-time/src/main/kotlin/nebulosa/time/SystemClock.kt new file mode 100644 index 000000000..9021c3209 --- /dev/null +++ b/nebulosa-time/src/main/kotlin/nebulosa/time/SystemClock.kt @@ -0,0 +1,15 @@ +package nebulosa.time + +import java.time.Clock +import java.time.ZoneId + +object SystemClock : Clock() { + + private val clock = systemDefaultZone() + + override fun getZone() = ZoneId.systemDefault() + + override fun withZone(zone: ZoneId) = clock.withZone(zone) + + override fun instant() = clock.instant() +} diff --git a/nebulosa-time/src/test/kotlin/DeltaTTest.kt b/nebulosa-time/src/test/kotlin/DeltaTTest.kt index 9920849db..7e68a74e4 100644 --- a/nebulosa-time/src/test/kotlin/DeltaTTest.kt +++ b/nebulosa-time/src/test/kotlin/DeltaTTest.kt @@ -1,19 +1,18 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.DeltaT import nebulosa.time.TimeDelta import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test -class DeltaTTest : StringSpec(), TimeDelta by DeltaT { +class DeltaTTest : TimeDelta by DeltaT { - init { - "delta" { - delta(TimeYMDHMS(-720)) shouldBe (20370.94276516515 plusOrMinus 1e-4) - delta(TimeYMDHMS(1170)) shouldBe (997.1912592573422 plusOrMinus 1e-4) - delta(TimeYMDHMS(1980)) shouldBe (50.53915198016422 plusOrMinus 1e-4) - delta(TimeYMDHMS(2023)) shouldBe (69.203827 plusOrMinus 1e-4) - delta(TimeYMDHMS(2600)) shouldBe (1623.7769924989768 plusOrMinus 1e-4) - } + @Test + fun delta() { + delta(TimeYMDHMS(-720)) shouldBe (20370.94276516515 plusOrMinus 1e-4) + delta(TimeYMDHMS(1170)) shouldBe (997.1912592573422 plusOrMinus 1e-4) + delta(TimeYMDHMS(1980)) shouldBe (50.53915198016422 plusOrMinus 1e-4) + delta(TimeYMDHMS(2023)) shouldBe (69.203827 plusOrMinus 1e-4) + delta(TimeYMDHMS(2600)) shouldBe (1623.7769924989768 plusOrMinus 1e-4) } } diff --git a/nebulosa-time/src/test/kotlin/IERSTest.kt b/nebulosa-time/src/test/kotlin/IERSTest.kt index 29a440795..a91e6019e 100644 --- a/nebulosa-time/src/test/kotlin/IERSTest.kt +++ b/nebulosa-time/src/test/kotlin/IERSTest.kt @@ -1,67 +1,76 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.* -import java.nio.file.Path +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream -class IERSTest : StringSpec() { +class IERSTest { - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) + @Test + fun iersa() { + IERS.attach(IERSA) - val iersb = IERSB() - iersb.load(Path.of("../data/eopc04.1962-now.txt").inputStream()) + with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-18) + fraction shouldBe (-1.2784074073774842e-06 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2461042.0 plusOrMinus 1E-18) + fraction shouldBe (4.693171296595673e-07 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2438396.0 plusOrMinus 1E-18) + fraction shouldBe (9.353564814864956e-06 plusOrMinus 1E-8) + } + } - "A" { - IERS.attach(iersa) + @Test + fun iersb() { + IERS.attach(IERSB) - with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-18) - fraction shouldBe (-1.2784074073774842e-06 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2461042.0 plusOrMinus 1E-18) - fraction shouldBe (4.693171296595673e-07 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2438396.0 plusOrMinus 1E-18) - fraction shouldBe (9.353564814864956e-06 plusOrMinus 1E-8) - } + with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-18) + fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-18) } - "B" { - IERS.attach(iersb) + with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2461042.0 plusOrMinus 1E-18) + fraction shouldBe (1.442650463262399e-07 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2438396.0 plusOrMinus 1E-18) + fraction shouldBe (-9.322685184683761e-07 plusOrMinus 1E-8) + } + } - with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-18) - fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2461042.0 plusOrMinus 1E-18) - fraction shouldBe (1.442650463262399e-07 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2438396.0 plusOrMinus 1E-18) - fraction shouldBe (-9.322685184683761e-07 plusOrMinus 1E-8) - } + @Test + fun iersab() { + val iersab = IERSAB(IERSA, IERSB) + IERS.attach(iersab) + + with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-18) + fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2461042.0 plusOrMinus 1E-18) + fraction shouldBe (4.693171296595673e-07 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2438396.0 plusOrMinus 1E-18) + fraction shouldBe (-9.322685184683761e-07 plusOrMinus 1E-8) } - "AB" { - val iersab = IERSAB(iersa, iersb) - IERS.attach(iersab) + } + + companion object { + + @JvmStatic private val IERSA = IERSA() + @JvmStatic private val IERSB = IERSB() - with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-18) - fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2461042.0 plusOrMinus 1E-18) - fraction shouldBe (4.693171296595673e-07 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2438396.0 plusOrMinus 1E-18) - fraction shouldBe (-9.322685184683761e-07 plusOrMinus 1E-8) - } + init { + dataDirectory.concat("finals2000A.all").inputStream().use(IERSA::load) + dataDirectory.concat("eopc04.1962-now.txt").inputStream().use(IERSB::load) } } } diff --git a/nebulosa-time/src/test/kotlin/InstantOfTimeTest.kt b/nebulosa-time/src/test/kotlin/InstantOfTimeTest.kt index 18b167310..36f331232 100644 --- a/nebulosa-time/src/test/kotlin/InstantOfTimeTest.kt +++ b/nebulosa-time/src/test/kotlin/InstantOfTimeTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe @@ -7,44 +6,50 @@ import nebulosa.math.hours import nebulosa.math.toDegrees import nebulosa.math.toHours import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test -class InstantOfTimeTest : StringSpec() { +class InstantOfTimeTest { - init { - // https://dc.zah.uni-heidelberg.de/apfs/times/q/form + // https://dc.zah.uni-heidelberg.de/apfs/times/q/form - "gast" { - val angle = "06 39 30.2996".hours - TimeYMDHMS(2023, 1, 30, 22).gast.toHours shouldBe (angle.toHours plusOrMinus 1e-8) - } - "gmst" { - val angle = "06 39 30.8663".hours - TimeYMDHMS(2023, 1, 30, 22).gmst.toHours shouldBe (angle.toHours plusOrMinus 1e-8) - } - "era" { - val angle = "99 34 58.365".deg - TimeYMDHMS(2023, 1, 30, 22).era.toDegrees shouldBe (angle.toDegrees plusOrMinus 1e-6) - } - "datetime" { - var ymdhms = TimeYMDHMS(2023, 1, 30, 22, 45, 33.5) - var datetime = ymdhms.asDateTime() - datetime.year shouldBeExactly 2023 - datetime.monthValue shouldBeExactly 1 - datetime.dayOfMonth shouldBeExactly 30 - datetime.hour shouldBeExactly 22 - datetime.minute shouldBeExactly 45 - datetime.second shouldBeExactly 33 - datetime.nano shouldBeExactly 500000000 + @Test + fun gast() { + val angle = "06 39 30.2996".hours + TimeYMDHMS(2023, 1, 30, 22).gast.toHours shouldBe (angle.toHours plusOrMinus 1e-8) + } + + @Test + fun gmst() { + val angle = "06 39 30.8663".hours + TimeYMDHMS(2023, 1, 30, 22).gmst.toHours shouldBe (angle.toHours plusOrMinus 1e-8) + } + + @Test + fun era() { + val angle = "99 34 58.365".deg + TimeYMDHMS(2023, 1, 30, 22).era.toDegrees shouldBe (angle.toDegrees plusOrMinus 1e-6) + } + + @Test + fun datetime() { + var ymdhms = TimeYMDHMS(2023, 1, 30, 22, 45, 33.5) + var datetime = ymdhms.asDateTime() + datetime.year shouldBeExactly 2023 + datetime.monthValue shouldBeExactly 1 + datetime.dayOfMonth shouldBeExactly 30 + datetime.hour shouldBeExactly 22 + datetime.minute shouldBeExactly 45 + datetime.second shouldBeExactly 33 + datetime.nano shouldBeExactly 500000000 - ymdhms = TimeYMDHMS(2023, 1, 30, 0, 0, 0.0) - datetime = ymdhms.asDateTime() - datetime.year shouldBeExactly 2023 - datetime.monthValue shouldBeExactly 1 - datetime.dayOfMonth shouldBeExactly 30 - datetime.hour shouldBeExactly 0 - datetime.minute shouldBeExactly 0 - datetime.second shouldBeExactly 0 - datetime.nano shouldBeExactly 0 - } + ymdhms = TimeYMDHMS(2023, 1, 30, 0, 0, 0.0) + datetime = ymdhms.asDateTime() + datetime.year shouldBeExactly 2023 + datetime.monthValue shouldBeExactly 1 + datetime.dayOfMonth shouldBeExactly 30 + datetime.hour shouldBeExactly 0 + datetime.minute shouldBeExactly 0 + datetime.second shouldBeExactly 0 + datetime.nano shouldBeExactly 0 } } diff --git a/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrison2004Test.kt b/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrison2004Test.kt index d95019521..c9552cf86 100644 --- a/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrison2004Test.kt +++ b/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrison2004Test.kt @@ -1,79 +1,80 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.ParabolaOfStephensonMorrison2004 import nebulosa.time.Spline +import org.junit.jupiter.api.Test -class ParabolaOfStephensonMorrison2004Test : StringSpec(), Spline by ParabolaOfStephensonMorrison2004 { +class ParabolaOfStephensonMorrison2004Test : Spline by ParabolaOfStephensonMorrison2004 { - init { - "parabola" { - compute(0.0) shouldBe (10579.679999999998 plusOrMinus 1E-6) - compute(100.0) shouldBe (9446.88 plusOrMinus 1E-6) - compute(200.0) shouldBe (8378.08 plusOrMinus 1E-6) - compute(300.0) shouldBe (7373.28 plusOrMinus 1E-6) - compute(400.0) shouldBe (6432.48 plusOrMinus 1E-6) - compute(500.0) shouldBe (5555.679999999999 plusOrMinus 1E-6) - compute(600.0) shouldBe (4742.879999999999 plusOrMinus 1E-6) - compute(700.0) shouldBe (3994.0799999999995 plusOrMinus 1E-6) - compute(800.0) shouldBe (3309.2799999999997 plusOrMinus 1E-6) - compute(900.0) shouldBe (2688.4799999999996 plusOrMinus 1E-6) - compute(1000.0) shouldBe (2131.68 plusOrMinus 1E-6) - compute(1100.0) shouldBe (1638.88 plusOrMinus 1E-6) - compute(1200.0) shouldBe (1210.0800000000002 plusOrMinus 1E-6) - compute(1300.0) shouldBe (845.2800000000001 plusOrMinus 1E-6) - compute(1400.0) shouldBe (544.48 plusOrMinus 1E-6) - compute(1500.0) shouldBe (307.68000000000006 plusOrMinus 1E-6) - compute(1600.0) shouldBe (134.88000000000002 plusOrMinus 1E-6) - compute(1700.0) shouldBe (26.08 plusOrMinus 1E-6) - compute(1800.0) shouldBe (-18.72 plusOrMinus 1E-6) - compute(1900.0) shouldBe (0.480000000000004 plusOrMinus 1E-6) - compute(2000.0) shouldBe (83.68 plusOrMinus 1E-6) - compute(2100.0) shouldBe (230.87999999999997 plusOrMinus 1E-6) - compute(2200.0) shouldBe (442.08 plusOrMinus 1E-6) - compute(2300.0) shouldBe (717.28 plusOrMinus 1E-6) - compute(2400.0) shouldBe (1056.48 plusOrMinus 1E-6) - compute(2500.0) shouldBe (1459.6799999999998 plusOrMinus 1E-6) - compute(2600.0) shouldBe (1926.8799999999999 plusOrMinus 1E-6) - compute(2700.0) shouldBe (2458.0800000000004 plusOrMinus 1E-6) - compute(2800.0) shouldBe (3053.2800000000007 plusOrMinus 1E-6) - compute(2900.0) shouldBe (3712.4800000000005 plusOrMinus 1E-6) - compute(3000.0) shouldBe (4435.68 plusOrMinus 1E-6) - } - "derivative" { - with(derivative) { - compute(0.0) shouldBe (-11.648 plusOrMinus 1E-6) - compute(100.0) shouldBe (-11.008 plusOrMinus 1E-6) - compute(200.0) shouldBe (-10.368 plusOrMinus 1E-6) - compute(300.0) shouldBe (-9.728 plusOrMinus 1E-6) - compute(400.0) shouldBe (-9.088 plusOrMinus 1E-6) - compute(500.0) shouldBe (-8.448 plusOrMinus 1E-6) - compute(600.0) shouldBe (-7.808 plusOrMinus 1E-6) - compute(700.0) shouldBe (-7.167999999999999 plusOrMinus 1E-6) - compute(800.0) shouldBe (-6.528 plusOrMinus 1E-6) - compute(900.0) shouldBe (-5.888 plusOrMinus 1E-6) - compute(1000.0) shouldBe (-5.247999999999999 plusOrMinus 1E-6) - compute(1100.0) shouldBe (-4.6080000000000005 plusOrMinus 1E-6) - compute(1200.0) shouldBe (-3.9680000000000004 plusOrMinus 1E-6) - compute(1300.0) shouldBe (-3.3280000000000003 plusOrMinus 1E-6) - compute(1400.0) shouldBe (-2.688 plusOrMinus 1E-6) - compute(1500.0) shouldBe (-2.048 plusOrMinus 1E-6) - compute(1600.0) shouldBe (-1.4080000000000001 plusOrMinus 1E-6) - compute(1700.0) shouldBe (-0.768 plusOrMinus 1E-6) - compute(1800.0) shouldBe (-0.128 plusOrMinus 1E-6) - compute(1900.0) shouldBe (0.512 plusOrMinus 1E-6) - compute(2000.0) shouldBe (1.1520000000000001 plusOrMinus 1E-6) - compute(2100.0) shouldBe (1.7919999999999998 plusOrMinus 1E-6) - compute(2200.0) shouldBe (2.432 plusOrMinus 1E-6) - compute(2300.0) shouldBe (3.072 plusOrMinus 1E-6) - compute(2400.0) shouldBe (3.7119999999999997 plusOrMinus 1E-6) - compute(2500.0) shouldBe (4.352 plusOrMinus 1E-6) - compute(2600.0) shouldBe (4.992 plusOrMinus 1E-6) - compute(2700.0) shouldBe (5.632000000000001 plusOrMinus 1E-6) - compute(2800.0) shouldBe (6.272 plusOrMinus 1E-6) - compute(2900.0) shouldBe (6.912000000000001 plusOrMinus 1E-6) - compute(3000.0) shouldBe (7.5520000000000005 plusOrMinus 1E-6) - } + @Test + fun parabola() { + compute(0.0) shouldBe (10579.679999999998 plusOrMinus 1E-6) + compute(100.0) shouldBe (9446.88 plusOrMinus 1E-6) + compute(200.0) shouldBe (8378.08 plusOrMinus 1E-6) + compute(300.0) shouldBe (7373.28 plusOrMinus 1E-6) + compute(400.0) shouldBe (6432.48 plusOrMinus 1E-6) + compute(500.0) shouldBe (5555.679999999999 plusOrMinus 1E-6) + compute(600.0) shouldBe (4742.879999999999 plusOrMinus 1E-6) + compute(700.0) shouldBe (3994.0799999999995 plusOrMinus 1E-6) + compute(800.0) shouldBe (3309.2799999999997 plusOrMinus 1E-6) + compute(900.0) shouldBe (2688.4799999999996 plusOrMinus 1E-6) + compute(1000.0) shouldBe (2131.68 plusOrMinus 1E-6) + compute(1100.0) shouldBe (1638.88 plusOrMinus 1E-6) + compute(1200.0) shouldBe (1210.0800000000002 plusOrMinus 1E-6) + compute(1300.0) shouldBe (845.2800000000001 plusOrMinus 1E-6) + compute(1400.0) shouldBe (544.48 plusOrMinus 1E-6) + compute(1500.0) shouldBe (307.68000000000006 plusOrMinus 1E-6) + compute(1600.0) shouldBe (134.88000000000002 plusOrMinus 1E-6) + compute(1700.0) shouldBe (26.08 plusOrMinus 1E-6) + compute(1800.0) shouldBe (-18.72 plusOrMinus 1E-6) + compute(1900.0) shouldBe (0.480000000000004 plusOrMinus 1E-6) + compute(2000.0) shouldBe (83.68 plusOrMinus 1E-6) + compute(2100.0) shouldBe (230.87999999999997 plusOrMinus 1E-6) + compute(2200.0) shouldBe (442.08 plusOrMinus 1E-6) + compute(2300.0) shouldBe (717.28 plusOrMinus 1E-6) + compute(2400.0) shouldBe (1056.48 plusOrMinus 1E-6) + compute(2500.0) shouldBe (1459.6799999999998 plusOrMinus 1E-6) + compute(2600.0) shouldBe (1926.8799999999999 plusOrMinus 1E-6) + compute(2700.0) shouldBe (2458.0800000000004 plusOrMinus 1E-6) + compute(2800.0) shouldBe (3053.2800000000007 plusOrMinus 1E-6) + compute(2900.0) shouldBe (3712.4800000000005 plusOrMinus 1E-6) + compute(3000.0) shouldBe (4435.68 plusOrMinus 1E-6) + } + + @Test + fun derivative() { + with(derivative) { + compute(0.0) shouldBe (-11.648 plusOrMinus 1E-6) + compute(100.0) shouldBe (-11.008 plusOrMinus 1E-6) + compute(200.0) shouldBe (-10.368 plusOrMinus 1E-6) + compute(300.0) shouldBe (-9.728 plusOrMinus 1E-6) + compute(400.0) shouldBe (-9.088 plusOrMinus 1E-6) + compute(500.0) shouldBe (-8.448 plusOrMinus 1E-6) + compute(600.0) shouldBe (-7.808 plusOrMinus 1E-6) + compute(700.0) shouldBe (-7.167999999999999 plusOrMinus 1E-6) + compute(800.0) shouldBe (-6.528 plusOrMinus 1E-6) + compute(900.0) shouldBe (-5.888 plusOrMinus 1E-6) + compute(1000.0) shouldBe (-5.247999999999999 plusOrMinus 1E-6) + compute(1100.0) shouldBe (-4.6080000000000005 plusOrMinus 1E-6) + compute(1200.0) shouldBe (-3.9680000000000004 plusOrMinus 1E-6) + compute(1300.0) shouldBe (-3.3280000000000003 plusOrMinus 1E-6) + compute(1400.0) shouldBe (-2.688 plusOrMinus 1E-6) + compute(1500.0) shouldBe (-2.048 plusOrMinus 1E-6) + compute(1600.0) shouldBe (-1.4080000000000001 plusOrMinus 1E-6) + compute(1700.0) shouldBe (-0.768 plusOrMinus 1E-6) + compute(1800.0) shouldBe (-0.128 plusOrMinus 1E-6) + compute(1900.0) shouldBe (0.512 plusOrMinus 1E-6) + compute(2000.0) shouldBe (1.1520000000000001 plusOrMinus 1E-6) + compute(2100.0) shouldBe (1.7919999999999998 plusOrMinus 1E-6) + compute(2200.0) shouldBe (2.432 plusOrMinus 1E-6) + compute(2300.0) shouldBe (3.072 plusOrMinus 1E-6) + compute(2400.0) shouldBe (3.7119999999999997 plusOrMinus 1E-6) + compute(2500.0) shouldBe (4.352 plusOrMinus 1E-6) + compute(2600.0) shouldBe (4.992 plusOrMinus 1E-6) + compute(2700.0) shouldBe (5.632000000000001 plusOrMinus 1E-6) + compute(2800.0) shouldBe (6.272 plusOrMinus 1E-6) + compute(2900.0) shouldBe (6.912000000000001 plusOrMinus 1E-6) + compute(3000.0) shouldBe (7.5520000000000005 plusOrMinus 1E-6) } } } diff --git a/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrisonHohenkerk2016Test.kt b/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrisonHohenkerk2016Test.kt index e8e40f14e..34723e01a 100644 --- a/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrisonHohenkerk2016Test.kt +++ b/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrisonHohenkerk2016Test.kt @@ -1,79 +1,80 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.ParabolaOfStephensonMorrisonHohenkerk2016 import nebulosa.time.Spline +import org.junit.jupiter.api.Test -class ParabolaOfStephensonMorrisonHohenkerk2016Test : StringSpec(), Spline by ParabolaOfStephensonMorrisonHohenkerk2016 { +class ParabolaOfStephensonMorrisonHohenkerk2016Test : Spline by ParabolaOfStephensonMorrisonHohenkerk2016 { - init { - "parabola" { - compute(0.0) shouldBe (10504.53125 plusOrMinus 1E-6) - compute(100.0) shouldBe (9350.78125 plusOrMinus 1E-6) - compute(200.0) shouldBe (8262.03125 plusOrMinus 1E-6) - compute(300.0) shouldBe (7238.28125 plusOrMinus 1E-6) - compute(400.0) shouldBe (6279.53125 plusOrMinus 1E-6) - compute(500.0) shouldBe (5385.78125 plusOrMinus 1E-6) - compute(600.0) shouldBe (4557.03125 plusOrMinus 1E-6) - compute(700.0) shouldBe (3793.28125 plusOrMinus 1E-6) - compute(800.0) shouldBe (3094.53125 plusOrMinus 1E-6) - compute(900.0) shouldBe (2460.78125 plusOrMinus 1E-6) - compute(1000.0) shouldBe (1892.03125 plusOrMinus 1E-6) - compute(1100.0) shouldBe (1388.28125 plusOrMinus 1E-6) - compute(1200.0) shouldBe (949.53125 plusOrMinus 1E-6) - compute(1300.0) shouldBe (575.78125 plusOrMinus 1E-6) - compute(1400.0) shouldBe (267.03125 plusOrMinus 1E-6) - compute(1500.0) shouldBe (23.28125 plusOrMinus 1E-6) - compute(1600.0) shouldBe (-155.46875 plusOrMinus 1E-6) - compute(1700.0) shouldBe (-269.21875 plusOrMinus 1E-6) - compute(1800.0) shouldBe (-317.96875 plusOrMinus 1E-6) - compute(1900.0) shouldBe (-301.71875 plusOrMinus 1E-6) - compute(2000.0) shouldBe (-220.46875 plusOrMinus 1E-6) - compute(2100.0) shouldBe (-74.21875 plusOrMinus 1E-6) - compute(2200.0) shouldBe (137.03125 plusOrMinus 1E-6) - compute(2300.0) shouldBe (413.28125 plusOrMinus 1E-6) - compute(2400.0) shouldBe (754.53125 plusOrMinus 1E-6) - compute(2500.0) shouldBe (1160.78125 plusOrMinus 1E-6) - compute(2600.0) shouldBe (1632.03125 plusOrMinus 1E-6) - compute(2700.0) shouldBe (2168.28125 plusOrMinus 1E-6) - compute(2800.0) shouldBe (2769.53125 plusOrMinus 1E-6) - compute(2900.0) shouldBe (3435.78125 plusOrMinus 1E-6) - compute(3000.0) shouldBe (4167.03125 plusOrMinus 1E-6) - } - "derivative" { - with(derivative) { - compute(0.0) shouldBe (-11.8625 plusOrMinus 1E-6) - compute(100.0) shouldBe (-11.2125 plusOrMinus 1E-6) - compute(200.0) shouldBe (-10.5625 plusOrMinus 1E-6) - compute(300.0) shouldBe (-9.9125 plusOrMinus 1E-6) - compute(400.0) shouldBe (-9.262500000000001 plusOrMinus 1E-6) - compute(500.0) shouldBe (-8.6125 plusOrMinus 1E-6) - compute(600.0) shouldBe (-7.9625 plusOrMinus 1E-6) - compute(700.0) shouldBe (-7.3125 plusOrMinus 1E-6) - compute(800.0) shouldBe (-6.6625000000000005 plusOrMinus 1E-6) - compute(900.0) shouldBe (-6.0125 plusOrMinus 1E-6) - compute(1000.0) shouldBe (-5.3625 plusOrMinus 1E-6) - compute(1100.0) shouldBe (-4.7125 plusOrMinus 1E-6) - compute(1200.0) shouldBe (-4.0625 plusOrMinus 1E-6) - compute(1300.0) shouldBe (-3.4125 plusOrMinus 1E-6) - compute(1400.0) shouldBe (-2.7625 plusOrMinus 1E-6) - compute(1500.0) shouldBe (-2.1125000000000003 plusOrMinus 1E-6) - compute(1600.0) shouldBe (-1.4625000000000001 plusOrMinus 1E-6) - compute(1700.0) shouldBe (-0.8125 plusOrMinus 1E-6) - compute(1800.0) shouldBe (-0.1625 plusOrMinus 1E-6) - compute(1900.0) shouldBe (0.48750000000000004 plusOrMinus 1E-6) - compute(2000.0) shouldBe (1.1375 plusOrMinus 1E-6) - compute(2100.0) shouldBe (1.7875 plusOrMinus 1E-6) - compute(2200.0) shouldBe (2.4375 plusOrMinus 1E-6) - compute(2300.0) shouldBe (3.0875 plusOrMinus 1E-6) - compute(2400.0) shouldBe (3.7375000000000003 plusOrMinus 1E-6) - compute(2500.0) shouldBe (4.3875 plusOrMinus 1E-6) - compute(2600.0) shouldBe (5.0375000000000005 plusOrMinus 1E-6) - compute(2700.0) shouldBe (5.6875 plusOrMinus 1E-6) - compute(2800.0) shouldBe (6.3375 plusOrMinus 1E-6) - compute(2900.0) shouldBe (6.9875 plusOrMinus 1E-6) - compute(3000.0) shouldBe (7.6375 plusOrMinus 1E-6) - } + @Test + fun parabola() { + compute(0.0) shouldBe (10504.53125 plusOrMinus 1E-6) + compute(100.0) shouldBe (9350.78125 plusOrMinus 1E-6) + compute(200.0) shouldBe (8262.03125 plusOrMinus 1E-6) + compute(300.0) shouldBe (7238.28125 plusOrMinus 1E-6) + compute(400.0) shouldBe (6279.53125 plusOrMinus 1E-6) + compute(500.0) shouldBe (5385.78125 plusOrMinus 1E-6) + compute(600.0) shouldBe (4557.03125 plusOrMinus 1E-6) + compute(700.0) shouldBe (3793.28125 plusOrMinus 1E-6) + compute(800.0) shouldBe (3094.53125 plusOrMinus 1E-6) + compute(900.0) shouldBe (2460.78125 plusOrMinus 1E-6) + compute(1000.0) shouldBe (1892.03125 plusOrMinus 1E-6) + compute(1100.0) shouldBe (1388.28125 plusOrMinus 1E-6) + compute(1200.0) shouldBe (949.53125 plusOrMinus 1E-6) + compute(1300.0) shouldBe (575.78125 plusOrMinus 1E-6) + compute(1400.0) shouldBe (267.03125 plusOrMinus 1E-6) + compute(1500.0) shouldBe (23.28125 plusOrMinus 1E-6) + compute(1600.0) shouldBe (-155.46875 plusOrMinus 1E-6) + compute(1700.0) shouldBe (-269.21875 plusOrMinus 1E-6) + compute(1800.0) shouldBe (-317.96875 plusOrMinus 1E-6) + compute(1900.0) shouldBe (-301.71875 plusOrMinus 1E-6) + compute(2000.0) shouldBe (-220.46875 plusOrMinus 1E-6) + compute(2100.0) shouldBe (-74.21875 plusOrMinus 1E-6) + compute(2200.0) shouldBe (137.03125 plusOrMinus 1E-6) + compute(2300.0) shouldBe (413.28125 plusOrMinus 1E-6) + compute(2400.0) shouldBe (754.53125 plusOrMinus 1E-6) + compute(2500.0) shouldBe (1160.78125 plusOrMinus 1E-6) + compute(2600.0) shouldBe (1632.03125 plusOrMinus 1E-6) + compute(2700.0) shouldBe (2168.28125 plusOrMinus 1E-6) + compute(2800.0) shouldBe (2769.53125 plusOrMinus 1E-6) + compute(2900.0) shouldBe (3435.78125 plusOrMinus 1E-6) + compute(3000.0) shouldBe (4167.03125 plusOrMinus 1E-6) + } + + @Test + fun derivative() { + with(derivative) { + compute(0.0) shouldBe (-11.8625 plusOrMinus 1E-6) + compute(100.0) shouldBe (-11.2125 plusOrMinus 1E-6) + compute(200.0) shouldBe (-10.5625 plusOrMinus 1E-6) + compute(300.0) shouldBe (-9.9125 plusOrMinus 1E-6) + compute(400.0) shouldBe (-9.262500000000001 plusOrMinus 1E-6) + compute(500.0) shouldBe (-8.6125 plusOrMinus 1E-6) + compute(600.0) shouldBe (-7.9625 plusOrMinus 1E-6) + compute(700.0) shouldBe (-7.3125 plusOrMinus 1E-6) + compute(800.0) shouldBe (-6.6625000000000005 plusOrMinus 1E-6) + compute(900.0) shouldBe (-6.0125 plusOrMinus 1E-6) + compute(1000.0) shouldBe (-5.3625 plusOrMinus 1E-6) + compute(1100.0) shouldBe (-4.7125 plusOrMinus 1E-6) + compute(1200.0) shouldBe (-4.0625 plusOrMinus 1E-6) + compute(1300.0) shouldBe (-3.4125 plusOrMinus 1E-6) + compute(1400.0) shouldBe (-2.7625 plusOrMinus 1E-6) + compute(1500.0) shouldBe (-2.1125000000000003 plusOrMinus 1E-6) + compute(1600.0) shouldBe (-1.4625000000000001 plusOrMinus 1E-6) + compute(1700.0) shouldBe (-0.8125 plusOrMinus 1E-6) + compute(1800.0) shouldBe (-0.1625 plusOrMinus 1E-6) + compute(1900.0) shouldBe (0.48750000000000004 plusOrMinus 1E-6) + compute(2000.0) shouldBe (1.1375 plusOrMinus 1E-6) + compute(2100.0) shouldBe (1.7875 plusOrMinus 1E-6) + compute(2200.0) shouldBe (2.4375 plusOrMinus 1E-6) + compute(2300.0) shouldBe (3.0875 plusOrMinus 1E-6) + compute(2400.0) shouldBe (3.7375000000000003 plusOrMinus 1E-6) + compute(2500.0) shouldBe (4.3875 plusOrMinus 1E-6) + compute(2600.0) shouldBe (5.0375000000000005 plusOrMinus 1E-6) + compute(2700.0) shouldBe (5.6875 plusOrMinus 1E-6) + compute(2800.0) shouldBe (6.3375 plusOrMinus 1E-6) + compute(2900.0) shouldBe (6.9875 plusOrMinus 1E-6) + compute(3000.0) shouldBe (7.6375 plusOrMinus 1E-6) } } } diff --git a/nebulosa-time/src/test/kotlin/S15Test.kt b/nebulosa-time/src/test/kotlin/S15Test.kt index 6d7cf2034..5e125ffc7 100644 --- a/nebulosa-time/src/test/kotlin/S15Test.kt +++ b/nebulosa-time/src/test/kotlin/S15Test.kt @@ -1,79 +1,80 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.S15 import nebulosa.time.Spline +import org.junit.jupiter.api.Test -class S15Test : StringSpec(), Spline by S15 { +class S15Test : Spline by S15 { - init { - "curve" { - compute(0.0) shouldBe (10441.312576 plusOrMinus 1E-8) - compute(100.0) shouldBe (9405.044447999999 plusOrMinus 1E-8) - compute(200.0) shouldBe (8424.698832 plusOrMinus 1E-8) - compute(300.0) shouldBe (7476.110944 plusOrMinus 1E-8) - compute(400.0) shouldBe (6535.116 plusOrMinus 1E-8) - compute(500.0) shouldBe (5586.600523148149 plusOrMinus 1E-8) - compute(600.0) shouldBe (4651.654629629629 plusOrMinus 1E-8) - compute(700.0) shouldBe (3760.419625 plusOrMinus 1E-8) - compute(800.0) shouldBe (2943.036814814815 plusOrMinus 1E-8) - compute(900.0) shouldBe (2229.647504629629 plusOrMinus 1E-8) - compute(1000.0) shouldBe (1650.393 plusOrMinus 1E-8) - compute(1100.0) shouldBe (1222.8812962962963 plusOrMinus 1E-8) - compute(1200.0) shouldBe (914.6107037037036 plusOrMinus 1E-8) - compute(1300.0) shouldBe (681.149 plusOrMinus 1E-8) - compute(1400.0) shouldBe (482.288 plusOrMinus 1E-8) - compute(1500.0) shouldBe (292.343 plusOrMinus 1E-8) - compute(1600.0) shouldBe (109.127 plusOrMinus 1E-8) - compute(1700.0) shouldBe (14.099507288629734 plusOrMinus 1E-8) - compute(1800.0) shouldBe (18.367 plusOrMinus 1E-8) - compute(1900.0) shouldBe (-1.977 plusOrMinus 1E-8) - compute(2000.0) shouldBe (63.808962962962966 plusOrMinus 1E-8) - compute(2100.0) shouldBe (-2952.9510000000005 plusOrMinus 1E-8) - compute(2200.0) shouldBe (-31950.310259259262 plusOrMinus 1E-8) - compute(2300.0) shouldBe (-117798.78062962965 plusOrMinus 1E-8) - compute(2400.0) shouldBe (-291387.25100000005 plusOrMinus 1E-8) - compute(2500.0) shouldBe (-583604.6102592595 plusOrMinus 1E-8) - compute(2600.0) shouldBe (-1025339.7472962962 plusOrMinus 1E-8) - compute(2700.0) shouldBe (-1647481.5510000002 plusOrMinus 1E-8) - compute(2800.0) shouldBe (-2480918.9102592585 plusOrMinus 1E-8) - compute(2900.0) shouldBe (-3556540.7139629633 plusOrMinus 1E-8) - compute(3000.0) shouldBe (-4905235.851000001 plusOrMinus 1E-8) - } - "derivative" { - with(derivative) { - compute(0.0) shouldBe (-10.72284312 plusOrMinus 1E-8) - compute(100.0) shouldBe (-10.04279408 plusOrMinus 1E-8) - compute(200.0) shouldBe (-9.60439288 plusOrMinus 1E-8) - compute(300.0) shouldBe (-9.40763952 plusOrMinus 1E-8) - compute(400.0) shouldBe (-9.452531666666667 plusOrMinus 1E-8) - compute(500.0) shouldBe (-9.467542361111112 plusOrMinus 1E-8) - compute(600.0) shouldBe (-9.181140000000001 plusOrMinus 1E-8) - compute(700.0) shouldBe (-8.593324583333334 plusOrMinus 1E-8) - compute(800.0) shouldBe (-7.704096111111111 plusOrMinus 1E-8) - compute(900.0) shouldBe (-6.513454583333333 plusOrMinus 1E-8) - compute(1000.0) shouldBe (-5.0214 plusOrMinus 1E-8) - compute(1100.0) shouldBe (-3.6039333333333334 plusOrMinus 1E-8) - compute(1200.0) shouldBe (-2.635517777777778 plusOrMinus 1E-8) - compute(1300.0) shouldBe (-2.106725 plusOrMinus 1E-8) - compute(1400.0) shouldBe (-1.9072624999999999 plusOrMinus 1E-8) - compute(1500.0) shouldBe (-1.9284100000000002 plusOrMinus 1E-8) - compute(1600.0) shouldBe (-1.5739400000000001 plusOrMinus 1E-8) - compute(1700.0) shouldBe (-0.23690262390670547 plusOrMinus 1E-8) - compute(1800.0) shouldBe (-0.34809999999999997 plusOrMinus 1E-8) - compute(1900.0) shouldBe (1.143 plusOrMinus 1E-8) - compute(2000.0) shouldBe (0.32577777777777783 plusOrMinus 1E-8) - compute(2100.0) shouldBe (-108.681 plusOrMinus 1E-8) - compute(2200.0) shouldBe (-522.7476666666668 plusOrMinus 1E-8) - compute(2300.0) shouldBe (-1245.7032222222224 plusOrMinus 1E-8) - compute(2400.0) shouldBe (-2277.547666666667 plusOrMinus 1E-8) - compute(2500.0) shouldBe (-3618.281000000001 plusOrMinus 1E-8) - compute(2600.0) shouldBe (-5267.903222222221 plusOrMinus 1E-8) - compute(2700.0) shouldBe (-7226.414333333333 plusOrMinus 1E-8) - compute(2800.0) shouldBe (-9493.814333333332 plusOrMinus 1E-8) - compute(2900.0) shouldBe (-12070.103222222226 plusOrMinus 1E-8) - compute(3000.0) shouldBe (-14955.281000000003 plusOrMinus 1E-8) - } + @Test + fun curve() { + compute(0.0) shouldBe (10441.312576 plusOrMinus 1E-8) + compute(100.0) shouldBe (9405.044447999999 plusOrMinus 1E-8) + compute(200.0) shouldBe (8424.698832 plusOrMinus 1E-8) + compute(300.0) shouldBe (7476.110944 plusOrMinus 1E-8) + compute(400.0) shouldBe (6535.116 plusOrMinus 1E-8) + compute(500.0) shouldBe (5586.600523148149 plusOrMinus 1E-8) + compute(600.0) shouldBe (4651.654629629629 plusOrMinus 1E-8) + compute(700.0) shouldBe (3760.419625 plusOrMinus 1E-8) + compute(800.0) shouldBe (2943.036814814815 plusOrMinus 1E-8) + compute(900.0) shouldBe (2229.647504629629 plusOrMinus 1E-8) + compute(1000.0) shouldBe (1650.393 plusOrMinus 1E-8) + compute(1100.0) shouldBe (1222.8812962962963 plusOrMinus 1E-8) + compute(1200.0) shouldBe (914.6107037037036 plusOrMinus 1E-8) + compute(1300.0) shouldBe (681.149 plusOrMinus 1E-8) + compute(1400.0) shouldBe (482.288 plusOrMinus 1E-8) + compute(1500.0) shouldBe (292.343 plusOrMinus 1E-8) + compute(1600.0) shouldBe (109.127 plusOrMinus 1E-8) + compute(1700.0) shouldBe (14.099507288629734 plusOrMinus 1E-8) + compute(1800.0) shouldBe (18.367 plusOrMinus 1E-8) + compute(1900.0) shouldBe (-1.977 plusOrMinus 1E-8) + compute(2000.0) shouldBe (63.808962962962966 plusOrMinus 1E-8) + compute(2100.0) shouldBe (-2952.9510000000005 plusOrMinus 1E-8) + compute(2200.0) shouldBe (-31950.310259259262 plusOrMinus 1E-8) + compute(2300.0) shouldBe (-117798.78062962965 plusOrMinus 1E-8) + compute(2400.0) shouldBe (-291387.25100000005 plusOrMinus 1E-8) + compute(2500.0) shouldBe (-583604.6102592595 plusOrMinus 1E-8) + compute(2600.0) shouldBe (-1025339.7472962962 plusOrMinus 1E-8) + compute(2700.0) shouldBe (-1647481.5510000002 plusOrMinus 1E-8) + compute(2800.0) shouldBe (-2480918.9102592585 plusOrMinus 1E-8) + compute(2900.0) shouldBe (-3556540.7139629633 plusOrMinus 1E-8) + compute(3000.0) shouldBe (-4905235.851000001 plusOrMinus 1E-8) + } + + @Test + fun derivative() { + with(derivative) { + compute(0.0) shouldBe (-10.72284312 plusOrMinus 1E-8) + compute(100.0) shouldBe (-10.04279408 plusOrMinus 1E-8) + compute(200.0) shouldBe (-9.60439288 plusOrMinus 1E-8) + compute(300.0) shouldBe (-9.40763952 plusOrMinus 1E-8) + compute(400.0) shouldBe (-9.452531666666667 plusOrMinus 1E-8) + compute(500.0) shouldBe (-9.467542361111112 plusOrMinus 1E-8) + compute(600.0) shouldBe (-9.181140000000001 plusOrMinus 1E-8) + compute(700.0) shouldBe (-8.593324583333334 plusOrMinus 1E-8) + compute(800.0) shouldBe (-7.704096111111111 plusOrMinus 1E-8) + compute(900.0) shouldBe (-6.513454583333333 plusOrMinus 1E-8) + compute(1000.0) shouldBe (-5.0214 plusOrMinus 1E-8) + compute(1100.0) shouldBe (-3.6039333333333334 plusOrMinus 1E-8) + compute(1200.0) shouldBe (-2.635517777777778 plusOrMinus 1E-8) + compute(1300.0) shouldBe (-2.106725 plusOrMinus 1E-8) + compute(1400.0) shouldBe (-1.9072624999999999 plusOrMinus 1E-8) + compute(1500.0) shouldBe (-1.9284100000000002 plusOrMinus 1E-8) + compute(1600.0) shouldBe (-1.5739400000000001 plusOrMinus 1E-8) + compute(1700.0) shouldBe (-0.23690262390670547 plusOrMinus 1E-8) + compute(1800.0) shouldBe (-0.34809999999999997 plusOrMinus 1E-8) + compute(1900.0) shouldBe (1.143 plusOrMinus 1E-8) + compute(2000.0) shouldBe (0.32577777777777783 plusOrMinus 1E-8) + compute(2100.0) shouldBe (-108.681 plusOrMinus 1E-8) + compute(2200.0) shouldBe (-522.7476666666668 plusOrMinus 1E-8) + compute(2300.0) shouldBe (-1245.7032222222224 plusOrMinus 1E-8) + compute(2400.0) shouldBe (-2277.547666666667 plusOrMinus 1E-8) + compute(2500.0) shouldBe (-3618.281000000001 plusOrMinus 1E-8) + compute(2600.0) shouldBe (-5267.903222222221 plusOrMinus 1E-8) + compute(2700.0) shouldBe (-7226.414333333333 plusOrMinus 1E-8) + compute(2800.0) shouldBe (-9493.814333333332 plusOrMinus 1E-8) + compute(2900.0) shouldBe (-12070.103222222226 plusOrMinus 1E-8) + compute(3000.0) shouldBe (-14955.281000000003 plusOrMinus 1E-8) } } } diff --git a/nebulosa-time/src/test/kotlin/SystemClockTest.kt b/nebulosa-time/src/test/kotlin/SystemClockTest.kt new file mode 100644 index 000000000..09b50f117 --- /dev/null +++ b/nebulosa-time/src/test/kotlin/SystemClockTest.kt @@ -0,0 +1,22 @@ +import io.kotest.matchers.longs.shouldBeLessThanOrEqual +import io.kotest.matchers.types.shouldBeSameInstanceAs +import nebulosa.time.SystemClock +import org.junit.jupiter.api.Test +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.temporal.ChronoUnit +import java.util.TimeZone + +class SystemClockTest { + + @Test + fun systemDefault() { + ChronoUnit.MICROS.between(LocalDateTime.now(SystemClock), LocalDateTime.now()) shouldBeLessThanOrEqual 1000L + SystemClock.zone shouldBeSameInstanceAs ZoneId.systemDefault() + + TimeZone.setDefault(TimeZone.getTimeZone("America/Manaus")) + + ChronoUnit.MICROS.between(LocalDateTime.now(SystemClock), LocalDateTime.now()) shouldBeLessThanOrEqual 1000L + SystemClock.zone shouldBeSameInstanceAs ZoneId.systemDefault() + } +} diff --git a/nebulosa-time/src/test/kotlin/TAIMinusUTCTest.kt b/nebulosa-time/src/test/kotlin/TAIMinusUTCTest.kt index 28cbe7cc3..c98793722 100644 --- a/nebulosa-time/src/test/kotlin/TAIMinusUTCTest.kt +++ b/nebulosa-time/src/test/kotlin/TAIMinusUTCTest.kt @@ -1,17 +1,16 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.shouldBeExactly import nebulosa.time.TAIMinusUTC import nebulosa.time.TimeDelta import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test -class TAIMinusUTCTest : StringSpec(), TimeDelta by TAIMinusUTC { +class TAIMinusUTCTest : TimeDelta by TAIMinusUTC { - init { - "delta" { - delta(TimeYMDHMS(2003, 6, 1)) shouldBeExactly 32.0 - delta(TimeYMDHMS(2008, 1, 17)) shouldBeExactly 33.0 - delta(TimeYMDHMS(2017, 9, 1)) shouldBeExactly 37.0 - delta(TimeYMDHMS(2026, 1, 1)) shouldBeExactly 37.0 - } + @Test + fun delta() { + delta(TimeYMDHMS(2003, 6, 1)) shouldBeExactly 32.0 + delta(TimeYMDHMS(2008, 1, 17)) shouldBeExactly 33.0 + delta(TimeYMDHMS(2017, 9, 1)) shouldBeExactly 37.0 + delta(TimeYMDHMS(2026, 1, 1)) shouldBeExactly 37.0 } } diff --git a/nebulosa-time/src/test/kotlin/TDBMinusTTByFairheadAndBretagnon1990Test.kt b/nebulosa-time/src/test/kotlin/TDBMinusTTByFairheadAndBretagnon1990Test.kt index 213306b9f..749b3ff75 100644 --- a/nebulosa-time/src/test/kotlin/TDBMinusTTByFairheadAndBretagnon1990Test.kt +++ b/nebulosa-time/src/test/kotlin/TDBMinusTTByFairheadAndBretagnon1990Test.kt @@ -1,18 +1,17 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.TDB import nebulosa.time.TDBMinusTTByFairheadAndBretagnon1990 import nebulosa.time.TimeDelta +import org.junit.jupiter.api.Test -class TDBMinusTTByFairheadAndBretagnon1990Test : StringSpec(), TimeDelta by TDBMinusTTByFairheadAndBretagnon1990 { +class TDBMinusTTByFairheadAndBretagnon1990Test : TimeDelta by TDBMinusTTByFairheadAndBretagnon1990 { - init { - "delta" { - delta(TDB(2440423.345833333)) shouldBe (-0.00046798717637519603 plusOrMinus 1e-16) - delta(TDB(2448031.5)) shouldBe (0.0011585185926349208 plusOrMinus 1e-16) - delta(TDB(2451545.0)) shouldBe (-9.575743486095212e-05 plusOrMinus 1e-16) - delta(TDB(2456164.5)) shouldBe (-0.001241030165936046 plusOrMinus 1e-16) - } + @Test + fun delta() { + delta(TDB(2440423.345833333)) shouldBe (-0.00046798717637519603 plusOrMinus 1e-16) + delta(TDB(2448031.5)) shouldBe (0.0011585185926349208 plusOrMinus 1e-16) + delta(TDB(2451545.0)) shouldBe (-9.575743486095212e-05 plusOrMinus 1e-16) + delta(TDB(2456164.5)) shouldBe (-0.001241030165936046 plusOrMinus 1e-16) } } diff --git a/nebulosa-time/src/test/kotlin/TimeTest.kt b/nebulosa-time/src/test/kotlin/TimeTest.kt index 7c35d70cd..c654c5d15 100644 --- a/nebulosa-time/src/test/kotlin/TimeTest.kt +++ b/nebulosa-time/src/test/kotlin/TimeTest.kt @@ -1,960 +1,1034 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.* -import java.nio.file.Path +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test import java.time.LocalDateTime import java.time.ZoneOffset import kotlin.io.path.inputStream -class TimeTest : StringSpec() { - - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) - - val iersb = IERSB() - iersb.load(Path.of("../data/eopc04.1962-now.txt").inputStream()) - - IERS.attach(IERSAB(iersa, iersb)) - - "convert jd to datetime" { - TimeJD(2459902.1234).asDateTime().toString() shouldBe "2022-11-18T14:57:41.759993433" - TimeJD(2459902.6789).asDateTime().toString() shouldBe "2022-11-19T04:17:36.960014998" - TimeJD(2299161.0).asDateTime().toString() shouldBe "1582-10-15T12:00" - TimeJD(2299160.0).asDateTime(JulianCalendarCutOff.GREGORIAN_START).toString() shouldBe "1582-10-04T12:00" - } - "jd as year, month, day and fraction" { - val (yearMonthDay, fraction) = TimeJD(2400000.5, 50123.9999).asYearMonthDayAndFraction() - val (year, month, day) = yearMonthDay - year shouldBeExactly 1996 - month shouldBeExactly 2 - day shouldBeExactly 10 - fraction[0] shouldBe (0.9999 plusOrMinus 1e-9) - } - "time unix & time ymdhms" { - val now = LocalDateTime.now(ZoneOffset.UTC).withNano(0) - val unix = TimeUnix(now.toEpochSecond(ZoneOffset.UTC).toDouble()).value - val ymdhms = TimeYMDHMS(now).value - ymdhms shouldBeExactly unix - } - "high precision jd" { - val jd = TimeJD(2444495.5, 0.4788310185185185) - jd.whole shouldBe (2444496.0 plusOrMinus 1E-16) - jd.fraction shouldBe (-0.021168981481481497 plusOrMinus 1E-16) - } - "jd" { - val jd = TimeJD(2444495.9788310183) - jd.whole shouldBe (2444496.0 plusOrMinus 1E-9) - jd.fraction shouldBe (-0.021168981 plusOrMinus 1E-9) - } - "ymdhms" { - val jd = TimeYMDHMS(1980, 9, 12, 23, 29, 31.0) - jd.whole shouldBe (2444495.0 plusOrMinus 1E-16) - jd.fraction shouldBe (0.4788310185185185 plusOrMinus 1E-16) - } - "mjd" { - val mdj = TimeMJD(50001.47883101852) - mdj.whole shouldBe (2450002.0 plusOrMinus 1e-8) - mdj.fraction shouldBe (-0.021168981482333038 plusOrMinus 1e-12) - } - "utc:2022-01-01 12:00:00" { - val time = UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0004282407407407707 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008007407407407707 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008121958147759566 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008007396238607902 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.001055591574221432 plusOrMinus 1E-14) - } - } - "utc:2023-06-01 23:59:59" { - val time = UTC(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.499987890906273 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49958333333333327 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49921083333333327 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49919901829547053 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49921082275980533 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49894796237489814 plusOrMinus 1E-14) - } - } - "utc:2024-01-01 00:00:00" { - val time = UTC(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49999989822685187 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49957175925925923 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49919925925925923 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49918729577550847 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4991992606390423 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4989330976467987 plusOrMinus 1E-14) - } - } - "utc:2025-12-31 17:59:43" { - val time = UTC(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980371005787044 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25023148148148155 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25060398148148155 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2506164542459721 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2506039804506639 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25088147386323706 plusOrMinus 1E-14) - } - } - "ut1:2022-01-01 12:00:00" { - val time = UT1(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (1.2782876138691737e-06 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0004295190283546413 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008020190283546413 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.000813474102390718 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008020179114750974 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0010568698618555591 plusOrMinus 1E-14) - } - } - "ut1:2023-06-01 23:59:59" { - val time = UT1(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998896094558004 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49958279831367913 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49921029831367913 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49919848327581595 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4992102877401513 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4989474273552358 plusOrMinus 1E-14) - } - } - "ut1:2024-01-01 00:00:00" { - val time = UT1(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4999998982268517 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4995718610324076 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4991993610324076 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4991873975486568 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49919936241219065 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49893319941994874 plusOrMinus 1E-14) - } - } - "ut1:2025-12-31 17:59:43" { - val time = UT1(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980277142361115 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25023101216435195 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25060351216435195 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2506159849288422 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25060351113353413 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2508810045461 plusOrMinus 1E-14) - } - } - "tai:2022-01-01 12:00:00" { - val time = TAI(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00042824074074071516 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00042951902898347746 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.00037249999999999995 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.00038395507373673244 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.00037249888297375804 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0006273508266944423 plusOrMinus 1E-14) - } - } - "tai:2023-06-01 23:59:59" { - val time = TAI(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4995601851851852 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49955965016637977 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49963907407407404 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49962725903650973 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49963906350042875 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49937620312216147 plusOrMinus 1E-14) - } - } - "tai:2024-01-01 00:00:00" { - val time = TAI(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49957175925925923 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4995718610330666 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4996275 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49961553651654766 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4996275013799246 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49936133839432106 plusOrMinus 1E-14) - } - } - "tai:2025-12-31 17:59:43" { - val time = TAI(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.249375 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24937546931712967 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2501757407407408 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2501882135049329 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2501757397097831 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25045323311571627 plusOrMinus 1E-14) - } - } - "tt:2022-01-01 12:00:00" { - val time = TT(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008007407407407215 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008020190295288602 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00037249999999999995 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (1.1455073477126416e-05 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-1.1171534654999348e-09 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0002548508207915326 plusOrMinus 1E-14) - } - } - "tt:2023-06-01 23:59:59" { - val time = TT(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4991876851851852 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.499187150167117 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49961592592592596 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4999997590367693 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998843649967323 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49974870312783515 plusOrMinus 1E-14) - } - } - "tt:2024-01-01 00:00:00" { - val time = TT(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49919925925925923 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49919936103364004 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4996275 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49998803651680723 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4999999986199522 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4997338384002199 plusOrMinus 1E-14) - } - } - "tt:2025-12-31 17:59:43" { - val time = TT(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24900250000000002 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24900296931712967 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2494307407407408 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2498157135046733 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2498032397096612 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2500807331098187 plusOrMinus 1E-14) - } - } - "tcb:2022-01-01 12:00:00" { - val time = TCB(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.001055591557493719 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0010568698466550197 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0006273508167529781 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00025485081675297817 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00024339574345346468 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00025485193399348575 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - } - "tcb:2023-06-01 23:59:59" { - val time = TCB(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4989248142429501 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49892427922540217 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49935305498369087 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49972555498369087 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4997373700208124 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4997255655575102 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - } - "tcb:2024-01-01 00:00:00" { - val time = TCB(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.498933097663694 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4989331994384845 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49936133840443475 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49973383840443475 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.499745801887442 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49973383702429897 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - } - "tcb:2025-12-31 17:59:43" { - val time = TCB(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24872500763531546 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24872547695244512 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2491532483760562 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2495257483760562 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24953822113979535 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24952574734488586 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - } - "tcg:2022-01-01 12:00:00" { - val time = TCG(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008121958142098429 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.000813474103014745 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.000383955073469143 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-1.1455073469143043e-05 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-1.1456190626520906e-05 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.00024339574714086394 plusOrMinus 1E-14) - } - } - "tcg:2023-06-01 23:59:59" { - val time = TCG(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49917587014788867 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49917533512984386 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49960411088862944 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49997661088862944 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49997662146238 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49976051816531164 plusOrMinus 1E-14) - } - } - "tcg:2024-01-01 00:00:00" { - val time = TCG(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4991872957760748 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49918739755047403 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49961553651681556 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998803651681556 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4999880351367638 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49974580188359374 plusOrMinus 1E-14) - } - } - "tcg:2025-12-31 17:59:43" { - val time = TCG(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24899002723607622 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24899049655320588 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24941826797681696 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24979076797681696 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2497907669457333 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2500682603456974 plusOrMinus 1E-14) - } - } - "tdb:2022-01-01 12:00:00" { - val time = TDB(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008007396235872465 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008020179123753776 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00037249888284653445 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (1.1171534654999348e-09 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (1.1456190630592697e-05 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0002548519379450154 plusOrMinus 1E-14) - } - } - "tdb:2023-06-01 23:59:59" { - val time = TDB(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.499187674611438 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4991871395933698 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4996159153521787 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4999884153521787 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4999997696105166 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4997487137015826 plusOrMinus 1E-14) - } - } - "tdb:2024-01-01 00:00:00" { - val time = TDB(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.499199260639307 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4991993624136878 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49962750138004774 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49999999861995226 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4999880351367595 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49973383702017204 plusOrMinus 1E-14) - } - } - "tdb:2025-12-31 17:59:43" { - val time = TDB(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24900250103107957 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24900297034820923 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24943074177182037 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324177182037 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24981571453575288 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2500807341408983 plusOrMinus 1E-14) - } +class TimeTest { + + @Test + fun convertJdToDatetime() { + TimeJD(2459902.1234).asDateTime().toString() shouldBe "2022-11-18T14:57:41.759993433" + TimeJD(2459902.6789).asDateTime().toString() shouldBe "2022-11-19T04:17:36.960014998" + TimeJD(2299161.0).asDateTime().toString() shouldBe "1582-10-15T12:00" + TimeJD(2299160.0).asDateTime(JulianCalendarCutOff.GREGORIAN_START).toString() shouldBe "1582-10-04T12:00" + } + + @Test + fun jdAsYearMonthDayAndFraction() { + val (yearMonthDay, fraction) = TimeJD(2400000.5, 50123.9999).asYearMonthDayAndFraction() + val (year, month, day) = yearMonthDay + year shouldBeExactly 1996 + month shouldBeExactly 2 + day shouldBeExactly 10 + fraction[0] shouldBe (0.9999 plusOrMinus 1e-9) + } + + @Test + fun timeUnixAndTimeYmdhms() { + val now = LocalDateTime.now(ZoneOffset.UTC).withNano(0) + val unix = TimeUnix(now.toEpochSecond(ZoneOffset.UTC).toDouble()).value + val ymdhms = TimeYMDHMS(now).value + ymdhms shouldBeExactly unix + } + + @Test + fun highPrecisionJd() { + val jd = TimeJD(2444495.5, 0.4788310185185185) + jd.whole shouldBe (2444496.0 plusOrMinus 1E-16) + jd.fraction shouldBe (-0.021168981481481497 plusOrMinus 1E-16) + } + + @Test + fun jd() { + val jd = TimeJD(2444495.9788310183) + jd.whole shouldBe (2444496.0 plusOrMinus 1E-9) + jd.fraction shouldBe (-0.021168981 plusOrMinus 1E-9) + } + + @Test + fun ymdhms() { + val jd = TimeYMDHMS(1980, 9, 12, 23, 29, 31.0) + jd.whole shouldBe (2444495.0 plusOrMinus 1E-16) + jd.fraction shouldBe (0.4788310185185185 plusOrMinus 1E-16) + } + + @Test + fun mjd() { + val mdj = TimeMJD(50001.47883101852) + mdj.whole shouldBe (2450002.0 plusOrMinus 1e-8) + mdj.fraction shouldBe (-0.021168981482333038 plusOrMinus 1e-12) + } + + @Test + fun utc20220101120000() { + val time = UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0004282407407407707 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008007407407407707 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008121958147759566 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008007396238607902 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.001055591574221432 plusOrMinus 1E-14) + } + } + + @Test + fun utc20230601235959() { + val time = UTC(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.499987890906273 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49958333333333327 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49921083333333327 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49919901829547053 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49921082275980533 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49894796237489814 plusOrMinus 1E-14) + } + } + + @Test + fun utc20240101000000() { + val time = UTC(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49999989822685187 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49957175925925923 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49919925925925923 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49918729577550847 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4991992606390423 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4989330976467987 plusOrMinus 1E-14) + } + } + + @Test + fun utc20251231175943() { + val time = UTC(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980371005787044 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25023148148148155 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25060398148148155 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2506164542459721 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2506039804506639 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25088147386323706 plusOrMinus 1E-14) + } + } + + @Test + fun ut120220101120000() { + val time = UT1(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (1.2782876138691737e-06 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0004295190283546413 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008020190283546413 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.000813474102390718 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008020179114750974 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0010568698618555591 plusOrMinus 1E-14) + } + } + + @Test + fun ut120230601235959() { + val time = UT1(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998896094558004 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49958279831367913 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49921029831367913 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49919848327581595 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4992102877401513 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4989474273552358 plusOrMinus 1E-14) + } + } + + @Test + fun ut120240101000000() { + val time = UT1(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4999998982268517 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4995718610324076 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4991993610324076 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4991873975486568 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49919936241219065 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49893319941994874 plusOrMinus 1E-14) + } + } + + @Test + fun ut120251231175943() { + val time = UT1(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980277142361115 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25023101216435195 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25060351216435195 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2506159849288422 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25060351113353413 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2508810045461 plusOrMinus 1E-14) + } + } + + @Test + fun tai20220101120000() { + val time = TAI(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00042824074074071516 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00042951902898347746 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.00037249999999999995 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.00038395507373673244 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.00037249888297375804 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0006273508266944423 plusOrMinus 1E-14) + } + } + + @Test + fun tai20230601235959() { + val time = TAI(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4995601851851852 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49955965016637977 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49963907407407404 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49962725903650973 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49963906350042875 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49937620312216147 plusOrMinus 1E-14) + } + } + + @Test + fun tai20240101000000() { + val time = TAI(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49957175925925923 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4995718610330666 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4996275 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49961553651654766 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4996275013799246 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49936133839432106 plusOrMinus 1E-14) + } + } + + @Test + fun tai20251231175943() { + val time = TAI(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.249375 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24937546931712967 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2501757407407408 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2501882135049329 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2501757397097831 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25045323311571627 plusOrMinus 1E-14) + } + } + + @Test + fun tt20220101120000() { + val time = TT(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008007407407407215 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008020190295288602 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00037249999999999995 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (1.1455073477126416e-05 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-1.1171534654999348e-09 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0002548508207915326 plusOrMinus 1E-14) + } + } + + @Test + fun tt20230601235959() { + val time = TT(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4991876851851852 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.499187150167117 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49961592592592596 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4999997590367693 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998843649967323 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49974870312783515 plusOrMinus 1E-14) + } + } + + @Test + fun tt20240101000000() { + val time = TT(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49919925925925923 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49919936103364004 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4996275 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49998803651680723 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4999999986199522 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4997338384002199 plusOrMinus 1E-14) + } + } + + @Test + fun tt20251231175943() { + val time = TT(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24900250000000002 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24900296931712967 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2494307407407408 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2498157135046733 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2498032397096612 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2500807331098187 plusOrMinus 1E-14) + } + } + + @Test + fun tcb20220101120000() { + val time = TCB(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.001055591557493719 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0010568698466550197 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0006273508167529781 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00025485081675297817 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00024339574345346468 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00025485193399348575 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + } + + @Test + fun tcb20230601235959() { + val time = TCB(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4989248142429501 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49892427922540217 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49935305498369087 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49972555498369087 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4997373700208124 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4997255655575102 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + } + + @Test + fun tcb20240101000000() { + val time = TCB(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.498933097663694 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4989331994384845 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49936133840443475 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49973383840443475 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.499745801887442 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49973383702429897 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + } + + @Test + fun tcb20251231175943() { + val time = TCB(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24872500763531546 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24872547695244512 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2491532483760562 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2495257483760562 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24953822113979535 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24952574734488586 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + } + + @Test + fun tcg20220101120000() { + val time = TCG(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008121958142098429 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.000813474103014745 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.000383955073469143 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-1.1455073469143043e-05 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-1.1456190626520906e-05 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.00024339574714086394 plusOrMinus 1E-14) + } + } + + @Test + fun tcg20230601235959() { + val time = TCG(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49917587014788867 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49917533512984386 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49960411088862944 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49997661088862944 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49997662146238 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49976051816531164 plusOrMinus 1E-14) + } + } + + @Test + fun tcg20240101000000() { + val time = TCG(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4991872957760748 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49918739755047403 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49961553651681556 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998803651681556 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4999880351367638 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49974580188359374 plusOrMinus 1E-14) + } + } + + @Test + fun tcg20251231175943() { + val time = TCG(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24899002723607622 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24899049655320588 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24941826797681696 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24979076797681696 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2497907669457333 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2500682603456974 plusOrMinus 1E-14) + } + } + + @Test + fun tdb20220101120000() { + val time = TDB(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008007396235872465 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008020179123753776 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00037249888284653445 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (1.1171534654999348e-09 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (1.1456190630592697e-05 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0002548519379450154 plusOrMinus 1E-14) + } + } + + @Test + fun tdb20230601235959() { + val time = TDB(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.499187674611438 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4991871395933698 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4996159153521787 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4999884153521787 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4999997696105166 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4997487137015826 plusOrMinus 1E-14) + } + } + + @Test + fun tdb20240101000000() { + val time = TDB(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.499199260639307 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4991993624136878 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49962750138004774 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49999999861995226 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4999880351367595 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49973383702017204 plusOrMinus 1E-14) + } + } + + @Test + fun tdb20251231175943() { + val time = TDB(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24900250103107957 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24900297034820923 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24943074177182037 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324177182037 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24981571453575288 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2500807341408983 plusOrMinus 1E-14) + } + } + + companion object { + + @JvmStatic + @BeforeAll + fun loadIERS() { + val iersa = IERSA() + val iersb = IERSB() + dataDirectory.concat("finals2000A.all").inputStream().use(iersa::load) + dataDirectory.concat("eopc04.1962-now.txt").inputStream().use(iersb::load) + IERS.attach(IERSAB(iersa, iersb)) } } } diff --git a/nebulosa-vizier/src/test/kotlin/VizierServiceTest.kt b/nebulosa-vizier/src/test/kotlin/VizierServiceTest.kt index c05846618..8010ce996 100644 --- a/nebulosa-vizier/src/test/kotlin/VizierServiceTest.kt +++ b/nebulosa-vizier/src/test/kotlin/VizierServiceTest.kt @@ -1,29 +1,32 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import nebulosa.vizier.VizierTAPService +import org.junit.jupiter.api.Test -class VizierServiceTest : StringSpec() { +class VizierServiceTest { - init { - "query" { - val vizier = VizierTAPService() - val query = """ + @Test + fun query() { + val query = """ SELECT TOP 100 sao.SAO, sao.HD, sao.Pmag, sao.Vmag, sao.SpType, sao.RA2000, sao.DE2000, sao.pmRA2000, sao.pmDE2000 FROM "I/131A/sao" AS sao ORDER BY SAO ASC """.trimIndent() - val data = vizier.query(query).execute().body().shouldNotBeNull() - data.size shouldBeExactly 100 + val data = SERVICE.query(query).execute().body().shouldNotBeNull() + data.size shouldBeExactly 100 - data[0].getField("SAO") shouldBe "1" - data[0].getField("HD") shouldBe "225019" - data[0].getField("RA2000") shouldBe "0.6735416666666666" + data[0].getField("SAO") shouldBe "1" + data[0].getField("HD") shouldBe "225019" + data[0].getField("RA2000") shouldBe "0.6735416666666666" - data[99].getField("SAO") shouldBe "100" - data[99].getField("HD").trim() shouldBe "" - data[99].getField("RA2000") shouldBe "9.303554166666665" - } + data[99].getField("SAO") shouldBe "100" + data[99].getField("HD").trim() shouldBe "" + data[99].getField("RA2000") shouldBe "9.303554166666665" + } + + companion object { + + @JvmStatic private val SERVICE = VizierTAPService() } } diff --git a/nebulosa-watney/src/test/kotlin/EquationsTest.kt b/nebulosa-watney/src/test/kotlin/EquationsTest.kt index 1efb1a557..27548bf50 100644 --- a/nebulosa-watney/src/test/kotlin/EquationsTest.kt +++ b/nebulosa-watney/src/test/kotlin/EquationsTest.kt @@ -1,24 +1,23 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.watney.platesolver.math.solveLeastSquares +import org.junit.jupiter.api.Test -class EquationsTest : StringSpec() { +class EquationsTest { - init { - "solve least squares" { - val equations = listOf( - doubleArrayOf(7.0, -6.0, 8.0, -15.0), - doubleArrayOf(3.0, 5.0, -2.0, -27.0), - doubleArrayOf(2.0, -2.0, 7.0, -20.0), - doubleArrayOf(4.0, 2.0, -5.0, -2.0), - doubleArrayOf(9.0, -8.0, 7.0, -5.0), - ) + @Test + fun solveLeastSquares() { + val equations = listOf( + doubleArrayOf(7.0, -6.0, 8.0, -15.0), + doubleArrayOf(3.0, 5.0, -2.0, -27.0), + doubleArrayOf(2.0, -2.0, 7.0, -20.0), + doubleArrayOf(4.0, 2.0, -5.0, -2.0), + doubleArrayOf(9.0, -8.0, 7.0, -5.0), + ) - val (a, b, c) = solveLeastSquares(equations) - a shouldBe (2.474 plusOrMinus 1e-3) - b shouldBe (5.397 plusOrMinus 1e-3) - c shouldBe (3.723 plusOrMinus 1e-3) - } + val (a, b, c) = solveLeastSquares(equations) + a shouldBe (2.474 plusOrMinus 1e-3) + b shouldBe (5.397 plusOrMinus 1e-3) + c shouldBe (3.723 plusOrMinus 1e-3) } } diff --git a/nebulosa-watney/src/test/kotlin/QuadDatabaseTest.kt b/nebulosa-watney/src/test/kotlin/QuadDatabaseTest.kt index 30525a065..275c829fb 100644 --- a/nebulosa-watney/src/test/kotlin/QuadDatabaseTest.kt +++ b/nebulosa-watney/src/test/kotlin/QuadDatabaseTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.longs.shouldBeExactly import io.kotest.matchers.nulls.shouldNotBeNull @@ -8,47 +6,51 @@ import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldEndWith import nebulosa.io.ByteOrder import nebulosa.math.deg -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import nebulosa.test.concat +import nebulosa.test.homeDirectory import nebulosa.watney.platesolver.quad.CellStarQuad import nebulosa.watney.platesolver.quad.CompactQuadDatabase import nebulosa.watney.platesolver.quad.QuadDatabaseCellFileIndex -import java.nio.file.Path +import org.junit.jupiter.api.Test -@Suppress("NestedLambdaShadowedImplicitParameter") -@EnabledIf(NonGitHubOnlyCondition::class) -class QuadDatabaseTest : StringSpec() { +@NonGitHubOnly +class QuadDatabaseTest { - init { - val quadDir = Path.of("/home/tiagohm/Downloads/watneyqdb") + @Test + fun cellIndexFile() { + val source = QUAD_DIR.concat("gaia2-00-07-20.qdbindex") + val index = QuadDatabaseCellFileIndex.read(source) - "cell index file" { - val source = Path.of("$quadDir", "gaia2-00-07-20.qdbindex") - val index = QuadDatabaseCellFileIndex.read(source) + index.byteOrder shouldBe ByteOrder.LITTLE + index.files shouldHaveSize 406 + index.files.forEach { "${it.descriptor.path}" shouldContain it.descriptor.id shouldEndWith ".qdb" } + val totalSizeInBytes = index.files.sumOf { (descriptor) -> descriptor.passes.sumOf { it.dataBlockByteLength } } + totalSizeInBytes shouldBeExactly 397360102 - (406 * 13) // 406 files, 13 bytes header + } + + @Test + fun cellQuads() { + val source = QUAD_DIR.concat("gaia2-00-07-20.qdbindex") + val index = QuadDatabaseCellFileIndex.read(source) + val cellFile = index.files.find { it.descriptor.id == "b10c01" }.shouldNotBeNull() + val quads = cellFile.quads(0.0, 0.0, 22.5.deg, 1, 4, 0, STAR_QUADS) + quads shouldHaveSize 1 + } - index.byteOrder shouldBe ByteOrder.LITTLE - index.files shouldHaveSize 406 - index.files.forEach { it.descriptor.path.toString().shouldContain(it.descriptor.id) shouldEndWith ".qdb" } - val totalSizeInBytes = index.files.sumOf { it.descriptor.passes.sumOf { it.dataBlockByteLength } } - totalSizeInBytes shouldBeExactly 397360102 - (406 * 13) // 406 files, 13 bytes header - } - "cell quads" { - val source = Path.of("$quadDir", "gaia2-00-07-20.qdbindex") - val index = QuadDatabaseCellFileIndex.read(source) - val cellFile = index.files.find { it.descriptor.id == "b10c01" }.shouldNotBeNull() - val quads = cellFile.quads(0.0, 0.0, 22.5.deg, 1, 4, 0, STAR_QUADS) - quads shouldHaveSize 1 - } - "find quads" { - val quadDatabase = CompactQuadDatabase(quadDir) - val quads = quadDatabase.quads(0.0, 0.0, 22.5.deg, 0, intArrayOf(-2, -1, 0, 1, 2), 4, 0, STAR_QUADS) - // StarQuad([0.46529815, 0.56207234, 0.56207234, 0.7299413, 0.95694715], 0.3420919, [11.586886405944824, -14.680888175964355]), - // StarQuad([0.44183773, 0.5151515, 0.542522, 0.56751466, 0.8023483], 0.3123361, [5.6454176902771, 10.346973419189453]), - println(quads) - } + @Test + fun findQuads() { + val quadDatabase = CompactQuadDatabase(QUAD_DIR) + val quads = quadDatabase.quads(0.0, 0.0, 22.5.deg, 0, intArrayOf(-2, -1, 0, 1, 2), 4, 0, STAR_QUADS) + // StarQuad([0.46529815, 0.56207234, 0.56207234, 0.7299413, 0.95694715], 0.3420919, [11.586886405944824, -14.680888175964355]), + // StarQuad([0.44183773, 0.5151515, 0.542522, 0.56751466, 0.8023483], 0.3123361, [5.6454176902771, 10.346973419189453]), + println(quads) } companion object { + @JvmStatic private val QUAD_DIR = homeDirectory.concat("Downloads", "watneyqdb") + @JvmStatic private val STAR_QUADS = listOf( CellStarQuad(doubleArrayOf(0.48065594, 0.5005079, 0.52168995, 0.59679097, 0.9971423), 259.138, 1933.375, 135.125), CellStarQuad(doubleArrayOf(0.1827525, 0.2504689, 0.40770558, 0.81601685, 0.97027695), 150.899, 1103.0, 59.0), diff --git a/nebulosa-watney/src/test/kotlin/SearchStrategyTest.kt b/nebulosa-watney/src/test/kotlin/SearchStrategyTest.kt index bc1753a19..61f32cd5c 100644 --- a/nebulosa-watney/src/test/kotlin/SearchStrategyTest.kt +++ b/nebulosa-watney/src/test/kotlin/SearchStrategyTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.maps.shouldContainKey import nebulosa.math.deg @@ -8,56 +7,58 @@ import nebulosa.watney.platesolver.BlindSearchStrategy import nebulosa.watney.platesolver.BlindSearchStrategyOptions import nebulosa.watney.platesolver.NearbySearchStrategy import nebulosa.watney.platesolver.NearbySearchStrategyOptions +import org.junit.jupiter.api.Test -class SearchStrategyTest : StringSpec() { - - init { - "blind" { - var div = 128 - var i = 0 - - val searchRunSizes = intArrayOf(898184, 226556, 57644, 14916, 3964, 1096, 308, 76) - val searchRunSizesByRadius = listOf( - // @formatter:off - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728, 0.3515625 to 168912, 0.17578125 to 671628), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728, 0.3515625 to 168912), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788), - listOf(22.5 to 76, 11.25 to 232), - listOf(22.5 to 76), - // @formatter:on - ) - - while (div >= 1) { - val options = BlindSearchStrategyOptions(minRadius = (22.5 / div).deg) - val strategy = BlindSearchStrategy(options) - val searchRuns = strategy.searchQueue() - searchRuns shouldHaveSize searchRunSizes[i] - val groupedSearchRuns = searchRuns.groupBy { it.radius.toDegrees } - - searchRunSizesByRadius[i].forEach { - groupedSearchRuns shouldContainKey it.first - groupedSearchRuns[it.first]!! shouldHaveSize it.second - } - - div /= 2 - i++ +class SearchStrategyTest { + + @Test + fun blind() { + var div = 128 + var i = 0 + + val searchRunSizes = intArrayOf(898184, 226556, 57644, 14916, 3964, 1096, 308, 76) + val searchRunSizesByRadius = listOf( + // @formatter:off + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728, 0.3515625 to 168912, 0.17578125 to 671628), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728, 0.3515625 to 168912), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788), + listOf(22.5 to 76, 11.25 to 232), + listOf(22.5 to 76), + // @formatter:on + ) + + while (div >= 1) { + val options = BlindSearchStrategyOptions(minRadius = (22.5 / div).deg) + val strategy = BlindSearchStrategy(options) + val searchRuns = strategy.searchQueue() + searchRuns shouldHaveSize searchRunSizes[i] + val groupedSearchRuns = searchRuns.groupBy { it.radius.toDegrees } + + searchRunSizesByRadius[i].forEach { + groupedSearchRuns shouldContainKey it.first + groupedSearchRuns[it.first]!! shouldHaveSize it.second } + + div /= 2 + i++ } - "nearby" { - val intermediateFieldRadiusSteps = intArrayOf(-1, -1, 0, 3) - val minFieldRadii = intArrayOf(0, 1, 1, 1) - val maxFieldRadii = intArrayOf(2, 8, 8, 8) - val searchRunsCount = intArrayOf(39, 211, 158, 193) - - repeat(intermediateFieldRadiusSteps.size) { - val options = NearbySearchStrategyOptions(10.deg, minFieldRadii[it].deg, maxFieldRadii[it].deg, intermediateFieldRadiusSteps[it]) - val strategy = NearbySearchStrategy("06 45 08.91728".hours, "-16 42 58.0171".deg, options) - val queue = strategy.searchQueue() - queue shouldHaveSize searchRunsCount[it] - } + } + + @Test + fun nearby() { + val intermediateFieldRadiusSteps = intArrayOf(-1, -1, 0, 3) + val minFieldRadii = intArrayOf(0, 1, 1, 1) + val maxFieldRadii = intArrayOf(2, 8, 8, 8) + val searchRunsCount = intArrayOf(39, 211, 158, 193) + + repeat(intermediateFieldRadiusSteps.size) { + val options = NearbySearchStrategyOptions(10.deg, minFieldRadii[it].deg, maxFieldRadii[it].deg, intermediateFieldRadiusSteps[it]) + val strategy = NearbySearchStrategy("06 45 08.91728".hours, "-16 42 58.0171".deg, options) + val queue = strategy.searchQueue() + queue shouldHaveSize searchRunsCount[it] } } } diff --git a/nebulosa-watney/src/test/kotlin/SkySegmentSphereTest.kt b/nebulosa-watney/src/test/kotlin/SkySegmentSphereTest.kt index 6119dce66..f866c8460 100644 --- a/nebulosa-watney/src/test/kotlin/SkySegmentSphereTest.kt +++ b/nebulosa-watney/src/test/kotlin/SkySegmentSphereTest.kt @@ -1,26 +1,27 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.math.toDegrees import nebulosa.watney.platesolver.quad.SkySegmentSphere +import org.junit.jupiter.api.Test -class SkySegmentSphereTest : StringSpec() { +class SkySegmentSphereTest { - init { - "size" { - SkySegmentSphere shouldHaveSize CELLS.size - } - "cell" { - for (cell in CELLS) { - with(SkySegmentSphere[cell[0].toInt(), cell[1].toInt()]) { - width.toDegrees shouldBe (cell[2] plusOrMinus 1e-4) - height.toDegrees shouldBe (cell[3] plusOrMinus 1e-4) - bounds.left.toDegrees shouldBe (cell[4] plusOrMinus 1e-4) - bounds.right.toDegrees shouldBe (cell[5] plusOrMinus 1e-4) - bounds.bottom.toDegrees shouldBe (cell[6] plusOrMinus 1e-4) - bounds.top.toDegrees shouldBe (cell[7] plusOrMinus 1e-4) - } + @Test + fun size() { + SkySegmentSphere shouldHaveSize CELLS.size + } + + @Test + fun cell() { + for (cell in CELLS) { + with(SkySegmentSphere[cell[0].toInt(), cell[1].toInt()]) { + width.toDegrees shouldBe (cell[2] plusOrMinus 1e-4) + height.toDegrees shouldBe (cell[3] plusOrMinus 1e-4) + bounds.left.toDegrees shouldBe (cell[4] plusOrMinus 1e-4) + bounds.right.toDegrees shouldBe (cell[5] plusOrMinus 1e-4) + bounds.bottom.toDegrees shouldBe (cell[6] plusOrMinus 1e-4) + bounds.top.toDegrees shouldBe (cell[7] plusOrMinus 1e-4) } } } diff --git a/nebulosa-watney/src/test/kotlin/WatnetPlateSolverTest.kt b/nebulosa-watney/src/test/kotlin/WatnetPlateSolverTest.kt index ab76a4cee..a13b581a5 100644 --- a/nebulosa-watney/src/test/kotlin/WatnetPlateSolverTest.kt +++ b/nebulosa-watney/src/test/kotlin/WatnetPlateSolverTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.annotation.EnabledIf import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.ints.shouldBeExactly @@ -6,148 +5,151 @@ import io.kotest.matchers.shouldBe import nebulosa.fits.fits import nebulosa.image.Image import nebulosa.math.deg -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.M31_FITS +import nebulosa.test.NonGitHubOnly +import nebulosa.test.concat +import nebulosa.test.homeDirectory import nebulosa.watney.platesolver.WatneyPlateSolver import nebulosa.watney.platesolver.quad.CompactQuadDatabase import nebulosa.watney.stardetector.Star -import java.nio.file.Path +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class WatnetPlateSolverTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class WatnetPlateSolverTest { - init { - val quadDir = Path.of("/home/tiagohm/Downloads/watneyqdb") - val quadDatabase = CompactQuadDatabase(quadDir) - val solver = WatneyPlateSolver(quadDatabase) + @Test + fun blind() { + val image = M31_FITS.fits().use(Image::open) + println(SOLVER.solve(null, image, 0.0, 0.0, 0.deg)) + } - "blind" { - val image = Image.open(closeAfterEach(M31_FITS.fits())) - println(solver.solve(null, image, 0.0, 0.0, 0.deg)) - } - "form image star quads" { - val (quads, countInFirstPass) = WatneyPlateSolver.formImageStarQuads(M31_STARS) - quads shouldHaveSize 42 - countInFirstPass shouldBeExactly 36 + @Test + fun formImageStarQuads() { + val (quads, countInFirstPass) = WatneyPlateSolver.formImageStarQuads(M31_STARS) + quads shouldHaveSize 42 + countInFirstPass shouldBeExactly 36 - // Original Quads generated by Watney C# implementation using the M31 image. + // Original Quads generated by Watney C# implementation using the M31 image. - val origQuadStars = listOf( - doubleArrayOf(358.5, 0.5, 399.5, 58.5, 371.5, 164.0, 487.5, 108.0), - doubleArrayOf(530.0, 3.0, 514.5, 110.0, 487.5, 108.0, 399.5, 58.5), - doubleArrayOf(957.0, 3.5, 971.5, 6.5, 1034.0, 192.0, 985.5, 240.5), - doubleArrayOf(514.5, 110.0, 487.5, 108.0, 566.5, 203.5, 530.0, 3.0), - doubleArrayOf(697.0, 111.5, 642.5, 187.0, 657.0, 224.5, 566.5, 203.5), - doubleArrayOf(371.5, 164.0, 399.5, 58.5, 487.5, 108.0, 514.5, 110.0), - doubleArrayOf(17.0, 169.5, 82.5, 180.5, 70.5, 283.5, 114.0, 284.0), - doubleArrayOf(642.5, 187.0, 657.0, 224.5, 599.5, 248.5, 566.5, 203.5), - doubleArrayOf(1034.0, 192.0, 985.5, 240.5, 1060.0, 287.0, 971.5, 6.5), - doubleArrayOf(566.5, 203.5, 599.5, 248.5, 583.5, 259.5, 642.5, 187.0), - doubleArrayOf(657.0, 224.5, 642.5, 187.0, 599.5, 248.5, 583.5, 259.5), - doubleArrayOf(985.5, 240.5, 1034.0, 192.0, 1060.0, 287.0, 1053.0, 426.0), - doubleArrayOf(599.5, 248.5, 583.5, 259.5, 566.5, 203.5, 657.0, 224.5), - doubleArrayOf(583.5, 259.5, 599.5, 248.5, 574.5, 309.5, 566.5, 203.5), - doubleArrayOf(70.5, 283.5, 114.0, 284.0, 126.5, 300.5, 133.0, 305.0), - doubleArrayOf(5.5, 321.0, 70.5, 283.5, 114.0, 284.0, 126.5, 300.5), - doubleArrayOf(403.5, 326.5, 474.5, 379.0, 271.0, 374.0, 372.5, 474.5), - doubleArrayOf(240.5, 370.0, 271.0, 374.0, 248.5, 462.0, 133.0, 305.0), - doubleArrayOf(271.0, 374.0, 240.5, 370.0, 248.5, 462.0, 403.5, 326.5), - doubleArrayOf(474.5, 379.0, 403.5, 326.5, 540.5, 460.0, 574.5, 309.5), - doubleArrayOf(738.5, 402.5, 686.5, 411.5, 616.5, 459.0, 828.0, 539.0), - doubleArrayOf(1258.5, 410.0, 1196.5, 573.0, 1208.0, 587.5, 1053.0, 426.0), - doubleArrayOf(686.5, 411.5, 738.5, 402.5, 616.5, 459.0, 574.5, 309.5), - doubleArrayOf(1053.0, 426.0, 1060.0, 287.0, 985.5, 240.5, 1196.5, 573.0), - doubleArrayOf(616.5, 459.0, 540.5, 460.0, 686.5, 411.5, 533.5, 488.0), - doubleArrayOf(540.5, 460.0, 533.5, 488.0, 616.5, 459.0, 474.5, 379.0), - doubleArrayOf(248.5, 462.0, 195.0, 503.0, 271.0, 374.0, 240.5, 370.0), - doubleArrayOf(372.5, 474.5, 365.5, 492.5, 248.5, 462.0, 474.5, 379.0), - doubleArrayOf(365.5, 492.5, 372.5, 474.5, 248.5, 462.0, 271.0, 374.0), - doubleArrayOf(828.0, 539.0, 738.5, 402.5, 680.5, 656.0, 686.5, 411.5), - doubleArrayOf(24.0, 563.0, 195.0, 503.0, 188.5, 655.5, 5.5, 321.0), - doubleArrayOf(1196.5, 573.0, 1208.0, 587.5, 1066.5, 642.5, 1258.5, 410.0), - doubleArrayOf(652.5, 637.5, 651.0, 655.0, 680.5, 656.0, 524.0, 691.0), - doubleArrayOf(1066.5, 642.5, 1196.5, 573.0, 1208.0, 587.5, 1053.0, 426.0), - doubleArrayOf(188.5, 655.5, 296.5, 658.0, 195.0, 503.0, 24.0, 563.0), - doubleArrayOf(296.5, 658.0, 188.5, 655.5, 365.5, 492.5, 195.0, 503.0), - doubleArrayOf(828.0, 539.0, 738.5, 402.5, 686.5, 411.5, 652.5, 637.5), - doubleArrayOf(652.5, 637.5, 651.0, 655.0, 616.5, 459.0, 533.5, 488.0), - doubleArrayOf(188.5, 655.5, 195.0, 503.0, 24.0, 563.0, 248.5, 462.0), - doubleArrayOf(24.0, 563.0, 195.0, 503.0, 5.5, 321.0, 248.5, 462.0), - doubleArrayOf(652.5, 637.5, 616.5, 459.0, 533.5, 488.0, 828.0, 539.0), - doubleArrayOf(1258.5, 410.0, 1196.5, 573.0, 1053.0, 426.0, 1060.0, 287.0), - ) + val origQuadStars = listOf( + doubleArrayOf(358.5, 0.5, 399.5, 58.5, 371.5, 164.0, 487.5, 108.0), + doubleArrayOf(530.0, 3.0, 514.5, 110.0, 487.5, 108.0, 399.5, 58.5), + doubleArrayOf(957.0, 3.5, 971.5, 6.5, 1034.0, 192.0, 985.5, 240.5), + doubleArrayOf(514.5, 110.0, 487.5, 108.0, 566.5, 203.5, 530.0, 3.0), + doubleArrayOf(697.0, 111.5, 642.5, 187.0, 657.0, 224.5, 566.5, 203.5), + doubleArrayOf(371.5, 164.0, 399.5, 58.5, 487.5, 108.0, 514.5, 110.0), + doubleArrayOf(17.0, 169.5, 82.5, 180.5, 70.5, 283.5, 114.0, 284.0), + doubleArrayOf(642.5, 187.0, 657.0, 224.5, 599.5, 248.5, 566.5, 203.5), + doubleArrayOf(1034.0, 192.0, 985.5, 240.5, 1060.0, 287.0, 971.5, 6.5), + doubleArrayOf(566.5, 203.5, 599.5, 248.5, 583.5, 259.5, 642.5, 187.0), + doubleArrayOf(657.0, 224.5, 642.5, 187.0, 599.5, 248.5, 583.5, 259.5), + doubleArrayOf(985.5, 240.5, 1034.0, 192.0, 1060.0, 287.0, 1053.0, 426.0), + doubleArrayOf(599.5, 248.5, 583.5, 259.5, 566.5, 203.5, 657.0, 224.5), + doubleArrayOf(583.5, 259.5, 599.5, 248.5, 574.5, 309.5, 566.5, 203.5), + doubleArrayOf(70.5, 283.5, 114.0, 284.0, 126.5, 300.5, 133.0, 305.0), + doubleArrayOf(5.5, 321.0, 70.5, 283.5, 114.0, 284.0, 126.5, 300.5), + doubleArrayOf(403.5, 326.5, 474.5, 379.0, 271.0, 374.0, 372.5, 474.5), + doubleArrayOf(240.5, 370.0, 271.0, 374.0, 248.5, 462.0, 133.0, 305.0), + doubleArrayOf(271.0, 374.0, 240.5, 370.0, 248.5, 462.0, 403.5, 326.5), + doubleArrayOf(474.5, 379.0, 403.5, 326.5, 540.5, 460.0, 574.5, 309.5), + doubleArrayOf(738.5, 402.5, 686.5, 411.5, 616.5, 459.0, 828.0, 539.0), + doubleArrayOf(1258.5, 410.0, 1196.5, 573.0, 1208.0, 587.5, 1053.0, 426.0), + doubleArrayOf(686.5, 411.5, 738.5, 402.5, 616.5, 459.0, 574.5, 309.5), + doubleArrayOf(1053.0, 426.0, 1060.0, 287.0, 985.5, 240.5, 1196.5, 573.0), + doubleArrayOf(616.5, 459.0, 540.5, 460.0, 686.5, 411.5, 533.5, 488.0), + doubleArrayOf(540.5, 460.0, 533.5, 488.0, 616.5, 459.0, 474.5, 379.0), + doubleArrayOf(248.5, 462.0, 195.0, 503.0, 271.0, 374.0, 240.5, 370.0), + doubleArrayOf(372.5, 474.5, 365.5, 492.5, 248.5, 462.0, 474.5, 379.0), + doubleArrayOf(365.5, 492.5, 372.5, 474.5, 248.5, 462.0, 271.0, 374.0), + doubleArrayOf(828.0, 539.0, 738.5, 402.5, 680.5, 656.0, 686.5, 411.5), + doubleArrayOf(24.0, 563.0, 195.0, 503.0, 188.5, 655.5, 5.5, 321.0), + doubleArrayOf(1196.5, 573.0, 1208.0, 587.5, 1066.5, 642.5, 1258.5, 410.0), + doubleArrayOf(652.5, 637.5, 651.0, 655.0, 680.5, 656.0, 524.0, 691.0), + doubleArrayOf(1066.5, 642.5, 1196.5, 573.0, 1208.0, 587.5, 1053.0, 426.0), + doubleArrayOf(188.5, 655.5, 296.5, 658.0, 195.0, 503.0, 24.0, 563.0), + doubleArrayOf(296.5, 658.0, 188.5, 655.5, 365.5, 492.5, 195.0, 503.0), + doubleArrayOf(828.0, 539.0, 738.5, 402.5, 686.5, 411.5, 652.5, 637.5), + doubleArrayOf(652.5, 637.5, 651.0, 655.0, 616.5, 459.0, 533.5, 488.0), + doubleArrayOf(188.5, 655.5, 195.0, 503.0, 24.0, 563.0, 248.5, 462.0), + doubleArrayOf(24.0, 563.0, 195.0, 503.0, 5.5, 321.0, 248.5, 462.0), + doubleArrayOf(652.5, 637.5, 616.5, 459.0, 533.5, 488.0, 828.0, 539.0), + doubleArrayOf(1258.5, 410.0, 1196.5, 573.0, 1053.0, 426.0, 1060.0, 287.0), + ) - val origQuads = listOf( - doubleArrayOf(0.4229872, 0.6012765, 0.6500249, 0.7670894, 0.9767487, 167.92, 404.25, 82.75), - doubleArrayOf(0.19091523, 0.7119774, 0.76239824, 0.7987724, 0.8885384, 141.81, 482.88, 69.88), - doubleArrayOf(0.06203029, 0.28733647, 0.82002467, 0.85301214, 0.9820323, 238.71, 987.0, 110.63), - doubleArrayOf(0.1328489, 0.52497375, 0.530517, 0.55582803, 0.6081619, 203.8, 524.63, 106.13), - doubleArrayOf(0.25180638, 0.48707265, 0.5818563, 0.58317775, 0.75074446, 159.67, 640.75, 181.63), - doubleArrayOf(0.17712061, 0.66053337, 0.7140859, 0.8243369, 0.84268737, 152.86, 443.25, 110.13), - doubleArrayOf(0.28989518, 0.44259232, 0.6910156, 0.7209406, 0.8391714, 150.06, 71.0, 229.38), - doubleArrayOf(0.43276387, 0.6006513, 0.67066383, 0.80772877, 0.8371013, 92.9, 616.38, 215.88), - doubleArrayOf(0.23319396, 0.29857823, 0.3348643, 0.66550833, 0.79698896, 294.13, 1012.75, 181.5), - doubleArrayOf(0.20772238, 0.59699667, 0.6260989, 0.8028142, 0.83200794, 93.47, 598.0, 224.63), - doubleArrayOf(0.20772238, 0.43013072, 0.6665832, 0.8028142, 0.8709221, 93.47, 620.63, 229.88), - doubleArrayOf(0.2921554, 0.37407166, 0.41953236, 0.59281886, 0.8408198, 234.77, 1033.13, 286.38), - doubleArrayOf(0.208994, 0.6006513, 0.6299317, 0.67066383, 0.8762536, 92.9, 601.63, 234.0), - doubleArrayOf(0.18265495, 0.47791952, 0.5249526, 0.5505428, 0.6201627, 106.3, 581.0, 255.25), - doubleArrayOf(0.11961175, 0.31319097, 0.42847058, 0.6581907, 0.8854501, 66.09, 111.0, 293.25), - doubleArrayOf(0.16867274, 0.35447648, 0.47686976, 0.6114654, 0.93408805, 122.72, 79.13, 297.25), - doubleArrayOf(0.43378574, 0.6864228, 0.69147134, 0.7016915, 0.74283123, 203.56, 380.38, 388.5), - doubleArrayOf(0.15782383, 0.46601853, 0.473798, 0.6445258, 0.79159623, 194.91, 223.25, 377.75), - doubleArrayOf(0.14941548, 0.44119054, 0.44855553, 0.6836948, 0.8194446, 205.88, 290.88, 383.13), - doubleArrayOf(0.46161732, 0.5462143, 0.6366278, 0.8065975, 0.8983449, 191.29, 498.25, 368.75), - doubleArrayOf(0.23338081, 0.37410653, 0.5945751, 0.7218385, 0.8423202, 226.12, 717.38, 453.0), - doubleArrayOf(0.082676105, 0.77907515, 0.8244223, 0.9177246, 0.92081827, 223.85, 1179.0, 499.13), - doubleArrayOf(0.2799132, 0.44869733, 0.71312374, 0.803495, 0.8236593, 188.53, 654.0, 395.63), - doubleArrayOf(0.22300959, 0.35341972, 0.50126994, 0.5216613, 0.80473685, 393.8, 1073.75, 381.63), - doubleArrayOf(0.16872369, 0.44432908, 0.4945342, 0.5139766, 0.8993664, 171.06, 594.25, 454.63), - doubleArrayOf(0.17708255, 0.4663419, 0.53943986, 0.64106923, 0.76046133, 162.98, 541.25, 446.5), - doubleArrayOf(0.20545381, 0.45018867, 0.60665923, 0.6167865, 0.9388506, 149.72, 238.75, 427.25), - doubleArrayOf(0.080217935, 0.50220335, 0.5176477, 0.58036906, 0.6536133, 240.76, 365.25, 452.0), - doubleArrayOf(0.12742372, 0.5992795, 0.79773456, 0.82226735, 0.942405, 151.57, 314.38, 450.75), - doubleArrayOf(0.20293406, 0.6276678, 0.7239713, 0.7324315, 0.9404851, 260.05, 733.38, 502.25), - doubleArrayOf(0.40032506, 0.47528812, 0.4949649, 0.63654554, 0.6890982, 381.29, 103.25, 510.63), - doubleArrayOf(0.0613762, 0.48887974, 0.50347656, 0.57836145, 0.61202574, 301.53, 1182.38, 553.25), - doubleArrayOf(0.10952552, 0.18405987, 0.20926912, 0.8231408, 0.86796653, 160.37, 627.0, 659.88), - doubleArrayOf(0.082676105, 0.6585399, 0.67820233, 0.9177246, 0.96905917, 223.85, 1131.0, 557.25), - doubleArrayOf(0.3743402, 0.5289205, 0.6279637, 0.64201605, 0.65396124, 288.58, 176.0, 594.88), - doubleArrayOf(0.44896066, 0.63435477, 0.70992845, 0.7451902, 0.7699947, 240.62, 261.38, 577.25), - doubleArrayOf(0.21088836, 0.65227014, 0.7611402, 0.8042312, 0.9132892, 250.24, 726.38, 497.63), - doubleArrayOf(0.086017, 0.43057266, 0.8917693, 0.935772, 0.9746273, 204.19, 613.38, 559.88), - doubleArrayOf(0.27380574, 0.62004495, 0.7361518, 0.76662827, 0.82295257, 246.17, 164.0, 545.88), - doubleArrayOf(0.23991768, 0.64504075, 0.8638924, 0.8762333, 0.9352145, 280.94, 118.25, 462.25), - doubleArrayOf(0.294163, 0.609248, 0.63931024, 0.6733474, 0.75656414, 298.88, 657.63, 530.88), - doubleArrayOf(0.43917423, 0.55030274, 0.64823836, 0.6504235, 0.7368766, 316.9, 1142.0, 424.0), - ) + val origQuads = listOf( + doubleArrayOf(0.4229872, 0.6012765, 0.6500249, 0.7670894, 0.9767487, 167.92, 404.25, 82.75), + doubleArrayOf(0.19091523, 0.7119774, 0.76239824, 0.7987724, 0.8885384, 141.81, 482.88, 69.88), + doubleArrayOf(0.06203029, 0.28733647, 0.82002467, 0.85301214, 0.9820323, 238.71, 987.0, 110.63), + doubleArrayOf(0.1328489, 0.52497375, 0.530517, 0.55582803, 0.6081619, 203.8, 524.63, 106.13), + doubleArrayOf(0.25180638, 0.48707265, 0.5818563, 0.58317775, 0.75074446, 159.67, 640.75, 181.63), + doubleArrayOf(0.17712061, 0.66053337, 0.7140859, 0.8243369, 0.84268737, 152.86, 443.25, 110.13), + doubleArrayOf(0.28989518, 0.44259232, 0.6910156, 0.7209406, 0.8391714, 150.06, 71.0, 229.38), + doubleArrayOf(0.43276387, 0.6006513, 0.67066383, 0.80772877, 0.8371013, 92.9, 616.38, 215.88), + doubleArrayOf(0.23319396, 0.29857823, 0.3348643, 0.66550833, 0.79698896, 294.13, 1012.75, 181.5), + doubleArrayOf(0.20772238, 0.59699667, 0.6260989, 0.8028142, 0.83200794, 93.47, 598.0, 224.63), + doubleArrayOf(0.20772238, 0.43013072, 0.6665832, 0.8028142, 0.8709221, 93.47, 620.63, 229.88), + doubleArrayOf(0.2921554, 0.37407166, 0.41953236, 0.59281886, 0.8408198, 234.77, 1033.13, 286.38), + doubleArrayOf(0.208994, 0.6006513, 0.6299317, 0.67066383, 0.8762536, 92.9, 601.63, 234.0), + doubleArrayOf(0.18265495, 0.47791952, 0.5249526, 0.5505428, 0.6201627, 106.3, 581.0, 255.25), + doubleArrayOf(0.11961175, 0.31319097, 0.42847058, 0.6581907, 0.8854501, 66.09, 111.0, 293.25), + doubleArrayOf(0.16867274, 0.35447648, 0.47686976, 0.6114654, 0.93408805, 122.72, 79.13, 297.25), + doubleArrayOf(0.43378574, 0.6864228, 0.69147134, 0.7016915, 0.74283123, 203.56, 380.38, 388.5), + doubleArrayOf(0.15782383, 0.46601853, 0.473798, 0.6445258, 0.79159623, 194.91, 223.25, 377.75), + doubleArrayOf(0.14941548, 0.44119054, 0.44855553, 0.6836948, 0.8194446, 205.88, 290.88, 383.13), + doubleArrayOf(0.46161732, 0.5462143, 0.6366278, 0.8065975, 0.8983449, 191.29, 498.25, 368.75), + doubleArrayOf(0.23338081, 0.37410653, 0.5945751, 0.7218385, 0.8423202, 226.12, 717.38, 453.0), + doubleArrayOf(0.082676105, 0.77907515, 0.8244223, 0.9177246, 0.92081827, 223.85, 1179.0, 499.13), + doubleArrayOf(0.2799132, 0.44869733, 0.71312374, 0.803495, 0.8236593, 188.53, 654.0, 395.63), + doubleArrayOf(0.22300959, 0.35341972, 0.50126994, 0.5216613, 0.80473685, 393.8, 1073.75, 381.63), + doubleArrayOf(0.16872369, 0.44432908, 0.4945342, 0.5139766, 0.8993664, 171.06, 594.25, 454.63), + doubleArrayOf(0.17708255, 0.4663419, 0.53943986, 0.64106923, 0.76046133, 162.98, 541.25, 446.5), + doubleArrayOf(0.20545381, 0.45018867, 0.60665923, 0.6167865, 0.9388506, 149.72, 238.75, 427.25), + doubleArrayOf(0.080217935, 0.50220335, 0.5176477, 0.58036906, 0.6536133, 240.76, 365.25, 452.0), + doubleArrayOf(0.12742372, 0.5992795, 0.79773456, 0.82226735, 0.942405, 151.57, 314.38, 450.75), + doubleArrayOf(0.20293406, 0.6276678, 0.7239713, 0.7324315, 0.9404851, 260.05, 733.38, 502.25), + doubleArrayOf(0.40032506, 0.47528812, 0.4949649, 0.63654554, 0.6890982, 381.29, 103.25, 510.63), + doubleArrayOf(0.0613762, 0.48887974, 0.50347656, 0.57836145, 0.61202574, 301.53, 1182.38, 553.25), + doubleArrayOf(0.10952552, 0.18405987, 0.20926912, 0.8231408, 0.86796653, 160.37, 627.0, 659.88), + doubleArrayOf(0.082676105, 0.6585399, 0.67820233, 0.9177246, 0.96905917, 223.85, 1131.0, 557.25), + doubleArrayOf(0.3743402, 0.5289205, 0.6279637, 0.64201605, 0.65396124, 288.58, 176.0, 594.88), + doubleArrayOf(0.44896066, 0.63435477, 0.70992845, 0.7451902, 0.7699947, 240.62, 261.38, 577.25), + doubleArrayOf(0.21088836, 0.65227014, 0.7611402, 0.8042312, 0.9132892, 250.24, 726.38, 497.63), + doubleArrayOf(0.086017, 0.43057266, 0.8917693, 0.935772, 0.9746273, 204.19, 613.38, 559.88), + doubleArrayOf(0.27380574, 0.62004495, 0.7361518, 0.76662827, 0.82295257, 246.17, 164.0, 545.88), + doubleArrayOf(0.23991768, 0.64504075, 0.8638924, 0.8762333, 0.9352145, 280.94, 118.25, 462.25), + doubleArrayOf(0.294163, 0.609248, 0.63931024, 0.6733474, 0.75656414, 298.88, 657.63, 530.88), + doubleArrayOf(0.43917423, 0.55030274, 0.64823836, 0.6504235, 0.7368766, 316.9, 1142.0, 424.0), + ) - origQuads.forEachIndexed { i, q -> - with(quads[i]) { - ratios[0] shouldBe (q[0] plusOrMinus 1e-7) - ratios[1] shouldBe (q[1] plusOrMinus 1e-7) - ratios[2] shouldBe (q[2] plusOrMinus 1e-7) - ratios[3] shouldBe (q[3] plusOrMinus 1e-7) - ratios[4] shouldBe (q[4] plusOrMinus 1e-7) - largestDistance shouldBe (q[5] plusOrMinus 1e-2) - midPointX shouldBe (q[6] plusOrMinus 1e-1) - midPointY shouldBe (q[7] plusOrMinus 1e-1) + origQuads.forEachIndexed { i, q -> + with(quads[i]) { + ratios[0] shouldBe (q[0] plusOrMinus 1e-7) + ratios[1] shouldBe (q[1] plusOrMinus 1e-7) + ratios[2] shouldBe (q[2] plusOrMinus 1e-7) + ratios[3] shouldBe (q[3] plusOrMinus 1e-7) + ratios[4] shouldBe (q[4] plusOrMinus 1e-7) + largestDistance shouldBe (q[5] plusOrMinus 1e-2) + midPointX shouldBe (q[6] plusOrMinus 1e-1) + midPointY shouldBe (q[7] plusOrMinus 1e-1) - stars[0].x shouldBe (origQuadStars[i][0] plusOrMinus 1e-1) - stars[0].y shouldBe (origQuadStars[i][1] plusOrMinus 1e-1) - stars[1].x shouldBe (origQuadStars[i][2] plusOrMinus 1e-1) - stars[1].y shouldBe (origQuadStars[i][3] plusOrMinus 1e-1) - stars[2].x shouldBe (origQuadStars[i][4] plusOrMinus 1e-1) - stars[2].y shouldBe (origQuadStars[i][5] plusOrMinus 1e-1) - stars[3].x shouldBe (origQuadStars[i][6] plusOrMinus 1e-1) - stars[3].y shouldBe (origQuadStars[i][7] plusOrMinus 1e-1) - } + stars[0].x shouldBe (origQuadStars[i][0] plusOrMinus 1e-1) + stars[0].y shouldBe (origQuadStars[i][1] plusOrMinus 1e-1) + stars[1].x shouldBe (origQuadStars[i][2] plusOrMinus 1e-1) + stars[1].y shouldBe (origQuadStars[i][3] plusOrMinus 1e-1) + stars[2].x shouldBe (origQuadStars[i][4] plusOrMinus 1e-1) + stars[2].y shouldBe (origQuadStars[i][5] plusOrMinus 1e-1) + stars[3].x shouldBe (origQuadStars[i][6] plusOrMinus 1e-1) + stars[3].y shouldBe (origQuadStars[i][7] plusOrMinus 1e-1) } } } companion object { + @JvmStatic private val QUAD_DIR = homeDirectory.concat("Downloads", "watneyqdb") + @JvmStatic private val QUAD_DATABASE = CompactQuadDatabase(QUAD_DIR) + @JvmStatic private val SOLVER = WatneyPlateSolver(QUAD_DATABASE) + @JvmStatic private val M31_STARS = listOf( Star(358.5, 0.5), Star(530.0, 3.0), Star(957.0, 3.5), Star(971.5, 6.5), Star(399.5, 58.5), Star(487.5, 108.0), diff --git a/nebulosa-watney/src/test/kotlin/WatneyStarDetectorTest.kt b/nebulosa-watney/src/test/kotlin/WatneyStarDetectorTest.kt index 1bc8dee5a..b6e99dd38 100644 --- a/nebulosa-watney/src/test/kotlin/WatneyStarDetectorTest.kt +++ b/nebulosa-watney/src/test/kotlin/WatneyStarDetectorTest.kt @@ -2,33 +2,34 @@ import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe import nebulosa.fits.fits import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.transformation.Draw import nebulosa.image.algorithms.transformation.convolution.Mean import nebulosa.stardetector.StarPoint -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.M6707HH +import nebulosa.test.NGC3344_COLOR_32_FITS +import nebulosa.test.save import nebulosa.watney.stardetector.WatneyStarDetector +import org.junit.jupiter.api.Test import java.awt.Color import java.awt.Graphics2D import kotlin.math.roundToInt -class WatneyStarDetectorTest : AbstractFitsAndXisfTest() { +class WatneyStarDetectorTest { - init { - val detector = WatneyStarDetector(computeHFD = true) + @Test + fun detectStars() { + var image = NGC3344_COLOR_32_FITS.fits().asImage() + var stars = DETECTOR.detect(image.transform(Mean)) + stars shouldHaveSize 1 + image.transform(ImageStarsDraw(stars)).save("color-detected-stars-1") + .second shouldBe "bb237ce03f7cc9e44e69a5354b7a6fd1" - "detect stars" { - var image = Image.open(NGC3344_COLOR_32_FITS.fits()) - var stars = detector.detect(image.transform(Mean)) - stars shouldHaveSize 1 - image.transform(ImageStarsDraw(stars)).save("color-detected-stars-1") - .second shouldBe "bb237ce03f7cc9e44e69a5354b7a6fd1" - - image = Image.open(M6707HH.fits()) - stars = detector.detect(image.transform(Mean)) - stars shouldHaveSize 870 - image.transform(ImageStarsDraw(stars)).save("color-detected-stars-870") - .second shouldBe "004e4d2b4d9725c5367f6865986f6756" - } + image = M6707HH.fits().asImage() + stars = DETECTOR.detect(image.transform(Mean)) + stars shouldHaveSize 870 + image.transform(ImageStarsDraw(stars)).save("color-detected-stars-870") + .second shouldBe "004e4d2b4d9725c5367f6865986f6756" } private data class ImageStarsDraw(private val stars: List) : Draw() { @@ -41,4 +42,9 @@ class WatneyStarDetectorTest : AbstractFitsAndXisfTest() { } } } + + companion object { + + @JvmStatic private val DETECTOR = WatneyStarDetector(computeHFD = true) + } } diff --git a/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt b/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt index 18e21d869..b71772deb 100644 --- a/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt +++ b/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt @@ -1,70 +1,178 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.fits.fits import nebulosa.image.format.ReadableHeader import nebulosa.math.formatHMS import nebulosa.math.formatSignedDMS -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.LinuxOnly +import nebulosa.test.download +import nebulosa.wcs.LibWCS import nebulosa.wcs.WCS +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.random.Random // https://www.atnf.csiro.au/people/mcalabre/WCS/example_data.html -@EnabledIf(NonGitHubOnlyCondition::class) -class LibWCSTest : StringSpec() { +@LinuxOnly +class LibWCSTest { - init { - for (projection in PROJECTIONS) { - projection { - pixToSky(projection, 192, 192) - } - } + @Test + fun air() { + pixToSky("AIR", 192, 192) + } - "TAN-SIP" { - pixToSky("TAN-SIP", 1280, 720) - } + @Test + fun ait() { + pixToSky("AIT", 192, 192) } - private fun pixToSky(projectionName: String, width: Int, height: Int) { - val data = Array(2048) { intArrayOf(Random.nextInt(width), Random.nextInt(height)) } + @Test + fun arc() { + pixToSky("ARC", 192, 192) + } - WCS(readHeaderFromFits(projectionName)).use { - val topLeft = it.pixToSky(0.0, 0.0) - val topRight = it.pixToSky(width.toDouble(), 0.0) - val bottomLeft = it.pixToSky(0.0, height.toDouble()) - val bottomRight = it.pixToSky(width.toDouble(), height.toDouble()) - val center = it.pixToSky(width / 2.0, height / 2.0) + @Test + fun azp() { + pixToSky("AZP", 192, 192) + } - println("top left: ${topLeft.rightAscension.formatHMS()} ${topLeft.declination.formatSignedDMS()}") - println("top right: ${topRight.rightAscension.formatHMS()} ${topRight.declination.formatSignedDMS()}") - println("bottom left: ${bottomLeft.rightAscension.formatHMS()} ${bottomLeft.declination.formatSignedDMS()}") - println("bottom right: ${bottomRight.rightAscension.formatHMS()} ${bottomRight.declination.formatSignedDMS()}") - println("center: ${center.rightAscension.formatHMS()} ${center.declination.formatSignedDMS()}") + @Test + fun car() { + pixToSky("CAR", 192, 192) + } - for ((x0, y0) in data) { - val (rightAscension, declination) = it.pixToSky(x0.toDouble(), y0.toDouble()) - val (x1, y1) = it.skyToPix(rightAscension, declination) - x1 shouldBe (x0.toDouble() plusOrMinus 1.0) - y1 shouldBe (y0.toDouble() plusOrMinus 1.0) - } - } + @Test + fun cea() { + pixToSky("CEA", 192, 192) + } + + @Test + fun csc() { + pixToSky("CSC", 192, 192) + } + + @Test + fun cyp() { + pixToSky("CYP", 192, 192) } - private fun readHeaderFromFits(name: String): ReadableHeader { - return Path.of("src/test/resources/$name.fits").fits().use { it.first!!.header } + @Test + fun hpx() { + pixToSky("HPX", 192, 192) } + @Test + fun mer() { + pixToSky("MER", 192, 192) + } + + @Test + fun mol() { + pixToSky("MOL", 192, 192) + } + + @Test + fun ncp() { + pixToSky("NCP", 192, 192) + } + + @Test + fun par() { + pixToSky("PAR", 192, 192) + } + + @Test + fun pco() { + pixToSky("PCO", 192, 192) + } + + @Test + fun qsc() { + pixToSky("QSC", 192, 192) + } + + @Test + fun sfl() { + pixToSky("SFL", 192, 192) + } + + @Test + fun sin() { + pixToSky("SIN", 192, 192) + } + + @Test + fun stg() { + pixToSky("STG", 192, 192) + } + + @Test + fun szp() { + pixToSky("SZP", 192, 192) + } + + @Test + fun tan() { + pixToSky("TAN", 192, 192) + } + + @Test + fun tsc() { + pixToSky("TSC", 192, 192) + } + + @Test + fun zea() { + pixToSky("ZEA", 192, 192) + } + + @Test + fun tanSip() { + pixToSky("TAN-SIP", 1280, 720) + } + + // "BON", "COD", "COE", "COO", "COP", "ZPN" // FAILED + companion object { - @JvmStatic private val PROJECTIONS = arrayOf( - "AIR", "AIT", "ARC", "AZP", "CAR", "CEA", - "CSC", "CYP", "HPX", "MER", "MOL", - "NCP", "PAR", "PCO", "QSC", "SFL", "SIN", "STG", "SZP", - "TAN", "TSC", "ZEA", - "BON", "COD", "COE", "COO", "COP", "ZPN" // FAILED - ) + @BeforeAll + @JvmStatic + fun loadLibWCS() { + val libPath = download("https://github.com/tiagohm/nebulosa.data/raw/main/wcs/linux-x86-64/libwcs.so") + System.setProperty(LibWCS.PATH, "$libPath") + } + + @JvmStatic + private fun pixToSky(projectionName: String, width: Int, height: Int) { + val data = Array(2048) { intArrayOf(Random.nextInt(width), Random.nextInt(height)) } + + WCS(readHeaderFromFits(projectionName)).use { + val topLeft = it.pixToSky(0.0, 0.0) + val topRight = it.pixToSky(width.toDouble(), 0.0) + val bottomLeft = it.pixToSky(0.0, height.toDouble()) + val bottomRight = it.pixToSky(width.toDouble(), height.toDouble()) + val center = it.pixToSky(width / 2.0, height / 2.0) + + println("top left: ${topLeft.rightAscension.formatHMS()} ${topLeft.declination.formatSignedDMS()}") + println("top right: ${topRight.rightAscension.formatHMS()} ${topRight.declination.formatSignedDMS()}") + println("bottom left: ${bottomLeft.rightAscension.formatHMS()} ${bottomLeft.declination.formatSignedDMS()}") + println("bottom right: ${bottomRight.rightAscension.formatHMS()} ${bottomRight.declination.formatSignedDMS()}") + println("center: ${center.rightAscension.formatHMS()} ${center.declination.formatSignedDMS()}") + + for ((x0, y0) in data) { + val (rightAscension, declination) = it.pixToSky(x0.toDouble(), y0.toDouble()) + val (x1, y1) = it.skyToPix(rightAscension, declination) + x1 shouldBe (x0.toDouble() plusOrMinus 1.0) + y1 shouldBe (y0.toDouble() plusOrMinus 1.0) + } + } + } + + @JvmStatic + private fun readHeaderFromFits(name: String): ReadableHeader { + return Path.of("src/test/resources/$name.fits").fits().use { it.first().header } + } } } diff --git a/nebulosa-xisf/src/main/kotlin/nebulosa/xisf/XisfFormat.kt b/nebulosa-xisf/src/main/kotlin/nebulosa/xisf/XisfFormat.kt index 3db2be341..c1534f639 100644 --- a/nebulosa-xisf/src/main/kotlin/nebulosa/xisf/XisfFormat.kt +++ b/nebulosa-xisf/src/main/kotlin/nebulosa/xisf/XisfFormat.kt @@ -161,7 +161,7 @@ data object XisfFormat : ImageFormat { } @JvmStatic - fun BufferedSource.readSignature() = readString(8L, Charsets.US_ASCII) + fun BufferedSource.readSignature() = if (request(8L)) readString(8L, Charsets.US_ASCII) else "" @JvmStatic internal fun Buffer.readPixel(format: SampleFormat, byteOrder: ByteOrder): Float { diff --git a/nebulosa-xisf/src/test/kotlin/CompressionByteShufflerTest.kt b/nebulosa-xisf/src/test/kotlin/CompressionByteShufflerTest.kt index 66556a1ad..560b087de 100644 --- a/nebulosa-xisf/src/test/kotlin/CompressionByteShufflerTest.kt +++ b/nebulosa-xisf/src/test/kotlin/CompressionByteShufflerTest.kt @@ -1,30 +1,31 @@ -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.ints.shouldBeExactly +import io.kotest.matchers.shouldBe import nebulosa.xisf.CompressionByteShuffler +import org.junit.jupiter.api.Test import kotlin.random.Random -class CompressionByteShufflerTest : StringSpec() { +class CompressionByteShufflerTest { - init { - "shuffle & unshuffle" { - val original = ByteArray(256) { (it / 32).toByte() } - val buffer = ByteArray(256) - val unshuffled = ByteArray(256) + @Test + fun shuffleAndUnshuffle() { + val original = ByteArray(256) { (it / 32).toByte() } + val buffer = ByteArray(256) + val unshuffled = ByteArray(256) - CompressionByteShuffler.shuffle(original, buffer, 8) - CompressionByteShuffler.unshuffle(buffer, unshuffled, 8) + CompressionByteShuffler.shuffle(original, buffer, 8) + CompressionByteShuffler.unshuffle(buffer, unshuffled, 8) - repeat(original.size) { unshuffled[it].toInt() shouldBeExactly original[it].toInt() } - } - "random shuffle & unshuffle" { - val original = Random.nextBytes(256) - val buffer = ByteArray(256) - val unshuffled = ByteArray(256) + unshuffled shouldBe original + } + + @Test + fun randomShuffleAndUnshuffle() { + val original = Random.nextBytes(256) + val buffer = ByteArray(256) + val unshuffled = ByteArray(256) - CompressionByteShuffler.shuffle(original, buffer, 8) - CompressionByteShuffler.unshuffle(buffer, unshuffled, 8) + CompressionByteShuffler.shuffle(original, buffer, 8) + CompressionByteShuffler.unshuffle(buffer, unshuffled, 8) - repeat(original.size) { unshuffled[it].toInt() shouldBeExactly original[it].toInt() } - } + unshuffled shouldBe original } } diff --git a/nebulosa-xisf/src/test/kotlin/XisfFormatTest.kt b/nebulosa-xisf/src/test/kotlin/XisfFormatTest.kt index 79ac306e4..81d74e7b0 100644 --- a/nebulosa-xisf/src/test/kotlin/XisfFormatTest.kt +++ b/nebulosa-xisf/src/test/kotlin/XisfFormatTest.kt @@ -1,8 +1,6 @@ -import io.kotest.engine.spec.tempfile import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.collections.shouldHaveSize -import io.kotest.matchers.floats.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeSameInstanceAs @@ -10,403 +8,439 @@ import io.kotest.matchers.types.shouldNotBeSameInstanceAs import nebulosa.fits.FitsFormat import nebulosa.fits.bitpix import nebulosa.image.format.ImageHdu +import nebulosa.image.format.ImageHdu.Companion.makeImage import nebulosa.io.seekableSink import nebulosa.io.seekableSource -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.* import nebulosa.xisf.XisfFormat import nebulosa.xisf.isXisf +import org.junit.jupiter.api.Test -class XisfFormatTest : AbstractFitsAndXisfTest() { +class XisfFormatTest : AbstractTest() { - init { - "should be xisf format" { - NGC3344_COLOR_8_FITS.isXisf().shouldBeFalse() - M82_COLOR_16_XISF.isXisf().shouldBeTrue() - } - "mono:planar:8" { - val source = closeAfterEach(M82_MONO_8_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-8").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + @Test + fun shouldBeXisfFormat() { + NGC3344_COLOR_8_FITS.isXisf().shouldBeFalse() + M82_COLOR_16_XISF.isXisf().shouldBeTrue() + tempPath("empty-", ".xisf").isXisf().shouldBeFalse() + } + + @Test + fun monoPlanar8Bit() { + val source = M82_MONO_8_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-8").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:planar:16" { - val source = closeAfterEach(M82_MONO_16_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-16").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanar16Bit() { + val source = M82_MONO_16_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-16").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:planar:32" { - val source = closeAfterEach(M82_MONO_32_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-32").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanar32Bit() { + val source = M82_MONO_32_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-32").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:planar:F32" { - val source = closeAfterEach(M82_MONO_F32_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-F32").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanarFloat32Bit() { + val source = M82_MONO_F32_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-F32").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:planar:F64" { - val source = closeAfterEach(M82_MONO_F64_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-F64").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanarFloat64() { + val source = M82_MONO_F64_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-F64").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "color:planar:8" { - val source = closeAfterEach(M82_COLOR_8_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-8").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanar8Bit() { + val source = M82_COLOR_8_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-8").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "color:planar:16" { - val source = closeAfterEach(M82_COLOR_16_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-16").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanar16Bit() { + val source = M82_COLOR_16_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-16").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "color:planar:32" { - val source = closeAfterEach(M82_COLOR_32_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-32").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanar32Bit() { + val source = M82_COLOR_32_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-32").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "color:planar:F32" { - val source = closeAfterEach(M82_COLOR_F32_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-F32").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanarFloat32Bit() { + val source = M82_COLOR_F32_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-F32").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "color:planar:F64" { - val source = closeAfterEach(M82_COLOR_F64_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-F64").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanarFloat64() { + val source = M82_COLOR_F64_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-F64").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "mono:planar:8:zlib" { - val source = closeAfterEach(M82_MONO_8_ZLIB_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-8-zlib").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanar8BitZLib() { + val source = M82_MONO_8_ZLIB_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-8-zlib").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:write" { - val formats = arrayOf(PALETTE_MONO_8_XISF, PALETTE_MONO_16_XISF, PALETTE_MONO_32_XISF, PALETTE_MONO_F32_XISF, PALETTE_MONO_F64_XISF) - - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = XisfFormat.read(source0) - - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - XisfFormat.write(sink, hdus0) - - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = XisfFormat.read(source1) - - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 1 - hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } - - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("xisf-mono-write-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" - } + } + + @Test + fun monoWrite() { + val formats = arrayOf(PALETTE_MONO_8_XISF, PALETTE_MONO_16_XISF, PALETTE_MONO_32_XISF, PALETTE_MONO_F32_XISF, PALETTE_MONO_F64_XISF) + + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = XisfFormat.read(source0) + + val outputPath = tempPath("mono-", ".xisf") + val sink = outputPath.seekableSink().autoClose() + XisfFormat.write(sink, hdus0) + + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = XisfFormat.read(source1) + + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 1 + hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red + + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("xisf-mono-write-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" } - "color:write" { - val formats = arrayOf(PALETTE_COLOR_8_XISF, PALETTE_COLOR_16_XISF, PALETTE_COLOR_32_XISF, PALETTE_COLOR_F32_XISF, PALETTE_COLOR_F64_XISF) - - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = XisfFormat.read(source0) - - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - XisfFormat.write(sink, hdus0) - - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = XisfFormat.read(source1) - - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 3 - hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } - hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green - hdus1[0].data.green.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.green[i] } - hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue - hdus1[0].data.blue.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.blue[i] } - - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("xisf-color-write-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" - } + } + + @Test + fun colorWrite() { + val formats = arrayOf(PALETTE_COLOR_8_XISF, PALETTE_COLOR_16_XISF, PALETTE_COLOR_32_XISF, PALETTE_COLOR_F32_XISF, PALETTE_COLOR_F64_XISF) + + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = XisfFormat.read(source0) + + val outputPath = tempPath("color-", ".xisf") + val sink = outputPath.seekableSink().autoClose() + XisfFormat.write(sink, hdus0) + + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = XisfFormat.read(source1) + + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 3 + hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red + hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green + hdus1[0].data.green shouldBe hdus0[0].data.green + hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue + hdus1[0].data.blue shouldBe hdus0[0].data.blue + + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("xisf-color-write-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" } - "fits-to-xisf:mono" { - val formats = arrayOf(PALETTE_MONO_8_FITS, PALETTE_MONO_16_FITS, PALETTE_MONO_32_FITS, PALETTE_MONO_F32_FITS, PALETTE_MONO_F64_FITS) + } + + @Test + fun fitsToXisfMono() { + val formats = arrayOf(PALETTE_MONO_8_FITS, PALETTE_MONO_16_FITS, PALETTE_MONO_32_FITS, PALETTE_MONO_F32_FITS, PALETTE_MONO_F64_FITS) - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = FitsFormat.read(source0).filterIsInstance() + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = FitsFormat.read(source0).filterIsInstance() - hdus0 shouldHaveSize 1 + hdus0 shouldHaveSize 1 - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - XisfFormat.write(sink, hdus0) + val outputPath = tempPath("fits-xisf-mono-", ".xisf") + val sink = outputPath.seekableSink().autoClose() + XisfFormat.write(sink, hdus0) - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = XisfFormat.read(source1) + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = XisfFormat.read(source1) - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 1 - // hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 1 + // hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("fits-to-xisf-mono-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" - } + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("fits-to-xisf-mono-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" } - "fits-to-xisf:color" { - val formats = arrayOf(PALETTE_COLOR_8_FITS, PALETTE_COLOR_16_FITS, PALETTE_COLOR_32_FITS, PALETTE_COLOR_F32_FITS, PALETTE_COLOR_F64_FITS) - - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = FitsFormat.read(source0).filterIsInstance() - - hdus0 shouldHaveSize 1 - - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - XisfFormat.write(sink, hdus0) - - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = XisfFormat.read(source1) - - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 3 - // hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } - hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green - hdus1[0].data.green.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.green[i] } - hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue - hdus1[0].data.blue.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.blue[i] } - - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("fits-to-xisf-color-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" - } + } + + @Test + fun fitsToXisfColor() { + val formats = arrayOf(PALETTE_COLOR_8_FITS, PALETTE_COLOR_16_FITS, PALETTE_COLOR_32_FITS, PALETTE_COLOR_F32_FITS, PALETTE_COLOR_F64_FITS) + + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = FitsFormat.read(source0).filterIsInstance() + + hdus0 shouldHaveSize 1 + + val outputPath = tempPath("fits-xisf-color", ".xisf") + val sink = outputPath.seekableSink().autoClose() + XisfFormat.write(sink, hdus0) + + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = XisfFormat.read(source1) + + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 3 + // hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red + hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green + hdus1[0].data.green shouldBe hdus0[0].data.green + hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue + hdus1[0].data.blue shouldBe hdus0[0].data.blue + + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("fits-to-xisf-color-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" } - "xisf-to-fits:mono" { - val formats = arrayOf(PALETTE_MONO_8_XISF, PALETTE_MONO_16_XISF, PALETTE_MONO_32_XISF, PALETTE_MONO_F32_XISF, PALETTE_MONO_F64_XISF) + } + + @Test + fun xisfToFitsMono() { + val formats = arrayOf(PALETTE_MONO_8_XISF, PALETTE_MONO_16_XISF, PALETTE_MONO_32_XISF, PALETTE_MONO_F32_XISF, PALETTE_MONO_F64_XISF) - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = XisfFormat.read(source0) + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = XisfFormat.read(source0) - hdus0 shouldHaveSize 1 + hdus0 shouldHaveSize 1 - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - FitsFormat.write(sink, hdus0) + val outputPath = tempPath("xisf-fits-mono", ".fits") + val sink = outputPath.seekableSink().autoClose() + FitsFormat.write(sink, hdus0) - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = FitsFormat.read(source1).filterIsInstance() + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = FitsFormat.read(source1).filterIsInstance() - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 1 - // hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 1 + // hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("xisf-to-fits-mono-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" - } + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("xisf-to-fits-mono-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" } - "xisf-to-fits:color" { - val formats = arrayOf(PALETTE_COLOR_8_XISF, PALETTE_COLOR_16_XISF, PALETTE_COLOR_32_XISF, PALETTE_COLOR_F32_XISF, PALETTE_COLOR_F64_XISF) - - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = XisfFormat.read(source0) - - hdus0 shouldHaveSize 1 - - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - FitsFormat.write(sink, hdus0) - - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = FitsFormat.read(source1).filterIsInstance() - - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 3 - // hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } - hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green - hdus1[0].data.green.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.green[i] } - hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue - hdus1[0].data.blue.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.blue[i] } - - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("xisf-to-fits-color-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" - } + } + + @Test + fun xisfToFitsColor() { + val formats = arrayOf(PALETTE_COLOR_8_XISF, PALETTE_COLOR_16_XISF, PALETTE_COLOR_32_XISF, PALETTE_COLOR_F32_XISF, PALETTE_COLOR_F64_XISF) + + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = XisfFormat.read(source0) + + hdus0 shouldHaveSize 1 + + val outputPath = tempPath("xisf-fits-color", ".fits") + val sink = outputPath.seekableSink().autoClose() + FitsFormat.write(sink, hdus0) + + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = FitsFormat.read(source1).filterIsInstance() + + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 3 + // hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red + hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green + hdus1[0].data.green shouldBe hdus0[0].data.green + hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue + hdus1[0].data.blue shouldBe hdus0[0].data.blue + + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("xisf-to-fits-color-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" } } } diff --git a/nebulosa-xisf/src/test/kotlin/XisfHeaderInputStreamTest.kt b/nebulosa-xisf/src/test/kotlin/XisfHeaderInputStreamTest.kt index 457aa4c2f..025ef5b36 100644 --- a/nebulosa-xisf/src/test/kotlin/XisfHeaderInputStreamTest.kt +++ b/nebulosa-xisf/src/test/kotlin/XisfHeaderInputStreamTest.kt @@ -4,227 +4,253 @@ import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeInstanceOf import nebulosa.io.ByteOrder -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.* import nebulosa.xisf.XisfHeaderInputStream import nebulosa.xisf.XisfMonolithicFileHeader +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream -class XisfHeaderInputStreamTest : AbstractFitsAndXisfTest() { - - init { - "read:8:gray" { - val stream = closeAfterEach(M82_MONO_8_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } +class XisfHeaderInputStreamTest : AbstractTest() { + + @Test + fun read8BitGray() { + val stream = M82_MONO_8_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:8:gray:zlib" { - val stream = closeAfterEach(M82_MONO_8_ZLIB_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.ZLIB - keywords shouldHaveSize 23 - } + } + + @Test + fun read8BitGrayZLib() { + val stream = M82_MONO_8_ZLIB_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.ZLIB + keywords shouldHaveSize 23 } - "read:8:gray:lz4" { - val stream = closeAfterEach(M82_MONO_8_LZ4_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.LZ4 - keywords shouldHaveSize 23 - } + } + + @Test + fun read8BitGrayLz4() { + val stream = M82_MONO_8_LZ4_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.LZ4 + keywords shouldHaveSize 23 } - "read:8:gray:lz4-hc" { - val stream = closeAfterEach(M82_MONO_8_LZ4_HC_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.LZ4_HC - keywords shouldHaveSize 23 - } + } + + @Test + fun read8BitGrayLz4HC() { + val stream = M82_MONO_8_LZ4_HC_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.LZ4_HC + keywords shouldHaveSize 23 } - "read:8:gray:zstd" { - val stream = closeAfterEach(M82_MONO_8_ZSTANDARD_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.ZSTD - keywords shouldHaveSize 23 - } + } + + @Test + fun read8BitGrayZStd() { + val stream = M82_MONO_8_ZSTANDARD_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.ZSTD + keywords shouldHaveSize 23 } - "read:16:gray" { - val stream = closeAfterEach(M82_MONO_16_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT16 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } + } + + @Test + fun read16BitGray() { + val stream = M82_MONO_16_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT16 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:32:gray" { - val stream = closeAfterEach(M82_MONO_32_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT32 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } + } + + @Test + fun read32BitGray() { + val stream = M82_MONO_32_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT32 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:F32:gray" { - val stream = closeAfterEach(M82_MONO_F32_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT32 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } + } + + @Test + fun readFloat32Gray() { + val stream = M82_MONO_F32_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT32 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:F64:gray" { - val stream = closeAfterEach(M82_MONO_F64_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT64 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } + } + + @Test + fun readFloat64Gray() { + val stream = M82_MONO_F64_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT64 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:8:rgb" { - val stream = closeAfterEach(M82_COLOR_8_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun read8BitRGB() { + val stream = M82_COLOR_8_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } - "read:16:rgb" { - val stream = closeAfterEach(M82_COLOR_16_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT16 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun read16BitRGB() { + val stream = M82_COLOR_16_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT16 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } - "read:32:rgb" { - val stream = closeAfterEach(M82_COLOR_32_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT32 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun read32BitRGB() { + val stream = M82_COLOR_32_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT32 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } - "read:F32:rgb" { - val stream = closeAfterEach(M82_COLOR_F32_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT32 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun readFloat32RGB() { + val stream = M82_COLOR_F32_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT32 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } - "read:F64:rgb" { - val stream = closeAfterEach(M82_COLOR_F64_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT64 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun readFloat64RGB() { + val stream = M82_COLOR_F64_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT64 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 3e378dd64..05ba1e938 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,28 +15,28 @@ dependencyResolutionManagement { library("okio", "com.squareup.okio:okio:3.9.0") library("okhttp", "com.squareup.okhttp3:okhttp:4.12.0") library("okhttp-logging", "com.squareup.okhttp3:logging-interceptor:4.12.0") - library("jackson-core", "com.fasterxml.jackson.core:jackson-databind:2.17.1") - library("jackson-jsr310", "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.1") - library("jackson-kt", "com.fasterxml.jackson.module:jackson-module-kotlin:2.17.1") + library("jackson-core", "com.fasterxml.jackson.core:jackson-databind:2.17.2") + library("jackson-jsr310", "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2") + library("jackson-kt", "com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2") library("retrofit", "com.squareup.retrofit2:retrofit:2.11.0") library("retrofit-jackson", "com.squareup.retrofit2:converter-jackson:2.11.0") library("rx", "io.reactivex.rxjava3:rxjava:3.1.8") library("logback", "ch.qos.logback:logback-classic:1.5.6") library("eventbus", "org.greenrobot:eventbus-java:3.3.1") - library("netty-transport", "io.netty:netty-transport:4.1.110.Final") - library("netty-codec", "io.netty:netty-codec:4.1.110.Final") - library("xml", "com.fasterxml:aalto-xml:1.3.2") - library("csv", "de.siegmar:fastcsv:3.1.0") - library("apache-lang3", "org.apache.commons:commons-lang3:3.14.0") - library("apache-codec", "commons-codec:commons-codec:1.17.0") + library("netty-transport", "io.netty:netty-transport:4.1.112.Final") + library("netty-codec", "io.netty:netty-codec:4.1.112.Final") + library("xml", "com.fasterxml:aalto-xml:1.3.3") + library("csv", "de.siegmar:fastcsv:3.2.0") + library("apache-lang3", "org.apache.commons:commons-lang3:3.15.0") + library("apache-codec", "commons-codec:commons-codec:1.17.1") library("apache-collections", "org.apache.commons:commons-collections4:4.4") library("apache-math", "org.apache.commons:commons-math3:3.6.1") library("apache-numbers-complex", "org.apache.commons:commons-numbers-complex:1.1") - library("oshi", "com.github.oshi:oshi-core:6.6.1") + library("oshi", "com.github.oshi:oshi-core:6.6.2") library("jna", "net.java.dev.jna:jna:5.14.0") - library("kotest-assertions-core", "io.kotest:kotest-assertions-core:5.9.0") - library("kotest-runner-junit5", "io.kotest:kotest-runner-junit5:5.9.0") - bundle("kotest", listOf("kotest-assertions-core", "kotest-runner-junit5")) + library("kotest", "io.kotest:kotest-assertions-core:5.9.1") + library("junit-api", "org.junit.jupiter:junit-jupiter-api:5.10.3") + library("junit-engine", "org.junit.jupiter:junit-jupiter-engine:5.10.3") bundle("netty", listOf("netty-transport", "netty-codec")) bundle("jackson", listOf("jackson-core", "jackson-jsr310", "jackson-kt")) } @@ -69,6 +69,7 @@ include(":nebulosa-indi-client") include(":nebulosa-indi-device") include(":nebulosa-indi-protocol") include(":nebulosa-io") +include(":nebulosa-json") include(":nebulosa-livestacker") include(":nebulosa-log") include(":nebulosa-lx200-protocol") @@ -88,6 +89,7 @@ include(":nebulosa-skycatalog-hyg") include(":nebulosa-skycatalog-sao") include(":nebulosa-skycatalog-stellarium") include(":nebulosa-stardetector") +include(":nebulosa-stacker") include(":nebulosa-stellarium-protocol") include(":nebulosa-test") include(":nebulosa-time")