From 235c4577c20bc5fac02ea23d58148ce88ad34f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a?= Date: Fri, 21 Aug 2026 21:59:30 -0300 Subject: [PATCH 1/6] Add 22 Kotlin Multiplatform targets for 0.7.0 --- .github/workflows/gradle.yml | 187 ++++++++++++++---- README.md | 22 ++- docs/api-concepts.md | 14 +- docs/getting-started.md | 20 +- docs/lifecycle-and-delivery.md | 16 +- docs/slf4j.md | 2 +- gradle/libs.versions.toml | 3 +- plans/0.7.0-implementation.md | 122 ++++++++++++ scribe-slf4j/build.gradle.kts | 2 +- scribe/build.gradle.kts | 33 +++- .../scribe/GlobalExceptionHandler.android.kt | 10 +- .../rafambn/scribe/GlobalExceptionHandler.kt | 8 +- .../com/rafambn/scribe/IgnitionRegistry.kt | 102 ++++++++++ .../kotlin/com/rafambn/scribe/Scribe.kt | 26 ++- .../com/rafambn/scribe/ScribeTestFixtures.kt | 16 +- .../scribe/GlobalExceptionHandler.js.kt | 75 +++++++ .../scribe/GlobalExceptionHandlerJsTest.kt | 43 ++++ .../scribe/GlobalExceptionHandler.jvm.kt | 10 +- .../scribe/GlobalIgnitionLifecycleJvmTest.kt | 94 +++++++++ .../scribe/GlobalExceptionHandler.native.kt | 18 +- .../scribe/GlobalExceptionHandler.wasmJs.kt | 48 +++++ .../scribe/GlobalExceptionHandler.wasmWasi.kt | 6 + .../scribe/WasmWasiExceptionHandlerTest.kt | 19 ++ 23 files changed, 832 insertions(+), 64 deletions(-) create mode 100644 plans/0.7.0-implementation.md create mode 100644 scribe/src/commonMain/kotlin/com/rafambn/scribe/IgnitionRegistry.kt create mode 100644 scribe/src/jsMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.js.kt create mode 100644 scribe/src/jsTest/kotlin/com/rafambn/scribe/GlobalExceptionHandlerJsTest.kt create mode 100644 scribe/src/jvmTest/kotlin/com/rafambn/scribe/GlobalIgnitionLifecycleJvmTest.kt create mode 100644 scribe/src/wasmJsMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.wasmJs.kt create mode 100644 scribe/src/wasmWasiMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.wasmWasi.kt create mode 100644 scribe/src/wasmWasiTest/kotlin/com/rafambn/scribe/WasmWasiExceptionHandlerTest.kt diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 28afa57..6bbc586 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -1,59 +1,174 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle - name: Java CI with Gradle on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] workflow_call: permissions: contents: read +env: + GRADLE_OPTS: -Dorg.gradle.daemon=false + jobs: - build: + compile: + name: Compile ${{ matrix.name }} strategy: + fail-fast: false matrix: include: - - target: jvmTest + - name: JVM os: ubuntu-latest - - target: :scribe-slf4j:test + task: :scribe:compileKotlinJvm + - name: Android os: ubuntu-latest - - target: iosArm64TestKlibrary - os: macos-latest - - target: testAndroidHostTest + task: :scribe:compileAndroidMain + - name: JS + os: ubuntu-latest + task: :scribe:compileKotlinJs + - name: wasmJs + os: ubuntu-latest + task: :scribe:compileKotlinWasmJs + - name: wasmWasi os: ubuntu-latest - - target: :scribe:linuxX64Test + task: :scribe:compileKotlinWasmWasi + - name: androidNativeArm32 os: ubuntu-latest - - target: :scribe:macosArm64Test + task: :scribe:compileKotlinAndroidNativeArm32 + - name: androidNativeArm64 + os: ubuntu-latest + task: :scribe:compileKotlinAndroidNativeArm64 + - name: androidNativeX86 + os: ubuntu-latest + task: :scribe:compileKotlinAndroidNativeX86 + - name: androidNativeX64 + os: ubuntu-latest + task: :scribe:compileKotlinAndroidNativeX64 + - name: iosArm64 + os: macos-latest + task: :scribe:compileKotlinIosArm64 + - name: iosSimulatorArm64 + os: macos-latest + task: :scribe:compileKotlinIosSimulatorArm64 + - name: iosX64 os: macos-latest - - target: :scribe:mingwX64Test + task: :scribe:compileKotlinIosX64 + - name: macosArm64 + os: macos-latest + task: :scribe:compileKotlinMacosArm64 + - name: tvosArm64 + os: macos-latest + task: :scribe:compileKotlinTvosArm64 + - name: tvosSimulatorArm64 + os: macos-latest + task: :scribe:compileKotlinTvosSimulatorArm64 + - name: watchosArm32 + os: macos-latest + task: :scribe:compileKotlinWatchosArm32 + - name: watchosArm64 + os: macos-latest + task: :scribe:compileKotlinWatchosArm64 + - name: watchosDeviceArm64 + os: macos-latest + task: :scribe:compileKotlinWatchosDeviceArm64 + - name: watchosSimulatorArm64 + os: macos-latest + task: :scribe:compileKotlinWatchosSimulatorArm64 + - name: linuxArm64 + os: ubuntu-latest + task: :scribe:compileKotlinLinuxArm64 + - name: linuxX64 + os: ubuntu-latest + task: :scribe:compileKotlinLinuxX64 + - name: mingwX64 os: windows-latest + task: :scribe:compileKotlinMingwX64 runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Validate Gradle Wrapper + uses: gradle/actions/wrapper-validation@v4 + - uses: actions/cache@v4 + with: + path: | + ~/.konan + ${{ env.USERPROFILE }}\.konan + key: ${{ runner.os }}-${{ hashFiles('**/.lock') }} + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: temurin + - name: Compile target + uses: gradle/gradle-build-action@ce999babab2de1c4b649dc15f0ee67e6246c994f + with: + arguments: ${{ matrix.task }} --no-configuration-cache + runtime: + name: Test ${{ matrix.name }} + strategy: + fail-fast: false + matrix: + include: + - name: JVM + os: ubuntu-latest + task: :scribe:jvmTest + - name: Android host + os: ubuntu-latest + task: :scribe:testAndroidHostTest + - name: SLF4J JVM + os: ubuntu-latest + task: :scribe-slf4j:test + - name: LinuxX64 + os: ubuntu-latest + task: :scribe:linuxX64Test + - name: JS Node + os: ubuntu-latest + task: :scribe:jsNodeTest + - name: wasmJs Node + os: ubuntu-latest + task: :scribe:wasmJsNodeTest + - name: wasmWasi Node + os: ubuntu-latest + task: :scribe:wasmWasiNodeTest + - name: JS Chrome Headless + os: ubuntu-latest + browser: true + task: :scribe:jsBrowserTest + - name: wasmJs Chrome Headless + os: ubuntu-latest + browser: true + task: :scribe:wasmJsBrowserTest + - name: macOS Arm64 + os: macos-latest + task: :scribe:macosArm64Test + - name: iOS simulator + os: macos-latest + task: :scribe:iosSimulatorArm64Test + - name: tvOS simulator + os: macos-latest + task: :scribe:tvosSimulatorArm64Test + - name: watchOS simulator + os: macos-latest + task: :scribe:watchosSimulatorArm64Test + - name: MinGW + os: windows-latest + task: :scribe:mingwX64Test + runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 - - name: Validate Gradle Wrapper - uses: gradle/actions/wrapper-validation@v4 - - uses: actions/cache@v4 - with: - path: | - ~/.konan - ${{ env.USERPROFILE }}\.konan - key: ${{ runner.os }}-${{ hashFiles('**/.lock') }} - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - name: Build with Gradle - uses: gradle/gradle-build-action@ce999babab2de1c4b649dc15f0ee67e6246c994f - with: - arguments: ${{ matrix.target }} + - uses: actions/checkout@v4 + - name: Set up Chrome Headless + if: matrix.browser == true + uses: browser-actions/setup-chrome@v1 + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: temurin + - name: Run target tests + uses: gradle/gradle-build-action@ce999babab2de1c4b649dc15f0ee67e6246c994f + with: + arguments: ${{ matrix.task }} --no-configuration-cache diff --git a/README.md b/README.md index e21003e..687fc7f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ License - Platform Targets + Platform Targets

@@ -45,7 +45,7 @@ Add Scribe to your `commonMain` dependencies: kotlin { sourceSets { commonMain.dependencies { - implementation("com.rafambn:scribe:0.6.0") + implementation("com.rafambn:scribe:0.7.0") } } } @@ -95,13 +95,27 @@ scroll.seal(BillingScribe) Each `Scribe` object has independent configuration and delivery lifecycle. A `Scroll` is a mutable JSON-element map initialized by `newScroll(...)`; pass the runtime that should enrich and deliver it to `scroll.seal(scribe)`. Each `seal(...)` call emits a separate snapshot of the scroll data. +## Supported targets + +Version 0.7.0 publishes the core `scribe` module for exactly these 22 targets: + +- JVM: `jvm`, `android` +- Web: `js`, `wasmJs`, `wasmWasi` +- Android Native: `androidNativeArm32`, `androidNativeArm64`, `androidNativeX86`, `androidNativeX64` +- Apple: `iosArm64`, `iosSimulatorArm64`, `iosX64`, `macosArm64`, `tvosArm64`, `tvosSimulatorArm64`, `watchosArm32`, `watchosArm64`, `watchosDeviceArm64`, `watchosSimulatorArm64` +- Other Native: `linuxArm64`, `linuxX64`, `mingwX64` + +The `js` and `wasmJs` targets support both browser and Node.js execution; `wasmWasi` is configured for Node.js. The project inherits Kotlin `2.4.10`, kotlinx.serialization `1.11.0`, and kotlinx.coroutines `1.11.0` runtime requirements. The `scribe-slf4j` module remains JVM-only. + +Both artifacts are consumed from Maven Central. Scribe does not publish an npm package; Kotlin/JS and Kotlin/Wasm consumers use the Gradle Multiplatform dependency above. + ## SLF4J For JVM applications, add the SLF4J provider: ```kotlin dependencies { - implementation("com.rafambn:scribe-slf4j:0.6.0") + implementation("com.rafambn:scribe-slf4j:0.7.0") } ``` @@ -131,6 +145,8 @@ The provider discovers the annotated backend once on the first SLF4J access and See the [full documentation](https://scribe.rafambn.com/) for lifecycle controls, overflow behavior, margins, and SLF4J field mapping. +An optional `onIgnition` callback observes uncaught failures through the platform's global error mechanism. It is registered on the first `hire()` and removed by `retire()`. In Node.js, Scribe observes `uncaughtExceptionMonitor` and preserves Node's normal termination policy; unhandled rejections are reported there only when the host's current `--unhandled-rejections` policy promotes them. `wasmWasi` has no portable global hook, so a Scribe configured with `onIgnition` must fail at `hire()`; leave it null there for normal logging. + ## Performance Scribe is designed for high-throughput and thread-safe concurrent logging. diff --git a/docs/api-concepts.md b/docs/api-concepts.md index b43dc36..91dd0f9 100644 --- a/docs/api-concepts.md +++ b/docs/api-concepts.md @@ -184,7 +184,15 @@ object ApplicationScribe : Scribe() { ApplicationScribe.hire() ``` -`onIgnition` is read when processing is first hired, but handles uncaught -exceptions at the platform level. Multiple runtimes should not independently -claim this application-global hook. Archivist failures are reported by the +`onIgnition` is registered on the first `hire()` and unregistered by `retire()`. +Scribe keeps one internal platform dispatcher and fans each failure out once to +every active callback; a callback failure does not prevent the remaining +callbacks or the runtime's previous handler from observing it. Browser JS and +wasmJs observe global errors and unhandled Promise rejections. Node uses only +its uncaught-exception monitor, distinguishing `uncaughtException` from +`unhandledRejection` through the monitor's `origin` argument, so it does not +change Node's normal termination behavior. An unhandled rejection reaches the +monitor only when the host's current `--unhandled-rejections` policy promotes +it. wasmWasi has no portable global hook, so `hire()` rejects a non-null +`onIgnition` with an explicit unsupported-operation error. Archivist failures are reported by the `onArchiveFailure` property defined by the implementation. diff --git a/docs/getting-started.md b/docs/getting-started.md index b1fcd38..377c2d5 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -4,11 +4,13 @@ Use the library from shared code in your Kotlin Multiplatform module: +The published artifacts are available from Maven Central. Add the dependency to your shared source set: + ```kotlin kotlin { sourceSets { commonMain.dependencies { - implementation("com.rafambn:scribe:0.6.0") + implementation("com.rafambn:scribe:0.7.0") } } } @@ -106,6 +108,22 @@ AnalyticsScribe.hire() Dismissing `PaymentsScribe` pauses only its job and does not stop `AnalyticsScribe`. +## Version 0.7.0 targets and runtime requirements + +The core module publishes exactly 22 targets: `jvm`, `android`, `js`, `wasmJs`, +`wasmWasi`, `androidNativeArm32`, `androidNativeArm64`, `androidNativeX86`, +`androidNativeX64`, `iosArm64`, `iosSimulatorArm64`, `iosX64`, `macosArm64`, +`tvosArm64`, `tvosSimulatorArm64`, `watchosArm32`, `watchosArm64`, +`watchosDeviceArm64`, `watchosSimulatorArm64`, `linuxArm64`, `linuxX64`, and +`mingwX64`. JS and wasmJs support browser and Node.js environments; wasmWasi is +configured for Node.js. Scribe 0.7.0 inherits Kotlin `2.4.10`, kotlinx.serialization +`1.11.0`, and kotlinx.coroutines `1.11.0`. The separate `scribe-slf4j` artifact is +JVM-only. + +`onIgnition` observes global uncaught failures after the first `hire()` and is +unregistered by `retire()`. wasmWasi has no portable global hook; configure +`onIgnition = null` there or `hire()` reports an unsupported-operation error. + The emitted event shape is the scroll map itself: ```json diff --git a/docs/lifecycle-and-delivery.md b/docs/lifecycle-and-delivery.md index 904cd84..25ff78e 100644 --- a/docs/lifecycle-and-delivery.md +++ b/docs/lifecycle-and-delivery.md @@ -82,7 +82,15 @@ in the backend's private buffer. ## Uncaught Exceptions -Override `onIgnition` on an application-owned `Scribe` to install the platform uncaught exception -hook when processing is first hired. The hook is platform-global even though it is configured on -one instance. Archivist failures are handled separately by the implementation's -`onArchiveFailure` property. +Override `onIgnition` on an application-owned `Scribe` to observe platform-level failures. The +callback is registered on the first `hire()` and unregistered during `retire()`. Scribe installs +one internal platform dispatcher and delivers each failure once to every active Scribe callback; +callback failures are isolated and the previous platform handler is preserved. Browser JS and +wasmJs observe synchronous global errors and unhandled Promise rejections. Node observes uncaught +exceptions through `uncaughtExceptionMonitor`, using its `origin` argument to distinguish ordinary +uncaught exceptions from promoted unhandled rejections, without replacing Node's normal termination +path. A rejection is visible there only when the host's current `--unhandled-rejections` policy +promotes it. wasmWasi has no portable global hook, so a non-null `onIgnition` makes `hire()` fail with +an unsupported-operation message; normal logging works when it is null. Fatal delivery is best effort +and does not wait for the asynchronous Scribe buffer to drain. Archivist failures are handled +separately by the implementation's `onArchiveFailure` property. diff --git a/docs/slf4j.md b/docs/slf4j.md index 636e9e7..13c8907 100644 --- a/docs/slf4j.md +++ b/docs/slf4j.md @@ -9,7 +9,7 @@ Add the provider to a JVM application's runtime dependencies: ```kotlin dependencies { - implementation("com.rafambn:scribe-slf4j:0.6.0") + implementation("com.rafambn:scribe-slf4j:0.7.0") } ``` diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f3fba36..f3834bb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,8 +17,9 @@ kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotl kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinxSerialization" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "material3" } -scribe = { module = "com.rafambn:scribe", version = "0.5.0" } +scribe = { module = "com.rafambn:scribe", version = "0.7.0" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } classgraph = { module = "io.github.classgraph:classgraph", version.ref = "classgraph" } diff --git a/plans/0.7.0-implementation.md b/plans/0.7.0-implementation.md new file mode 100644 index 0000000..d6c866f --- /dev/null +++ b/plans/0.7.0-implementation.md @@ -0,0 +1,122 @@ +# Implement Scribe 0.7.0 + +## Outcome + +Release-ready code on `dev-0.7.0` that publishes the `scribe` core for the 22 targets below. Keep `scribe-slf4j` JVM-only. Set both modules to version `0.7.0`. + +The work stops at validated source changes. Do not commit, tag, create a GitHub release, or publish artifacts. + +## Target contract + +Configure these targets in `:scribe`: + +- JVM: `jvm`, `android` +- Web: `js`, `wasmJs`, `wasmWasi` +- Android Native: `androidNativeArm32`, `androidNativeArm64`, `androidNativeX86`, `androidNativeX64` +- Apple: `iosArm64`, `iosSimulatorArm64`, `iosX64`, `macosArm64`, `tvosArm64`, `tvosSimulatorArm64`, `watchosArm32`, `watchosArm64`, `watchosDeviceArm64`, `watchosSimulatorArm64` +- Other Native: `linuxArm64`, `linuxX64`, `mingwX64` + +There must be exactly 22 targets. Exclude the Kotlin-deprecated `macosX64`, `tvosX64`, and `watchosX64`. Also exclude `linuxArm32Hfp`, which is not in the dependency intersection. + +Configure both browser and Node environments for `js` and `wasmJs`. Configure Node for `wasmWasi`. Publish only through the existing Maven Central setup. Do not add npm publishing. + +Keep Kotlin `2.4.10`, kotlinx.serialization `1.11.0`, and kotlinx.coroutines `1.11.0` unless the build proves that a version change is required. If a change is required, report the evidence before expanding scope. + +## Error-hook contract + +Keep the public `onIgnition` behavior and make its lifecycle explicit: + +- Register it on the first `hire()` for that `Scribe`. +- Unregister it during `retire()`. +- If several active `Scribe` instances configure `onIgnition`, deliver each global error once to every active callback. +- Install at most one platform-level dispatcher per process or global environment. Restore or detach the previous platform handler after the last callback unregisters. +- Observe failures without suppressing or replacing the runtime's normal termination behavior. +- Treat delivery during a fatal error as best effort. Do not block or promise to drain the asynchronous Scribe buffer. +- Preserve previous handlers even when a Scribe callback throws. + +Platform behavior: + +- JVM and Android use the platform uncaught-thread mechanism. +- Native uses Kotlin's unhandled-exception hook. +- Browser JS and browser `wasmJs` observe synchronous global errors and unhandled Promise rejections. +- Node JS and Node `wasmJs` observe uncaught exceptions without changing Node's default exit behavior, and observe unhandled Promise rejections. +- `wasmWasi` has no portable hook. If `onIgnition` is non-null, `hire()` must fail with a clear unsupported-operation message. Normal Scribe behavior must work when `onIgnition` is null. +- Wrap non-`Throwable` JavaScript rejection reasons in a Kotlin exception with a useful message. + +Choose the smallest source-set hierarchy that expresses these behaviors without platform checks in `commonMain`. Keep every new top-level class, interface, enum, or object in its own file. + +## Implementation sequence + +### 1. Establish the baseline + +Inspect the current Gradle targets, source-set hierarchy, tests, publication tasks, and working tree before editing. Run a focused existing build or test that proves the branch starts green. + +Complete when the baseline result and any pre-existing failure are recorded before the first source change. + +### 2. Configure targets and versions + +Add the exact target matrix, Web execution environments, and required source sets. Reuse the Kotlin default hierarchy where it already supplies shared Native source sets. Change both published module versions to `0.7.0` and update internal version references that would otherwise point examples or test modules at an older release. + +Complete when Gradle configures successfully, exposes compilations for all 22 targets, excludes the four targets named as exclusions above, and the two published modules report version `0.7.0`. + +### 3. Implement global error registration + +Refactor the internal expect/actual boundary as needed to support registration plus unregistration. Keep global-state ownership internal. Add the JS, `wasmJs`, and `wasmWasi` behavior specified above. Preserve the existing JVM, Android, and Native behavior while adding cleanup and multi-instance delivery. + +Complete when tests prove registration, multi-instance dispatch, callback isolation, removal on `retire()`, previous-handler preservation where testable, synchronous and asynchronous Web failures, and explicit `wasmWasi` rejection. + +### 4. Extend verification + +Run common tests on every locally runnable target. At minimum, cover JVM, Android host, JS browser and Node, `wasmJs` browser and Node, `wasmWasi` Node, and the runnable host-native target. Compile every remaining target, including device-only and cross-architecture Native targets. + +Use the Gradle task names produced by this project rather than guessing them. Keep tests deterministic and avoid timing-based waits when a completion primitive is available. + +Complete when every locally runnable test passes and every target has a successful compilation or a precisely documented host limitation. + +### 5. Update CI + +Keep the workflow triggers for pull requests and pushes to `main`. The full matrix must run for both. Configure CI to: + +- compile all 22 targets; +- execute tests on targets with direct GitHub-hosted runner support; +- run JS and `wasmJs` in Chrome Headless and Node; +- run `wasmWasi` in Node; +- retain JVM, Android host, SLF4J, Linux, macOS, iOS simulator, tvOS simulator, watchOS simulator, and MinGW coverage where Gradle exposes runnable tasks. + +Group compilation tasks when that shortens the workflow without hiding which target failed. Keep the current publication workflow and Maven Central behavior unchanged. + +Complete when the workflow YAML parses, every declared target has either a compile or runtime-test gate, and no deprecated target remains in the matrix. + +### 6. Document 0.7.0 + +Update README and relevant MkDocs pages with version `0.7.0`, Maven usage, Web environments, the exact 22-target list, the inherited Kotlin runtime requirements, and the `wasmWasi` `onIgnition` limitation. Remove stale badges or text that claims a smaller platform set. Keep SLF4J documented as JVM-only. + +Do not create `CHANGELOG.md`; GitHub Release notes remain the release-history mechanism. + +Complete when repository-wide searches find no user-facing dependency example pinned to `0.5.0` or `0.6.0`, except historical material that is explicitly labeled as such, and every target claim matches this file. + +### 7. Final gate + +Run the broadest build that the current host supports after all edits. Run focused Web, Wasm, lifecycle, and SLF4J tests separately when the broad build does not execute them. Inspect `git diff --check`, the final diff, and the working tree for generated files or unrelated edits. + +Complete when all available checks pass, host limitations are listed with their exact failing command and reason, the diff contains only 0.7.0 work, and no commit exists. + +## Guardrails + +- Use the `kotlin-specialist` skill and its KMP and coroutine guidance. +- Preserve coroutine cancellation. A retiring Scribe must cancel its owned scope only after accepted work completes as the existing contract requires. +- Keep the solution direct. A small internal registry is acceptable; a public framework for platform hooks is outside scope. +- Public API breaks are allowed when required for this target work, but lifecycle, buffering, and archivist APIs are otherwise unchanged. +- Use `bun` for any standalone JavaScript tooling. Prefer Kotlin Gradle tasks for Kotlin/JS and Kotlin/Wasm tests. +- Prefix shell commands with `rtk`, except `./gradlew` commands. +- Preserve unrelated user changes if any appear during implementation. + +## Handoff + +Report: + +1. Changed behavior and target count. +2. Files changed. +3. Commands run and their results. +4. Any target not compiled or tested, with the exact host limitation. +5. Remaining release action. The expected remaining action is human review followed by commit, PR, GitHub Release, and Maven Central publication through the existing workflow. diff --git a/scribe-slf4j/build.gradle.kts b/scribe-slf4j/build.gradle.kts index e66871a..90cb66f 100644 --- a/scribe-slf4j/build.gradle.kts +++ b/scribe-slf4j/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } group = "com.rafambn" -version = "0.6.0" +version = "0.7.0" kotlin { jvmToolchain(11) diff --git a/scribe/build.gradle.kts b/scribe/build.gradle.kts index 963d6c2..47d5d16 100644 --- a/scribe/build.gradle.kts +++ b/scribe/build.gradle.kts @@ -1,6 +1,7 @@ import com.vanniktech.maven.publish.JavadocJar import com.vanniktech.maven.publish.KotlinMultiplatform import com.vanniktech.maven.publish.SourcesJar +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { @@ -11,8 +12,9 @@ plugins { } group = "com.rafambn" -version = "0.6.0" +version = "0.7.0" +@OptIn(ExperimentalWasmDsl::class) kotlin { jvm { compilerOptions { @@ -35,10 +37,38 @@ kotlin { jvmTarget.set(JvmTarget.JVM_11) } } + js { + browser() + nodejs { + testTask { + useMocha { + timeout = "120s" + } + } + } + } + wasmJs { + browser() + nodejs() + } + wasmWasi { + nodejs() + } + androidNativeArm32() + androidNativeArm64() + androidNativeX86() + androidNativeX64() iosX64() iosArm64() iosSimulatorArm64() + tvosArm64() + tvosSimulatorArm64() + watchosArm32() + watchosArm64() + watchosDeviceArm64() + watchosSimulatorArm64() linuxX64() + linuxArm64() mingwX64() macosArm64() @@ -51,6 +81,7 @@ kotlin { commonTest.dependencies { implementation(libs.kotlin.test) + implementation(libs.kotlinx.coroutines.test) } } } diff --git a/scribe/src/androidMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.android.kt b/scribe/src/androidMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.android.kt index d63277f..ad7ef65 100644 --- a/scribe/src/androidMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.android.kt +++ b/scribe/src/androidMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.android.kt @@ -1,12 +1,18 @@ package com.rafambn.scribe -internal actual fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit) { +internal actual fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit): () -> Unit { val previous = Thread.getDefaultUncaughtExceptionHandler() - Thread.setDefaultUncaughtExceptionHandler { thread, throwable -> + val installed = Thread.UncaughtExceptionHandler { thread, throwable -> try { handler(throwable) } finally { previous?.uncaughtException(thread, throwable) } } + Thread.setDefaultUncaughtExceptionHandler(installed) + return { + if (Thread.getDefaultUncaughtExceptionHandler() === installed) { + Thread.setDefaultUncaughtExceptionHandler(previous) + } + } } diff --git a/scribe/src/commonMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.kt b/scribe/src/commonMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.kt index f327d1d..32a2893 100644 --- a/scribe/src/commonMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.kt +++ b/scribe/src/commonMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.kt @@ -1,3 +1,9 @@ package com.rafambn.scribe -internal expect fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit) +/** + * Installs one platform-level observer and returns the operation that removes it. + * + * The returned function must restore the platform's previous observer when one + * existed. Ownership of the returned operation remains internal to Scribe. + */ +internal expect fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit): () -> Unit diff --git a/scribe/src/commonMain/kotlin/com/rafambn/scribe/IgnitionRegistry.kt b/scribe/src/commonMain/kotlin/com/rafambn/scribe/IgnitionRegistry.kt new file mode 100644 index 0000000..fe45125 --- /dev/null +++ b/scribe/src/commonMain/kotlin/com/rafambn/scribe/IgnitionRegistry.kt @@ -0,0 +1,102 @@ +package com.rafambn.scribe + +import kotlin.concurrent.atomics.AtomicBoolean +import kotlin.concurrent.atomics.AtomicReference +import kotlin.concurrent.atomics.ExperimentalAtomicApi + +/** + * Owns the process-wide exception observer while keeping callback ownership + * with individual [Scribe] instances. + */ +@OptIn(ExperimentalAtomicApi::class) +internal object IgnitionRegistry { + private class Subscription(val callback: (Throwable) -> Unit) + private data class State( + val subscriptions: List, + val uninstall: (() -> Unit)?, + ) + + private val state = AtomicReference(State(emptyList(), null)) + private val changingPlatformHandler = AtomicBoolean(false) + + fun register(callback: (Throwable) -> Unit): () -> Unit { + val subscription = Subscription(callback) + while (true) { + val current = state.load() + if (current.subscriptions.isNotEmpty()) { + val next = current.copy(subscriptions = current.subscriptions + subscription) + if (state.compareAndSet(current, next)) { + return { unregister(subscription) } + } + continue + } + + if (!changingPlatformHandler.compareAndSet(false, true)) { + continue + } + + try { + val uninstall = installUncaughtExceptionHandler(::dispatch) + val next = State(listOf(subscription), uninstall) + if (state.compareAndSet(current, next)) { + return { unregister(subscription) } + } + uninstall() + } finally { + changingPlatformHandler.store(false) + } + } + } + + private fun unregister(subscription: Subscription) { + while (true) { + val current = state.load() + if (subscription !in current.subscriptions) { + return + } + + val remaining = current.subscriptions.filterNot { it === subscription } + if (remaining.isNotEmpty()) { + if (state.compareAndSet(current, current.copy(subscriptions = remaining))) { + return + } + continue + } + + if (!changingPlatformHandler.compareAndSet(false, true)) { + continue + } + + try { + val latest = state.load() + if (subscription !in latest.subscriptions) { + return + } + if (latest.subscriptions.size != 1) { + continue + } + if (state.compareAndSet(latest, State(emptyList(), null))) { + latest.uninstall?.invoke() + return + } + } finally { + changingPlatformHandler.store(false) + } + } + } + + private fun dispatch(throwable: Throwable) { + val snapshot = state.load().subscriptions + snapshot.forEach { subscription -> + try { + subscription.callback(throwable) + } catch (_: Throwable) { + // A callback must not prevent another active callback or the + // platform's original handler from observing the failure. + } + } + } +} + +internal fun registerGlobalIgnitionHandler(callback: (Throwable) -> Unit): () -> Unit = + IgnitionRegistry.register(callback) diff --git a/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt b/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt index f3b9e96..5465ade 100644 --- a/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt +++ b/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt @@ -19,6 +19,7 @@ import kotlinx.coroutines.launch import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import kotlin.concurrent.atomics.AtomicBoolean +import kotlin.concurrent.atomics.AtomicReference import kotlin.concurrent.atomics.ExperimentalAtomicApi /** @@ -64,6 +65,7 @@ abstract class Scribe { /** * Optional uncaught exception callback. * + * It is registered on the first [hire] and unregistered during [retire]. * Although configured on an instance, uncaught exception handling is a * platform-global hook and should normally be owned by the application. */ @@ -79,7 +81,7 @@ abstract class Scribe { private val intakeOpen = AtomicBoolean(true) private val processingEnabled = MutableStateFlow(false) private val retiring = AtomicBoolean(false) - private val ignitionInstalled = AtomicBoolean(false) + private val ignitionRegistration = AtomicReference<(() -> Unit)?>(null) private val retirementCompleted = CompletableDeferred() private val processorJob: Job by lazy { ownedScope.launch { @@ -113,8 +115,15 @@ abstract class Scribe { require(configuredArchivists.isNotEmpty()) { "At least one archivist is required." } check(!retiring.load()) { "This Scribe has been retired." } val exceptionHandler = onIgnition - if (exceptionHandler != null && ignitionInstalled.compareAndSet(expectedValue = false, newValue = true)) { - installUncaughtExceptionHandler(exceptionHandler) + if (exceptionHandler != null && ignitionRegistration.load() == null) { + val uninstall = registerGlobalIgnitionHandler(exceptionHandler) + if (!ignitionRegistration.compareAndSet(null, uninstall)) { + uninstall() + } + if (retiring.load()) { + removeIgnitionRegistration() + check(!retiring.load()) { "This Scribe has been retired." } + } } val processor = processorJob @@ -173,6 +182,7 @@ abstract class Scribe { if (retiring.compareAndSet(expectedValue = false, newValue = true)) { intakeOpen.store(false) + removeIgnitionRegistration() processingEnabled.value = true queue.close() @@ -216,6 +226,16 @@ abstract class Scribe { return queue.trySend(entry).isSuccess } + private fun removeIgnitionRegistration() { + while (true) { + val uninstall = ignitionRegistration.load() ?: return + if (ignitionRegistration.compareAndSet(uninstall, null)) { + uninstall() + return + } + } + } + private suspend fun archive( configuredArchivists: List, entry: Entry, diff --git a/scribe/src/commonTest/kotlin/com/rafambn/scribe/ScribeTestFixtures.kt b/scribe/src/commonTest/kotlin/com/rafambn/scribe/ScribeTestFixtures.kt index 27138f3..09412ec 100644 --- a/scribe/src/commonTest/kotlin/com/rafambn/scribe/ScribeTestFixtures.kt +++ b/scribe/src/commonTest/kotlin/com/rafambn/scribe/ScribeTestFixtures.kt @@ -1,9 +1,11 @@ package com.rafambn.scribe import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import kotlinx.coroutines.test.runTest import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive @@ -19,12 +21,14 @@ internal fun scribeWithScrollShelves( onArchivist: (archivist: Archivist, entry: Entry, error: Throwable) -> Unit = { _, _, _ -> }, margins: Margin? = null, startProcessing: Boolean = true, + onIgnition: ((Throwable) -> Unit)? = null, ): Scribe { val configuredShelves = shelves.toList() val configuredImprint = imprint val configuredMargins = margins val configuredBufferCapacity = bufferCapacity val configuredBufferOverflow = bufferOverflow + val configuredOnIgnition = onIgnition return object : Scribe() { override val archivists: List = configuredShelves override val bufferCapacity: Int = configuredBufferCapacity @@ -32,6 +36,7 @@ internal fun scribeWithScrollShelves( override val onArchiveFailure = onArchivist override val imprint: Map = configuredImprint override val margins: Margin? = configuredMargins + override val onIgnition: ((Throwable) -> Unit)? = configuredOnIgnition }.also { if (startProcessing) it.hire() } @@ -45,12 +50,14 @@ internal fun scribeWithArchivists( bufferOverflow: BufferOverflow = BufferOverflow.DROP_OLDEST, onArchivist: (archivist: Archivist, entry: Entry, error: Throwable) -> Unit = { _, _, _ -> }, startProcessing: Boolean = true, + onIgnition: ((Throwable) -> Unit)? = null, ): Scribe { val configuredShelves = shelves val configuredImprint = imprint val configuredMargins = margins val configuredBufferCapacity = bufferCapacity val configuredBufferOverflow = bufferOverflow + val configuredOnIgnition = onIgnition return object : Scribe() { override val archivists: List = configuredShelves override val bufferCapacity: Int = configuredBufferCapacity @@ -58,12 +65,17 @@ internal fun scribeWithArchivists( override val onArchiveFailure = onArchivist override val imprint: Map = configuredImprint override val margins: Margin? = configuredMargins + override val onIgnition: ((Throwable) -> Unit)? = configuredOnIgnition }.also { if (startProcessing) it.hire() } } -internal fun runSuspend(block: suspend () -> T): T = runBlocking { block() } +internal fun runSuspend(block: suspend () -> Unit) = runTest { + withContext(Dispatchers.Default.limitedParallelism(1)) { + block() + } +} internal fun createScribeInHelperAndEmit(shelf: Archivist): Scribe { val scribe = scribeWithScrollShelves(shelf) diff --git a/scribe/src/jsMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.js.kt b/scribe/src/jsMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.js.kt new file mode 100644 index 0000000..571206b --- /dev/null +++ b/scribe/src/jsMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.js.kt @@ -0,0 +1,75 @@ +package com.rafambn.scribe + +import kotlin.js.js +import kotlin.js.jsTypeOf + +internal actual fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit): () -> Unit = + if (isNodeRuntime()) { + installNodeExceptionHandler(handler) + } else { + installBrowserExceptionHandler(handler) + } + +private fun isNodeRuntime(): Boolean = + js("typeof process !== 'undefined' && process.versions != null && process.versions.node != null") + +private fun installNodeExceptionHandler(handler: (Throwable) -> Unit): () -> Unit { + val process: dynamic = js("process") + val uncaughtExceptionMonitor: (dynamic, dynamic) -> Unit = { reason, origin -> + val context = if (origin == "unhandledRejection") { + "Unhandled Node.js promise rejection" + } else { + "Uncaught Node.js exception" + } + handler(asScribeThrowable(reason, context)) + } + + // uncaughtExceptionMonitor observes failures without adding an + // uncaughtException listener, so Node keeps its normal termination path. + process.on("uncaughtExceptionMonitor", uncaughtExceptionMonitor) + + return { + process.removeListener("uncaughtExceptionMonitor", uncaughtExceptionMonitor) + } +} + +private fun installBrowserExceptionHandler(handler: (Throwable) -> Unit): () -> Unit { + val global: dynamic = js("globalThis") + val errorListener: (dynamic) -> Unit = { event -> + val reason = event?.error ?: event?.message + handler(asScribeThrowable(reason, "Uncaught browser error")) + } + val unhandledRejection: (dynamic) -> Unit = { event -> + handler(asScribeThrowable(event?.reason, "Unhandled browser promise rejection")) + } + + global.addEventListener("error", errorListener) + global.addEventListener("unhandledrejection", unhandledRejection) + + return { + global.removeEventListener("error", errorListener) + global.removeEventListener("unhandledrejection", unhandledRejection) + } +} + +private fun asScribeThrowable(value: dynamic, context: String): Throwable = + try { + if (value is Throwable) { + value + } else { + IllegalStateException("$context: ${describeJavaScriptValue(value)}") + } + } catch (error: Throwable) { + IllegalStateException("$context: ${describeJavaScriptValue(value)}", error) + } + +private fun describeJavaScriptValue(value: dynamic): String = + try { + when (jsTypeOf(value)) { + "undefined" -> "undefined" + "object" -> value?.toString() ?: "null" + else -> value.toString() + } + } catch (_: Throwable) { + "" + } diff --git a/scribe/src/jsTest/kotlin/com/rafambn/scribe/GlobalExceptionHandlerJsTest.kt b/scribe/src/jsTest/kotlin/com/rafambn/scribe/GlobalExceptionHandlerJsTest.kt new file mode 100644 index 0000000..11ec534 --- /dev/null +++ b/scribe/src/jsTest/kotlin/com/rafambn/scribe/GlobalExceptionHandlerJsTest.kt @@ -0,0 +1,43 @@ +package com.rafambn.scribe + +import kotlin.js.js +import kotlin.test.Test +import kotlin.test.assertTrue + +class GlobalExceptionHandlerJsTest { + @Test + fun node_global_failures_reach_the_callback_and_non_throwables_are_wrapped() = runSuspend { + if (!isNodeRuntime()) return@runSuspend + + val calls = mutableListOf() + val scribe = scribeWithScrollShelves( + Archivist { }, + onIgnition = { calls += it.message.orEmpty() }, + ) + + try { + emitNodeFailure("uncaught-value", "uncaughtException") + emitNodeFailure("rejected-value", "unhandledRejection") + emitUnhandledRejection("ignored-value") + assertTrue(calls.size == 2) + assertTrue(calls.any { it.contains("Uncaught Node.js") }) + assertTrue(calls.any { it.contains("Unhandled Node.js") }) + assertTrue(calls.all { it.contains("value") }) + } finally { + scribe.retire() + } + } + + private fun isNodeRuntime(): Boolean = + js("typeof process !== 'undefined' && process.versions != null && process.versions.node != null") + + private fun emitNodeFailure(value: String, origin: String?) { + val process: dynamic = js("process") + process.emit("uncaughtExceptionMonitor", value, origin) + } + + private fun emitUnhandledRejection(value: String) { + val process: dynamic = js("process") + process.emit("unhandledRejection", value) + } +} diff --git a/scribe/src/jvmMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.jvm.kt b/scribe/src/jvmMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.jvm.kt index d63277f..ad7ef65 100644 --- a/scribe/src/jvmMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.jvm.kt +++ b/scribe/src/jvmMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.jvm.kt @@ -1,12 +1,18 @@ package com.rafambn.scribe -internal actual fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit) { +internal actual fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit): () -> Unit { val previous = Thread.getDefaultUncaughtExceptionHandler() - Thread.setDefaultUncaughtExceptionHandler { thread, throwable -> + val installed = Thread.UncaughtExceptionHandler { thread, throwable -> try { handler(throwable) } finally { previous?.uncaughtException(thread, throwable) } } + Thread.setDefaultUncaughtExceptionHandler(installed) + return { + if (Thread.getDefaultUncaughtExceptionHandler() === installed) { + Thread.setDefaultUncaughtExceptionHandler(previous) + } + } } diff --git a/scribe/src/jvmTest/kotlin/com/rafambn/scribe/GlobalIgnitionLifecycleJvmTest.kt b/scribe/src/jvmTest/kotlin/com/rafambn/scribe/GlobalIgnitionLifecycleJvmTest.kt new file mode 100644 index 0000000..cf7fd0b --- /dev/null +++ b/scribe/src/jvmTest/kotlin/com/rafambn/scribe/GlobalIgnitionLifecycleJvmTest.kt @@ -0,0 +1,94 @@ +package com.rafambn.scribe + +import java.util.Collections +import kotlinx.coroutines.runBlocking +import kotlin.test.Test +import kotlin.test.assertEquals + +class GlobalIgnitionLifecycleJvmTest { + @Test + fun active_scribes_receive_one_notification_and_retire_unregisters_them() = runBlocking { + val original = Thread.getDefaultUncaughtExceptionHandler() + val calls = Collections.synchronizedList(mutableListOf()) + val previous = Thread.UncaughtExceptionHandler { _, throwable -> + calls += "previous:${throwable.message}" + } + Thread.setDefaultUncaughtExceptionHandler(previous) + + val first = scribeWithScrollShelves( + Archivist { }, + onIgnition = { calls += "first:${it.message}" }, + ) + val second = scribeWithScrollShelves( + Archivist { }, + onIgnition = { calls += "second:${it.message}" }, + ) + + try { + first.hire() + val thread = Thread { throw IllegalStateException("before-retire") } + thread.start() + thread.join(2_000) + assertEquals( + listOf("first:before-retire", "second:before-retire", "previous:before-retire"), + calls.toList(), + ) + + first.retire() + first.retire() + calls.clear() + val afterFirstRetire = Thread { throw IllegalStateException("after-first-retire") } + afterFirstRetire.start() + afterFirstRetire.join(2_000) + assertEquals( + listOf("second:after-first-retire", "previous:after-first-retire"), + calls.toList(), + ) + + second.retire() + calls.clear() + val afterAllRetire = Thread { throw IllegalStateException("after-all-retire") } + afterAllRetire.start() + afterAllRetire.join(2_000) + assertEquals(listOf("previous:after-all-retire"), calls.toList()) + } finally { + if (first.isIntakeOpen) first.retire() + if (second.isIntakeOpen) second.retire() + Thread.setDefaultUncaughtExceptionHandler(original) + } + } + + @Test + fun a_throwing_callback_does_not_block_other_callbacks_or_previous_handler() = runBlocking { + val original = Thread.getDefaultUncaughtExceptionHandler() + val calls = Collections.synchronizedList(mutableListOf()) + Thread.setDefaultUncaughtExceptionHandler { _, throwable -> + calls += "previous:${throwable.message}" + } + val throwing = scribeWithScrollShelves( + Archivist { }, + onIgnition = { + calls += "throwing:${it.message}" + error("callback-failed") + }, + ) + val healthy = scribeWithScrollShelves( + Archivist { }, + onIgnition = { calls += "healthy:${it.message}" }, + ) + + try { + val thread = Thread { throw IllegalStateException("isolated") } + thread.start() + thread.join(2_000) + assertEquals( + listOf("throwing:isolated", "healthy:isolated", "previous:isolated"), + calls.toList(), + ) + } finally { + if (throwing.isIntakeOpen) throwing.retire() + if (healthy.isIntakeOpen) healthy.retire() + Thread.setDefaultUncaughtExceptionHandler(original) + } + } +} diff --git a/scribe/src/nativeMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.native.kt b/scribe/src/nativeMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.native.kt index b604969..8bb6cd7 100644 --- a/scribe/src/nativeMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.native.kt +++ b/scribe/src/nativeMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.native.kt @@ -1,13 +1,25 @@ package com.rafambn.scribe @OptIn(kotlin.experimental.ExperimentalNativeApi::class) -internal actual fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit) { +internal actual fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit): () -> Unit { var previous: ((Throwable) -> Unit)? = null - previous = setUnhandledExceptionHook { throwable -> + lateinit var installed: (Throwable) -> Unit + installed = { throwable -> try { handler(throwable) } finally { - previous?.invoke(throwable) + val previousHandler = previous + if (previousHandler != null && previousHandler !== installed) { + previousHandler(throwable) + } else { + terminateWithUnhandledException(throwable) + } + } + } + previous = setUnhandledExceptionHook(installed) + return { + if (getUnhandledExceptionHook() === installed) { + setUnhandledExceptionHook(previous) } } } diff --git a/scribe/src/wasmJsMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.wasmJs.kt b/scribe/src/wasmJsMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.wasmJs.kt new file mode 100644 index 0000000..304ca05 --- /dev/null +++ b/scribe/src/wasmJsMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.wasmJs.kt @@ -0,0 +1,48 @@ +package com.rafambn.scribe + +import kotlin.js.ExperimentalWasmJsInterop +import kotlin.js.JsAny +import kotlin.js.js + +@OptIn(ExperimentalWasmJsInterop::class) +internal actual fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit): () -> Unit { + val bridge: (JsAny?, JsAny?) -> Unit = { reason, origin -> + val context = if (origin?.toString() == "unhandledRejection") { + "Unhandled WebAssembly JavaScript promise rejection" + } else { + "Uncaught WebAssembly JavaScript exception" + } + handler(reason.asScribeThrowable(context)) + } + return installWasmJavaScriptHooks(bridge) +} + +@OptIn(ExperimentalWasmJsInterop::class) +private fun installWasmJavaScriptHooks(handler: (JsAny?, JsAny?) -> Unit): () -> Unit = js( + """(function() { + const root = globalThis; + const isNode = typeof process !== 'undefined' && + process.versions != null && process.versions.node != null; + const onUncaught = (value, origin) => handler(value, origin); + const onBrowserError = event => handler(event.error ?? event.message, null); + const onBrowserRejection = event => handler(event.reason, null); + + if (isNode && typeof process.on === 'function') { + process.on('uncaughtExceptionMonitor', onUncaught); + return () => { + process.removeListener('uncaughtExceptionMonitor', onUncaught); + }; + } + + root.addEventListener('error', onBrowserError); + root.addEventListener('unhandledrejection', onBrowserRejection); + return () => { + root.removeEventListener('error', onBrowserError); + root.removeEventListener('unhandledrejection', onBrowserRejection); + }; + })()""", +) + +@OptIn(ExperimentalWasmJsInterop::class) +private fun JsAny?.asScribeThrowable(context: String): Throwable = + (this as? Throwable) ?: IllegalStateException("$context: ${this ?: "undefined"}") diff --git a/scribe/src/wasmWasiMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.wasmWasi.kt b/scribe/src/wasmWasiMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.wasmWasi.kt new file mode 100644 index 0000000..5c4b897 --- /dev/null +++ b/scribe/src/wasmWasiMain/kotlin/com/rafambn/scribe/GlobalExceptionHandler.wasmWasi.kt @@ -0,0 +1,6 @@ +package com.rafambn.scribe + +internal actual fun installUncaughtExceptionHandler(handler: (Throwable) -> Unit): () -> Unit = + throw UnsupportedOperationException( + "Scribe onIgnition is unsupported on wasmWasi because it has no portable global error hook.", + ) diff --git a/scribe/src/wasmWasiTest/kotlin/com/rafambn/scribe/WasmWasiExceptionHandlerTest.kt b/scribe/src/wasmWasiTest/kotlin/com/rafambn/scribe/WasmWasiExceptionHandlerTest.kt new file mode 100644 index 0000000..f791a96 --- /dev/null +++ b/scribe/src/wasmWasiTest/kotlin/com/rafambn/scribe/WasmWasiExceptionHandlerTest.kt @@ -0,0 +1,19 @@ +package com.rafambn.scribe + +import kotlin.test.Test +import kotlin.test.assertFailsWith +import kotlin.test.assertTrue + +class WasmWasiExceptionHandlerTest { + @Test + fun onIgnition_is_rejected_with_an_explicit_platform_limitation() { + val scribe = scribeWithScrollShelves( + Archivist { }, + startProcessing = false, + onIgnition = {}, + ) + + val error = assertFailsWith { scribe.hire() } + assertTrue(error.message.orEmpty().contains("wasmWasi")) + } +} From 3d25c71ae311116a58b76d24ab68978ea9ad2efc Mon Sep 17 00:00:00 2001 From: rafambn Date: Sat, 22 Aug 2026 09:48:25 -0300 Subject: [PATCH 2/6] Make ignition lifecycle safe under concurrent hire --- .github/workflows/gradle.yml | 14 +- README.md | 2 - docs/getting-started.md | 12 -- gradle/libs.versions.toml | 4 - plans/0.7.0-implementation.md | 122 ------------------ .../com/rafambn/scribe/IgnitionRegistry.kt | 3 + .../kotlin/com/rafambn/scribe/Scribe.kt | 33 ++++- .../ScribeIgnitionRegistrationJvmTest.kt | 81 ++++++++++++ 8 files changed, 120 insertions(+), 151 deletions(-) delete mode 100644 plans/0.7.0-implementation.md create mode 100644 scribe/src/jvmTest/kotlin/com/rafambn/scribe/ScribeIgnitionRegistrationJvmTest.kt diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 6bbc586..5470887 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -93,10 +93,9 @@ jobs: uses: gradle/actions/wrapper-validation@v4 - uses: actions/cache@v4 with: - path: | - ~/.konan - ${{ env.USERPROFILE }}\.konan - key: ${{ runner.os }}-${{ hashFiles('**/.lock') }} + path: ~/.konan + key: ${{ runner.os }}-konan-${{ hashFiles('gradle/libs.versions.toml', 'scribe/build.gradle.kts', 'scribe-slf4j/build.gradle.kts') }} + restore-keys: ${{ runner.os }}-konan- - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -160,6 +159,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 + - name: Validate Gradle Wrapper + uses: gradle/actions/wrapper-validation@v4 + - uses: actions/cache@v4 + with: + path: ~/.konan + key: ${{ runner.os }}-konan-${{ hashFiles('gradle/libs.versions.toml', 'scribe/build.gradle.kts', 'scribe-slf4j/build.gradle.kts') }} + restore-keys: ${{ runner.os }}-konan- - name: Set up Chrome Headless if: matrix.browser == true uses: browser-actions/setup-chrome@v1 diff --git a/README.md b/README.md index 687fc7f..1db77a2 100644 --- a/README.md +++ b/README.md @@ -145,8 +145,6 @@ The provider discovers the annotated backend once on the first SLF4J access and See the [full documentation](https://scribe.rafambn.com/) for lifecycle controls, overflow behavior, margins, and SLF4J field mapping. -An optional `onIgnition` callback observes uncaught failures through the platform's global error mechanism. It is registered on the first `hire()` and removed by `retire()`. In Node.js, Scribe observes `uncaughtExceptionMonitor` and preserves Node's normal termination policy; unhandled rejections are reported there only when the host's current `--unhandled-rejections` policy promotes them. `wasmWasi` has no portable global hook, so a Scribe configured with `onIgnition` must fail at `hire()`; leave it null there for normal logging. - ## Performance Scribe is designed for high-throughput and thread-safe concurrent logging. diff --git a/docs/getting-started.md b/docs/getting-started.md index 377c2d5..ba7cfa5 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -108,18 +108,6 @@ AnalyticsScribe.hire() Dismissing `PaymentsScribe` pauses only its job and does not stop `AnalyticsScribe`. -## Version 0.7.0 targets and runtime requirements - -The core module publishes exactly 22 targets: `jvm`, `android`, `js`, `wasmJs`, -`wasmWasi`, `androidNativeArm32`, `androidNativeArm64`, `androidNativeX86`, -`androidNativeX64`, `iosArm64`, `iosSimulatorArm64`, `iosX64`, `macosArm64`, -`tvosArm64`, `tvosSimulatorArm64`, `watchosArm32`, `watchosArm64`, -`watchosDeviceArm64`, `watchosSimulatorArm64`, `linuxArm64`, `linuxX64`, and -`mingwX64`. JS and wasmJs support browser and Node.js environments; wasmWasi is -configured for Node.js. Scribe 0.7.0 inherits Kotlin `2.4.10`, kotlinx.serialization -`1.11.0`, and kotlinx.coroutines `1.11.0`. The separate `scribe-slf4j` artifact is -JVM-only. - `onIgnition` observes global uncaught failures after the first `hire()` and is unregistered by `retire()`. wasmWasi has no portable global hook; configure `onIgnition = null` there or `hire()` reports an unsupported-operation error. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f3834bb..9d5fabb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,11 +4,9 @@ kotlin = "2.4.10" compose-multiplatform = "1.11.1" android-minSdk = "24" android-compileSdk = "37" -material3 = "1.9.0" vanniktechMavenPublish = "0.37.0" kotlinxSerialization = "1.11.0" kotlinxCoroutines = "1.11.0" -androidx-activity-compose = "1.13.0" slf4j = "2.0.18" classgraph = "4.8.181" @@ -18,9 +16,7 @@ kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serializa kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } -material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "material3" } scribe = { module = "com.rafambn:scribe", version = "0.7.0" } -androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } classgraph = { module = "io.github.classgraph:classgraph", version.ref = "classgraph" } diff --git a/plans/0.7.0-implementation.md b/plans/0.7.0-implementation.md deleted file mode 100644 index d6c866f..0000000 --- a/plans/0.7.0-implementation.md +++ /dev/null @@ -1,122 +0,0 @@ -# Implement Scribe 0.7.0 - -## Outcome - -Release-ready code on `dev-0.7.0` that publishes the `scribe` core for the 22 targets below. Keep `scribe-slf4j` JVM-only. Set both modules to version `0.7.0`. - -The work stops at validated source changes. Do not commit, tag, create a GitHub release, or publish artifacts. - -## Target contract - -Configure these targets in `:scribe`: - -- JVM: `jvm`, `android` -- Web: `js`, `wasmJs`, `wasmWasi` -- Android Native: `androidNativeArm32`, `androidNativeArm64`, `androidNativeX86`, `androidNativeX64` -- Apple: `iosArm64`, `iosSimulatorArm64`, `iosX64`, `macosArm64`, `tvosArm64`, `tvosSimulatorArm64`, `watchosArm32`, `watchosArm64`, `watchosDeviceArm64`, `watchosSimulatorArm64` -- Other Native: `linuxArm64`, `linuxX64`, `mingwX64` - -There must be exactly 22 targets. Exclude the Kotlin-deprecated `macosX64`, `tvosX64`, and `watchosX64`. Also exclude `linuxArm32Hfp`, which is not in the dependency intersection. - -Configure both browser and Node environments for `js` and `wasmJs`. Configure Node for `wasmWasi`. Publish only through the existing Maven Central setup. Do not add npm publishing. - -Keep Kotlin `2.4.10`, kotlinx.serialization `1.11.0`, and kotlinx.coroutines `1.11.0` unless the build proves that a version change is required. If a change is required, report the evidence before expanding scope. - -## Error-hook contract - -Keep the public `onIgnition` behavior and make its lifecycle explicit: - -- Register it on the first `hire()` for that `Scribe`. -- Unregister it during `retire()`. -- If several active `Scribe` instances configure `onIgnition`, deliver each global error once to every active callback. -- Install at most one platform-level dispatcher per process or global environment. Restore or detach the previous platform handler after the last callback unregisters. -- Observe failures without suppressing or replacing the runtime's normal termination behavior. -- Treat delivery during a fatal error as best effort. Do not block or promise to drain the asynchronous Scribe buffer. -- Preserve previous handlers even when a Scribe callback throws. - -Platform behavior: - -- JVM and Android use the platform uncaught-thread mechanism. -- Native uses Kotlin's unhandled-exception hook. -- Browser JS and browser `wasmJs` observe synchronous global errors and unhandled Promise rejections. -- Node JS and Node `wasmJs` observe uncaught exceptions without changing Node's default exit behavior, and observe unhandled Promise rejections. -- `wasmWasi` has no portable hook. If `onIgnition` is non-null, `hire()` must fail with a clear unsupported-operation message. Normal Scribe behavior must work when `onIgnition` is null. -- Wrap non-`Throwable` JavaScript rejection reasons in a Kotlin exception with a useful message. - -Choose the smallest source-set hierarchy that expresses these behaviors without platform checks in `commonMain`. Keep every new top-level class, interface, enum, or object in its own file. - -## Implementation sequence - -### 1. Establish the baseline - -Inspect the current Gradle targets, source-set hierarchy, tests, publication tasks, and working tree before editing. Run a focused existing build or test that proves the branch starts green. - -Complete when the baseline result and any pre-existing failure are recorded before the first source change. - -### 2. Configure targets and versions - -Add the exact target matrix, Web execution environments, and required source sets. Reuse the Kotlin default hierarchy where it already supplies shared Native source sets. Change both published module versions to `0.7.0` and update internal version references that would otherwise point examples or test modules at an older release. - -Complete when Gradle configures successfully, exposes compilations for all 22 targets, excludes the four targets named as exclusions above, and the two published modules report version `0.7.0`. - -### 3. Implement global error registration - -Refactor the internal expect/actual boundary as needed to support registration plus unregistration. Keep global-state ownership internal. Add the JS, `wasmJs`, and `wasmWasi` behavior specified above. Preserve the existing JVM, Android, and Native behavior while adding cleanup and multi-instance delivery. - -Complete when tests prove registration, multi-instance dispatch, callback isolation, removal on `retire()`, previous-handler preservation where testable, synchronous and asynchronous Web failures, and explicit `wasmWasi` rejection. - -### 4. Extend verification - -Run common tests on every locally runnable target. At minimum, cover JVM, Android host, JS browser and Node, `wasmJs` browser and Node, `wasmWasi` Node, and the runnable host-native target. Compile every remaining target, including device-only and cross-architecture Native targets. - -Use the Gradle task names produced by this project rather than guessing them. Keep tests deterministic and avoid timing-based waits when a completion primitive is available. - -Complete when every locally runnable test passes and every target has a successful compilation or a precisely documented host limitation. - -### 5. Update CI - -Keep the workflow triggers for pull requests and pushes to `main`. The full matrix must run for both. Configure CI to: - -- compile all 22 targets; -- execute tests on targets with direct GitHub-hosted runner support; -- run JS and `wasmJs` in Chrome Headless and Node; -- run `wasmWasi` in Node; -- retain JVM, Android host, SLF4J, Linux, macOS, iOS simulator, tvOS simulator, watchOS simulator, and MinGW coverage where Gradle exposes runnable tasks. - -Group compilation tasks when that shortens the workflow without hiding which target failed. Keep the current publication workflow and Maven Central behavior unchanged. - -Complete when the workflow YAML parses, every declared target has either a compile or runtime-test gate, and no deprecated target remains in the matrix. - -### 6. Document 0.7.0 - -Update README and relevant MkDocs pages with version `0.7.0`, Maven usage, Web environments, the exact 22-target list, the inherited Kotlin runtime requirements, and the `wasmWasi` `onIgnition` limitation. Remove stale badges or text that claims a smaller platform set. Keep SLF4J documented as JVM-only. - -Do not create `CHANGELOG.md`; GitHub Release notes remain the release-history mechanism. - -Complete when repository-wide searches find no user-facing dependency example pinned to `0.5.0` or `0.6.0`, except historical material that is explicitly labeled as such, and every target claim matches this file. - -### 7. Final gate - -Run the broadest build that the current host supports after all edits. Run focused Web, Wasm, lifecycle, and SLF4J tests separately when the broad build does not execute them. Inspect `git diff --check`, the final diff, and the working tree for generated files or unrelated edits. - -Complete when all available checks pass, host limitations are listed with their exact failing command and reason, the diff contains only 0.7.0 work, and no commit exists. - -## Guardrails - -- Use the `kotlin-specialist` skill and its KMP and coroutine guidance. -- Preserve coroutine cancellation. A retiring Scribe must cancel its owned scope only after accepted work completes as the existing contract requires. -- Keep the solution direct. A small internal registry is acceptable; a public framework for platform hooks is outside scope. -- Public API breaks are allowed when required for this target work, but lifecycle, buffering, and archivist APIs are otherwise unchanged. -- Use `bun` for any standalone JavaScript tooling. Prefer Kotlin Gradle tasks for Kotlin/JS and Kotlin/Wasm tests. -- Prefix shell commands with `rtk`, except `./gradlew` commands. -- Preserve unrelated user changes if any appear during implementation. - -## Handoff - -Report: - -1. Changed behavior and target count. -2. Files changed. -3. Commands run and their results. -4. Any target not compiled or tested, with the exact host limitation. -5. Remaining release action. The expected remaining action is human review followed by commit, PR, GitHub Release, and Maven Central publication through the existing workflow. diff --git a/scribe/src/commonMain/kotlin/com/rafambn/scribe/IgnitionRegistry.kt b/scribe/src/commonMain/kotlin/com/rafambn/scribe/IgnitionRegistry.kt index fe45125..6a38015 100644 --- a/scribe/src/commonMain/kotlin/com/rafambn/scribe/IgnitionRegistry.kt +++ b/scribe/src/commonMain/kotlin/com/rafambn/scribe/IgnitionRegistry.kt @@ -36,6 +36,9 @@ internal object IgnitionRegistry { } try { + if (state.load().subscriptions.isNotEmpty()) { + continue + } val uninstall = installUncaughtExceptionHandler(::dispatch) val next = State(listOf(subscription), uninstall) if (state.compareAndSet(current, next)) { diff --git a/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt b/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt index 5465ade..c908af7 100644 --- a/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt +++ b/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt @@ -82,6 +82,7 @@ abstract class Scribe { private val processingEnabled = MutableStateFlow(false) private val retiring = AtomicBoolean(false) private val ignitionRegistration = AtomicReference<(() -> Unit)?>(null) + private val ignitionRegistrationInFlight = AtomicBoolean(false) private val retirementCompleted = CompletableDeferred() private val processorJob: Job by lazy { ownedScope.launch { @@ -104,6 +105,9 @@ abstract class Scribe { val isProcessing: Boolean get() = processingEnabled.value + internal open fun registerIgnitionHandler(callback: (Throwable) -> Unit): () -> Unit = + registerGlobalIgnitionHandler(callback) + /** * Starts or resumes delivery from this instance's private buffer. * @@ -115,23 +119,38 @@ abstract class Scribe { require(configuredArchivists.isNotEmpty()) { "At least one archivist is required." } check(!retiring.load()) { "This Scribe has been retired." } val exceptionHandler = onIgnition - if (exceptionHandler != null && ignitionRegistration.load() == null) { - val uninstall = registerGlobalIgnitionHandler(exceptionHandler) - if (!ignitionRegistration.compareAndSet(null, uninstall)) { - uninstall() - } - if (retiring.load()) { - removeIgnitionRegistration() + if (exceptionHandler != null) { + while (ignitionRegistration.load() == null) { check(!retiring.load()) { "This Scribe has been retired." } + if (!ignitionRegistrationInFlight.compareAndSet(false, true)) { + continue + } + + try { + val uninstall = registerIgnitionHandler(exceptionHandler) + if (ignitionRegistration.compareAndSet(null, uninstall)) { + if (retiring.load()) { + removeIgnitionRegistration() + check(!retiring.load()) { "This Scribe has been retired." } + } + } else { + uninstall() + } + } finally { + ignitionRegistrationInFlight.store(false) + } } } val processor = processorJob + check(!retiring.load()) { "This Scribe has been retired." } processingEnabled.value = true + check(!retiring.load()) { "This Scribe has been retired." } if (processor.isCompleted) { processingEnabled.value = false error("The Scribe processor has terminated and cannot be restarted.") } + check(!retiring.load()) { "This Scribe has been retired." } } /** Allows new entries to be accepted into the private buffer. */ diff --git a/scribe/src/jvmTest/kotlin/com/rafambn/scribe/ScribeIgnitionRegistrationJvmTest.kt b/scribe/src/jvmTest/kotlin/com/rafambn/scribe/ScribeIgnitionRegistrationJvmTest.kt new file mode 100644 index 0000000..c296e2e --- /dev/null +++ b/scribe/src/jvmTest/kotlin/com/rafambn/scribe/ScribeIgnitionRegistrationJvmTest.kt @@ -0,0 +1,81 @@ +package com.rafambn.scribe + +import java.util.Collections +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger +import kotlinx.coroutines.runBlocking +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class ScribeIgnitionRegistrationJvmTest { + @Test + fun concurrent_hires_retry_when_the_registration_fails() { + val firstRegistrationStarted = CountDownLatch(1) + val releaseFirstRegistration = CountDownLatch(1) + val bothHiresStarted = CountDownLatch(2) + val registrationCalls = AtomicInteger(0) + val failures = Collections.synchronizedList(mutableListOf()) + val scribe = object : Scribe() { + override val archivists: List + get() { + bothHiresStarted.countDown() + return listOf(Archivist {}) + } + + override val onIgnition: ((Throwable) -> Unit) = {} + + override fun registerIgnitionHandler(callback: (Throwable) -> Unit): () -> Unit { + if (registrationCalls.incrementAndGet() == 1) { + firstRegistrationStarted.countDown() + releaseFirstRegistration.await(2, TimeUnit.SECONDS) + } + throw UnsupportedOperationException("registration failed") + } + } + val first = Thread { + try { + scribe.hire() + } catch (error: Throwable) { + failures += error + } + } + val second = Thread { + try { + scribe.hire() + } catch (error: Throwable) { + failures += error + } + } + + try { + first.start() + assertTrue(firstRegistrationStarted.await(2, TimeUnit.SECONDS)) + second.start() + assertTrue(bothHiresStarted.await(2, TimeUnit.SECONDS)) + releaseFirstRegistration.countDown() + + first.join(2_000) + second.join(2_000) + assertFalse(first.isAlive) + assertFalse(second.isAlive) + assertEquals(2, failures.size) + assertTrue(failures.all { it is UnsupportedOperationException }) + + val retry = runCatching { scribe.hire() }.exceptionOrNull() + assertTrue(retry is UnsupportedOperationException) + assertEquals(3, registrationCalls.get()) + } finally { + releaseFirstRegistration.countDown() + first.join(2_000) + second.join(2_000) + runBlocking { + if (scribe.isIntakeOpen) { + scribe.retire() + } + } + } + } +} From b1f67a3c99fc3533a84a466c2469c532a5954217 Mon Sep 17 00:00:00 2001 From: rafambn Date: Sat, 22 Aug 2026 10:08:17 -0300 Subject: [PATCH 3/6] Invoke global ignition registration directly and mock it in JVM test --- gradle/libs.versions.toml | 2 ++ scribe/build.gradle.kts | 4 ++++ .../kotlin/com/rafambn/scribe/Scribe.kt | 5 +---- .../ScribeIgnitionRegistrationJvmTest.kt | 20 +++++++++++-------- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9d5fabb..82c775a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,7 @@ kotlinxSerialization = "1.11.0" kotlinxCoroutines = "1.11.0" slf4j = "2.0.18" classgraph = "4.8.181" +mockk = "1.14.11" [libraries] kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } @@ -19,6 +20,7 @@ kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-t scribe = { module = "com.rafambn:scribe", version = "0.7.0" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } classgraph = { module = "io.github.classgraph:classgraph", version.ref = "classgraph" } +mockk = { module = "io.mockk:mockk", version.ref = "mockk" } [plugins] android-kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" } diff --git a/scribe/build.gradle.kts b/scribe/build.gradle.kts index 47d5d16..6c4819b 100644 --- a/scribe/build.gradle.kts +++ b/scribe/build.gradle.kts @@ -83,6 +83,10 @@ kotlin { implementation(libs.kotlin.test) implementation(libs.kotlinx.coroutines.test) } + + jvmTest.dependencies { + implementation(libs.mockk) + } } } diff --git a/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt b/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt index c908af7..0d0a870 100644 --- a/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt +++ b/scribe/src/commonMain/kotlin/com/rafambn/scribe/Scribe.kt @@ -105,9 +105,6 @@ abstract class Scribe { val isProcessing: Boolean get() = processingEnabled.value - internal open fun registerIgnitionHandler(callback: (Throwable) -> Unit): () -> Unit = - registerGlobalIgnitionHandler(callback) - /** * Starts or resumes delivery from this instance's private buffer. * @@ -127,7 +124,7 @@ abstract class Scribe { } try { - val uninstall = registerIgnitionHandler(exceptionHandler) + val uninstall = registerGlobalIgnitionHandler(exceptionHandler) if (ignitionRegistration.compareAndSet(null, uninstall)) { if (retiring.load()) { removeIgnitionRegistration() diff --git a/scribe/src/jvmTest/kotlin/com/rafambn/scribe/ScribeIgnitionRegistrationJvmTest.kt b/scribe/src/jvmTest/kotlin/com/rafambn/scribe/ScribeIgnitionRegistrationJvmTest.kt index c296e2e..ceb089b 100644 --- a/scribe/src/jvmTest/kotlin/com/rafambn/scribe/ScribeIgnitionRegistrationJvmTest.kt +++ b/scribe/src/jvmTest/kotlin/com/rafambn/scribe/ScribeIgnitionRegistrationJvmTest.kt @@ -4,6 +4,9 @@ import java.util.Collections import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicInteger +import io.mockk.every +import io.mockk.mockkStatic +import io.mockk.unmockkStatic import kotlinx.coroutines.runBlocking import kotlin.test.Test import kotlin.test.assertEquals @@ -18,6 +21,14 @@ class ScribeIgnitionRegistrationJvmTest { val bothHiresStarted = CountDownLatch(2) val registrationCalls = AtomicInteger(0) val failures = Collections.synchronizedList(mutableListOf()) + mockkStatic(::registerGlobalIgnitionHandler) + every { registerGlobalIgnitionHandler(any()) } answers { + if (registrationCalls.incrementAndGet() == 1) { + firstRegistrationStarted.countDown() + releaseFirstRegistration.await(2, TimeUnit.SECONDS) + } + throw UnsupportedOperationException("registration failed") + } val scribe = object : Scribe() { override val archivists: List get() { @@ -26,14 +37,6 @@ class ScribeIgnitionRegistrationJvmTest { } override val onIgnition: ((Throwable) -> Unit) = {} - - override fun registerIgnitionHandler(callback: (Throwable) -> Unit): () -> Unit { - if (registrationCalls.incrementAndGet() == 1) { - firstRegistrationStarted.countDown() - releaseFirstRegistration.await(2, TimeUnit.SECONDS) - } - throw UnsupportedOperationException("registration failed") - } } val first = Thread { try { @@ -71,6 +74,7 @@ class ScribeIgnitionRegistrationJvmTest { releaseFirstRegistration.countDown() first.join(2_000) second.join(2_000) + unmockkStatic(::registerGlobalIgnitionHandler) runBlocking { if (scribe.isIntakeOpen) { scribe.retire() From 423cf514b8472a2aca7fed1d6f9c63e43bad773c Mon Sep 17 00:00:00 2001 From: rafambn Date: Sat, 22 Aug 2026 10:08:17 -0300 Subject: [PATCH 4/6] Raise Karma mocha timeouts so browser throughput tests pass in CI --- kotlin-js-store/yarn.lock | 2198 +++++++++++++++++++++++++++++ scribe/karma.config.d/timeouts.js | 14 + 2 files changed, 2212 insertions(+) create mode 100644 kotlin-js-store/yarn.lock create mode 100644 scribe/karma.config.d/timeouts.js diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock new file mode 100644 index 0000000..7fc980d --- /dev/null +++ b/kotlin-js-store/yarn.lock @@ -0,0 +1,2198 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@^0.6.1": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz#f13c7c205915eb91ae54c557f5e92bddd8be0e83" + integrity sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + 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" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.11" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.11.tgz#b21835cbd36db656b857c2ad02ebd413cc13a9ba" + integrity sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.31" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" + integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== + +"@types/cors@^2.8.12": + version "2.8.19" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.19.tgz#d93ea2673fd8c9f697367f5eeefc2bbfa94f0342" + integrity sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.7": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "9.6.1" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-9.6.1.tgz#d5795ad732ce81715f27f75da913004a56751584" + integrity sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.8": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.9.tgz#cf3f0e876d7bee15a93ab925b82bf570a3904a24" + integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg== + +"@types/json-schema@*", "@types/json-schema@^7.0.15", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/node@*", "@types/node@>=10.0.0": + version "26.2.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-26.2.0.tgz#5a4875a862fda8fdc57de8faa579bb81ecba1685" + integrity sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg== + dependencies: + undici-types "~8.3.0" + +"@types/ws@^8.5.12": + version "8.18.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.18.1.tgz#48464e4bf2ddfd17db13d845467f6070ffea4aa9" + integrity sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg== + dependencies: + "@types/node" "*" + +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" + integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== + dependencies: + "@webassemblyjs/helper-numbers" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + +"@webassemblyjs/floating-point-hex-parser@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz#fcca1eeddb1cc4e7b6eed4fc7956d6813b21b9fb" + integrity sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA== + +"@webassemblyjs/helper-api-error@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz#e0a16152248bc38daee76dd7e21f15c5ef3ab1e7" + integrity sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ== + +"@webassemblyjs/helper-buffer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz#822a9bc603166531f7d5df84e67b5bf99b72b96b" + integrity sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA== + +"@webassemblyjs/helper-numbers@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz#dbd932548e7119f4b8a7877fd5a8d20e63490b2d" + integrity sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.13.2" + "@webassemblyjs/helper-api-error" "1.13.2" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz#e556108758f448aae84c850e593ce18a0eb31e0b" + integrity sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA== + +"@webassemblyjs/helper-wasm-section@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz#9629dda9c4430eab54b591053d6dc6f3ba050348" + integrity sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/wasm-gen" "1.14.1" + +"@webassemblyjs/ieee754@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz#1c5eaace1d606ada2c7fd7045ea9356c59ee0dba" + integrity sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.13.2.tgz#57c5c3deb0105d02ce25fa3fd74f4ebc9fd0bbb0" + integrity sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.13.2": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.13.2.tgz#917a20e93f71ad5602966c2d685ae0c6c21f60f1" + integrity sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ== + +"@webassemblyjs/wasm-edit@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz#ac6689f502219b59198ddec42dcd496b1004d597" + integrity sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/helper-wasm-section" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-opt" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + "@webassemblyjs/wast-printer" "1.14.1" + +"@webassemblyjs/wasm-gen@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz#991e7f0c090cb0bb62bbac882076e3d219da9570" + integrity sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wasm-opt@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz#e6f71ed7ccae46781c206017d3c14c50efa8106b" + integrity sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-buffer" "1.14.1" + "@webassemblyjs/wasm-gen" "1.14.1" + "@webassemblyjs/wasm-parser" "1.14.1" + +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz#b3e13f1893605ca78b52c68e54cf6a865f90b9fb" + integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@webassemblyjs/helper-api-error" "1.13.2" + "@webassemblyjs/helper-wasm-bytecode" "1.13.2" + "@webassemblyjs/ieee754" "1.13.2" + "@webassemblyjs/leb128" "1.13.2" + "@webassemblyjs/utf8" "1.13.2" + +"@webassemblyjs/wast-printer@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz#3bb3e9638a8ae5fdaf9610e7a06b4d9f9aa6fe07" + integrity sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw== + dependencies: + "@webassemblyjs/ast" "1.14.1" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-3.0.1.tgz#76ac285b9658fa642ce238c276264589aa2b6b57" + integrity sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA== + +"@webpack-cli/info@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-3.0.1.tgz#3cff37fabb7d4ecaab6a8a4757d3826cf5888c63" + integrity sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ== + +"@webpack-cli/serve@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-3.0.1.tgz#bd8b1f824d57e30faa19eb78e4c0951056f72f00" + integrity sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +accepts@~1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-phases@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" + integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== + +acorn@^8.15.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.18.0.tgz#4faf01b2d6d326bfeed97aea1f52220b5f4c1940" + integrity sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ== + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^8.0.0, ajv@^8.9.0: + version "8.20.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.20.0.tgz#304b3636add88ba7d936760dd50ece006dea95f9" + integrity sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.2.2: + version "6.3.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.3.0.tgz#247c8e7b70a1a43b10ce14c0226fcbf58e8815d5" + integrity sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + +baseline-browser-mapping@^2.11.12: + version "2.11.17" + resolved "https://registry.yarnpkg.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.17.tgz#a168205490077c5d7c542f1610016af4ade8f875" + integrity sha512-KAUDn1OSS0fmPlGO+NOUMRcOQ/b/shUBH3OgkG73mPgdf+JD/BQ6fHboGxNOxnUmlwcq+lLq3dTkayRPuSfXwg== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +body-parser@^1.19.0: + version "1.20.6" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.6.tgz#60c789c78e0992d906da0a29d71ae01d15c1ed76" + integrity sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g== + dependencies: + bytes "~3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "~1.2.0" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + on-finished "~2.4.1" + qs "~6.15.1" + raw-body "~2.5.3" + type-is "~1.6.18" + unpipe "~1.0.0" + +brace-expansion@^1.1.7: + version "1.1.18" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.18.tgz#3ce74d89885136be1535341f8c3d4425c29a5cab" + integrity sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.4.tgz#589dab11c0018d0366be64cd8bf12c8dbecc8326" + integrity sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.24.0: + version "4.28.8" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.8.tgz#a3c79ceb70028527e5da7dafc887f3200b5168c0" + integrity sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA== + dependencies: + baseline-browser-mapping "^2.11.12" + caniuse-lite "^1.0.30001809" + electron-to-chromium "^1.5.402" + node-releases "^2.0.53" + update-browserslist-db "^1.3.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +call-bound@^1.0.2, call-bound@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001809: + version "1.0.30001809" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz#e6cf71f14ddfe008f114dd2a846923be3c03a07b" + integrity sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ== + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@^3.5.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +chrome-trace-event@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz#05bffd7ff928465093314708c93bdfa9bd1f0f5b" + integrity sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie@~0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + +cors@~2.8.5: + version "2.8.6" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.6.tgz#ff5dd69bd95e547503820d29aba4f8faf8dfec96" + integrity sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.3, cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== + +date-format@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@^4.3.4, debug@^4.3.5, debug@~4.4.1: + version "4.4.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +depd@2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== + +diff@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-7.0.0.tgz#3fb34d387cd76d803f6eebea67b921dab0182a9a" + integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== + +dom-serialize@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.5.402: + version "1.5.412" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.412.tgz#79b93ba8d96ada5c9ef0d916ea0663f01ea15498" + integrity sha512-z4rMe3esBzlzovKHj4gxJnsCGZRK5l4baUvm+gCGJBPE+gsyUMKsuU9tnEUtI1dOebXz1ytAPGjvXhmQ7rIPwA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +engine.io-parser@~5.2.1: + version "5.2.3" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" + integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== + +engine.io@~6.6.0: + version "6.6.9" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.6.9.tgz#fd17e9f4e3a256423592574b60ac262e91af4b82" + integrity sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg== + dependencies: + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + "@types/ws" "^8.5.12" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.7.2" + cors "~2.8.5" + debug "~4.4.1" + engine.io-parser "~5.2.1" + ws "~8.21.0" + +enhanced-resolve@^5.17.3: + version "5.24.5" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz#b4dad3255b7545f07ba5535189868e9f85f47573" + integrity sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.3.3" + +ent@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.2.tgz#22a5ed2fd7ce0cbcff1d1474cf4909a44bdb6e85" + integrity sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + punycode "^1.4.1" + safe-regex-test "^1.1.0" + +envinfo@^7.14.0: + version "7.21.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.21.0.tgz#04a251be79f92548541f37d13c8b6f22940c3bae" + integrity sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow== + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz#9159601561880a85f2734560a9099b2c31e5372a" + integrity sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz#a2d0b373205724dfa525d23b0c3e1b1ca582c99b" + integrity sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw== + dependencies: + es-errors "^1.3.0" + +escalade@^3.1.1, escalade@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" + integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-uri@^3.0.1: + version "3.1.5" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.1.5.tgz#610f37419a030270430cecd68d74e3d4d96725d0" + integrity sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.7: + version "3.4.4" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.4.tgz#aeeca2a506303f0cee61c59e6c9f2a88d2f29fc6" + integrity sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q== + +follow-redirects@^1.0.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.16.0.tgz#28474a159d3b9d11ef62050a14ed60e4df6d61bc" + integrity sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw== + +foreground-child@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + +format-util@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" + integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^10.4.5: + version "10.5.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.5.0.tgz#8ec0355919cd3338c28428a23d4f24ecc5fe738c" + integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== + 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" + +glob@^7.1.3, glob@^7.1.7: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2, hasown@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.4.tgz#8c62d8cb90beb2aad5d0a5b67581ad9854c3f003" + integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A== + dependencies: + function-bind "^1.1.2" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +http-errors@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b" + integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== + dependencies: + depd "~2.0.0" + inherits "~2.0.4" + setprototypeof "~1.2.0" + statuses "~2.0.2" + toidentifier "~1.0.1" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +iconv-lite@~0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +import-local@^3.0.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.2.0.tgz#c3d5c745798c02a6f8b897726aba5100186ee260" + integrity sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.16.1: + version "2.16.2" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.2.tgz#3e07450a8080ebce3fbf0cac494f4d2ab324e082" + integrity sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA== + dependencies: + hasown "^2.0.3" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +isbinaryfile@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" + integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-yaml@^4.1.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.3.1.tgz#01216c001d67f48e2cd560d708c7af21090a3848" + integrity sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ== + dependencies: + argparse "^2.0.1" + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +karma-chrome-launcher@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" + integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== + dependencies: + which "^1.2.1" + +karma-mocha@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" + integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== + dependencies: + minimist "^1.2.3" + +karma-sourcemap-loader@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" + integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== + dependencies: + graceful-fs "^4.2.10" + +karma-webpack@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.1.tgz#4eafd31bbe684a747a6e8f3e4ad373e53979ced4" + integrity sha512-oo38O+P3W2mSPCSUrQdySSPv1LvPpXP+f+bBimNomS5sW+1V4SuhCuW8TfJzV+rDv921w2fDSDw0xJbPe6U+kQ== + dependencies: + glob "^7.1.3" + minimatch "^9.0.3" + webpack-merge "^4.1.5" + +"karma@github:Kotlin/karma#6.4.5": + version "6.4.4" + resolved "https://codeload.github.com/Kotlin/karma/tar.gz/239a8fc984584f0d96b1dd750e7a5e2c79da93a6" + dependencies: + "@colors/colors" "1.5.0" + body-parser "^1.19.0" + braces "^3.0.2" + chokidar "^3.5.1" + connect "^3.7.0" + di "^0.0.1" + dom-serialize "^2.2.1" + glob "^7.1.7" + graceful-fs "^4.2.6" + http-proxy "^1.18.1" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" + minimatch "^3.0.4" + mkdirp "^0.5.5" + qjobs "^1.2.0" + range-parser "^1.2.1" + rimraf "^3.0.2" + socket.io "^4.7.2" + source-map "^0.6.1" + tmp "^0.2.1" + ua-parser-js "^0.7.30" + yargs "^16.1.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kotlin-web-helpers@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/kotlin-web-helpers/-/kotlin-web-helpers-3.0.0.tgz#3ed6b48f694f74bb60a737a9d7e2c0e3b29abdb9" + integrity sha512-kdQO4AJQkUPvpLh9aglkXDRyN+CfXO7pKq+GESEnxooBFkQpytLrqZis3ABvmFN1cGw/ZQ/K38u5sRGW+NfBnw== + dependencies: + format-util "^1.0.5" + +loader-runner@^4.2.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.2.tgz#9913d3a15971f8f635915e601fb5c9d495d918e9" + integrity sha512-DFEqQ3ihfS9blba08cLfYf1NRAIEm+dDjic073DRDc3/JspI/8wYmtDsHwd3+4hwvdxSK7PGaElfTmm0awWJ4w== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@^4.17.15, lodash@^4.17.21: + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" + integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log4js@^6.4.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" + integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.5" + +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.3, minimatch@^9.0.4, minimatch@^9.0.5: + version "9.0.9" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== + dependencies: + brace-expansion "^2.0.2" + +minimist@^1.2.3, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.3.tgz#79389b4eb1bb2d003a9bba87d492f2bd37bdc65b" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mocha@11.7.5: + version "11.7.5" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.7.5.tgz#58f5bbfa5e0211ce7e5ee6128107cefc2515a627" + integrity sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig== + dependencies: + browser-stdout "^1.3.1" + chokidar "^4.0.1" + debug "^4.3.5" + diff "^7.0.0" + escape-string-regexp "^4.0.0" + find-up "^5.0.0" + glob "^10.4.5" + he "^1.2.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + log-symbols "^4.1.0" + minimatch "^9.0.5" + ms "^2.1.3" + picocolors "^1.1.1" + serialize-javascript "^6.0.2" + strip-json-comments "^3.1.1" + supports-color "^8.1.1" + workerpool "^9.2.0" + yargs "^17.7.2" + yargs-parser "^21.1.1" + yargs-unparser "^2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-releases@^2.0.53: + version "2.0.53" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.53.tgz#0fe5ad8a7935e075172f574e56f0c20f07fa41af" + integrity sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.3, object-inspect@^1.13.4: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +on-finished@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +qjobs@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" + integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== + +qs@~6.15.1: + version "6.15.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.15.3.tgz#76852132a58ed5c7c0ef67e4441b9bb5d6061b3b" + integrity sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A== + dependencies: + es-define-property "^1.0.1" + side-channel "^1.1.1" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.3.0.tgz#d7f19be812bb62721472b45d3be219ef09572b47" + integrity sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw== + +raw-body@~2.5.3: + version "2.5.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2" + integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA== + dependencies: + bytes "~3.1.2" + http-errors "~2.0.1" + iconv-lite "~0.4.24" + unpipe "~1.0.0" + +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.12" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.12.tgz#f5b2a680897c69c238a13cd16b15671f8b73549f" + integrity sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA== + dependencies: + es-errors "^1.3.0" + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rfdc@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^4.3.0, schema-utils@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.3.3.tgz#5b1850912fa31df90716963d45d9121fdfc09f46" + integrity sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +setprototypeof@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel-list@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.1.tgz#c2e0b5a14a540aebee3bbc6c3f8666cc9b509127" + integrity sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.4" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + +side-channel@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.1.tgz#ea02c62e05dc4bea67d4442f0fb71ee192f8e0ab" + integrity sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.4" + side-channel-list "^1.0.1" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + +socket.io-adapter@~2.5.2: + version "2.5.8" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz#c9fcabf602dbd5b09258ca98e5ec6a7ae4360698" + integrity sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw== + dependencies: + debug "~4.4.1" + ws "~8.21.0" + +socket.io-parser@~4.2.4: + version "4.2.7" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.7.tgz#679e51fe24d1c81df90fc5f7efe4a5f432fe99c0" + integrity sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.4.1" + +socket.io@^4.7.2: + version "4.8.3" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.8.3.tgz#ca6ba1431c69532e1e0a6f496deebeb601dbc4df" + integrity sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + cors "~2.8.5" + debug "~4.4.1" + engine.io "~6.6.0" + socket.io-adapter "~2.5.2" + socket.io-parser "~4.2.4" + +source-map-js@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map-loader@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-5.0.0.tgz#f593a916e1cc54471cfc8851b905c8a845fc7e38" + integrity sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA== + dependencies: + iconv-lite "^0.6.3" + source-map-js "^1.0.2" + +source-map-support@0.5.21, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +statuses@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382" + integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== + +streamroller@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" + integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" + +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.2.0.tgz#d22a269522836a627af8d04b5c3fd2c7fa3e32e3" + integrity sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w== + dependencies: + ansi-regex "^6.2.2" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0, supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.3.3.tgz#5da7c9992c46038221267985ab28421a8879f160" + integrity sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A== + +terser-webpack-plugin@^5.3.11: + version "5.6.1" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz#47bc41bd8b8fab8383b62ec763b7394829097e7b" + integrity sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.25" + jest-worker "^27.4.5" + schema-utils "^4.3.0" + terser "^5.31.1" + +terser@^5.31.1: + version "5.50.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.50.0.tgz#4e560a46704f9172f96ba31c3bd6dc108ad2cead" + integrity sha512-CN9BVxWhgS/hRxtUMjtC2uRWSTcSfQFHMDWma6sKKfIivCD91sM+FOPfvwoaRMqCSrUpe1nv3jDamd9eEQ4y+w== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.15.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +tmp@^0.2.1: + version "0.2.7" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.7.tgz#26f4db11d1601ce8012dcb8a798ece1c06a99059" + integrity sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +ua-parser-js@^0.7.30: + version "0.7.41" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.41.tgz#9f6dee58c389e8afababa62a4a2dc22edb69a452" + integrity sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg== + +undici-types@~8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-8.3.0.tgz#44e9fc9f3244648cdea35e4f9bb2d681e9410809" + integrity sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz#a71c28dd22f505481dbc4689087b18d933e90afd" + integrity sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ== + dependencies: + escalade "^3.2.0" + picocolors "^1.1.1" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +vary@^1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== + +watchpack@^2.4.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.5.2.tgz#e12e82d84674266fc1c6dbfe38891b92ff0522ec" + integrity sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg== + dependencies: + graceful-fs "^4.1.2" + +webpack-cli@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-6.0.1.tgz#a1ce25da5ba077151afd73adfa12e208e5089207" + integrity sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw== + dependencies: + "@discoveryjs/json-ext" "^0.6.1" + "@webpack-cli/configtest" "^3.0.1" + "@webpack-cli/info" "^3.0.1" + "@webpack-cli/serve" "^3.0.1" + colorette "^2.0.14" + commander "^12.1.0" + cross-spawn "^7.0.3" + envinfo "^7.14.0" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^6.0.1" + +webpack-merge@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-merge@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-6.0.1.tgz#50c776868e080574725abc5869bd6e4ef0a16c6a" + integrity sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.1" + +webpack-sources@^3.3.3: + version "3.5.1" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.5.1.tgz#76c2418486dcc02b2aa0694c104176c2858fe84a" + integrity sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw== + +webpack@5.101.3: + version "5.101.3" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.101.3.tgz#3633b2375bb29ea4b06ffb1902734d977bc44346" + integrity sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A== + dependencies: + "@types/eslint-scope" "^3.7.7" + "@types/estree" "^1.0.8" + "@types/json-schema" "^7.0.15" + "@webassemblyjs/ast" "^1.14.1" + "@webassemblyjs/wasm-edit" "^1.14.1" + "@webassemblyjs/wasm-parser" "^1.14.1" + acorn "^8.15.0" + acorn-import-phases "^1.0.3" + browserslist "^4.24.0" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.17.3" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.11" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^4.3.2" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.11" + watchpack "^2.4.1" + webpack-sources "^3.3.3" + +which@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +workerpool@^9.2.0: + version "9.3.4" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.3.4.tgz#f6c92395b2141afd78e2a889e80cb338fe9fca41" + integrity sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@~8.21.0: + version "8.21.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.21.3.tgz#660b4faddb6a3e575c86e078126919961f4de4fc" + integrity sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs-unparser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@^16.1.1: + version "16.2.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.2.tgz#c56731dca0d2788ae0866dd3c83907d6bab85f7d" + integrity sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^17.7.2: + version "17.7.3" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.3.tgz#779dffe6bcafec596a7172e983289a588647faaa" + integrity sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/scribe/karma.config.d/timeouts.js b/scribe/karma.config.d/timeouts.js new file mode 100644 index 0000000..58e3255 --- /dev/null +++ b/scribe/karma.config.d/timeouts.js @@ -0,0 +1,14 @@ +// Merged into the Gradle-generated karma.conf.js. +// Mirrors the 120s mocha timeout configured for Node.js tests in build.gradle.kts; +// without it the default 2000ms per-test timeout kills throughput tests on shared runners. +module.exports = function (config) { + config.set({ + client: { + mocha: { + timeout: 120000 + } + }, + browserNoActivityTimeout: 300000, + captureTimeout: 120000 + }) +} From 8a4d7fa5c549545a8448302bdd5b9b385e1cf838 Mon Sep 17 00:00:00 2001 From: rafambn Date: Sat, 22 Aug 2026 10:26:46 -0300 Subject: [PATCH 5/6] Upload test results artifact when a CI test job fails --- .github/workflows/gradle.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 5470887..bd5d67d 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -178,3 +178,11 @@ jobs: uses: gradle/gradle-build-action@ce999babab2de1c4b649dc15f0ee67e6246c994f with: arguments: ${{ matrix.task }} --no-configuration-cache + - name: Upload test results on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: test-results-${{ matrix.name }} + path: | + scribe/build/test-results/ + scribe/build/reports/tests/ From c4ebd70f4facf7705c790752ce38d9c6a380c8bc Mon Sep 17 00:00:00 2001 From: rafambn Date: Sat, 22 Aug 2026 10:36:15 -0300 Subject: [PATCH 6/6] Fix Karma config merge so mocha timeout override is actually applied --- scribe/karma.config.d/timeouts.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/scribe/karma.config.d/timeouts.js b/scribe/karma.config.d/timeouts.js index 58e3255..13c5cf4 100644 --- a/scribe/karma.config.d/timeouts.js +++ b/scribe/karma.config.d/timeouts.js @@ -1,14 +1,12 @@ -// Merged into the Gradle-generated karma.conf.js. +// Merged into the Gradle-generated karma.conf.js as statements, with `config` in scope. // Mirrors the 120s mocha timeout configured for Node.js tests in build.gradle.kts; // without it the default 2000ms per-test timeout kills throughput tests on shared runners. -module.exports = function (config) { - config.set({ - client: { - mocha: { - timeout: 120000 - } - }, - browserNoActivityTimeout: 300000, - captureTimeout: 120000 - }) -} +config.set({ + client: { + mocha: { + timeout: 120000 + } + }, + browserNoActivityTimeout: 300000, + captureTimeout: 120000 +})