diff --git a/.gitignore b/.gitignore
index 1419237..6e0c4ce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,10 @@ release/
# Build
*.tsbuildinfo
build/
+# PyInstaller build artifacts (sidecar) — regenerated by `npm run sidecar:build`
+apps/desktop/build/sidecar-build/
+apps/desktop/build/sidecar.spec
+
!apps/desktop/build/
!apps/desktop/build/entitlements.mac.plist
sidecar-dist/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3454c8d..fdcef83 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -243,6 +243,11 @@ package-windows:
$env:CSC_LINK = (Resolve-Path -LiteralPath $env:WIN_CSC_PFX_FILE).Path
$env:CSC_KEY_PASSWORD = $env:WIN_CSC_KEY_PASSWORD
- node scripts/ci/sync-version.mjs --check --tag "$CI_COMMIT_TAG"
+ # 로컬 STT는 faster-whisper 사이드카에 의존한다. 이 번들이 빠지면 설치본에서
+ # 전사가 전혀 동작하지 않으므로, 패키징 전에 반드시 빌드하고 검증한다.
+ - npm run sidecar:setup --workspace=@d3ro/desktop
+ - npm run sidecar:build --workspace=@d3ro/desktop
+ - node scripts/ci/verify-sidecar-bundle.mjs
- npm run build --workspace=@d3ro/desktop
- cd apps/desktop
- npx electron-builder --win --x64 --config electron-builder.yml
@@ -270,6 +275,10 @@ package-macos:
- npm ci
script:
- node scripts/ci/sync-version.mjs --check --tag "$CI_COMMIT_TAG"
+ # 로컬 STT(faster-whisper 사이드카) 번들 — 누락 시 설치본 전사 불가.
+ - npm run sidecar:setup --workspace=@d3ro/desktop
+ - npm run sidecar:build --workspace=@d3ro/desktop
+ - node scripts/ci/verify-sidecar-bundle.mjs
- npm run build --workspace=@d3ro/desktop
- cd apps/desktop
- npx electron-builder --mac --arm64 --config electron-builder.yml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 842a6d8..afe66c4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Cloud-optional backup (encrypted, opt-in)
- Plugin system for custom pipelines
+## [1.3.0] - 2026-09-18
+
+> Published from an annotated tag through CI. Installer and update metadata are
+> served by the canonical Forgejo feed; no binaries are committed to this repository.
+
+### Added
+- **Live partial transcript while dictating**: while the hotkey is held, the app now
+ transcribes the recent window of audio every 1.5 seconds and shows it in the recording
+ tip, so the text can be seen forming before the key is released. Partials never reach
+ the clipboard or the result popup; only the final transcription is inserted.
+- **Instant first dictation**: the local speech engine (sidecar process + Whisper model)
+ is warmed up in the background at app start, so the first press does not wait for the
+ model to load.
+- **Packaged local speech engine**: desktop installers now ship the faster-whisper
+ sidecar (`sidecar.exe` plus runtime data, including the Silero VAD model) and ffmpeg, so
+ local transcription works on a fresh install without Python on the machine.
+- Sidecar build tooling: `npm --prefix apps/desktop run sidecar:setup` and
+ `sidecar:build`, plus a packaging-time bundle verifier that fails the build when the
+ engine or its VAD data is missing.
+
+### Changed
+- Local engine connections now target the IPv4 loopback (`127.0.0.1`) instead of
+ `localhost`. On machines where `localhost` resolves only to IPv6, every local request
+ (Ollama and the speech sidecar) was refused and local AI silently did nothing.
+- Whisper decoding is tuned for dictation: previous-text conditioning is disabled so
+ repeated hallucinations cannot compound, silence is trimmed more aggressively, and
+ low-confidence fallbacks are bounded. Same transcript quality, roughly five times
+ faster on the same machine.
+- The sidecar reuses an already-loaded model instead of reloading it, and reports the
+ load time it measured.
+- Local engine logs stream to the app log as complete UTF-8 lines instead of mangled
+ fragments, so failures are diagnosable.
+- Failed local-engine startup now fails immediately with an actionable message (missing
+ bundled engine, damaged virtualenv, or missing SoX) instead of waiting for a 30 second
+ health check and reporting a generic error.
+
+### Fixed
+- **Local transcription never worked in packaged builds**: the sidecar was not part of
+ the packaged resources and no pipeline step built it, so the app always fell back to a
+ system Python that had no faster-whisper installed.
+- **Local transcription and local LLM never worked in development**: the sidecar and SoX
+ paths were resolved against the Vite output directory (`out/main`) instead of the app
+ root, so recording failed with a SoX `ENOENT` and the sidecar fell back to a Python
+ without the runtime dependencies.
+- **Silero VAD data was missing from the bundled engine**, which would have made
+ silence-trimmed transcription fail at runtime even with the engine bundled.
+- Audio capture and the speech sidecar no longer flash a console window on Windows, and
+ SoX/spawn failures name the fix (`npm --prefix apps/desktop run setup:sox`).
+
## [1.2.0] - 2026-09-16
> Published from an annotated tag through CI. Installer and update metadata are
diff --git a/apps/admin-swagger/openapi.json b/apps/admin-swagger/openapi.json
index 140aded..1032f14 100644
--- a/apps/admin-swagger/openapi.json
+++ b/apps/admin-swagger/openapi.json
@@ -3,7 +3,7 @@
"info": {
"title": "D3RO-VOICE Admin API",
"description": "Admin CRM Edge Functions for user management, subscription CRUD, payment history, and audit logs.",
- "version": "1.2.0"
+ "version": "1.3.0"
},
"servers": [
{
diff --git a/apps/admin/package.json b/apps/admin/package.json
index 74e1de4..17c7840 100644
--- a/apps/admin/package.json
+++ b/apps/admin/package.json
@@ -1,6 +1,6 @@
{
"name": "@d3ro/admin",
- "version": "1.2.0",
+ "version": "1.3.0",
"private": true,
"description": "D3RO Voice Admin CRM — SaaS 관리 도구",
"scripts": {
diff --git a/apps/api-server/D3ROVoice.Api.csproj b/apps/api-server/D3ROVoice.Api.csproj
index c62425c..9b8fda2 100644
--- a/apps/api-server/D3ROVoice.Api.csproj
+++ b/apps/api-server/D3ROVoice.Api.csproj
@@ -2,7 +2,7 @@
net10.0
- 1.2.0
+ 1.3.0
enable
enable
diff --git a/apps/desktop/electron-builder.yml b/apps/desktop/electron-builder.yml
index 8742df8..290de60 100644
--- a/apps/desktop/electron-builder.yml
+++ b/apps/desktop/electron-builder.yml
@@ -36,6 +36,8 @@ asarUnpack:
- "node_modules/better-sqlite3/**"
- "node_modules/uiohook-napi/**"
- "node_modules/@nut-tree-fork/**"
+ # ffmpeg 정적 바이너리는 실행 파일이므로 asar 내부에서 spawn할 수 없다
+ - "node_modules/@ffmpeg-installer/**"
# ────────────────────────────────────────────────────────────────────
# Windows
@@ -108,16 +110,29 @@ extraResources:
- "*.wav"
# SoX 바이너리
+ # 주의: filter에 "**/*"를 쓰면 하위 디렉토리가 통째로 누락된다(실측).
+ # 디렉토리를 그대로 복사할 때는 filter를 지정하지 않는다.
- from: resources/sox/
to: sox/
+
+ # faster-whisper STT 사이드카 (PyInstaller onedir: sidecar.exe + _internal/).
+ # 반드시 존재해야 한다. 누락되면 로컬 전사가 전혀 동작하지 않는다.
+ # 빌드: npm --prefix apps/desktop run sidecar:build
+ # electron-builder는 이 트리를 재귀로 복사한다(_internal 포함).
+ # 서명 검증에 실패하면 복사가 중간에 끊겨 _internal이 빠지므로,
+ # 서명 없이 로컬 검증할 때는 -c.win.forceCodeSigning=false 를 사용한다.
+ - from: sidecar-dist/sidecar/
+ to: sidecar/
filter:
- "**/*"
+ # ffmpeg (파일 전사/미디어 변환용). CI가 resources/ffmpeg/에 배치한다.
+ - from: resources/ffmpeg/
+ to: ffmpeg/
+
# Ollama 바이너리 (포터블 zip을 사전 배치)
- from: resources/ollama/
to: ollama/
- filter:
- - "**/*"
nsis:
# 파일명 공백 금지 — mac.artifactName 주석과 동일한 이유 (latest.yml url 정합)
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 376115a..b7f828c 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -1,6 +1,6 @@
{
"name": "@d3ro/desktop",
- "version": "1.2.0",
+ "version": "1.3.0",
"productName": "d3ro-voice",
"description": "로컬 AI 음성 어시스턴트 (Electron)",
"main": "./out/main/index.js",
@@ -20,7 +20,10 @@
"dist": "electron-vite build && electron-builder --config electron-builder.yml",
"dist:win": "electron-vite build && electron-builder --win --config electron-builder.yml --publish never",
"dist:mac": "electron-vite build && electron-builder --mac --config electron-builder.yml --publish never -c.mac.identity=- -c.mac.hardenedRuntime=false",
- "setup:sox": "powershell -ExecutionPolicy Bypass -File scripts/download-sox.ps1"
+ "setup:sox": "powershell -ExecutionPolicy Bypass -File scripts/download-sox.ps1",
+ "sidecar:setup": "node scripts/setup-sidecar.mjs",
+ "sidecar:build": "node scripts/build-sidecar.mjs",
+ "dist:win:full": "npm run sidecar:build && electron-vite build && electron-builder --win --config electron-builder.yml --publish never"
},
"author": "D3RO",
"license": "MIT",
@@ -45,6 +48,7 @@
"@electron-toolkit/utils": "^4.0.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
+ "@ffmpeg-installer/ffmpeg": "^1.1.0",
"@mui/material": "^7.0.0",
"@nut-tree-fork/nut-js": "^4.2.6",
"@supabase/supabase-js": "^2.45.0",
diff --git a/apps/desktop/resources/ffmpeg/.gitkeep b/apps/desktop/resources/ffmpeg/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/apps/desktop/scripts/build-sidecar.mjs b/apps/desktop/scripts/build-sidecar.mjs
new file mode 100644
index 0000000..bec2783
--- /dev/null
+++ b/apps/desktop/scripts/build-sidecar.mjs
@@ -0,0 +1,127 @@
+// scripts/build-sidecar.mjs
+// faster-whisper 사이드카를 PyInstaller(onedir)로 묶어 sidecar-dist/sidecar/ 에 만든다.
+//
+// 사용: npm --prefix apps/desktop run sidecar:build
+// 출력: apps/desktop/sidecar-dist/sidecar/sidecar(.exe) ← electron-builder extraResources 대상
+//
+// 주의:
+// - `--collect-all faster_whisper` 가 필수다. faster-whisper는 VAD용
+// `assets/silero_vad_v6.onnx` 데이터 파일을 패키지 안에 두는데, 이걸 누락하면
+// 번들된 앱에서 VAD 사용 시 런타임에 실패한다.
+// - 콘솔 모드를 유지한다(--noconsole 금지). 메인 프로세스가 stdout/stderr를 로그로
+// 수집하는데, windowed 모드에서는 스트림이 사라져 진단이 불가능해진다.
+// 콘솔 창 깜빡임은 메인 프로세스 spawn의 windowsHide로 막는다.
+
+import { spawnSync } from 'node:child_process'
+import { existsSync, rmSync, statSync } from 'node:fs'
+import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+const scriptDir = path.dirname(fileURLToPath(import.meta.url))
+const desktopDir = path.resolve(scriptDir, '..')
+const sidecarDir = path.join(desktopDir, 'sidecar')
+const mainPy = path.join(sidecarDir, 'main.py')
+const outputDir = path.join(desktopDir, 'sidecar-dist')
+const isWindows = process.platform === 'win32'
+const exeSuffix = isWindows ? '.exe' : ''
+
+const venvPython = isWindows
+ ? path.join(sidecarDir, '.venv', 'Scripts', 'python.exe')
+ : path.join(sidecarDir, '.venv', 'bin', 'python3')
+
+const python = existsSync(venvPython)
+ ? venvPython
+ : isWindows
+ ? 'python'
+ : 'python3'
+
+if (!existsSync(mainPy)) {
+ console.error(`사이드카 소스를 찾을 수 없습니다: ${mainPy}`)
+ process.exit(1)
+}
+
+const probe = spawnSync(
+ python,
+ ['-c', 'import PyInstaller, faster_whisper; print("ok")'],
+ { encoding: 'utf-8' },
+)
+if (probe.status !== 0) {
+ console.error(
+ [
+ `PyInstaller/faster-whisper를 사용할 수 없습니다 (python: ${python}).`,
+ '먼저 사이드카 환경을 준비하세요:',
+ ' npm --prefix apps/desktop run sidecar:setup',
+ probe.stderr?.trim() || '',
+ ].join('\n'),
+ )
+ process.exit(1)
+}
+
+if (existsSync(outputDir)) {
+ rmSync(outputDir, { recursive: true, force: true })
+}
+
+const args = [
+ '-m',
+ 'PyInstaller',
+ '--name',
+ 'sidecar',
+ '--distpath',
+ outputDir,
+ '--workpath',
+ path.join(desktopDir, 'build', 'sidecar-build'),
+ '--specpath',
+ path.join(desktopDir, 'build'),
+ '--noconfirm',
+ '--clean',
+ // 패키지 데이터/바이너리 포함 (VAD onnx, ctranslate2 DLL 등)
+ '--collect-all',
+ 'faster_whisper',
+ '--collect-all',
+ 'ctranslate2',
+ '--collect-all',
+ 'tokenizers',
+ '--collect-all',
+ 'huggingface_hub',
+ // uvicorn은 동적 임포트를 사용하므로 명시
+ '--hidden-import',
+ 'uvicorn.logging',
+ '--hidden-import',
+ 'uvicorn.protocols.http',
+ '--hidden-import',
+ 'uvicorn.protocols.http.auto',
+ '--hidden-import',
+ 'uvicorn.protocols.http.h11_impl',
+ '--hidden-import',
+ 'uvicorn.protocols.websockets',
+ '--hidden-import',
+ 'uvicorn.protocols.websockets.auto',
+ '--hidden-import',
+ 'uvicorn.lifespan',
+ '--hidden-import',
+ 'uvicorn.lifespan.on',
+ '--hidden-import',
+ 'uvicorn.lifespan.off',
+ mainPy,
+]
+
+console.log('='.repeat(64))
+console.log(`D3RO-VOICE 사이드카 빌드 (${process.platform} ${process.arch})`)
+console.log('='.repeat(64))
+console.log(`\n$ ${python} ${args.join(' ')}\n`)
+
+const result = spawnSync(python, args, { stdio: 'inherit', cwd: sidecarDir })
+if (result.status !== 0) {
+ console.error(`\nPyInstaller 빌드 실패 (exit ${result.status ?? 'null'})`)
+ process.exit(result.status ?? 1)
+}
+
+const exePath = path.join(outputDir, 'sidecar', `sidecar${exeSuffix}`)
+if (!existsSync(exePath)) {
+ console.error(`\n빌드 산출물을 찾을 수 없습니다: ${exePath}`)
+ process.exit(1)
+}
+
+const exeMb = statSync(exePath).size / (1024 * 1024)
+console.log(`\n빌드 성공: ${exePath} (${exeMb.toFixed(1)} MB)`)
+console.log('electron-builder가 sidecar-dist/sidecar/ 를 resources/sidecar/ 로 복사한다.')
diff --git a/apps/desktop/scripts/setup-sidecar.mjs b/apps/desktop/scripts/setup-sidecar.mjs
new file mode 100644
index 0000000..96edc0a
--- /dev/null
+++ b/apps/desktop/scripts/setup-sidecar.mjs
@@ -0,0 +1,103 @@
+// scripts/setup-sidecar.mjs
+// STT 사이드카 개발/빌드 환경을 준비한다.
+//
+// - sidecar/.venv 가 없으면 생성한다.
+// - requirements.txt + pyinstaller 를 설치한다.
+//
+// 사용: npm --prefix apps/desktop run sidecar:setup
+//
+// 주의: faster-whisper/ctranslate2 휠은 수백 MB이며 최초 실행 시 네트워크가 필요하다.
+
+import { spawnSync } from 'node:child_process'
+import { existsSync, mkdirSync } from 'node:fs'
+import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+const scriptDir = path.dirname(fileURLToPath(import.meta.url))
+const desktopDir = path.resolve(scriptDir, '..')
+const sidecarDir = path.join(desktopDir, 'sidecar')
+const isWindows = process.platform === 'win32'
+const venvPython = isWindows
+ ? path.join(sidecarDir, '.venv', 'Scripts', 'python.exe')
+ : path.join(sidecarDir, '.venv', 'bin', 'python3')
+
+function run(command, args, label) {
+ console.log(`\n$ ${command} ${args.join(' ')}`)
+ const result = spawnSync(command, args, { stdio: 'inherit', cwd: sidecarDir })
+ if (result.status !== 0) {
+ console.error(`\n${label} 단계가 실패했습니다 (exit ${result.status ?? 'null'})`)
+ process.exit(result.status ?? 1)
+ }
+}
+
+function findSystemPython() {
+ const candidates = isWindows
+ ? [
+ ['py', ['-3.11']],
+ ['py', ['-3']],
+ ['python', []],
+ ['python3', []],
+ ]
+ : [
+ ['python3.11', []],
+ ['python3', []],
+ ['python', []],
+ ]
+
+ for (const [command, prefixArgs] of candidates) {
+ const probe = spawnSync(command, [...prefixArgs, '--version'], {
+ encoding: 'utf-8',
+ shell: isWindows,
+ })
+ if (probe.status === 0) {
+ console.log(`시스템 Python 발견: ${command} ${prefixArgs.join(' ')} → ${probe.stdout.trim()}`)
+ return { command, prefixArgs, shell: isWindows }
+ }
+ }
+
+ console.error(
+ 'Python 3.11+ 를 찾을 수 없습니다. https://www.python.org/downloads/ 에서 설치하거나 PATH에 추가하세요.',
+ )
+ process.exit(1)
+}
+
+function hasModule(python, moduleName) {
+ return (
+ spawnSync(python, ['-c', `import ${moduleName}`], { stdio: 'ignore' }).status === 0
+ )
+}
+
+if (!existsSync(sidecarDir)) {
+ console.error(`사이드카 디렉토리를 찾을 수 없습니다: ${sidecarDir}`)
+ process.exit(1)
+}
+
+if (existsSync(venvPython) && hasModule(venvPython, 'faster_whisper')) {
+ // CI/재실행 시 불필요한 재설치를 건너뛴다 (수백 MB 다운로드 방지).
+ console.log(`사이드카 환경이 이미 준비되어 있습니다: ${venvPython}`)
+ if (!hasModule(venvPython, 'PyInstaller')) {
+ run(venvPython, ['-m', 'pip', 'install', 'pyinstaller>=6.0'], 'PyInstaller 설치')
+ }
+} else {
+ if (!existsSync(venvPython)) {
+ const systemPython = findSystemPython()
+ mkdirSync(path.dirname(venvPython), { recursive: true })
+ run(
+ systemPython.command,
+ [...systemPython.prefixArgs, '-m', 'venv', path.join(sidecarDir, '.venv')],
+ '가상환경 생성',
+ )
+ } else {
+ console.log(`기존 가상환경 사용: ${venvPython}`)
+ }
+
+ run(venvPython, ['-m', 'pip', 'install', '--upgrade', 'pip'], 'pip 업그레이드')
+ run(
+ venvPython,
+ ['-m', 'pip', 'install', '-r', path.join(sidecarDir, 'requirements.txt')],
+ '사이드카 의존성 설치',
+ )
+ run(venvPython, ['-m', 'pip', 'install', 'pyinstaller>=6.0'], 'PyInstaller 설치')
+}
+
+console.log('\n사이드카 환경 준비 완료.')
diff --git a/apps/desktop/sidecar/main.py b/apps/desktop/sidecar/main.py
index 3c3a3ab..5504939 100644
--- a/apps/desktop/sidecar/main.py
+++ b/apps/desktop/sidecar/main.py
@@ -39,6 +39,14 @@ from fastapi.responses import JSONResponse
# ── 로깅 설정 ──────────────────────────────────────────────
+# Windows에서 파이프로 연결되면 Python이 로케일(cp949) 인코딩으로 출력해
+# 메인 프로세스의 UTF-8 로그가 깨진다. 명시적으로 UTF-8로 고정한다.
+for _stream in (sys.stdout, sys.stderr):
+ try:
+ _stream.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
+ except (AttributeError, ValueError):
+ pass
+
logging.basicConfig(
level=logging.INFO,
format="[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s",
@@ -121,6 +129,11 @@ def _detect_gpu() -> None:
logger.info("GPU 감지 실패, CPU 모드로 동작: %s", exc)
+def _cpu_threads() -> int:
+ """CPU 추론에 사용할 스레드 수 (과도한 점유 방지 위해 8로 상한)."""
+ return max(1, min(8, os.cpu_count() or 4))
+
+
# ── 모델 다운로드 헬퍼 ─────────────────────────────────────
@@ -250,6 +263,52 @@ def _cleanup_partial(model_id: str) -> None:
pass
+def _build_transcribe_kwargs(
+ language: str,
+ vad_filter: str,
+ initial_prompt: str,
+ is_partial: bool,
+) -> dict:
+ """전사 옵션을 만든다.
+
+ 받아쓰기 정합성을 위해 컨텍스트 누적(condition_on_previous_text)을 끈다.
+ Whisper가 앞 세그먼트 오류를 반복 증폭하는 현상(환각 루프)을 막는다.
+ 미리보기(partial)는 지연이 목표이므로 greedy + VAD 없음으로 디코딩한다.
+ """
+ if is_partial:
+ kwargs: dict = {
+ "beam_size": 1,
+ "temperature": 0.0,
+ "vad_filter": False,
+ "condition_on_previous_text": False,
+ "word_timestamps": False,
+ }
+ else:
+ kwargs = {
+ "beam_size": 5,
+ # 0.0 단일 온도는 실패 시 재시도가 없어 환각이 남는다.
+ # 낮은 온도 폴백만 허용하되 컨텍스트를 끊어 반복을 차단한다.
+ "temperature": [0.0, 0.2, 0.4],
+ "condition_on_previous_text": False,
+ "no_speech_threshold": 0.6,
+ "compression_ratio_threshold": 2.4,
+ "log_prob_threshold": -1.0,
+ "vad_filter": vad_filter.lower() == "true",
+ "word_timestamps": False,
+ }
+ if kwargs["vad_filter"]:
+ # 무음 구간을 촘촘히 잘라 속도를 올린다.
+ kwargs["vad_parameters"] = {"min_silence_duration_ms": 300}
+
+ if language != "auto":
+ kwargs["language"] = language
+
+ if initial_prompt and not is_partial:
+ kwargs["initial_prompt"] = initial_prompt
+
+ return kwargs
+
+
# ── 엔드포인트 ─────────────────────────────────────────────
@@ -260,7 +319,9 @@ async def health() -> JSONResponse:
content={
"status": "ready" if _model is not None else "no_model",
"model": _model_id,
+ "model_loaded": _model is not None,
"gpu": _gpu_available,
+ "device": "cuda" if _gpu_available else "cpu",
}
)
@@ -282,6 +343,18 @@ async def load_model(body: dict) -> JSONResponse: # noqa: ANN001
start_time = time.monotonic()
+ # 같은 모델이 이미 로딩되어 있으면 재사용 (재로딩은 수초 지연을 만든다)
+ if _model is not None and _model_id == model_id:
+ logger.info("이미 로딩된 모델 재사용: %s", model_id)
+ return JSONResponse(
+ content={
+ "status": "loaded",
+ "model_id": model_id,
+ "load_time_ms": 0,
+ "reused": True,
+ }
+ )
+
try:
from faster_whisper import WhisperModel
@@ -295,10 +368,15 @@ async def load_model(body: dict) -> JSONResponse: # noqa: ANN001
if local_dir:
logger.info("로컬 모델 디렉토리 사용: %s", local_dir)
+ # 모델 교체 시 이전 모델을 먼저 해제해 VRAM/RAM을 회수한다.
+ _model = None
+
_model = WhisperModel(
model_source,
device=device,
compute_type=compute_type,
+ cpu_threads=_cpu_threads(),
+ num_workers=1,
)
_model_id = model_id
@@ -333,14 +411,16 @@ async def transcribe(
language: str = Form("auto"),
vad_filter: str = Form("true"),
initial_prompt: str = Form(""),
+ partial: str = Form("false"),
) -> JSONResponse:
"""오디오 파일을 전사한다.
Multipart form:
- audio - PCM16 16kHz mono 바이너리 파일
- language - 언어 코드 ('auto', 'ko', 'en', ...)
- vad_filter - VAD 필터 활성화 ('true' / 'false')
+ audio - PCM16 16kHz mono 바이너리 파일
+ language - 언어 코드 ('auto', 'ko', 'en', ...)
+ vad_filter - VAD 필터 활성화 ('true' / 'false')
initial_prompt - 초기 프롬프트 (컨텍스트 힌트)
+ partial - 녹음 중 미리보기 모드 ('true'면 greedy 디코딩 + 컨텍스트 미사용)
"""
if _model is None:
return JSONResponse(
@@ -348,6 +428,7 @@ async def transcribe(
content={"status": "error", "message": "모델이 로딩되지 않았습니다"},
)
+ is_partial = partial.lower() == "true"
start_time = time.monotonic()
try:
@@ -367,22 +448,19 @@ async def transcribe(
audio_duration = len(audio_array) / sample_rate
logger.info(
- "전사 시작: %.1f초 오디오, language=%s, vad=%s",
+ "전사 시작: %.1f초 오디오, language=%s, vad=%s, partial=%s",
audio_duration,
language,
vad_filter,
+ is_partial,
)
- transcribe_kwargs: dict = {
- "vad_filter": vad_filter.lower() == "true",
- "beam_size": 5,
- }
-
- if language != "auto":
- transcribe_kwargs["language"] = language
-
- if initial_prompt:
- transcribe_kwargs["initial_prompt"] = initial_prompt
+ transcribe_kwargs = _build_transcribe_kwargs(
+ language=language,
+ vad_filter=vad_filter,
+ initial_prompt=initial_prompt,
+ is_partial=is_partial,
+ )
# VAD가 전체 오디오를 제거하면 max() 에러 발생 → VAD 없이 재시도
try:
diff --git a/apps/desktop/src/main/bootstrap.ts b/apps/desktop/src/main/bootstrap.ts
index 587eb38..58956d2 100644
--- a/apps/desktop/src/main/bootstrap.ts
+++ b/apps/desktop/src/main/bootstrap.ts
@@ -63,6 +63,7 @@ export async function bootstrap(): Promise {
{ name: 'popup-preload', critical: false, fn: initPopupWindows },
{ name: 'hotkey', critical: false, fn: initHotkey },
{ name: 'voice-mode', critical: false, fn: initVoiceMode },
+ { name: 'stt-warmup', critical: false, fn: initSTTWarmup },
{ name: 'llm-polling', critical: false, fn: initLLMPolling },
{ name: 'meeting-summary-wiring', critical: false, fn: initMeetingSummaryWiring },
{ name: 'meeting-mode', critical: false, fn: initMeetingMode },
@@ -270,6 +271,20 @@ async function initLLMPolling(): Promise {
await startLocalLLMAvailability()
}
+/**
+ * 로컬 STT(sidecar + Whisper 모델)를 앱 시작 시 백그라운드로 미리 데운다.
+ * 첫 받아쓰기에서 모델 로딩(수초)을 기다리는 체감 지연을 없앤다.
+ * bootstrap을 막지 않도록 await하지 않는다 — 실패는 warmUpLocal이 흡수한다.
+ */
+async function initSTTWarmup(): Promise {
+ try {
+ const { getSTTManager } = await import('./services/stt/STTManager')
+ void getSTTManager().warmUpLocal()
+ } catch (err) {
+ logger.warn('STT warmup scheduling failed:', err)
+ }
+}
+
async function initCloudSync(): Promise {
const { getCloudSyncService } = await import('./services/CloudSyncService')
const sync = getCloudSyncService()
diff --git a/apps/desktop/src/main/ipc/llm-handlers.ts b/apps/desktop/src/main/ipc/llm-handlers.ts
index 4e1bf03..1e29f78 100644
--- a/apps/desktop/src/main/ipc/llm-handlers.ts
+++ b/apps/desktop/src/main/ipc/llm-handlers.ts
@@ -7,6 +7,7 @@ import { getLocalLLMService } from '../services/LocalLLMService'
import { getPremiumLLMService } from '../services/PremiumLLMService'
import { getOnlineLLMService } from '../services/OnlineLLMService'
import { configGet, configSet } from '../services/ConfigService'
+import { normalizeLoopbackUrl } from '../utils/loopback'
import { getMainWindow } from '../windows/WindowManager'
import type { SetLLMModelParams, SetServerUrlParams, LLMProcessParams } from '@d3ro/core/types'
@@ -129,7 +130,7 @@ export function registerLLMHandlers(): void {
// ONLINE AUTH HANDLERS
ipcMain.handle(IPC_CHANNELS.ONLINE_AUTH.REGISTER, async (_event, params: { email: string; password: string }) => {
- const apiUrl = configGet('onlineApiUrl') ?? 'http://localhost:5000'
+ const apiUrl = normalizeLoopbackUrl(configGet('onlineApiUrl') ?? 'http://127.0.0.1:5000')
try {
const res = await fetch(`${apiUrl}/api/auth/register`, {
method: 'POST',
@@ -151,7 +152,7 @@ export function registerLLMHandlers(): void {
})
ipcMain.handle(IPC_CHANNELS.ONLINE_AUTH.LOGIN, async (_event, params: { email: string; password: string }) => {
- const apiUrl = configGet('onlineApiUrl') ?? 'http://localhost:5000'
+ const apiUrl = normalizeLoopbackUrl(configGet('onlineApiUrl') ?? 'http://127.0.0.1:5000')
try {
const res = await fetch(`${apiUrl}/api/auth/login`, {
method: 'POST',
diff --git a/apps/desktop/src/main/services/AudioCaptureService.ts b/apps/desktop/src/main/services/AudioCaptureService.ts
index 5cb3005..5a10c7e 100644
--- a/apps/desktop/src/main/services/AudioCaptureService.ts
+++ b/apps/desktop/src/main/services/AudioCaptureService.ts
@@ -135,7 +135,7 @@ class AudioCaptureService extends EventEmitter {
]
logger.info(`SoX args: ${soxArgs.join(' ')}`)
- this._soxProcess = spawn(soxExe, soxArgs, { stdio: ['pipe', 'pipe', 'pipe'] })
+ this._soxProcess = spawn(soxExe, soxArgs, { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true })
this._stream = this._soxProcess.stdout
this._residualBuffer = Buffer.alloc(0)
this._levelAccumulator = []
@@ -168,8 +168,12 @@ class AudioCaptureService extends EventEmitter {
this._soxProcess.on('error', (err: Error) => {
logger.error(`SoX process spawn error: ${err.message}`)
+ const hint =
+ soxExe === 'sox' && (err as NodeJS.ErrnoException).code === 'ENOENT'
+ ? ' 번들된 SoX(resources/sox/sox.exe)도, 시스템 PATH의 sox도 없습니다. `npm --prefix apps/desktop run setup:sox`로 내려받으세요.'
+ : ''
this._handleError(
- new D3ROError(ErrorCode.AudioCaptureStartFailed, `SoX 프로세스 시작 실패: ${err.message}`),
+ new D3ROError(ErrorCode.AudioCaptureStartFailed, `SoX 프로세스 시작 실패: ${err.message}.${hint}`),
'error'
)
})
@@ -392,7 +396,7 @@ class AudioCaptureService extends EventEmitter {
'-b', '16', '-e', 'signed-integer', '-t', 'raw', '-']
return new Promise((resolve, reject) => {
- const proc = spawn(soxExe, soxArgs, { stdio: ['ignore', 'pipe', 'pipe'] })
+ const proc = spawn(soxExe, soxArgs, { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true })
const buffers: Buffer[] = []
let peakRms = 0
diff --git a/apps/desktop/src/main/services/ConfigService.ts b/apps/desktop/src/main/services/ConfigService.ts
index f6079b6..2f0b2e9 100644
--- a/apps/desktop/src/main/services/ConfigService.ts
+++ b/apps/desktop/src/main/services/ConfigService.ts
@@ -26,13 +26,13 @@ const CONFIG_DEFAULTS: AppConfig = {
sttProvider: 'local' as const,
sttProviderConfigs: {
local: { modelId: 'large-v3-turbo' },
- 'd3ro-cloud': { modelId: 'default', apiKey: '', baseUrl: 'http://localhost:5000' },
+ 'd3ro-cloud': { modelId: 'default', apiKey: '', baseUrl: 'http://127.0.0.1:5000' },
openai: { modelId: 'whisper-1', apiKey: '', baseUrl: 'https://api.openai.com/v1' },
groq: { modelId: 'whisper-large-v3-turbo', apiKey: '', baseUrl: 'https://api.groq.com/openai/v1' },
deepgram: { modelId: 'nova-3', apiKey: '', baseUrl: 'https://api.deepgram.com' },
assemblyai: { modelId: 'best', apiKey: '', baseUrl: 'https://api.assemblyai.com/v2' },
google: { modelId: 'gemini-2.0-flash', apiKey: '', baseUrl: 'https://generativelanguage.googleapis.com/v1beta' },
- custom: { modelId: 'whisper-1', apiKey: '', baseUrl: 'http://localhost:8000/v1' },
+ custom: { modelId: 'whisper-1', apiKey: '', baseUrl: 'http://127.0.0.1:8000/v1' },
},
sttFallbackToLocal: true,
// large-v3 대비 6배 빠르고 정확도 손실 1~2%, 다운로드 1.6GB (온보딩에서 사전 다운로드)
@@ -40,10 +40,10 @@ const CONFIG_DEFAULTS: AppConfig = {
sttLanguage: 'auto',
ttsVoiceId: null,
ttsSpeed: 1.0,
- onlineApiUrl: 'http://localhost:5000',
+ onlineApiUrl: 'http://127.0.0.1:5000',
localModelsDir: '',
llmModelId: 'gemma-2-2b-it.Q4_K_M.gguf',
- ollamaServerUrl: 'http://localhost:11434',
+ ollamaServerUrl: 'http://127.0.0.1:11434',
appUsageMode: null,
authToken: null,
userEmail: null,
diff --git a/apps/desktop/src/main/services/LocalLLMService.ts b/apps/desktop/src/main/services/LocalLLMService.ts
index 94cdc4f..003f31e 100644
--- a/apps/desktop/src/main/services/LocalLLMService.ts
+++ b/apps/desktop/src/main/services/LocalLLMService.ts
@@ -12,9 +12,15 @@ import { D3ROError, ErrorCode } from '@d3ro/core/errors'
import type { LLMStatus, LLMModel, LLMAction, LLMConnectionState } from '@d3ro/core/types'
import { resolveSystemPrompt } from './llm-prompts'
import { getBundledOllamaPath } from '../utils/paths'
+import { normalizeLoopbackUrl } from '../utils/loopback'
const logger = getLogger('LocalLLMService')
+/** Ollama 서버 URL — localhost는 ::1로 해석되어 실패하므로 IPv4 루프백으로 정규화한다. */
+export function getOllamaServerUrl(): string {
+ return normalizeLoopbackUrl(configGet('ollamaServerUrl') || 'http://127.0.0.1:11434')
+}
+
// ============================================================
// 내부 타입
// ============================================================
@@ -172,7 +178,7 @@ class LocalLLMService extends EventEmitter {
* Ollama /api/version 또는 /api/tags 엔드포인트로 가용성 핑. 지정 타임아웃 내 응답이 오면 true.
*/
private async _ping(timeoutMs: number): Promise {
- const serverUrl = configGet('ollamaServerUrl') || 'http://localhost:11434'
+ const serverUrl = getOllamaServerUrl()
try {
const response = await fetch(`${serverUrl}/api/version`, {
signal: AbortSignal.timeout(timeoutMs)
@@ -325,7 +331,7 @@ class LocalLLMService extends EventEmitter {
throw new D3ROError(ErrorCode.LLMServerUnreachable, 'Ollama 서버에 연결할 수 없습니다')
}
- const serverUrl = configGet('ollamaServerUrl')
+ const serverUrl = getOllamaServerUrl()
const model = options?.model ?? configGet('llmModelId') ?? 'gemma4:e4b'
this._state = LLMState.Generating
@@ -392,7 +398,7 @@ class LocalLLMService extends EventEmitter {
throw new D3ROError(ErrorCode.LLMServerUnreachable, 'Ollama 서버에 연결할 수 없습니다')
}
- const serverUrl = configGet('ollamaServerUrl')
+ const serverUrl = getOllamaServerUrl()
const model = options?.model ?? configGet('llmModelId') ?? 'gemma4:e4b'
this._state = LLMState.Generating
@@ -536,7 +542,7 @@ class LocalLLMService extends EventEmitter {
* Ollama에 설치된 모델 목록을 조회한다.
*/
async getModels(): Promise {
- const serverUrl = configGet('ollamaServerUrl')
+ const serverUrl = getOllamaServerUrl()
try {
const response = await fetch(`${serverUrl}/api/tags`, {
@@ -582,7 +588,7 @@ class LocalLLMService extends EventEmitter {
}
private async _doPullModel(modelId: string): Promise {
- const serverUrl = configGet('ollamaServerUrl')
+ const serverUrl = getOllamaServerUrl()
logger.info(`Pull 시작: ${modelId}`)
const response = await fetch(`${serverUrl}/api/pull`, {
@@ -656,7 +662,7 @@ class LocalLLMService extends EventEmitter {
return {
connectionState,
- serverUrl: configGet('ollamaServerUrl') || 'http://localhost:11434',
+ serverUrl: getOllamaServerUrl(),
activeModel: configGet('llmModelId') || 'gemma2:2b',
serverVersion: this._serverVersion
}
@@ -679,7 +685,7 @@ class LocalLLMService extends EventEmitter {
throw new D3ROError(ErrorCode.LLMServerUnreachable, 'Ollama server not available')
}
- const serverUrl = configGet('ollamaServerUrl') || 'http://localhost:11434'
+ const serverUrl = getOllamaServerUrl()
const model = options?.model ?? configGet('llmModelId') ?? 'gemma2:2b'
this._abortController = new AbortController()
@@ -754,7 +760,7 @@ class LocalLLMService extends EventEmitter {
private async _checkAvailability(): Promise {
if (this._disposed) return
- const serverUrl = configGet('ollamaServerUrl') || 'http://localhost:11434'
+ const serverUrl = getOllamaServerUrl()
let isOk = false
let detectedVersion: string | null = null
diff --git a/apps/desktop/src/main/services/LocalSTTService.ts b/apps/desktop/src/main/services/LocalSTTService.ts
index 189ec3f..a52227f 100644
--- a/apps/desktop/src/main/services/LocalSTTService.ts
+++ b/apps/desktop/src/main/services/LocalSTTService.ts
@@ -10,7 +10,7 @@ import { existsSync } from 'fs'
import { join } from 'path'
import { getLogger } from './LoggerService'
import { configGet } from './ConfigService'
-import { getSidecarCommand, getWhisperModelsDir } from '../utils/paths'
+import { getSidecarCommand, getSidecarBaseUrl, getWhisperModelsDir } from '../utils/paths'
import { D3ROError, ErrorCode } from '@d3ro/core/errors'
import type {
STTModel,
@@ -52,6 +52,8 @@ export interface TranscribeOptions {
language?: string
initialPrompt?: string
vadFilter?: boolean
+ /** 음 중 실시간 미리보기 요청 — 상태/이벤트를 건드리지 않고 greedy 디코딩을 사용한다. */
+ partial?: boolean
}
/** sidecar /health 응답 */
@@ -109,6 +111,8 @@ const HEALTH_CHECK_INTERVAL_MS = 1000
const HEALTH_CHECK_TIMEOUT_MS = 30000
const MAX_RESTART_COUNT = 3
const SIDECAR_REQUEST_TIMEOUT_MS = 120000
+/** 부분 전사(미리보기) 타임아웃 — 실패해도 무시되므로 짧게 잡는다 */
+const SIDECAR_PARTIAL_TIMEOUT_MS = 15000
/** 알려진 Whisper 모델 카탈로그 */
const MODEL_CATALOG: STTModel[] = [
@@ -207,6 +211,11 @@ class LocalSTTService extends EventEmitter {
return this._currentModelId
}
+ /** sidecar HTTP 기본 URL — IPv4 루프백 고정 (localhost는 ::1로 해석되어 실패) */
+ private get _baseUrl(): string {
+ return getSidecarBaseUrl(this._port)
+ }
+
// ── 공개 메서드 ──
/**
@@ -300,6 +309,65 @@ class LocalSTTService extends EventEmitter {
return this._sendToSidecar(audioBuffer, options)
}
+ /**
+ * 앱 시작 시 sidecar와 모델을 미리 데운다.
+ * 첫 받아쓰기에서 모델 로딩(수초)을 기다리지 않게 하는 것이 목적이므로
+ * 실패는 조용히 경고로만 남기고 예외를 던지지 않는다.
+ */
+ async warmUp(): Promise {
+ if (this._disposed) return false
+ if (this._state === STTState.Ready && this._currentModelId) return true
+
+ const modelId = configGet('sttModelId')
+ if (!modelId) {
+ logger.info('STT 워밍업 생략: 모델이 선택되지 않았습니다')
+ return false
+ }
+
+ if (!existsSync(join(getWhisperModelsDir(), modelId, 'model.bin'))) {
+ logger.info(`STT 워밍업 생략: 모델 미설치 (${modelId})`)
+ return false
+ }
+
+ try {
+ await this.initialize(modelId)
+ return true
+ } catch (err) {
+ logger.warn(
+ `STT 워밍업 실패: ${err instanceof Error ? err.message : String(err)}`,
+ )
+ return false
+ }
+ }
+
+ /**
+ * 녹음 중 실시간 미리보기 전사.
+ * 최종 결과와 분리되어 삽입되지 않으며, 실패해도 빈 문자열을 반환한다.
+ * 지연 최소화를 위해 상태/이벤트를 건드리지 않는다.
+ */
+ async transcribePartial(
+ audioBuffer: Buffer,
+ options?: TranscribeOptions,
+ ): Promise {
+ if (this._disposed) return ''
+ if (!this._modelReady) return ''
+ if (audioBuffer.length === 0) return ''
+ if (!this._sidecarProcess || this._sidecarProcess.exitCode !== null) return ''
+
+ try {
+ const result = await this._sendToSidecar(audioBuffer, {
+ ...options,
+ partial: true,
+ })
+ return result.text
+ } catch (err) {
+ logger.debug(
+ `부분 전사 실패(무시): ${err instanceof Error ? err.message : String(err)}`,
+ )
+ return ''
+ }
+ }
+
/**
* 다운로드된 모델 목록 조회.
* models-dir 사전 다운로드 여부 + 현재 로딩 여부로 downloaded를 판정한다.
@@ -328,7 +396,7 @@ class LocalSTTService extends EventEmitter {
await this._ensureSidecarRunning()
- const startRes = await fetch(`http://localhost:${this._port}/download`, {
+ const startRes = await fetch(`${this._baseUrl}/download`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model_id: modelId }),
@@ -361,7 +429,7 @@ class LocalSTTService extends EventEmitter {
let status: DownloadStatusResponse
try {
- const res = await fetch(`http://localhost:${this._port}/download/status`, {
+ const res = await fetch(`${this._baseUrl}/download/status`, {
signal: AbortSignal.timeout(3000),
})
if (!res.ok) throw new Error(`HTTP ${res.status}`)
@@ -408,7 +476,7 @@ class LocalSTTService extends EventEmitter {
/** 진행 중인 모델 다운로드 취소 요청 */
async cancelDownload(): Promise {
try {
- await fetch(`http://localhost:${this._port}/download/cancel`, {
+ await fetch(`${this._baseUrl}/download/cancel`, {
method: 'POST',
signal: AbortSignal.timeout(3000),
})
@@ -570,77 +638,120 @@ class LocalSTTService extends EventEmitter {
// dev mode HMR/재시작으로 이전 sidecar가 orphan으로 남아있을 수 있음.
this._port = await this._findFreePort(SIDECAR_PORT, 20)
- const { command, args } = getSidecarCommand()
+ // 번들/venv 경로가 깨졌으면 여기서 즉시 실패한다 (조용한 PATH 폴백 금지).
+ const launch = getSidecarCommand()
const fullArgs = [
- ...args,
+ ...launch.args,
'--port',
String(this._port),
'--models-dir',
getWhisperModelsDir(),
]
- logger.info(`Sidecar 시작: ${command} ${fullArgs.join(' ')}`)
+ logger.info(
+ `Sidecar 시작(${launch.source}): ${launch.command} ${fullArgs.join(' ')}`,
+ )
return new Promise((resolve, reject) => {
- try {
- this._sidecarProcess = spawn(
- command,
- fullArgs,
- {
- stdio: ['pipe', 'pipe', 'pipe'],
- env: { ...process.env },
- },
- )
- } catch (err) {
- const d3roErr = new D3ROError(
- ErrorCode.STTSidecarSpawnFailed,
- `Sidecar 프로세스 생성 실패: ${err instanceof Error ? err.message : String(err)}`,
- )
- reject(d3roErr)
- return
- }
+ let settled = false
+ const child = spawn(launch.command, fullArgs, {
+ stdio: ['pipe', 'pipe', 'pipe'],
+ env: {
+ ...process.env,
+ // 사이드카 로그와 파일 경로가 UTF-8로 오가도록 고정 (Windows cp949 깨짐 방지)
+ PYTHONIOENCODING: 'utf-8',
+ PYTHONUTF8: '1',
+ },
+ // Windows에서 콘솔 창이 깜빡이지 않게 한다.
+ windowsHide: true,
+ })
+ this._sidecarProcess = child
- const sidecarLogger = getLogger('sidecar')
+ this._pipeSidecarLogs(child, getLogger('sidecar'))
- this._sidecarProcess.stdout?.on('data', (data: Buffer) => {
- const text = data.toString().trim()
- if (text) {
- sidecarLogger.info(text)
- }
+ // spawn 성공 = 프로세스가 실제로 시작됨. 즉시 resolve해 healthcheck로 넘어간다.
+ child.once('spawn', () => {
+ if (settled) return
+ settled = true
+ resolve()
})
- this._sidecarProcess.stderr?.on('data', (data: Buffer) => {
- const text = data.toString().trim()
- if (text) {
- sidecarLogger.warn(text)
- }
- })
-
- this._sidecarProcess.on('error', (err: Error) => {
+ // spawn 실패(ENOENT 등)는 즉시 실패시킨다. 예전엔 즉시 resolve 후
+ // healthcheck 30초를 헛되게 태우고 원인을 숨겼다.
+ child.once('error', (err: Error) => {
logger.error(`Sidecar 프로세스 에러: ${err.message}`)
- reject(
- new D3ROError(
- ErrorCode.STTSidecarSpawnFailed,
- `Sidecar 프로세스 에러: ${err.message}`,
- ),
- )
+ this._sidecarProcess = null
+ if (settled) return
+ settled = true
+ reject(this._spawnFailureError(err, launch))
})
- this._sidecarProcess.on('exit', (code: number | null, signal: string | null) => {
+ child.on('exit', (code: number | null, signal: string | null) => {
logger.warn(`Sidecar 프로세스 종료: code=${code}, signal=${signal}`)
- this._sidecarProcess = null
+ if (this._sidecarProcess === child) {
+ this._sidecarProcess = null
+ }
this._modelReady = false
if (!this._disposed) {
this._handleSidecarCrash()
}
})
-
- // spawn 자체는 비동기적이므로 즉시 resolve
- // 실제 준비는 _waitForHealth에서 확인
- resolve()
})
}
+ /** sidecar stdout/stderr를 줄 단위로 로그에 흘려보낸다. */
+ private _pipeSidecarLogs(
+ child: ChildProcess,
+ sidecarLogger: ReturnType,
+ ): void {
+ const consume = (
+ stream: NodeJS.ReadableStream | null | undefined,
+ write: (message: string) => void,
+ ): void => {
+ if (!stream) return
+ let pending = ''
+ stream.on('data', (chunk: Buffer) => {
+ pending += chunk.toString('utf8')
+ const lines = pending.split(/\r?\n/)
+ // 마지막 조각은 줄이 완성되지 않았을 수 있으니 다음 청크와 합친다.
+ pending = lines.pop() ?? ''
+ for (const line of lines) {
+ const trimmed = line.trim()
+ if (trimmed) write(trimmed)
+ }
+ })
+ }
+
+ consume(child.stdout, (message) => sidecarLogger.info(message))
+ consume(child.stderr, (message) => sidecarLogger.warn(message))
+ }
+
+ /** sidecar 기동 실패 원인을 사용자가 조치할 수 있는 문구로 바꾼다. */
+ private _spawnFailureError(
+ err: Error,
+ launch: { command: string; source: 'bundled' | 'venv' | 'python' },
+ ): D3ROError {
+ const enoent = (err as NodeJS.ErrnoException).code === 'ENOENT'
+ if (!enoent) {
+ return new D3ROError(
+ ErrorCode.STTSidecarSpawnFailed,
+ `Sidecar 프로세스 에러: ${err.message}`,
+ )
+ }
+
+ const hint =
+ launch.source === 'bundled'
+ ? '번들된 사이드카 실행 파일이 손상되었거나 백신이 차단했습니다. 앱을 다시 설치하세요.'
+ : launch.source === 'venv'
+ ? '사이드카 가상환경이 손상되었습니다. `npm --prefix apps/desktop run sidecar:setup`을 실행하세요.'
+ : '시스템 Python을 찾을 수 없습니다. `npm --prefix apps/desktop run sidecar:setup`으로 가상환경을 만드세요.'
+
+ return new D3ROError(
+ ErrorCode.STTSidecarSpawnFailed,
+ `Sidecar 실행 파일을 찾을 수 없습니다: ${launch.command} (${launch.source}). ${hint}`,
+ )
+ }
+
private async _waitForHealth(): Promise {
const startTime = Date.now()
@@ -655,7 +766,7 @@ class LocalSTTService extends EventEmitter {
}
try {
- const response = await fetch(`http://localhost:${this._port}/health`, {
+ const response = await fetch(`${this._baseUrl}/health`, {
signal: AbortSignal.timeout(2000),
})
@@ -685,7 +796,7 @@ class LocalSTTService extends EventEmitter {
logger.info(`모델 로딩 시작: ${modelId}`)
const startTime = Date.now()
- const response = await fetch(`http://localhost:${this._port}/load`, {
+ const response = await fetch(`${this._baseUrl}/load`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model_id: modelId }),
@@ -729,12 +840,16 @@ class LocalSTTService extends EventEmitter {
)
}
- this._setState(STTState.Transcribing)
+ const isPartial = options?.partial === true
+
+ if (!isPartial) {
+ this._setState(STTState.Transcribing)
+ }
const startTime = Date.now()
try {
const language = options?.language ?? configGet('sttLanguage')
- const vadFilter = options?.vadFilter ?? true
+ const vadFilter = options?.vadFilter ?? !isPartial
const initialPrompt = options?.initialPrompt ?? ''
// Node 18+ 내장 fetch + FormData + Blob으로 multipart 전송
@@ -751,18 +866,19 @@ class LocalSTTService extends EventEmitter {
)
formData.append('language', language)
formData.append('vad_filter', String(vadFilter))
+ // 부분 전사는 greedy 디코딩 + 컨텍스트 미사용으로 지연을 최소화한다.
+ formData.append('partial', String(isPartial))
if (initialPrompt) {
formData.append('initial_prompt', initialPrompt)
}
- const response = await fetch(
- `http://localhost:${this._port}/transcribe`,
- {
- method: 'POST',
- body: formData,
- signal: AbortSignal.timeout(SIDECAR_REQUEST_TIMEOUT_MS),
- },
- )
+ const response = await fetch(`${this._baseUrl}/transcribe`, {
+ method: 'POST',
+ body: formData,
+ signal: AbortSignal.timeout(
+ isPartial ? SIDECAR_PARTIAL_TIMEOUT_MS : SIDECAR_REQUEST_TIMEOUT_MS,
+ ),
+ })
if (!response.ok) {
const errorText = await response.text()
@@ -788,6 +904,12 @@ class LocalSTTService extends EventEmitter {
processingTime,
}
+ if (isPartial) {
+ // 미리보기 — 상태/이벤트를 건드리지 않는다 (최종 삽입과 무관).
+ logger.debug(`부분 전사: "${result.text.substring(0, 40)}" (${processingTime}ms)`)
+ return result
+ }
+
// 중간 결과 이벤트 (isFinal=true)
this.emit('transcription-delta', { text: result.text, isFinal: true })
this.emit('transcription-complete', { result })
@@ -800,7 +922,9 @@ class LocalSTTService extends EventEmitter {
return result
} catch (err) {
- this._setState(STTState.Ready) // 에러 후에도 Ready 복귀 (sidecar가 살아있으면)
+ if (!isPartial) {
+ this._setState(STTState.Ready) // 에러 후에도 Ready 복귀 (sidecar가 살아있으면)
+ }
if (err instanceof D3ROError) {
throw err
@@ -874,7 +998,7 @@ class LocalSTTService extends EventEmitter {
try {
// POST /shutdown 요청
- await fetch(`http://localhost:${this._port}/shutdown`, {
+ await fetch(`${this._baseUrl}/shutdown`, {
method: 'POST',
signal: AbortSignal.timeout(3000),
})
diff --git a/apps/desktop/src/main/services/OnlineLLMService.ts b/apps/desktop/src/main/services/OnlineLLMService.ts
index 556784b..b2d68f8 100644
--- a/apps/desktop/src/main/services/OnlineLLMService.ts
+++ b/apps/desktop/src/main/services/OnlineLLMService.ts
@@ -4,6 +4,7 @@
import { EventEmitter } from 'events'
import { configGet, configSet } from './ConfigService'
+import { normalizeLoopbackUrl } from '../utils/loopback'
import { D3ROError, ErrorCode } from '@d3ro/core/errors'
import type { LLMAction } from '@d3ro/core/types'
import { resolveSystemPrompt } from './llm-prompts'
@@ -47,7 +48,7 @@ class OnlineLLMService extends EventEmitter {
customPrompt?: string
): Promise {
const token = this._ensureAuth()
- const apiUrl = configGet('onlineApiUrl') ?? 'http://localhost:5000'
+ const apiUrl = normalizeLoopbackUrl(configGet('onlineApiUrl') ?? 'http://127.0.0.1:5000')
const systemPrompt = resolveSystemPrompt(action, targetLanguage, customPrompt)
try {
@@ -100,7 +101,7 @@ class OnlineLLMService extends EventEmitter {
options?: { model?: string; temperature?: number }
): AsyncGenerator {
const token = this._ensureAuth()
- const apiUrl = configGet('onlineApiUrl') ?? 'http://localhost:5000'
+ const apiUrl = normalizeLoopbackUrl(configGet('onlineApiUrl') ?? 'http://127.0.0.1:5000')
const response = await fetch(`${apiUrl}/api/llm/chat`, {
method: 'POST',
diff --git a/apps/desktop/src/main/services/RAGService.ts b/apps/desktop/src/main/services/RAGService.ts
index 319f91a..41fa603 100644
--- a/apps/desktop/src/main/services/RAGService.ts
+++ b/apps/desktop/src/main/services/RAGService.ts
@@ -7,7 +7,7 @@ import path from 'path'
import { eq } from 'drizzle-orm'
import { getLogger } from './LoggerService'
import { getPremiumLLMService } from './PremiumLLMService'
-import { configGet } from './ConfigService'
+import { getOllamaServerUrl } from './LocalLLMService'
import { getDatabase } from '../db'
import { ragDocuments, ragChunks } from '../db/schema'
import { getMainWindow } from '../windows/WindowManager'
@@ -307,7 +307,7 @@ ${context}`
* Ollama /api/embed 엔드포인트로 텍스트 임베딩
*/
private async _embed(text: string): Promise {
- const serverUrl = configGet('ollamaServerUrl')
+ const serverUrl = getOllamaServerUrl()
try {
const response = await fetch(`${serverUrl}/api/embed`, {
diff --git a/apps/desktop/src/main/services/VoiceModeService.ts b/apps/desktop/src/main/services/VoiceModeService.ts
index 212710b..c8e8b72 100644
--- a/apps/desktop/src/main/services/VoiceModeService.ts
+++ b/apps/desktop/src/main/services/VoiceModeService.ts
@@ -27,6 +27,7 @@ import {
hideRecordingTip,
updateRecordingTipState,
sendAudioLevelToTip,
+ sendPartialTranscriptToTip,
showResultPopup,
} from '../windows/WindowManager'
import type { ScreenContext } from '@d3ro/core/types'
@@ -98,6 +99,18 @@ const TERMINAL_STATES = new Set([
RecognitionState.DESTROYED
])
+// ── 실시간 부분 전사(미리보기) ──
+// 16kHz 16bit mono = 32 bytes/ms
+const BYTES_PER_MS = 32
+/** 부분 전사 주기 */
+const PARTIAL_INTERVAL_MS = 1500
+/** 부분 전사를 시작할 최소 녹음 길이 */
+const PARTIAL_MIN_AUDIO_MS = 1200
+/** 부분 전사에 보낼 최대 오디오 창(끝부분만) — 오래 말해도 지연이 늘지 않게 한다 */
+const PARTIAL_MAX_WINDOW_MS = 7500
+/** 녹음 종료 시 진행 중 부분 전사를 기다리는 최대 시간 */
+const PARTIAL_DRAIN_TIMEOUT_MS = 2500
+
// ============================================================
// VoiceModeService
// ============================================================
@@ -123,6 +136,10 @@ class VoiceModeService extends EventEmitter {
/** 녹음 종료 후 STT 준비 대기 타이머 — 전사 시작 시 반드시 해제 */
private _sttWaitTimer: NodeJS.Timeout | null = null
+ // 실시간 부분 전사(미리보기)
+ private _partialTimer: NodeJS.Timeout | null = null
+ private _partialInFlight: Promise | null = null
+
// Action Queue (이벤트 직렬화)
private _actionQueue: VoiceAction[] = []
private _isProcessingQueue = false
@@ -462,6 +479,8 @@ class VoiceModeService extends EventEmitter {
this._setAudioState(AudioState.STREAMING)
logger.info('Audio capture started')
+ this._startPartialLoop()
+
this._tryFlushAll()
} catch (error) {
if (this._isInTerminalState()) return
@@ -488,6 +507,7 @@ class VoiceModeService extends EventEmitter {
this._audioLevelHandler = null
}
this._audioStarted = false
+ this._stopPartialLoop()
try {
await audio.stop()
@@ -496,6 +516,73 @@ class VoiceModeService extends EventEmitter {
}
}
+ // ── 실시간 부분 전사(미리보기) ─────────────────────────────
+
+ /**
+ * 녹음 중 주기적으로 지금까지의 오디오를 전사해 RecordingTip에 미리보기를 띄운다.
+ * 최종 삽입 텍스트와는 완전히 분리된 경로이며, 실패는 조용히 무시된다.
+ */
+ private _startPartialLoop(): void {
+ this._stopPartialLoop()
+ if ((configGet('sttProvider') ?? 'local') !== 'local') return
+
+ this._partialTimer = setInterval(() => {
+ void this._runPartial()
+ }, PARTIAL_INTERVAL_MS)
+ }
+
+ private _stopPartialLoop(): void {
+ if (this._partialTimer) {
+ clearInterval(this._partialTimer)
+ this._partialTimer = null
+ }
+ }
+
+ /** 진행 중인 부분 전사가 끝나기를 최대 PARTIAL_DRAIN_TIMEOUT_MS까지 기다린다. */
+ private async _drainPartial(): Promise {
+ const inFlight = this._partialInFlight
+ if (!inFlight) return
+ await Promise.race([
+ inFlight,
+ new Promise((resolve) => setTimeout(resolve, PARTIAL_DRAIN_TIMEOUT_MS)),
+ ])
+ }
+
+ private async _runPartial(): Promise {
+ if (!this._audioStarted || this._partialInFlight) return
+ if (this._isInTerminalState()) return
+ if (!this._sttReady) return
+ if (this._audioBufferBytes < PARTIAL_MIN_AUDIO_MS * BYTES_PER_MS) return
+
+ const sessionId = this._session?.id
+ const merged = Buffer.concat(this._audioBuffer)
+ const maxBytes = PARTIAL_MAX_WINDOW_MS * BYTES_PER_MS
+ const window = merged.length > maxBytes ? merged.subarray(merged.length - maxBytes) : merged
+ const language = configGet('sttLanguage')
+
+ const task = (async (): Promise => {
+ try {
+ const text = await getSTTManager().transcribePartial(window, {
+ language: language === 'auto' ? undefined : language,
+ vadFilter: false,
+ })
+ // 녹음이 끝났거나 세션이 바뀌었으면 미리보기를 버린다.
+ if (!this._audioStarted || this._isInTerminalState()) return
+ if (this._session?.id !== sessionId) return
+ if (!text) return
+ sendPartialTranscriptToTip(text)
+ } catch (err) {
+ logger.debug(
+ `부분 전사 미리보기 무시: ${err instanceof Error ? err.message : String(err)}`,
+ )
+ } finally {
+ this._partialInFlight = null
+ }
+ })()
+
+ this._partialInFlight = task
+ }
+
// ── 이중 조건 플러시 ───────────────────────────────────
private _tryFlushAll(): void {
@@ -553,6 +640,10 @@ class VoiceModeService extends EventEmitter {
// DictionaryService 미초기화 시 무시
}
+ // 사이드카는 요청을 직렬 처리하므로, 진행 중인 미리보기 요청이 최종 전사를
+ // 지연시키지 않도록 먼저 배수한다(최대 PARTIAL_DRAIN_TIMEOUT_MS).
+ await this._drainPartial()
+
const result: TranscriptionResult = await stt.transcribe(merged, {
language: language === 'auto' ? undefined : language,
initialPrompt,
@@ -864,6 +955,8 @@ class VoiceModeService extends EventEmitter {
private _resetToIdle(): void {
this._clearSttWaitTimer()
+ this._stopPartialLoop()
+ this._partialInFlight = null
this._session = null
this._audioBuffer = []
this._audioBufferBytes = 0
diff --git a/apps/desktop/src/main/services/stt/STTManager.ts b/apps/desktop/src/main/services/stt/STTManager.ts
index 40a60d4..9f3f6df 100644
--- a/apps/desktop/src/main/services/stt/STTManager.ts
+++ b/apps/desktop/src/main/services/stt/STTManager.ts
@@ -22,6 +22,7 @@ import { AssemblyAIDriver } from './drivers/AssemblyAIDriver'
import { GoogleDriver } from './drivers/GoogleDriver'
import { CustomDriver } from './drivers/CustomDriver'
import { D3ROCloudDriver } from './drivers/D3ROCloudDriver'
+import { normalizeLoopbackUrl } from '../../utils/loopback'
const logger = getLogger('STTManager')
@@ -43,7 +44,7 @@ export const STT_PROVIDERS_META: STTProviderInfo[] = [
badge: 'Cloud · Zero Config',
requiresApiKey: false,
defaultModel: 'default',
- defaultBaseUrl: 'http://localhost:5000',
+ defaultBaseUrl: 'http://127.0.0.1:5000',
models: ['default', 'whisper-large-v3-turbo', 'nova-3', 'gemini-2.0-flash'],
isCloud: true,
},
@@ -109,7 +110,7 @@ export const STT_PROVIDERS_META: STTProviderInfo[] = [
badge: 'Self-Hosted / Proxy',
requiresApiKey: false,
defaultModel: 'whisper-1',
- defaultBaseUrl: 'http://localhost:8000/v1',
+ defaultBaseUrl: 'http://127.0.0.1:8000/v1',
models: ['whisper-1', 'custom'],
isCloud: true,
},
@@ -155,7 +156,8 @@ export class STTManager extends EventEmitter {
return {
apiKey: specificConfig.apiKey ?? '',
- baseUrl: specificConfig.baseUrl ?? meta?.defaultBaseUrl ?? '',
+ // 저장된 값이 localhost일 수 있다(IPv6 해석 실패) → IPv4 루프백으로 정규화.
+ baseUrl: normalizeLoopbackUrl(specificConfig.baseUrl ?? meta?.defaultBaseUrl ?? ''),
modelId: specificConfig.modelId ?? meta?.defaultModel ?? '',
temperature: specificConfig.temperature ?? 0,
}
@@ -245,6 +247,25 @@ export class STTManager extends EventEmitter {
}
}
+ /**
+ * 녹음 중 실시간 미리보기 전사(최종 삽입과 무관).
+ * 로컬 Whisper에서만 지원한다 — 클라우드 공급자는 요청 비용/지연이 커서 사용하지 않는다.
+ * 실패는 빈 문자열로 흡수된다.
+ */
+ async transcribePartial(audioBuffer: Buffer, options?: TranscribeOptions): Promise {
+ if (this.getActiveProvider() !== 'local') return ''
+ return getLocalSTTService().transcribePartial(audioBuffer, options)
+ }
+
+ /**
+ * 로컬 STT 엔진(sidecar + 모델)을 백그라운드로 미리 데운다.
+ * 첫 받아쓰기 지연을 없애는 것이 목적이며 실패해도 조용히 넘어간다.
+ */
+ async warmUpLocal(): Promise {
+ if (this.getActiveProvider() !== 'local') return false
+ return getLocalSTTService().warmUp()
+ }
+
getStatus(): STTStatus {
const provider = this.getActiveProvider()
if (provider === 'local') {
diff --git a/apps/desktop/src/main/utils/loopback.ts b/apps/desktop/src/main/utils/loopback.ts
new file mode 100644
index 0000000..13e1709
--- /dev/null
+++ b/apps/desktop/src/main/utils/loopback.ts
@@ -0,0 +1,39 @@
+// src/main/utils/loopback.ts
+// 로컬 엔진(Ollama, STT sidecar) URL 정규화.
+//
+// 배경: Windows 호스트 파일에 `::1 localhost`만 있고 `127.0.0.1 localhost`가 없으면
+// localhost가 IPv6(::1)로만 해석된다. Ollama/uvicorn은 IPv4(127.0.0.1)에만 바인딩하므로
+// `http://localhost:` 요청이 전부 ECONNREFUSED로 실패한다(실측).
+// 로컬 엔진은 바인딩 주소가 IPv4 루프백으로 고정이므로 항상 127.0.0.1로 정규화한다.
+
+/** IPv4 루프백으로 정규화할 호스트 이름 */
+const LOOPBACK_HOSTNAMES = new Set(['localhost', 'localhost.'])
+
+/** 로컬 엔진 기본 호스트 */
+export const LOOPBACK_HOST = '127.0.0.1'
+
+/**
+ * URL의 호스트가 localhost 계열이면 127.0.0.1로 바꾼다.
+ * 그 외 호스트/잘못된 URL은 원본을 그대로 반환한다.
+ */
+export function normalizeLoopbackUrl(rawUrl: string): string {
+ if (!rawUrl) return rawUrl
+
+ try {
+ const parsed = new URL(rawUrl)
+ if (!LOOPBACK_HOSTNAMES.has(parsed.hostname.toLowerCase())) {
+ return rawUrl
+ }
+ parsed.hostname = LOOPBACK_HOST
+ // URL 직렬화는 경로가 없을 때 '/'를 붙인다. 호출측이 `${base}/api/...`로
+ // 이어 붙이므로 말미 슬래시는 제거해 중복 래시를 막는다.
+ return parsed.toString().replace(/\/$/, '')
+ } catch {
+ return rawUrl.replace(/^(https?:\/\/)localhost(?=[:/]|$)/i, `$1${LOOPBACK_HOST}`)
+ }
+}
+
+/** 루프백 호스트로 접속 가능한 로컬 엔진 기본 URL을 만든다. */
+export function loopbackUrl(port: number, protocol = 'http'): string {
+ return `${protocol}://${LOOPBACK_HOST}:${port}`
+}
\ No newline at end of file
diff --git a/apps/desktop/src/main/utils/paths.ts b/apps/desktop/src/main/utils/paths.ts
index f13a403..958c782 100644
--- a/apps/desktop/src/main/utils/paths.ts
+++ b/apps/desktop/src/main/utils/paths.ts
@@ -4,6 +4,8 @@
import path from 'path'
import { app } from 'electron'
import { existsSync } from 'fs'
+import { D3ROError, ErrorCode } from '@d3ro/core/errors'
+import { loopbackUrl } from './loopback'
/** Windows는 .exe 접미사, 그 외는 없음 */
const EXE_SUFFIX = process.platform === 'win32' ? '.exe' : ''
@@ -17,33 +19,112 @@ function isPackaged(): boolean {
}
/**
- * SoX 실행 파일 경로. 번들된 게 있으면 그것, 없으면 시스템 PATH의 sox.
- * - Windows: sox.exe
- * - macOS/Linux: sox (brew install sox / apt install sox 필요)
+ * dev 실행 시 리소스 루트(apps/desktop)를 찾는다.
+ *
+ * electron-vite는 electron을 `out/main/index.js`로 직접 띄우기 때문에
+ * `app.getAppPath()`가 `apps/desktop/out/main`을 가리킨다. 그대로 쓰면
+ * `out/main/sidecar/main.py`, `out/main/resources/sox` 같은 존재하지 않는 경로가
+ * 만들어져 sidecar/SoX가 조용히 시스템 PATH 폴백으로 새고 로컬 전사가 실패한다(실측).
+ * 따라서 상위 디렉토리를 훑어 실제 앱 루트를 찾아 캐시한다.
*/
-export function getSoxPath(): string {
- const soxBin = `sox${EXE_SUFFIX}`
- const bundledSox = isPackaged()
- ? path.join(process.resourcesPath, 'sox', soxBin)
- : path.join(app.getAppPath(), 'resources', 'sox', soxBin)
+const APP_ROOT_MARKERS = [
+ path.join('sidecar', 'main.py'),
+ path.join('resources', 'sox'),
+ 'electron-builder.yml',
+]
- if (existsSync(bundledSox)) {
- return bundledSox
+const MAX_ROOT_WALK_UP = 4
+
+let cachedAppRoot: string | null = null
+
+function looksLikeAppRoot(dir: string): boolean {
+ return APP_ROOT_MARKERS.some((marker) => existsSync(path.join(dir, marker)))
+}
+
+/** 리소스 루트(apps/desktop)를 반환한다. dev에서 못 찾으면 app.getAppPath(). */
+export function getAppRoot(): string {
+ if (cachedAppRoot) return cachedAppRoot
+
+ const bases: string[] = [app.getAppPath(), process.cwd()]
+ // electron-vite는 main 번들을 CJS로 내보내므로 __dirname 사용 가능.
+ if (typeof __dirname === 'string' && __dirname) {
+ bases.push(__dirname)
}
- // 번들 없으면 시스템 PATH에서 찾기 (dev 환경 폴백)
- return 'sox'
+ for (const base of bases) {
+ let dir = base
+ for (let step = 0; step <= MAX_ROOT_WALK_UP; step++) {
+ if (looksLikeAppRoot(dir)) {
+ cachedAppRoot = dir
+ return dir
+ }
+ const parent = path.dirname(dir)
+ if (parent === dir) break
+ dir = parent
+ }
+ }
+
+ cachedAppRoot = app.getAppPath()
+ return cachedAppRoot
+}
+
+/** 테스트에서 경로 캐시를 초기화한다. */
+export function resetPathCache(): void {
+ cachedAppRoot = null
+ cachedSoxPath = undefined
}
/**
- * rec 실행 파일 경로 (SoX의 녹음 명령).
+ * 번들 리소스의 dev 경로.
+ * packaged → process.resourcesPath/, dev → <앱 루트>/resources/
+ */
+function devResourcePath(...segments: string[]): string {
+ return path.join(getAppRoot(), 'resources', ...segments)
+}
+
+function packagedResourcePath(...segments: string[]): string {
+ return path.join(process.resourcesPath, ...segments)
+}
+
+let cachedSoxPath: string | undefined
+
+/**
+ * SoX 실행 파일 경로. 번들된 실행 파일을 우선 사용한다.
+ * - packaged: resources/sox/sox(.exe)
+ * - dev: <앱 루트>/resources/sox/sox(.exe)
+ * 번들이 없으면 시스템 PATH의 `sox`로 폴백한다(설치 안내는 호출측에서 처리).
+ */
+export function getSoxPath(): string {
+ if (cachedSoxPath !== undefined) return cachedSoxPath
+
+ const soxBin = `sox${EXE_SUFFIX}`
+ const candidates = [
+ isPackaged()
+ ? packagedResourcePath('sox', soxBin)
+ : devResourcePath('sox', soxBin),
+ ]
+
+ for (const candidate of candidates) {
+ if (existsSync(candidate)) {
+ cachedSoxPath = candidate
+ return candidate
+ }
+ }
+
+ // 번들 없으면 시스템 PATH에서 찾기 (dev 환경 폴백)
+ cachedSoxPath = 'sox'
+ return cachedSoxPath
+}
+
+/**
+ * rec 실행 파일 경로 (SoX의 음 명령).
* node-record-lpcm16은 rec를 사용한다.
*/
export function getRecPath(): string {
const recBin = `rec${EXE_SUFFIX}`
const bundledRec = isPackaged()
- ? path.join(process.resourcesPath, 'sox', recBin)
- : path.join(app.getAppPath(), 'resources', 'sox', recBin)
+ ? packagedResourcePath('sox', recBin)
+ : devResourcePath('sox', recBin)
if (existsSync(bundledRec)) {
return bundledRec
@@ -52,52 +133,75 @@ export function getRecPath(): string {
return 'rec'
}
+/** sidecar 실행 방법 */
+export interface SidecarLaunch {
+ command: string
+ args: string[]
+ /** 어디에서 결정되었는지 (로그/진단용) */
+ source: 'bundled' | 'venv' | 'python'
+}
+
/**
* STT sidecar 실행 경로.
- * - dev: sidecar/.venv/bin/python (있으면) + sidecar/main.py, 없으면 시스템 python3
- * - production: sidecar/sidecar(.exe) (PyInstaller 빌드)
+ * - packaged: resources/sidecar/sidecar(.exe) — 없으면 명확한 에러 (조용한 폴백 금지)
+ * - dev: sidecar/.venv python + sidecar/main.py (없으면 시스템 python 폴백)
*/
-export function getSidecarCommand(): { command: string; args: string[] } {
+export function getSidecarCommand(): SidecarLaunch {
const sidecarBin = `sidecar${EXE_SUFFIX}`
if (isPackaged()) {
- const exePath = path.join(process.resourcesPath, 'sidecar', sidecarBin)
+ const exePath = packagedResourcePath('sidecar', sidecarBin)
if (existsSync(exePath)) {
- return { command: exePath, args: [] }
+ return { command: exePath, args: [], source: 'bundled' }
}
- // PyInstaller 번들 실패 대비 폴백
- const pyPath = path.join(process.resourcesPath, 'sidecar', 'main.py')
- const pythonCmd = process.platform === 'win32' ? 'python' : 'python3'
- return { command: pythonCmd, args: [pyPath] }
+ throw new D3ROError(
+ ErrorCode.STTSidecarSpawnFailed,
+ `번들된 STT 사이드카를 찾을 수 없습니다: ${exePath}. ` +
+ '설치 패키지에 sidecar 리소스가 누락되었습니다(로컬 전사 불가). ' +
+ '앱을 다시 설치하거나 개발 모드에서 `npm run sidecar:build`로 빌드하세요.',
+ )
}
// dev: venv 우선 → 없으면 시스템 python
- const sidecarDir = path.join(app.getAppPath(), 'sidecar')
+ const sidecarDir = path.join(getAppRoot(), 'sidecar')
const sidecarPath = path.join(sidecarDir, 'main.py')
+ if (!existsSync(sidecarPath)) {
+ throw new D3ROError(
+ ErrorCode.STTSidecarSpawnFailed,
+ `STT 사이드카 소스를 찾을 수 없습니다: ${sidecarPath}`,
+ )
+ }
+
const venvPython =
process.platform === 'win32'
? path.join(sidecarDir, '.venv', 'Scripts', 'python.exe')
: path.join(sidecarDir, '.venv', 'bin', 'python3')
if (existsSync(venvPython)) {
- return { command: venvPython, args: [sidecarPath] }
+ return { command: venvPython, args: [sidecarPath], source: 'venv' }
}
const pythonCmd = process.platform === 'win32' ? 'python' : 'python3'
- return { command: pythonCmd, args: [sidecarPath] }
+ return { command: pythonCmd, args: [sidecarPath], source: 'python' }
+}
+
+/** STT 사이드카 HTTP 기본 URL. sidecar는 IPv4 프백에만 바인딩한다. */
+export function getSidecarBaseUrl(port: number): string {
+ return loopbackUrl(port)
}
/**
* 번들된 Ollama 실행 파일 경로. 존재하지 않으면 null을 반환해 시스템 설치본 탐색으로 폴백.
* - Windows: ollama.exe
* - macOS/Linux: ollama
+ * (Ollama 탐색은 LocalLLMService 참조)
*/
export function getBundledOllamaPath(): string | null {
const ollamaBin = `ollama${EXE_SUFFIX}`
const bundled = isPackaged()
- ? path.join(process.resourcesPath, 'ollama', ollamaBin)
- : path.join(app.getAppPath(), 'resources', 'ollama', ollamaBin)
+ ? packagedResourcePath('ollama', ollamaBin)
+ : devResourcePath('ollama', ollamaBin)
return existsSync(bundled) ? bundled : null
}
@@ -106,35 +210,46 @@ export function getBundledOllamaPath(): string | null {
* 효과음 파일 경로.
*/
export function getSoundPath(filename: string): string {
- if (isPackaged()) {
- return path.join(process.resourcesPath, 'sounds', filename)
- }
- return path.join(app.getAppPath(), 'resources', 'sounds', filename)
+ return isPackaged()
+ ? packagedResourcePath('sounds', filename)
+ : devResourcePath('sounds', filename)
}
/**
* ffmpeg 실행 파일 경로.
- * - dev: @ffmpeg-installer/ffmpeg의 node_modules 경로 (플랫폼별 자동)
- * - production: extraResources로 번들된 경로
+ * 1) extraResources로 번들된 resources/ffmpeg/ffmpeg(.exe)
+ * 2) @ffmpeg-installer/ffmpeg npm 패키지(플랫폼별 정적 바이너리)
+ * 3) 시스템 PATH의 ffmpeg
*/
export function getFfmpegPath(): string {
const ffmpegBin = `ffmpeg${EXE_SUFFIX}`
- if (isPackaged()) {
- const bundled = path.join(process.resourcesPath, 'ffmpeg', ffmpegBin)
- if (existsSync(bundled)) {
- return bundled
- }
+ const bundled = isPackaged()
+ ? packagedResourcePath('ffmpeg', ffmpegBin)
+ : devResourcePath('ffmpeg', ffmpegBin)
+
+ if (existsSync(bundled)) {
+ return bundled
}
- // dev: @ffmpeg-installer/ffmpeg에서 제공하는 경로 (플랫폼별 자동 선택)
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
- const installer = require('@ffmpeg-installer/ffmpeg')
- return installer.path as string
+ const installer = require('@ffmpeg-installer/ffmpeg') as { path?: string }
+ const installerPath = installer.path
+ if (installerPath) {
+ // asar 내부 경로는 실행 파일로 쓸 수 없다 → unpacked 경로로 치환
+ const unpacked = installerPath.replace(
+ `${path.sep}app.asar${path.sep}`,
+ `${path.sep}app.asar.unpacked${path.sep}`,
+ )
+ if (existsSync(unpacked)) return unpacked
+ if (existsSync(installerPath)) return installerPath
+ }
} catch {
- return 'ffmpeg'
+ // 설치 패키지 없음 → 시스템 PATH 폴백
}
+
+ return 'ffmpeg'
}
/**
@@ -161,7 +276,7 @@ export function getWhisperModelsDir(): string {
export function getAppIconPath(): string | null {
const filename = process.platform === 'win32' ? 'icon.ico' : 'icon.png'
const candidate = isPackaged()
- ? path.join(process.resourcesPath, 'icons', filename)
- : path.join(app.getAppPath(), 'build', filename)
+ ? packagedResourcePath('icons', filename)
+ : path.join(getAppRoot(), 'build', filename)
return existsSync(candidate) ? candidate : null
-}
+}
\ No newline at end of file
diff --git a/apps/desktop/tests/main/services/STTManager.test.ts b/apps/desktop/tests/main/services/STTManager.test.ts
index 6d21482..293af8b 100644
--- a/apps/desktop/tests/main/services/STTManager.test.ts
+++ b/apps/desktop/tests/main/services/STTManager.test.ts
@@ -383,4 +383,44 @@ describe('STTManager & Multi-provider Drivers', () => {
expect(result.text).toBe('로컬 Whisper 폴백 성공')
})
})
+
+ describe('STTManager Live Partial (미리보기)', () => {
+ it('routes partial transcription through the local engine', async () => {
+ const mgr = getSTTManager()
+ mgr.setProvider('local')
+
+ const { getLocalSTTService } = await import('../../../src/main/services/LocalSTTService')
+ const partialSpy = vi
+ .spyOn(getLocalSTTService(), 'transcribePartial')
+ .mockResolvedValue('미리보기 텍스트')
+
+ await expect(mgr.transcribePartial(Buffer.alloc(32000))).resolves.toBe('미리보기 텍스트')
+ expect(partialSpy).toHaveBeenCalledOnce()
+ })
+
+ it('does not call the local engine for cloud providers', async () => {
+ const mgr = getSTTManager()
+ mgr.setProvider('groq')
+ mgr.setProviderConfig('groq', { apiKey: 'gsk-test' })
+
+ const { getLocalSTTService } = await import('../../../src/main/services/LocalSTTService')
+ const partialSpy = vi.spyOn(getLocalSTTService(), 'transcribePartial')
+
+ await expect(mgr.transcribePartial(Buffer.alloc(32000))).resolves.toBe('')
+ expect(partialSpy).not.toHaveBeenCalled()
+ })
+
+ it('warms up the local engine only for the local provider', async () => {
+ const mgr = getSTTManager()
+ const { getLocalSTTService } = await import('../../../src/main/services/LocalSTTService')
+ const warmSpy = vi.spyOn(getLocalSTTService(), 'warmUp').mockResolvedValue(true)
+
+ mgr.setProvider('local')
+ await expect(mgr.warmUpLocal()).resolves.toBe(true)
+
+ mgr.setProvider('deepgram')
+ await expect(mgr.warmUpLocal()).resolves.toBe(false)
+ expect(warmSpy).toHaveBeenCalledOnce()
+ })
+ })
})
diff --git a/apps/desktop/tests/main/utils/loopback.test.ts b/apps/desktop/tests/main/utils/loopback.test.ts
new file mode 100644
index 0000000..afda159
--- /dev/null
+++ b/apps/desktop/tests/main/utils/loopback.test.ts
@@ -0,0 +1,54 @@
+// tests/main/utils/loopback.test.ts
+// 로컬 엔진 URL 정규화 테스트.
+// Windows에서 localhost가 ::1로만 해석되어 Ollama/sidecar 연결이 실패했던 회귀를 고정한다.
+
+import { describe, it, expect } from 'vitest'
+import { loopbackUrl, normalizeLoopbackUrl } from '../../../src/main/utils/loopback'
+
+describe('normalizeLoopbackUrl', () => {
+ it('rewrites a bare localhost host to the IPv4 loopback', () => {
+ expect(normalizeLoopbackUrl('http://localhost:11434')).toBe('http://127.0.0.1:11434')
+ })
+
+ it('keeps the port and path', () => {
+ expect(normalizeLoopbackUrl('http://localhost:8000/v1')).toBe('http://127.0.0.1:8000/v1')
+ })
+
+ it('handles https and trailing dot host forms', () => {
+ expect(normalizeLoopbackUrl('https://localhost:5000/health')).toBe(
+ 'https://127.0.0.1:5000/health',
+ )
+ expect(normalizeLoopbackUrl('http://localhost.:1234')).toBe('http://127.0.0.1:1234')
+ })
+
+ it('leaves remote hosts untouched', () => {
+ expect(normalizeLoopbackUrl('https://api.openai.com/v1')).toBe('https://api.openai.com/v1')
+ expect(normalizeLoopbackUrl('http://192.168.0.10:11434')).toBe('http://192.168.0.10:11434')
+ expect(normalizeLoopbackUrl('http://127.0.0.1:11434')).toBe('http://127.0.0.1:11434')
+ })
+
+ it('does not treat lookalike hostnames as loopback', () => {
+ expect(normalizeLoopbackUrl('http://localhost.evil.com')).toBe('http://localhost.evil.com')
+ })
+
+ it('falls back to a textual rewrite when the URL is not parseable', () => {
+ // 잘못된 포트는 URL 파서가 던진다 → 정규식 폴백 경로를 탄다.
+ expect(normalizeLoopbackUrl('http://localhost:99999999')).toBe(
+ 'http://127.0.0.1:99999999',
+ )
+ })
+
+ it('passes through inputs without a recognizable host', () => {
+ expect(normalizeLoopbackUrl('localhost:11434')).toBe('localhost:11434')
+ })
+
+ it('returns empty input unchanged', () => {
+ expect(normalizeLoopbackUrl('')).toBe('')
+ })
+})
+
+describe('loopbackUrl', () => {
+ it('builds an IPv4 loopback URL for a port', () => {
+ expect(loopbackUrl(18765)).toBe('http://127.0.0.1:18765')
+ })
+})
diff --git a/apps/desktop/tests/main/utils/paths.test.ts b/apps/desktop/tests/main/utils/paths.test.ts
new file mode 100644
index 0000000..54be575
--- /dev/null
+++ b/apps/desktop/tests/main/utils/paths.test.ts
@@ -0,0 +1,97 @@
+// tests/main/utils/paths.test.ts
+// dev/packaged 경로 해석 테스트.
+//
+// 회귀 배경: electron-vite dev에서 app.getAppPath()가 `out/main`을 가리켜
+// sidecar/SoX가 존재하지 않는 경로로 잡혔고, 결과적으로 로컬 전사와 녹음이
+// 모두 실패했다(시스템 python/PATH 폴백). 여기서 그 해석을 고정한다.
+
+import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
+import { existsSync } from 'node:fs'
+import path from 'node:path'
+import { app } from 'electron'
+import {
+ getAppRoot,
+ getSidecarCommand,
+ getSidecarBaseUrl,
+ getSoxPath,
+ resetPathCache,
+} from '../../../src/main/utils/paths'
+
+const desktopDir = path.resolve(__dirname, '..', '..', '..')
+
+describe('paths (dev)', () => {
+ beforeEach(() => {
+ resetPathCache()
+ })
+
+ afterEach(() => {
+ vi.restoreAllMocks()
+ resetPathCache()
+ })
+
+ it('resolves the app root a level above the vite out/main bundle', () => {
+ // dev에서 app.getAppPath() = /out/main 이다.
+ vi.mocked(app.getAppPath).mockReturnValue(path.join(desktopDir, 'out', 'main'))
+ resetPathCache()
+
+ expect(getAppRoot()).toBe(desktopDir)
+ })
+
+ it('prefers the bundled SoX binary over the system PATH', () => {
+ expect(getSoxPath()).toContain(path.join('resources', 'sox', 'sox'))
+ expect(existsSync(getSoxPath())).toBe(true)
+ expect(getSoxPath()).not.toBe('sox')
+ })
+
+ it('uses the sidecar virtualenv python when it exists', () => {
+ const launch = getSidecarCommand()
+
+ expect(launch.source).toBe('venv')
+ expect(launch.command).toContain('.venv')
+ expect(launch.args[0]).toBe(path.join(desktopDir, 'sidecar', 'main.py'))
+ expect(existsSync(launch.command)).toBe(true)
+ })
+
+ it('builds the sidecar base URL on the IPv4 loopback', () => {
+ expect(getSidecarBaseUrl(18765)).toBe('http://127.0.0.1:18765')
+ })
+})
+
+describe('paths (packaged)', () => {
+ beforeEach(() => {
+ resetPathCache()
+ Object.defineProperty(app, 'isPackaged', { value: true, configurable: true })
+ })
+
+ afterEach(() => {
+ Object.defineProperty(app, 'isPackaged', { value: false, configurable: true })
+ vi.restoreAllMocks()
+ resetPathCache()
+ })
+
+ it('fails loudly when the packaged sidecar bundle is missing', () => {
+ Object.defineProperty(process, 'resourcesPath', {
+ value: path.join(desktopDir, 'definitely-not-bundled'),
+ configurable: true,
+ })
+
+ expect(() => getSidecarCommand()).toThrowError(/사이드카를 찾을 수 없습니다/)
+ })
+
+ it('uses the packaged sidecar executable when present', () => {
+ Object.defineProperty(process, 'resourcesPath', {
+ value: path.join(desktopDir, 'resources'),
+ configurable: true,
+ })
+
+ // resources/sox 는 커밋되어 있으므로 resources/sidecar/sidecar(.exe)도
+ // 같은 방식으로 배치된다. 존재하지 않는 플랫폼이면 번들 실패로 처리된다.
+ const exeSuffix = process.platform === 'win32' ? '.exe' : ''
+ const bundled = path.join(desktopDir, 'resources', 'sidecar', `sidecar${exeSuffix}`)
+ if (existsSync(bundled)) {
+ expect(getSidecarCommand().source).toBe('bundled')
+ } else {
+ expect(() => getSidecarCommand()).toThrow()
+ }
+ })
+})
diff --git a/apps/mobile-rn/android/app/build.gradle b/apps/mobile-rn/android/app/build.gradle
index 8f1c98a..c99b448 100644
--- a/apps/mobile-rn/android/app/build.gradle
+++ b/apps/mobile-rn/android/app/build.gradle
@@ -151,8 +151,8 @@ def versionSettingsValid = configuredVersionName != null &&
configuredVersionName ==~ strictSemver &&
configuredVersionCodeValue != null &&
configuredVersionCodeValue <= 2100000000L
-def resolvedVersionName = versionSettingsValid ? configuredVersionName : "1.2.0"
-def resolvedVersionCode = versionSettingsValid ? configuredVersionCodeValue.toInteger() : 1020001
+def resolvedVersionName = versionSettingsValid ? configuredVersionName : "1.3.0"
+def resolvedVersionCode = versionSettingsValid ? configuredVersionCodeValue.toInteger() : 1030001
def requiredReleaseSettings = [
D3RO_RELEASE_STORE_FILE: releaseStoreFilePath,
diff --git a/apps/mobile-rn/ios/D3ROVoice.xcodeproj/project.pbxproj b/apps/mobile-rn/ios/D3ROVoice.xcodeproj/project.pbxproj
index 2eaba84..81d4618 100644
--- a/apps/mobile-rn/ios/D3ROVoice.xcodeproj/project.pbxproj
+++ b/apps/mobile-rn/ios/D3ROVoice.xcodeproj/project.pbxproj
@@ -257,7 +257,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 1020001;
+ CURRENT_PROJECT_VERSION = 1030001;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = D3ROVoice/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
@@ -265,7 +265,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.2.0;
+ MARKETING_VERSION = 1.3.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
@@ -287,14 +287,14 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CURRENT_PROJECT_VERSION = 1020001;
+ CURRENT_PROJECT_VERSION = 1030001;
INFOPLIST_FILE = D3ROVoice/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 1.2.0;
+ MARKETING_VERSION = 1.3.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
diff --git a/apps/mobile-rn/metadata/android/en-US/changelogs/1030001.txt b/apps/mobile-rn/metadata/android/en-US/changelogs/1030001.txt
new file mode 100644
index 0000000..6c7095a
--- /dev/null
+++ b/apps/mobile-rn/metadata/android/en-US/changelogs/1030001.txt
@@ -0,0 +1 @@
+A stability and quality update. Recording and transcription failures now explain the cause and the fix, and app startup behaviour is more predictable.
diff --git a/apps/mobile-rn/metadata/android/ko-KR/changelogs/1030001.txt b/apps/mobile-rn/metadata/android/ko-KR/changelogs/1030001.txt
new file mode 100644
index 0000000..d071632
--- /dev/null
+++ b/apps/mobile-rn/metadata/android/ko-KR/changelogs/1030001.txt
@@ -0,0 +1 @@
+안정성과 품질 개선 업데이트입니다. 녹음과 전사가 실패할 때 원인과 해결 방법을 더 분명하게 안내하고, 앱 시작 동작을 정리했습니다.
diff --git a/apps/mobile-rn/package-lock.json b/apps/mobile-rn/package-lock.json
index 6d68878..dc24c46 100644
--- a/apps/mobile-rn/package-lock.json
+++ b/apps/mobile-rn/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@d3ro/mobile-rn",
- "version": "1.2.0",
+ "version": "1.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@d3ro/mobile-rn",
- "version": "1.2.0",
+ "version": "1.3.0",
"dependencies": {
"@d3ro/api-client": "file:../../packages/api-client",
"@d3ro/core": "file:../../packages/core",
@@ -62,7 +62,7 @@
},
"../..": {
"name": "d3ro-voice-monorepo",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"workspaces": [
"apps/desktop",
@@ -81,7 +81,7 @@
},
"../../packages/api-client": {
"name": "@d3ro/api-client",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"dependencies": {
"@d3ro/core": "*",
@@ -98,7 +98,7 @@
},
"../../packages/core": {
"name": "@d3ro/core",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"dependencies": {
"docx": "^9.6.1"
@@ -109,7 +109,7 @@
},
"../../packages/i18n": {
"name": "@d3ro/i18n",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"devDependencies": {
"@types/react": "^19.0.0"
@@ -120,7 +120,7 @@
},
"../../packages/ui-native": {
"name": "@d3ro/ui-native",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"devDependencies": {
"@types/react": "*"
diff --git a/apps/mobile-rn/package.json b/apps/mobile-rn/package.json
index 7ea08ee..4d33564 100644
--- a/apps/mobile-rn/package.json
+++ b/apps/mobile-rn/package.json
@@ -1,6 +1,6 @@
{
"name": "@d3ro/mobile-rn",
- "version": "1.2.0",
+ "version": "1.3.0",
"private": true,
"scripts": {
"android": "react-native run-android",
diff --git a/apps/web/package.json b/apps/web/package.json
index c6cbf8b..2aef4ab 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,6 +1,6 @@
{
"name": "@d3ro/web",
- "version": "1.2.0",
+ "version": "1.3.0",
"private": true,
"description": "D3RO Voice 웹 앱 — Next.js 기반 SaaS 인터페이스",
"scripts": {
diff --git a/apps/web/src/components/layout/sidebar.tsx b/apps/web/src/components/layout/sidebar.tsx
index bcae6ea..ef28ded 100644
--- a/apps/web/src/components/layout/sidebar.tsx
+++ b/apps/web/src/components/layout/sidebar.tsx
@@ -139,7 +139,7 @@ export function Sidebar(): React.ReactElement {
- v1.2.0
+ v1.3.0
diff --git a/docs/map/00-index.md b/docs/map/00-index.md
index 09002bf..bda0005 100644
--- a/docs/map/00-index.md
+++ b/docs/map/00-index.md
@@ -2,7 +2,7 @@
> Status: ACTIVE
> Last full audit: 2026-09-13
-> Scope: entire monorepo `D:/workspace/D3ROVoice` at product version `1.2.0`
+> Scope: entire monorepo `D:/workspace/D3ROVoice` at product version `1.3.0`
> Purpose: let any agent (or human) answer two questions in under a minute:
> 1. **What infrastructure exists?** (build, CI, services, APIs, data, packages, deploy)
> 2. **How far is each feature developed?** (per surface, with file anchors and status)
diff --git a/docs/map/02-infrastructure.md b/docs/map/02-infrastructure.md
index b17e236..11803d4 100644
--- a/docs/map/02-infrastructure.md
+++ b/docs/map/02-infrastructure.md
@@ -97,7 +97,7 @@ Per-app commands that matter:
| App | Commands |
|---|---|
-| desktop | `npm run dev --workspace=@d3ro/desktop`, `build`, `typecheck`, `test` (vitest, 1266 tests), playwright e2e |
+| desktop | `npm run dev --workspace=@d3ro/desktop`, `build`, `typecheck`, `test` (vitest), playwright e2e; local STT engine: `npm --prefix apps/desktop run sidecar:setup` then `sidecar:build` (PyInstaller → `sidecar-dist/sidecar`), full local Windows package: `npm --prefix apps/desktop run dist:win:full`; `setup:sox` re-downloads the bundled SoX |
| mobile-rn | `npm run typecheck:mobile` / `lint:mobile` / `test:mobile` (root), or `npm --prefix apps/mobile-rn run lint/typecheck/test`; android gradle builds, Maestro E2E |
| api-server | `dotnet build`, `dotnet test` (also `apps/api-server.Tests`) |
| web | `next build`, playwright e2e in `apps/web/e2e` |
@@ -136,7 +136,7 @@ See [`03-shared-packages.md`](./03-shared-packages.md). Summary:
### GitLab CI (`.gitlab-ci.yml`)
-Stages `validate → test → build → e2e → package → publish → deploy`. Primary pipeline for desktop Windows/macOS releases (Forgejo Generic Registry is the canonical updater feed; GitLab project 1172 is a legacy mirror) and production mobile releases (`mobile-production-release`, manual/protected). Admin NAS deploy job is intentionally **disabled**.
+Stages `validate → test → build → e2e → package → publish → deploy`. Primary pipeline for desktop Windows/macOS releases (Forgejo Generic Registry is the canonical updater feed; GitLab project 1172 is a legacy mirror) and production mobile releases (`mobile-production-release`, manual/protected). Admin NAS deploy job is intentionally **disabled**. `package-windows`/`package-macos` build the faster-whisper sidecar (`sidecar:setup` → `sidecar:build`) and run `scripts/ci/verify-sidecar-bundle.mjs` before electron-builder, so a release can never ship without the local STT engine.
### Forgejo Actions (`.forgejo/workflows/`)
@@ -190,11 +190,11 @@ Full detail: [`09-supabase-backend.md`](./09-supabase-backend.md).
| File | Purpose |
|---|---|
-| `release/product-version.json` | version `1.2.0`, `androidVersionCode`/`iosBuildNumber` `1020001`, releaseDate, desktop license keyId |
+| `release/product-version.json` | version `1.3.0`, `androidVersionCode`/`iosBuildNumber` `1030001`, releaseDate, desktop license keyId |
| `release/android-release-identity.json` | package `com.d3ro.voice`, Play app ID, app-signing SHA-256, upload cert SHA-256, evidence keyId, AdMob unit IDs |
| `release/desktop-license-public.pem` | Ed25519 public key for desktop offline licenses |
| `release/mobile-release-evidence-public.pem` | Ed25519 public key for mobile release evidence |
-| `apps/desktop/electron-builder.yml` | appId `com.d3ro.voice`, NSIS x64 (forced code signing), macOS DMG/ZIP arm64, generic Forgejo publish feed, asarUnpack native modules, extraResources (icons, sounds, sox, ollama) |
+| `apps/desktop/electron-builder.yml` | appId `com.d3ro.voice`, NSIS x64 (forced code signing), macOS DMG/ZIP arm64, generic Forgejo publish feed, asarUnpack native modules + `@ffmpeg-installer`, extraResources (icons, sounds, sox, **sidecar**, ffmpeg, ollama) |
| `apps/desktop/src/main/update-feed.ts` | Auto-update feed SSOT (canonical Forgejo + legacy GitLab mirror, channels) |
| `release/update-policy.json` | Update policy SSOT (channels, minimum supported version, forced update, delta/full, staged rollout, kill switch) |
| `apps/desktop/src/main/update-policy.ts` | Policy parsing/decision logic |
@@ -207,6 +207,8 @@ Version sync is enforced by `scripts/ci/sync-version.mjs` and `verify-release-me
## 10. Resources & tests
- `resources/sox/` — bundled Windows SoX (`sox.exe` + DLLs) for audio capture.
+- `resources/ffmpeg/` — optional bundled ffmpeg (CI or manual); `getFfmpegPath()` also resolves the `@ffmpeg-installer/ffmpeg` binary from `app.asar.unpacked`.
+- `apps/desktop/sidecar-dist/` — PyInstaller sidecar bundle consumed by `extraResources` (gitignored; built by `npm --prefix apps/desktop run sidecar:build`).
- `resources/icons/` — empty; electron-builder falls back to `build/icon.ico|png`.
- Desktop tests: `apps/desktop/tests/` (vitest unit + playwright e2e), `apps/desktop/test-results/`.
- Mobile tests: `apps/mobile-rn/__tests__/` (57 suites / 353 tests per mobile SSOT), `.maestro/` + `.maestro-output/` E2E evidence. Note: a full parallel Jest run can hit the 5s render timeout on slow machines; re-run the failing spec in isolation before treating it as a regression.
diff --git a/docs/map/04-desktop-app.md b/docs/map/04-desktop-app.md
index 8bf4167..dd6e5df 100644
--- a/docs/map/04-desktop-app.md
+++ b/docs/map/04-desktop-app.md
@@ -28,8 +28,8 @@ Singleton + `EventEmitter` pattern (`getXService()` accessors).
| Service | Purpose |
|---|---|
| `VoiceModeService` | Orchestrator: 9-state `RecognitionState` + 4-state `AudioState`, dual-condition flush, action queue. Events: session-started/completed/cancelled, transcription-update, audio-level, recognition/audio-state-changed, premium-llm-fallback, error |
-| `AudioCaptureService` | Mic PCM16 16kHz mono (SoX on Windows, node-record-lpcm16 elsewhere). Events: audio-data, audio-level, device-changed, started, stopped, error |
-| `LocalSTTService` | faster-whisper Python sidecar manager (state machine, dual-flush, model download/cancel) |
+| `AudioCaptureService` | Mic PCM16 16kHz mono (bundled SoX on Windows, node-record-lpcm16 elsewhere). Spawns hidden (`windowsHide`); a missing SoX fails with the exact fix command |
+| `LocalSTTService` | faster-whisper Python sidecar manager (state machine, dual-flush, model download/cancel, background warm-up, live partial transcription). Connects over IPv4 loopback (`getSidecarBaseUrl`) and fails fast with an actionable message when the bundled engine or virtualenv is missing |
| `HotkeyService` | uiohook-napi global hooking (dictation/hands-free/command/caption). Events: hotkey-pressed/released, double-press, error |
| `TextInsertService` | Clipboard save→set→Ctrl+V→restore via nut-js |
| `SoundEffectService` | Preloaded WAV feedback (start/stop/error/cancel/chime) |
@@ -37,7 +37,7 @@ Singleton + `EventEmitter` pattern (`getXService()` accessors).
### STT engine layer (`services/stt/`)
| File | Purpose |
|---|---|
-| `STTManager` | Dispatcher across local + 6 cloud providers, auto-fallback (events provider-changed, config-changed, fallback-to-local) |
+| `STTManager` | Dispatcher across local + 6 cloud providers, auto-fallback (events provider-changed, config-changed, fallback-to-local). `transcribePartial`/`warmUpLocal` route to the local engine only |
| `types.ts` | `ISTTDriver` contract |
| `audio-utils.ts` | `pcmToWav`, `createProbeWav` |
| `drivers/OpenAI|Groq|Deepgram|AssemblyAI|Google|Custom|D3ROCloud` | Provider drivers; `D3ROCloudDriver` uses Supabase access token |
@@ -184,6 +184,8 @@ DB schema (`src/main/db/schema.ts`, drizzle SQLite): `history`, `dictionary`, `s
- Core dictation/LLM/history pipeline: **implemented + tested** (~590 desktop tests; vitest + playwright).
- Cross-platform packaging: Windows NSIS (signed, `forceCodeSigning`), macOS DMG/ZIP arm64 (ad-hoc signing); auto-update via canonical Forgejo feed with update policy (`release/update-policy.json`).
- Local-first AI (SoX + faster-whisper sidecar + bundled Ollama) and cloud paths both present.
+- **Local STT is packaged** (`1.3.0`): `electron-builder.yml` `extraResources` copies `sidecar-dist/sidecar` → `resources/sidecar` and `resources/ffmpeg` → `resources/ffmpeg`; `scripts/ci/verify-sidecar-bundle.mjs` gates packaging. Build locally with `npm --prefix apps/desktop run sidecar:setup && npm --prefix apps/desktop run sidecar:build`. The sidecar stays in console mode so `stdout`/`stderr` reach the app log (UTF-8, line-buffered); a packaged sidecar **must** exist or startup fails loudly instead of silently falling back to a system Python.
+- All local engine URLs (`LocalSTTService`, `LocalLLMService`, `RAGService`, `OnlineLLMService`, `STTManager`) pass through `src/main/utils/loopback.ts`, which rewrites `localhost` to `127.0.0.1`, because some Windows hosts resolve `localhost` to IPv6 only and local engines bind IPv4.
- Meeting intelligence, RAG, voice conversation (local + Realtime), captions, file transcription: implemented.
- **Ad mediation**: `DirectHouseSponsorAdapter` performs real configurable REST bids; the other 9 adapters remain fail-closed stubs pending official SDKs (see `11-gap-backlog.md` GAP-ADS-01/02).
- Tier resolution now routes through `@d3ro/core/entitlement` (`resolveEntitlement`, `normalizeEntitlementTier`); `useLicenseState.isPro` includes `pro_plus`.
@@ -206,3 +208,5 @@ DB schema (`src/main/db/schema.ts`, drizzle SQLite): `history`, `dictionary`, `s
| Renderer shell / routes | `src/renderer/components/AppLayout.tsx` |
| Update feed SSOT | `src/main/update-feed.ts` |
| Update policy SSOT | `release/update-policy.json` + `src/main/update-policy.ts` |
+| Path/loopback resolution | `src/main/utils/paths.ts`, `src/main/utils/loopback.ts` |
+| Sidecar source / packaging | `sidecar/main.py`, `scripts/setup-sidecar.mjs`, `scripts/build-sidecar.mjs`, `scripts/ci/verify-sidecar-bundle.mjs` |
diff --git a/docs/map/10-feature-catalog.md b/docs/map/10-feature-catalog.md
index 02de900..b2c35af 100644
--- a/docs/map/10-feature-catalog.md
+++ b/docs/map/10-feature-catalog.md
@@ -16,11 +16,11 @@ Status quick-reference: `[x]` done+verified · `[~]` partial/unverified · `[ ]`
|---|---|---|---|---|---|---|
| CAP-01 | Push-to-talk dictation (hold/release) | [x] | [-] | [x] | [-] | Desktop `VoiceModeService`; mobile RecordScreen via app CTA/notification action (no global hotkey) |
| CAP-02 | Hands-free toggle dictation | [x] | [-] | [x] | [-] | Desktop double-press; mobile toggle |
-| CAP-03 | Live partial transcript while recording | [x] | [ ] | [ ] | [-] | Desktop `voice:partialTranscript` + recording-tip |
+| CAP-03 | Live partial transcript while recording | [x] | [ ] | [ ] | [-] | Desktop `voice:partialTranscript` + recording-tip; producer added in `1.3.0` (`VoiceModeService._runPartial` → `LocalSTTService.transcribePartial`, 1.5 s cadence / 7.5 s window, never inserted). The row was `[x]` before any producer existed. |
| CAP-04 | Recording waveform + level meter | [x] | [x] | [x] | [-] | Desktop 9-bar cos distribution; mobile audio level |
| CAP-05 | Device/mic selection | [x] | [ ] | [~] | [-] | Desktop config; mobile uses system default |
| CAP-06 | System/loopback audio capture | [x] | [-] | [ ] | [-] | Desktop only (caption source); mobile policy-limited |
-| CAP-07 | Local Whisper STT | [x] | [-] | [x] | [-] | Desktop sidecar; mobile on-device Whisper (supported devices) |
+| CAP-07 | Local Whisper STT | [x] | [-] | [x] | [-] | Desktop ships the faster-whisper sidecar (`resources/sidecar`, built by `sidecar:build`, verified by `scripts/ci/verify-sidecar-bundle.mjs`), warms it up at app start, and connects over IPv4 loopback; mobile on-device Whisper (supported devices) |
| CAP-08 | Cloud STT (multi-provider) | [x] | [x] | [x] | [x] | Desktop 6 providers + D3RO Cloud; web/mobile via `stt-proxy`; .NET internal gateway |
| CAP-09 | STT auto-fallback + fail-closed | [x] | [x] | [x] | [x] | `STTManager`; SSOT R-021/R-022 GREEN |
| CAP-10 | STT model download/management UI | [x] | [-] | [~] | [-] | Desktop model manager + onboarding; mobile bundled model |
@@ -191,7 +191,8 @@ Status quick-reference: `[x]` done+verified · `[~]` partial/unverified · `[ ]`
| INFRA-11 | Docker + NAS deploy | [x] | `docker-compose.nas.yml`, `scripts/deploy-nas.*` |
| INFRA-12 | Cloudflare edge + tunnel | [x] | `server/cloudflare-worker`, Cloudflare Tunnel `kd-nas` |
| INFRA-13 | Site deploy (Cloudflare Pages + GitHub Pages) | [x] | `.forgejo/workflows/deploy-site.yml`, `.github/workflows/deploy-site.yml` |
-| INFRA-15 | Update & release system | [x] | Canonical Forgejo feed + channels/policy (`release/update-policy.json`, `src/main/update-policy.ts`), canonical publisher `scripts/ci/publish-forgejo-release.mjs`, legacy GitLab mirror; `npm run release:metadata:test`. v1.1.0 was published to Forgejo on 2026-09-15; product version moved to `1.2.0` as a forward-fix with CI-only publication, a same-version re-release guard, and download centers that link the feed instead of repository paths. |
+| INFRA-15 | Update & release system | [x] | Canonical Forgejo feed + channels/policy (`release/update-policy.json`, `src/main/update-policy.ts`), canonical publisher `scripts/ci/publish-forgejo-release.mjs`, legacy GitLab mirror; `npm run release:metadata:test`. v1.1.0 was published to Forgejo on 2026-09-15; product version moved to `1.2.0` as a forward-fix with CI-only publication, a same-version re-release guard, and download centers that link the feed instead of repository paths. `1.3.0` (2026-09-18) carries the local-STT fixes; Windows publication still needs the CI signing secrets (`11` GAP-REL-02). |
+| INFRA-16 | Desktop STT engine packaging | [x] | `apps/desktop/scripts/setup-sidecar.mjs` + `build-sidecar.mjs`, `electron-builder.yml` `extraResources` (`sidecar-dist/sidecar` → `resources/sidecar`, `resources/ffmpeg`), and `scripts/ci/verify-sidecar-bundle.mjs` run in `package-windows`/`package-macos` before electron-builder. Verified on the real bundle: `sidecar.exe` + `_internal` including `faster_whisper/assets/silero_vad_v6.onnx`, plus a packaged-engine transcription round-trip on GPU. |
---
diff --git a/docs/map/11-gap-backlog.md b/docs/map/11-gap-backlog.md
index 69a4c41..161eb6e 100644
--- a/docs/map/11-gap-backlog.md
+++ b/docs/map/11-gap-backlog.md
@@ -22,7 +22,7 @@ Legend: `[ ]` open · `[~]` in progress · `[!]` blocked externally · `[x]` res
|---|---|---|---|---|
| GAP-QA-01 | Quality | Extreme Red Team: headful end-to-end bug hunting across real desktop Electron, Web Next.js, and CI pipelines. | `red_team_log.md`, `tests/e2e/red_team_cycle*.spec.ts`, `apps/web/e2e/red_team_cycle4_web.spec.ts` | `[x]` 2026-09-15: 18 scenarios executed, 14 defects caught and 100% resolved (infinite chunking loop DEF-008, IPC signature mismatch DEF-004, markdown editor typing rollback DEF-006, Web RSC Link serialization DEF-012, secret scanner lookahead DEF-013, etc.). All 18 scenarios GREEN with zero regressions. |
| GAP-REL-01 | Release | Official release publication to Forgejo and active public download center deployment. | `scripts/ci/publish-forgejo-release.mjs`, `apps/web/src/app/download/page.tsx`, `site/src/sections/Download.tsx`, `apps/web/e2e/red_team_cycle4_web.spec.ts` | `[~]` 2026-09-15: v1.1.0 release assets (`D3RO-Voice-Setup-1.1.0-x64.exe`, `.blockmap`, `latest.yml`, `update-policy.json`) published to canonical Forgejo registry and release hub. 2026-09-16: the published 1.1.0 installer carries no Authenticode signature, so it does not satisfy the release policy; product version moved to `1.2.0` and publication must come from CI with the signing gate GREEN. Download centers in `apps/web` (`/download`) and `site` (`#download`) link the canonical Forgejo feed. |
-| GAP-REL-02 | Release | Windows stable publication needs an external public-trust Authenticode PFX, its password, the exact signer subject, and a Forgejo token, none of which live in the repository. | `.forgejo/workflows/release.yml`, `.gitlab-ci.yml`, `scripts/ci/verify-windows-release-artifact.ps1` | `[!]` 2026-09-16: every publisher fails closed without `WIN_CSC_*` and `FORGEJO_TOKEN`; provide them as protected CI secrets, then re-run the `1.2.0` tag pipeline. |
+| GAP-REL-02 | Release | Windows stable publication needs an external public-trust Authenticode PFX, its password, the exact signer subject, and a Forgejo token, none of which live in the repository. | `.forgejo/workflows/release.yml`, `.gitlab-ci.yml`, `scripts/ci/verify-windows-release-artifact.ps1` | `[!]` 2026-09-16: every publisher fails closed without `WIN_CSC_*` and `FORGEJO_TOKEN`; provide them as protected CI secrets, then re-run the tag pipeline. Still open as of `1.3.0` (2026-09-18): the `v1.3.0` tag must be built by CI with the signing gate GREEN. Local packaging cannot produce a signed installer (`forceCodeSigning: true`). |
| GAP-ADS-01 | Ads | 9 of 10 desktop ad adapters still extend `UnavailableAdAdapter` (`provider_not_integrated`). | `apps/desktop/src/main/services/ads/*` | `[~]` 2026-09-13: `DirectHouseSponsorAdapter` is now a real configurable REST adapter (bid/impression/click/reward via `endpointUrl`; fail-closed when unconfigured; 22 unit tests GREEN). Remaining 9 need official SDKs/authenticated endpoints. |
| GAP-ADS-02 | Ads | Desktop mediation reward accounting is not wired to license quota (`claimReward` still returns no tokens). | `AdMediationEngine.ts`, `AppLayout.tsx` | Wire verified `reportRewardCompletion` to `LicenseService` quota after the direct sponsor endpoint exists. |
| GAP-ID-01 | Identity | Supabase, .NET JWT/SQLite, and the desktop offline license each had their own tier/role shape. | `@d3ro/core/entitlement`, `LicenseService`, `entitlement-context` | `[~]` 2026-09-13: canonical `EntitlementSnapshot` + `resolveEntitlement` added with tests; desktop tier normalization + `isPro` fixed. Full adoption tracked as GAP-ID-02. |
@@ -42,6 +42,12 @@ Legend: `[ ]` open · `[~]` in progress · `[!]` blocked externally · `[x]` res
| GAP-PUSH-02 | Push | Nothing triggered `send-push?mode=drain`; enqueued notifications never left the outbox. | `server/cloudflare-worker/src/push-drain.ts`, `wrangler.toml` | `[x]` 2026-09-13: Cloudflare Cron Trigger (`* * * * *`) drains the outbox; tests in CI. Requires `SUPABASE_URL` + `SUPABASE_SERVICE_ROLE_KEY` secret on the worker. |
| GAP-PUSH-03 | Push | Mobile/web clients register only `fcm`; no service worker subscription or APNs device token. | `apps/mobile-rn/src/features/notifications/*`, `apps/web` | Add web service worker + `pushManager.subscribe` (store JSON subscription) and iOS APNs token registration. |
| GAP-PUSH-04 | Push | Android still depends on FCM (`google-services.json`). | `apps/mobile-rn/android`, `send-push` | Decide: minimal Firebase project, or UnifiedPush/ntfy. See `docs/deployment/push-transport-without-firebase.md`. |
+| GAP-STT-01 | Local STT | Packaged desktop builds shipped **no** faster-whisper sidecar: `electron-builder.yml` had no `extraResources` entry for it and no pipeline job built it. Local transcription was impossible on any installed build; the app fell back to a system Python without the runtime. | `apps/desktop/electron-builder.yml`, `.gitlab-ci.yml`, `apps/desktop/scripts/build-sidecar.mjs` | `[x]` 2026-09-18: sidecar bundled via `extraResources` (`sidecar-dist/sidecar` → `resources/sidecar`), `sidecar:setup`/`sidecar:build` scripts, and `scripts/ci/verify-sidecar-bundle.mjs` fails packaging when the engine or its Silero VAD data is missing. Verified on the real 242 MB bundle. |
+| GAP-STT-02 | Local STT | Dev resolved the sidecar/SoX paths against the Vite output dir (`out/main`), so the sidecar ran a Python without faster-whisper and SoX recording died with `spawn sox ENOENT`. | `apps/desktop/src/main/utils/paths.ts`, `apps/desktop/tests/main/utils/paths.test.ts` | `[x]` 2026-09-18: app root is discovered by walking up for app markers; packaged mode fails loudly instead of silently falling back. Unit tests pin both branches. |
+| GAP-STT-03 | Local engines | On hosts where `localhost` resolves only to IPv6, every local engine call (STT sidecar and Ollama) was refused. Audio capture and local LLM appeared dead. | `apps/desktop/src/main/utils/loopback.ts`, `LocalSTTService`, `LocalLLMService`, `RAGService`, `OnlineLLMService`, `STTManager` | `[x]` 2026-09-18: loopback normalization to `127.0.0.1` for all local engine URLs; defaults updated; 9 unit tests. Verified against the live sidecar and Ollama on a host with an IPv6-only `localhost`. |
+| GAP-STT-04 | Local STT | Live partial transcript (`CAP-03`, `voice:partialTranscript`) was marked done but had **no producer**: the channel, popup UI, and preload existed, nothing ever emitted. | `apps/desktop/src/main/services/VoiceModeService.ts`, `LocalSTTService.transcribePartial`, `STTManager.transcribePartial` | `[x]` 2026-09-18: 1.5 s cadence over a 7.5 s trailing window, greedy decode, drained before the final transcription; never inserted. |
+| GAP-STT-05 | Local STT | The bundled sidecar lacked faster-whisper's Silero VAD data, so `vad_filter=true` transcription would have failed at runtime even with the engine bundled. | `apps/desktop/scripts/build-sidecar.mjs`, `scripts/ci/verify-sidecar-bundle.mjs` | `[x]` 2026-09-18: `--collect-all faster_whisper` plus a packaging-time presence check for `assets/silero_vad_v6.onnx`. |
+| GAP-STT-06 | Local STT | Decode settings were untuned: previous-text conditioning let repeated hallucinations compound, and no VAD parameters meant slow, uneven segments. | `apps/desktop/sidecar/main.py` | `[x]` 2026-09-18: `condition_on_previous_text=false`, bounded low-temperature fallback, `no_speech`/`compression_ratio`/`log_prob` thresholds, 300 ms silence trimming. Same transcript, ~5x faster on the reference machine (7.7 s audio: 1609 ms → 303 ms). |
---
@@ -97,7 +103,9 @@ These are the mobile SSOT rows still `[ ]` / `[~]`. Do not duplicate the full te
## 5. Quick "is X done?" lookup
-- **Desktop local dictation / LLM / history / meetings / RAG / conversation:** yes, tested. Ads: one real adapter, rest stubs.
+- **Desktop local dictation / LLM / history / meetings / RAG / conversation:** local
+ dictation works in dev **and** in packaged builds as of `1.3.0` (engine bundled, paths
+ fixed, IPv4 loopback). Ads: one real adapter, rest stubs.
- **Web console:** yes, feature-complete for server-shared data; knowledge upload/search and team feed implemented.
- **Mobile:** code complete for most flows and tested locally; blocked mainly by external store/console gates, plus a11y and some E2E depth.
- **Backend:** fail-closed AI proxies, RLS, billing, ads SSV implemented; push transports (FCM + webpush + APNs) and cron drain implemented.
diff --git a/memory/project_status.md b/memory/project_status.md
index 8e09510..e634eb0 100644
--- a/memory/project_status.md
+++ b/memory/project_status.md
@@ -1,5 +1,52 @@
# D3RO-VOICE 프로젝트 현황
+## v1.3.0 — 로컬 전사가 실제로 동작하게 (2026-09-18) 🎙️
+
+설치본에서 로컬 전사가 **한 번도 성공한 적 없던** 원인을 끝까지 추적해 수정. 엔진 자체는
+정상이었고, 배선(경로/루프백/패키징)이 전부 어긋나 있었다.
+
+**원인 (실측 근거)**
+1. **패키지에 사이드카가 없었다**: `electron-builder.yml` extraResources에 sidecar 항목이
+ 없었고 CIS도 PyInstaller 빌드를 하지 않았다. 설치본 `resources/`에는 icons/sounds/sox만
+ 존재 → 항상 faster-whisper 없는 시스템 Python으로 폴백 → 즉사.
+2. **dev 경로 해석 실패**: electron-vite는 `out/main/index.js`를 직접 띄워 `app.getAppPath()`가
+ `apps/desktop/out/main`이 된다. 그래서 `out/main/sidecar/main.py`(없음) + 시스템 Python 3.14,
+ SoX도 `spawn sox ENOENT`. 실제 venv(`apps/desktop/sidecar/.venv`, py3.11 + faster-whisper 1.2.1)는
+ 한 번도 사용되지 않았다.
+3. **localhost = ::1 only**: 이 호스트는 `localhost`가 IPv6로만 해석된다. 사이드카(uvicorn)와
+ Ollama는 127.0.0.1에만 바인딩 → `http://localhost:...` 요청 전부 ECONNREFUSED. STT와 로컬 LLM이
+ 동시에 죽어 있었다.
+4. **VAD 데이터 누락**: faster-whisper의 `assets/silero_vad_v6.onnx`를 PyInstaller가 수집하지
+ 않아, 사이드카를 번들했더라도 VAD 사용 시 런타임 실패했을 것.
+5. **CAP-03(실시간 부분 전사)은 프로듀서가 없었다**: 채널/팝업/preload는 있는데 emit하는 코드가 0.
+
+**수정**
+- `paths.ts`: 앱 루트를 마커(`sidecar/main.py`, `resources/sox`)로 탐색. 패키지 모드에서 사이드카가
+ 없으면 조용히 폴백하지 않고 즉시 실패(명확한 조치 안내).
+- `loopback.ts`: `localhost` → `127.0.0.1` 정규화 (LocalSTT/LocalLLM/RAG/OnlineLLM/STTManager,
+ ConfigService 기본값). 단위 테스트 9개.
+- 패키징: extraResources에 `sidecar-dist/sidecar → resources/sidecar`, `resources/ffmpeg → ffmpeg`,
+ ffmpeg는 `@ffmpeg-installer` 의존성 + asarUnpack. `setup-sidecar.mjs`/`build-sidecar.mjs` +
+ `scripts/ci/verify-sidecar-bundle.mjs`(VAD onnx 존재 검증) → CI `package-windows/macos`에 추가.
+ 실측 번들: sidecar.exe 9.4MB + `_internal` 242MB, GPU 전사 왕복 성공.
+- 사이드카: 모델 재사용, `--collect-all faster_whisper`/`ctranslate2`, console 모드 유지(로그 수집) +
+ spawn `windowsHide`, UTF-8 강제(PYTHONIOENCODING/`reconfigure`), 줄 단위 로그 파이프.
+- 성능/정합성: `condition_on_previous_text=False`, 온도 폴백 제한([0.0,0.2,0.4]),
+ no_speech/compression/log_prob 임계, VAD `min_silence_duration_ms=300`, `cpu_threads` 8 상한.
+ **7.7초 오디오: 1609ms → 303ms (약 5배)**, 전사문 동일.
+- 체감: 앱 시작 시 `warmUpLocal()`로 sidecar+모델 예열(bootstrap `stt-warmup`), 녹음 중 1.5초마다
+ 최근 7.5초 창 부분 전사 → RecordingTip 미리보기(삽입되지 않음, 최종 전사 전에 drain).
+
+**검증**: typecheck 0, 변경 파일 lint 0, 신규 테스트 18개 GREEN(loopback 9 / paths 6 / STTManager 3 —
+기존 361건 실패는 better-sqlite3가 Electron ABI(130)로 빌드되어 Node 23(131)에서 로드 실패하는
+**선행 환경 문제**로 본 변경과 무관). 실제 사이드카 통합 테스트(실제 PCM 7.7초)로 최종 전사/부분
+전사 확인. electron-builder `--dir`로 패키지 산출물에 sidecar/_internal/VAD/ffmpeg/sox 존재 확인.
+
+**미해결(외부)**: Windows 공개 서명(WIN_CSC_*)이 없어 로컬에서 서명된 설치본을 만들 수 없다 →
+`v1.3.0` 태그는 CI가 서명 게이트와 함께 빌드해야 한다(`11` GAP-REL-02).
+
+---
+
## D3RO NAS Docker 클라우드 서비스 배포 체계 구축 (2026-08-19 /goal) 🚀
D3RO Voice의 클라우드 API 백엔드, AI 프록시, 관리자 백오피스(BackOffice)를 Synology, QNAP, Linux 등 NAS 환경에 Docker 컨테이너로 손쉽게 배포하고 운영할 수 있는 완전 자동화 인프라를 구축 완료.
diff --git a/package-lock.json b/package-lock.json
index 832c0b8..3e672a8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "d3ro-voice-monorepo",
- "version": "1.2.0",
+ "version": "1.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "d3ro-voice-monorepo",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"workspaces": [
"apps/desktop",
@@ -25,7 +25,7 @@
},
"apps/admin": {
"name": "@d3ro/admin",
- "version": "1.2.0",
+ "version": "1.3.0",
"dependencies": {
"@d3ro/api-client": "*",
"@d3ro/core": "*",
@@ -109,7 +109,7 @@
},
"apps/desktop": {
"name": "@d3ro/desktop",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"dependencies": {
"@d3ro/core": "*",
@@ -118,6 +118,7 @@
"@electron-toolkit/utils": "^4.0.0",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0",
+ "@ffmpeg-installer/ffmpeg": "^1.1.0",
"@mui/material": "^7.0.0",
"@nut-tree-fork/nut-js": "^4.2.6",
"@supabase/supabase-js": "^2.45.0",
@@ -154,62 +155,9 @@
"@rollup/rollup-win32-x64-msvc": "^4.60.1"
}
},
- "apps/mobile-rn": {
- "name": "@d3ro/mobile-rn",
- "version": "1.2.0",
- "extraneous": true,
- "dependencies": {
- "@d3ro/api-client": "file:../../packages/api-client",
- "@d3ro/core": "file:../../packages/core",
- "@d3ro/i18n": "file:../../packages/i18n",
- "@d3ro/ui-native": "file:../../packages/ui-native",
- "@noble/hashes": "^1.8.0",
- "@react-native-async-storage/async-storage": "^2.1.0",
- "@react-native-documents/picker": "12.0.2",
- "@react-navigation/bottom-tabs": "^7.3.0",
- "@react-navigation/native": "^7.1.0",
- "@react-navigation/native-stack": "^7.3.0",
- "@supabase/supabase-js": "^2.45.0",
- "react": "19.2.3",
- "react-native": "^0.85.0",
- "react-native-file-access": "^4.0.3",
- "react-native-gesture-handler": "^2.24.0",
- "react-native-google-mobile-ads": "16.3.4",
- "react-native-iap": "^16.3.1",
- "react-native-inappbrowser-reborn": "^3.7.0",
- "react-native-keychain": "^9.2.0",
- "react-native-nitro-modules": "^0.36.5",
- "react-native-nitro-sound": "^0.2.19",
- "react-native-safe-area-context": "^5.5.2",
- "react-native-screens": "^4.11.0",
- "react-native-url-polyfill": "^2.0.0",
- "whisper.rn": "0.7.2"
- },
- "devDependencies": {
- "@babel/core": "^7.25.2",
- "@babel/preset-env": "^7.25.3",
- "@babel/runtime": "^7.25.0",
- "@react-native-community/cli": "20.1.0",
- "@react-native-community/cli-platform-android": "20.1.0",
- "@react-native-community/cli-platform-ios": "20.1.0",
- "@react-native/babel-preset": "0.85.0",
- "@react-native/jest-preset": "0.85.0",
- "@react-native/metro-config": "0.85.0",
- "@react-native/typescript-config": "0.85.0",
- "@types/react": "^19.2.0",
- "@types/react-test-renderer": "^19.1.0",
- "jest": "^29.6.3",
- "prettier": "2.8.8",
- "react-test-renderer": "19.2.3",
- "typescript": "^5.8.3"
- },
- "engines": {
- "node": "^22.21.0 || ^24.3.0 || >=25.0.0"
- }
- },
"apps/web": {
"name": "@d3ro/web",
- "version": "1.2.0",
+ "version": "1.3.0",
"dependencies": {
"@d3ro/api-client": "*",
"@d3ro/core": "*",
@@ -344,20 +292,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-annotate-as-pure": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz",
- "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/types": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-compilation-targets": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
@@ -383,88 +317,6 @@
"semver": "bin/semver.js"
}
},
- "node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz",
- "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.29.7",
- "@babel/helper-member-expression-to-functions": "^7.29.7",
- "@babel/helper-optimise-call-expression": "^7.29.7",
- "@babel/helper-replace-supers": "^7.29.7",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7",
- "@babel/traverse": "^7.29.7",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-create-regexp-features-plugin": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz",
- "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.29.7",
- "regexpu-core": "^6.3.1",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-define-polyfill-provider": {
- "version": "0.6.8",
- "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz",
- "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-compilation-targets": "^7.28.6",
- "@babel/helper-plugin-utils": "^7.28.6",
- "debug": "^4.4.3",
- "lodash.debounce": "^4.0.8",
- "resolve": "^1.22.11"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
"node_modules/@babel/helper-globals": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
@@ -474,21 +326,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-member-expression-to-functions": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz",
- "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/traverse": "^7.29.7",
- "@babel/types": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-module-imports": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
@@ -519,20 +356,6 @@
"@babel/core": "^7.0.0"
}
},
- "node_modules/@babel/helper-optimise-call-expression": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz",
- "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/types": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-plugin-utils": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
@@ -543,59 +366,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-remap-async-to-generator": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz",
- "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.29.7",
- "@babel/helper-wrap-function": "^7.29.7",
- "@babel/traverse": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-replace-supers": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz",
- "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-member-expression-to-functions": "^7.29.7",
- "@babel/helper-optimise-call-expression": "^7.29.7",
- "@babel/traverse": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz",
- "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/traverse": "^7.29.7",
- "@babel/types": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-string-parser": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
@@ -623,22 +393,6 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-wrap-function": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz",
- "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/template": "^7.29.7",
- "@babel/traverse": "^7.29.7",
- "@babel/types": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helpers": {
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
@@ -667,133 +421,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/@babel/plugin-proposal-export-default-from": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.29.7.tgz",
- "integrity": "sha512-p+G5BNXDcy3bOXplhY4HybQ1GxH3i2Tppmdm/3epyRu2VgJJZuUlZ61MqRTg582Q7ZLBdP7fePYvsumSEkMxcQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-dynamic-import": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
- "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-export-default-from": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.29.7.tgz",
- "integrity": "sha512-foag0BB37ROhdeIX9O8G0jX7hw0UekJc04cHMrYLOnrErsnBKqJGHJ8eDRpoCFZBvEPPygmmtw4qyU97qa4oOw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-flow": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.29.7.tgz",
- "integrity": "sha512-ajMX6QPcyomotqwpzhkYGxcK2i/us0rs1Qo9QvUpa+Fca0FTmqrzKrctoIYLMxcOhGZldGT/BAVkRGTWBiR8gQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-jsx": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz",
- "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-typescript": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz",
- "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
"node_modules/@babel/plugin-transform-arrow-functions": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz",
@@ -810,318 +437,6 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/plugin-transform-async-generator-functions": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz",
- "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7",
- "@babel/helper-remap-async-to-generator": "^7.29.7",
- "@babel/traverse": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-async-to-generator": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz",
- "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-module-imports": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7",
- "@babel/helper-remap-async-to-generator": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-block-scoping": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz",
- "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-class-properties": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz",
- "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-classes": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz",
- "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.29.7",
- "@babel/helper-compilation-targets": "^7.29.7",
- "@babel/helper-globals": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7",
- "@babel/helper-replace-supers": "^7.29.7",
- "@babel/traverse": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-destructuring": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz",
- "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7",
- "@babel/traverse": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-flow-strip-types": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.29.7.tgz",
- "integrity": "sha512-wRHeUjUjCZnMHmiO5bRgjFLcoEh7JyTdByOW11ahhwNa4V0bmeGEaIvt51yq0zQp2yWIpqfxXXPyUP6GFJZHOQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7",
- "@babel/plugin-syntax-flow": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-for-of": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz",
- "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-modules-commonjs": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz",
- "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-module-transforms": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz",
- "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz",
- "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-optional-catch-binding": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz",
- "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-optional-chaining": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz",
- "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-private-methods": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz",
- "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-private-property-in-object": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz",
- "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.29.7",
- "@babel/helper-create-class-features-plugin": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-react-display-name": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.29.7.tgz",
- "integrity": "sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-react-jsx": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz",
- "integrity": "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.29.7",
- "@babel/helper-module-imports": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7",
- "@babel/plugin-syntax-jsx": "^7.29.7",
- "@babel/types": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
"node_modules/@babel/plugin-transform-react-jsx-self": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz",
@@ -1154,110 +469,6 @@
"@babel/core": "^7.0.0-0"
}
},
- "node_modules/@babel/plugin-transform-regenerator": {
- "version": "7.29.8",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.8.tgz",
- "integrity": "sha512-0UpIXPtdDtMXfnV2OJAVMLpj3H/92vmkA6lpSRakmycJvj3VUy6Xs1dM8tXRugupykr5WB+LpiVl0J8LMVg2mg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-runtime": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz",
- "integrity": "sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-module-imports": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7",
- "babel-plugin-polyfill-corejs2": "^0.4.14",
- "babel-plugin-polyfill-corejs3": "^0.13.0",
- "babel-plugin-polyfill-regenerator": "^0.6.5",
- "semver": "^6.3.1"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-runtime/node_modules/babel-plugin-polyfill-corejs3": {
- "version": "0.13.0",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz",
- "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.6.5",
- "core-js-compat": "^3.43.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/@babel/plugin-transform-runtime/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/plugin-transform-typescript": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz",
- "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-annotate-as-pure": "^7.29.7",
- "@babel/helper-create-class-features-plugin": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7",
- "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7",
- "@babel/plugin-syntax-typescript": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-transform-unicode-regex": {
- "version": "7.29.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz",
- "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-create-regexp-features-plugin": "^7.29.7",
- "@babel/helper-plugin-utils": "^7.29.7"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
"node_modules/@babel/runtime": {
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz",
@@ -2488,25 +1699,132 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
- "node_modules/@hapi/hoek": {
- "version": "9.3.0",
- "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
- "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==",
- "license": "BSD-3-Clause",
+ "node_modules/@ffmpeg-installer/darwin-arm64": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-arm64/-/darwin-arm64-4.1.5.tgz",
+ "integrity": "sha512-hYqTiP63mXz7wSQfuqfFwfLOfwwFChUedeCVKkBtl/cliaTM7/ePI9bVzfZ2c+dWu3TqCwLDRWNSJ5pqZl8otA==",
+ "cpu": [
+ "arm64"
+ ],
+ "hasInstallScript": true,
+ "license": "https://git.ffmpeg.org/gitweb/ffmpeg.git/blob_plain/HEAD:/LICENSE.md",
"optional": true,
- "peer": true
+ "os": [
+ "darwin"
+ ]
},
- "node_modules/@hapi/topo": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz",
- "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==",
- "license": "BSD-3-Clause",
+ "node_modules/@ffmpeg-installer/darwin-x64": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@ffmpeg-installer/darwin-x64/-/darwin-x64-4.1.0.tgz",
+ "integrity": "sha512-Z4EyG3cIFjdhlY8wI9aLUXuH8nVt7E9SlMVZtWvSPnm2sm37/yC2CwjUzyCQbJbySnef1tQwGG2Sx+uWhd9IAw==",
+ "cpu": [
+ "x64"
+ ],
+ "hasInstallScript": true,
+ "license": "LGPL-2.1",
"optional": true,
- "peer": true,
- "dependencies": {
- "@hapi/hoek": "^9.0.0"
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@ffmpeg-installer/ffmpeg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@ffmpeg-installer/ffmpeg/-/ffmpeg-1.1.0.tgz",
+ "integrity": "sha512-Uq4rmwkdGxIa9A6Bd/VqqYbT7zqh1GrT5/rFwCwKM70b42W5gIjWeVETq6SdcL0zXqDtY081Ws/iJWhr1+xvQg==",
+ "license": "LGPL-2.1",
+ "optionalDependencies": {
+ "@ffmpeg-installer/darwin-arm64": "4.1.5",
+ "@ffmpeg-installer/darwin-x64": "4.1.0",
+ "@ffmpeg-installer/linux-arm": "4.1.3",
+ "@ffmpeg-installer/linux-arm64": "4.1.4",
+ "@ffmpeg-installer/linux-ia32": "4.1.0",
+ "@ffmpeg-installer/linux-x64": "4.1.0",
+ "@ffmpeg-installer/win32-ia32": "4.1.0",
+ "@ffmpeg-installer/win32-x64": "4.1.0"
}
},
+ "node_modules/@ffmpeg-installer/linux-arm": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm/-/linux-arm-4.1.3.tgz",
+ "integrity": "sha512-NDf5V6l8AfzZ8WzUGZ5mV8O/xMzRag2ETR6+TlGIsMHp81agx51cqpPItXPib/nAZYmo55Bl2L6/WOMI3A5YRg==",
+ "cpu": [
+ "arm"
+ ],
+ "hasInstallScript": true,
+ "license": "GPLv3",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@ffmpeg-installer/linux-arm64": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-arm64/-/linux-arm64-4.1.4.tgz",
+ "integrity": "sha512-dljEqAOD0oIM6O6DxBW9US/FkvqvQwgJ2lGHOwHDDwu/pX8+V0YsDL1xqHbj1DMX/+nP9rxw7G7gcUvGspSoKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "hasInstallScript": true,
+ "license": "GPLv3",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@ffmpeg-installer/linux-ia32": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-ia32/-/linux-ia32-4.1.0.tgz",
+ "integrity": "sha512-0LWyFQnPf+Ij9GQGD034hS6A90URNu9HCtQ5cTqo5MxOEc7Rd8gLXrJvn++UmxhU0J5RyRE9KRYstdCVUjkNOQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "hasInstallScript": true,
+ "license": "GPLv3",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@ffmpeg-installer/linux-x64": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@ffmpeg-installer/linux-x64/-/linux-x64-4.1.0.tgz",
+ "integrity": "sha512-Y5BWhGLU/WpQjOArNIgXD3z5mxxdV8c41C+U15nsE5yF8tVcdCGet5zPs5Zy3Ta6bU7haGpIzryutqCGQA/W8A==",
+ "cpu": [
+ "x64"
+ ],
+ "hasInstallScript": true,
+ "license": "GPLv3",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@ffmpeg-installer/win32-ia32": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-ia32/-/win32-ia32-4.1.0.tgz",
+ "integrity": "sha512-FV2D7RlaZv/lrtdhaQ4oETwoFUsUjlUiasiZLDxhEUPdNDWcH1OU9K1xTvqz+OXLdsmYelUDuBS/zkMOTtlUAw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "GPLv3",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@ffmpeg-installer/win32-x64": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/@ffmpeg-installer/win32-x64/-/win32-x64-4.1.0.tgz",
+ "integrity": "sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "GPLv3",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
@@ -4481,826 +3799,6 @@
"url": "https://opencollective.com/popperjs"
}
},
- "node_modules/@react-native-community/cli": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-20.1.0.tgz",
- "integrity": "sha512-441WsVtRe4nGJ9OzA+QMU1+22lA6Q2hRWqqIMKD0wjEMLqcSfOZyu2UL9a/yRpL/dRpyUsU4n7AxqKfTKO/Csg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-clean": "20.1.0",
- "@react-native-community/cli-config": "20.1.0",
- "@react-native-community/cli-doctor": "20.1.0",
- "@react-native-community/cli-server-api": "20.1.0",
- "@react-native-community/cli-tools": "20.1.0",
- "@react-native-community/cli-types": "20.1.0",
- "commander": "^9.4.1",
- "deepmerge": "^4.3.0",
- "execa": "^5.0.0",
- "find-up": "^5.0.0",
- "fs-extra": "^8.1.0",
- "graceful-fs": "^4.1.3",
- "picocolors": "^1.1.1",
- "prompts": "^2.4.2",
- "semver": "^7.5.2"
- },
- "bin": {
- "rnc-cli": "build/bin.js"
- },
- "engines": {
- "node": ">=20.19.4"
- }
- },
- "node_modules/@react-native-community/cli-config-android": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-config-android/-/cli-config-android-20.1.0.tgz",
- "integrity": "sha512-3A01ZDyFeCALzzPcwP/fleHoP3sGNq1UX7FzxkTrOFX8RRL9ntXNXQd27E56VU4BBxGAjAJT4Utw8pcOjJceIA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-tools": "20.1.0",
- "fast-glob": "^3.3.2",
- "fast-xml-parser": "^4.4.1",
- "picocolors": "^1.1.1"
- }
- },
- "node_modules/@react-native-community/cli-config-android/node_modules/@react-native-community/cli-tools": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-20.1.0.tgz",
- "integrity": "sha512-/YmzHGOkY6Bgrv4OaA1L8rFqsBlQd1EB2/ipAoKPiieV0EcB5PUamUSuNeFU3sBZZTYQCUENwX4wgOHgFUlDnQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@vscode/sudo-prompt": "^9.0.0",
- "appdirsjs": "^1.2.4",
- "execa": "^5.0.0",
- "find-up": "^5.0.0",
- "launch-editor": "^2.9.1",
- "mime": "^2.4.1",
- "ora": "^5.4.1",
- "picocolors": "^1.1.1",
- "prompts": "^2.4.2",
- "semver": "^7.5.2"
- }
- },
- "node_modules/@react-native-community/cli-config-android/node_modules/execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
- }
- },
- "node_modules/@react-native-community/cli-config-android/node_modules/get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli-config-android/node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli-config-android/node_modules/npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "path-key": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@react-native-community/cli-config-apple": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-config-apple/-/cli-config-apple-20.1.0.tgz",
- "integrity": "sha512-n6JVs8Q3yxRbtZQOy05ofeb1kGtspGN3SgwPmuaqvURF9fsuS7c4/9up2Kp9C+1D2J1remPJXiZLNGOcJvfpOA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-tools": "20.1.0",
- "execa": "^5.0.0",
- "fast-glob": "^3.3.2",
- "picocolors": "^1.1.1"
- }
- },
- "node_modules/@react-native-community/cli-config-apple/node_modules/@react-native-community/cli-tools": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-20.1.0.tgz",
- "integrity": "sha512-/YmzHGOkY6Bgrv4OaA1L8rFqsBlQd1EB2/ipAoKPiieV0EcB5PUamUSuNeFU3sBZZTYQCUENwX4wgOHgFUlDnQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@vscode/sudo-prompt": "^9.0.0",
- "appdirsjs": "^1.2.4",
- "execa": "^5.0.0",
- "find-up": "^5.0.0",
- "launch-editor": "^2.9.1",
- "mime": "^2.4.1",
- "ora": "^5.4.1",
- "picocolors": "^1.1.1",
- "prompts": "^2.4.2",
- "semver": "^7.5.2"
- }
- },
- "node_modules/@react-native-community/cli-config-apple/node_modules/execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
- }
- },
- "node_modules/@react-native-community/cli-config-apple/node_modules/get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli-config-apple/node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli-config-apple/node_modules/npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "path-key": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@react-native-community/cli-platform-android": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-20.1.0.tgz",
- "integrity": "sha512-TeHPDThOwDppQRpndm9kCdRCBI8AMy3HSIQ+iy7VYQXL5BtZ5LfmGdusoj7nVN/ZGn0Lc6Gwts5qowyupXdeKg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-config-android": "20.1.0",
- "@react-native-community/cli-tools": "20.1.0",
- "execa": "^5.0.0",
- "logkitty": "^0.7.1",
- "picocolors": "^1.1.1"
- }
- },
- "node_modules/@react-native-community/cli-platform-android/node_modules/@react-native-community/cli-tools": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-20.1.0.tgz",
- "integrity": "sha512-/YmzHGOkY6Bgrv4OaA1L8rFqsBlQd1EB2/ipAoKPiieV0EcB5PUamUSuNeFU3sBZZTYQCUENwX4wgOHgFUlDnQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@vscode/sudo-prompt": "^9.0.0",
- "appdirsjs": "^1.2.4",
- "execa": "^5.0.0",
- "find-up": "^5.0.0",
- "launch-editor": "^2.9.1",
- "mime": "^2.4.1",
- "ora": "^5.4.1",
- "picocolors": "^1.1.1",
- "prompts": "^2.4.2",
- "semver": "^7.5.2"
- }
- },
- "node_modules/@react-native-community/cli-platform-android/node_modules/execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
- }
- },
- "node_modules/@react-native-community/cli-platform-android/node_modules/get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli-platform-android/node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli-platform-android/node_modules/npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "path-key": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@react-native-community/cli-platform-ios": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-20.1.0.tgz",
- "integrity": "sha512-XN7Da9z4WsJxtqVtEzY8q2bv22OsvzaFP5zy5+phMWNoJlU4lf7IvBSxqGYMpQ9XhYP7arDw5vmW4W34s06rnA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-platform-apple": "20.1.0"
- }
- },
- "node_modules/@react-native-community/cli-platform-ios/node_modules/@react-native-community/cli-platform-apple": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-20.1.0.tgz",
- "integrity": "sha512-0ih1hrYezSM2cuOlVnwBEFtMwtd8YgpTLmZauDJCv50rIumtkI1cQoOgLoS4tbPCj9U/Vn2a9BFH0DLFOOIacg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-config-apple": "20.1.0",
- "@react-native-community/cli-tools": "20.1.0",
- "execa": "^5.0.0",
- "fast-xml-parser": "^4.4.1",
- "picocolors": "^1.1.1"
- }
- },
- "node_modules/@react-native-community/cli-platform-ios/node_modules/@react-native-community/cli-tools": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-20.1.0.tgz",
- "integrity": "sha512-/YmzHGOkY6Bgrv4OaA1L8rFqsBlQd1EB2/ipAoKPiieV0EcB5PUamUSuNeFU3sBZZTYQCUENwX4wgOHgFUlDnQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@vscode/sudo-prompt": "^9.0.0",
- "appdirsjs": "^1.2.4",
- "execa": "^5.0.0",
- "find-up": "^5.0.0",
- "launch-editor": "^2.9.1",
- "mime": "^2.4.1",
- "ora": "^5.4.1",
- "picocolors": "^1.1.1",
- "prompts": "^2.4.2",
- "semver": "^7.5.2"
- }
- },
- "node_modules/@react-native-community/cli-platform-ios/node_modules/execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
- }
- },
- "node_modules/@react-native-community/cli-platform-ios/node_modules/get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli-platform-ios/node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli-platform-ios/node_modules/npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "path-key": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/@react-native-community/cli-clean": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-20.1.0.tgz",
- "integrity": "sha512-77L4DifWfxAT8ByHnkypge7GBMYpbJAjBGV+toowt5FQSGaTBDcBHCX+FFqFRukD5fH6i8sZ41Gtw+nbfCTTIA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-tools": "20.1.0",
- "execa": "^5.0.0",
- "fast-glob": "^3.3.2",
- "picocolors": "^1.1.1"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/@react-native-community/cli-config": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-20.1.0.tgz",
- "integrity": "sha512-1x9rhLLR/dKKb92Lb5O0l0EmUG08FHf+ZVyVEf9M+tX+p5QIm52MRiy43R0UAZ2jJnFApxRk+N3sxoYK4Dtnag==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-tools": "20.1.0",
- "cosmiconfig": "^9.0.0",
- "deepmerge": "^4.3.0",
- "fast-glob": "^3.3.2",
- "joi": "^17.2.1",
- "picocolors": "^1.1.1"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/@react-native-community/cli-doctor": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-20.1.0.tgz",
- "integrity": "sha512-QfJF1GVjA4PBrIT3SJ0vFFIu0km1vwOmLDlOYVqfojajZJ+Dnvl0f94GN1il/jT7fITAxom///XH3/URvi7YTQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-config": "20.1.0",
- "@react-native-community/cli-platform-android": "20.1.0",
- "@react-native-community/cli-platform-apple": "20.1.0",
- "@react-native-community/cli-platform-ios": "20.1.0",
- "@react-native-community/cli-tools": "20.1.0",
- "command-exists": "^1.2.8",
- "deepmerge": "^4.3.0",
- "envinfo": "^7.13.0",
- "execa": "^5.0.0",
- "node-stream-zip": "^1.9.1",
- "ora": "^5.4.1",
- "picocolors": "^1.1.1",
- "semver": "^7.5.2",
- "wcwidth": "^1.0.1",
- "yaml": "^2.2.1"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/@react-native-community/cli-platform-apple": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-apple/-/cli-platform-apple-20.1.0.tgz",
- "integrity": "sha512-0ih1hrYezSM2cuOlVnwBEFtMwtd8YgpTLmZauDJCv50rIumtkI1cQoOgLoS4tbPCj9U/Vn2a9BFH0DLFOOIacg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-config-apple": "20.1.0",
- "@react-native-community/cli-tools": "20.1.0",
- "execa": "^5.0.0",
- "fast-xml-parser": "^4.4.1",
- "picocolors": "^1.1.1"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/@react-native-community/cli-server-api": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-20.1.0.tgz",
- "integrity": "sha512-Tb415Oh8syXNT2zOzLzFkBXznzGaqKCiaichxKzGCDKg6JGHp3jSuCmcTcaPeYC7oc32n/S3Psw7798r4Q/7lA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native-community/cli-tools": "20.1.0",
- "body-parser": "^1.20.3",
- "compression": "^1.7.1",
- "connect": "^3.6.5",
- "errorhandler": "^1.5.1",
- "nocache": "^3.0.1",
- "open": "^6.2.0",
- "pretty-format": "^29.7.0",
- "serve-static": "^1.13.1",
- "ws": "^6.2.3"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/@react-native-community/cli-tools": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-20.1.0.tgz",
- "integrity": "sha512-/YmzHGOkY6Bgrv4OaA1L8rFqsBlQd1EB2/ipAoKPiieV0EcB5PUamUSuNeFU3sBZZTYQCUENwX4wgOHgFUlDnQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@vscode/sudo-prompt": "^9.0.0",
- "appdirsjs": "^1.2.4",
- "execa": "^5.0.0",
- "find-up": "^5.0.0",
- "launch-editor": "^2.9.1",
- "mime": "^2.4.1",
- "ora": "^5.4.1",
- "picocolors": "^1.1.1",
- "prompts": "^2.4.2",
- "semver": "^7.5.2"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/@react-native-community/cli-types": {
- "version": "20.1.0",
- "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-20.1.0.tgz",
- "integrity": "sha512-D0kDspcwgbVXyNjwicT7Bb1JgXjijTw1JJd+qxyF/a9+sHv7TU4IchV+gN38QegeXqVyM4Ym7YZIvXMFBmyJqA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "joi": "^17.2.1"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/commander": {
- "version": "9.5.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz",
- "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": "^12.20.0 || >=14"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/cosmiconfig": {
- "version": "9.0.2",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz",
- "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "env-paths": "^2.2.1",
- "import-fresh": "^3.3.0",
- "js-yaml": "^4.1.0",
- "parse-json": "^5.2.0"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/d-fischer"
- },
- "peerDependencies": {
- "typescript": ">=4.9.5"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/@react-native-community/cli/node_modules/execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "path-key": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/ws": {
- "version": "6.2.6",
- "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.6.tgz",
- "integrity": "sha512-XTrf1gv7kXoVf1hbC3PAyAiPgR8Wz1blcrYIjEsUmr08BLksT41R8KbjmS9408C2ERx7v1JDLD/BkpLEttjfKA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "async-limiter": "~1.0.0"
- }
- },
- "node_modules/@react-native-community/cli/node_modules/yaml": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
- "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/eemeli"
- }
- },
- "node_modules/@react-native/babel-plugin-codegen": {
- "version": "0.85.0",
- "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.85.0.tgz",
- "integrity": "sha512-+b+kilQXUMEREXyFZxmSrnvUZ6rIW2ATu6pkMZLsituRUx85fg22A8Z+hKHXj7q9Hyny1W0+506gHoxYr6zevw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/traverse": "^7.29.0",
- "@react-native/codegen": "0.85.0"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/babel-plugin-codegen/node_modules/@react-native/codegen": {
- "version": "0.85.0",
- "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.85.0.tgz",
- "integrity": "sha512-5CHJkC9UpBxQokGju7gD6W615RO1zR17INuB1PB4kcXNy3rre7tyy6ufct+sllDD6ildRC9A//cyh6TI03+jxA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/core": "^7.25.2",
- "@babel/parser": "^7.29.0",
- "hermes-parser": "0.33.3",
- "invariant": "^2.2.4",
- "nullthrows": "^1.1.1",
- "tinyglobby": "^0.2.15",
- "yargs": "^17.6.2"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- },
- "peerDependencies": {
- "@babel/core": "*"
- }
- },
- "node_modules/@react-native/babel-plugin-codegen/node_modules/hermes-estree": {
- "version": "0.33.3",
- "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz",
- "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/@react-native/babel-plugin-codegen/node_modules/hermes-parser": {
- "version": "0.33.3",
- "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz",
- "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "hermes-estree": "0.33.3"
- }
- },
- "node_modules/@react-native/babel-preset": {
- "version": "0.85.0",
- "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.85.0.tgz",
- "integrity": "sha512-xU3q7VeSiBGrT5n0cdtzWf/3NtQlw1C3rJXwOftbFPdbb8nvCpefOdSAKL2UKNHSvA3rcCkNqMeZEtjXRKjcCA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/core": "^7.25.2",
- "@babel/plugin-proposal-export-default-from": "^7.24.7",
- "@babel/plugin-syntax-dynamic-import": "^7.8.3",
- "@babel/plugin-syntax-export-default-from": "^7.24.7",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-transform-async-generator-functions": "^7.25.4",
- "@babel/plugin-transform-async-to-generator": "^7.24.7",
- "@babel/plugin-transform-block-scoping": "^7.25.0",
- "@babel/plugin-transform-class-properties": "^7.25.4",
- "@babel/plugin-transform-classes": "^7.25.4",
- "@babel/plugin-transform-destructuring": "^7.24.8",
- "@babel/plugin-transform-flow-strip-types": "^7.25.2",
- "@babel/plugin-transform-for-of": "^7.24.7",
- "@babel/plugin-transform-modules-commonjs": "^7.24.8",
- "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7",
- "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7",
- "@babel/plugin-transform-optional-catch-binding": "^7.24.7",
- "@babel/plugin-transform-optional-chaining": "^7.24.8",
- "@babel/plugin-transform-private-methods": "^7.24.7",
- "@babel/plugin-transform-private-property-in-object": "^7.24.7",
- "@babel/plugin-transform-react-display-name": "^7.24.7",
- "@babel/plugin-transform-react-jsx": "^7.25.2",
- "@babel/plugin-transform-react-jsx-self": "^7.24.7",
- "@babel/plugin-transform-react-jsx-source": "^7.24.7",
- "@babel/plugin-transform-regenerator": "^7.24.7",
- "@babel/plugin-transform-runtime": "^7.24.7",
- "@babel/plugin-transform-typescript": "^7.25.2",
- "@babel/plugin-transform-unicode-regex": "^7.24.7",
- "@react-native/babel-plugin-codegen": "0.85.0",
- "babel-plugin-syntax-hermes-parser": "0.33.3",
- "babel-plugin-transform-flow-enums": "^0.0.2",
- "react-refresh": "^0.14.0"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- },
- "peerDependencies": {
- "@babel/core": "*"
- }
- },
- "node_modules/@react-native/babel-preset/node_modules/react-refresh": {
- "version": "0.14.2",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
- "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/@react-native/debugger-shell": {
"version": "0.85.3",
"resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.85.3.tgz",
@@ -5316,523 +3814,6 @@
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
}
},
- "node_modules/@react-native/metro-config": {
- "version": "0.85.0",
- "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.85.0.tgz",
- "integrity": "sha512-3L245HZo2F377BndO6WJD9qRZEnBOIG+uKcXvJirna8SzGxD7wvUjDJHHWvwZo4TXSP6aG/FOa/FAHhCYxjiBg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@react-native/js-polyfills": "0.85.0",
- "@react-native/metro-babel-transformer": "0.85.0",
- "metro-config": "^0.84.0",
- "metro-runtime": "^0.84.0"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/@react-native/js-polyfills": {
- "version": "0.85.0",
- "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.85.0.tgz",
- "integrity": "sha512-h2nfIqNEA72Ebdcq5scJg1kyZ01B9xI+NJ2AA8ZpGN8SbxOBNAiZtWEqxzAUe6v5Iu7LE3+1WFBWcMQGtT4zLQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/@react-native/metro-babel-transformer": {
- "version": "0.85.0",
- "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.85.0.tgz",
- "integrity": "sha512-Fao5MQFz2hr4UY09EbXxt8UAJJnQ64QvxNIgoTek24ObTDQ6SqY6MoOLaeV9HGic5sBAHX4lVV/WazhmYkQ1jQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/core": "^7.25.2",
- "@react-native/babel-preset": "0.85.0",
- "hermes-parser": "0.33.3",
- "nullthrows": "^1.1.1"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- },
- "peerDependencies": {
- "@babel/core": "*"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/accepts": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
- "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "mime-types": "^3.0.0",
- "negotiator": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/ci-info": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
- "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/@react-native/metro-config/node_modules/hermes-estree": {
- "version": "0.33.3",
- "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.33.3.tgz",
- "integrity": "sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/@react-native/metro-config/node_modules/hermes-parser": {
- "version": "0.33.3",
- "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.33.3.tgz",
- "integrity": "sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "hermes-estree": "0.33.3"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro/-/metro-0.84.5.tgz",
- "integrity": "sha512-r1liLkyFZMVSEMNjU1CJU5pRzs3NdkxHqXS60O25c0rCIqAR+cGk7rPydw/g0WAIKVXojIBIF45yYBPagJGcgw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/code-frame": "^7.29.0",
- "@babel/core": "^7.25.2",
- "@babel/generator": "^7.29.1",
- "@babel/parser": "^7.29.0",
- "@babel/template": "^7.28.6",
- "@babel/traverse": "^7.29.0",
- "@babel/types": "^7.29.0",
- "accepts": "^2.0.0",
- "ci-info": "^2.0.0",
- "connect": "^3.6.5",
- "debug": "^4.4.0",
- "error-stack-parser": "^2.0.6",
- "flow-enums-runtime": "^0.0.6",
- "graceful-fs": "^4.2.4",
- "hermes-parser": "0.35.0",
- "invariant": "^2.2.4",
- "jest-worker": "^29.7.0",
- "jsc-safe-url": "^0.2.2",
- "lodash.throttle": "^4.1.1",
- "metro-babel-transformer": "0.84.5",
- "metro-cache": "0.84.5",
- "metro-cache-key": "0.84.5",
- "metro-config": "0.84.5",
- "metro-core": "0.84.5",
- "metro-file-map": "0.84.5",
- "metro-resolver": "0.84.5",
- "metro-runtime": "0.84.5",
- "metro-source-map": "0.84.5",
- "metro-symbolicate": "0.84.5",
- "metro-transform-plugins": "0.84.5",
- "metro-transform-worker": "0.84.5",
- "mime-types": "^3.0.1",
- "nullthrows": "^1.1.1",
- "serialize-error": "^2.1.0",
- "source-map": "^0.5.6",
- "throat": "^5.0.0",
- "ws": "^7.5.10",
- "yargs": "^17.6.2"
- },
- "bin": {
- "metro": "src/cli.js"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-babel-transformer": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.84.5.tgz",
- "integrity": "sha512-2WbHILKMiJUzfdjmGOQOqU1bWi9//gqiclc/tkk/AIsrrVw3efhZ1uhkOwMTxUEPOzqoo091H0olLmVZH5FHGQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/core": "^7.25.2",
- "flow-enums-runtime": "^0.0.6",
- "hermes-parser": "0.35.0",
- "metro-cache-key": "0.84.5",
- "nullthrows": "^1.1.1"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-babel-transformer/node_modules/hermes-estree": {
- "version": "0.35.0",
- "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz",
- "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/@react-native/metro-config/node_modules/metro-babel-transformer/node_modules/hermes-parser": {
- "version": "0.35.0",
- "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz",
- "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "hermes-estree": "0.35.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-cache": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.84.5.tgz",
- "integrity": "sha512-WHS0n2OxQqtwEjSeQFPePNrMvEFhmQcUQM9cRJMHByWoi/GMWFBEWOf7hVkAM/0KRutAXNbDlSu/cZB6CyxgQQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "exponential-backoff": "^3.1.1",
- "flow-enums-runtime": "^0.0.6",
- "https-proxy-agent": "^7.0.5",
- "metro-core": "0.84.5"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-cache-key": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.84.5.tgz",
- "integrity": "sha512-3dPB2TnvGjjf0/9O7AXVQURKXuQNauTZE7WpTGTlR017Gh/B5y0m/2wcqxfveUguHSpu89KhVxCAlr2k/H7uhQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "flow-enums-runtime": "^0.0.6"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-config": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.84.5.tgz",
- "integrity": "sha512-zie+uN6oohscowi2S7ByU+wUw6CrT4ZxW9uAbONOObSxx86RGmnIAmjXHLkfmcdYoY7jzOPEbqcI6oeVmqyBQA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "connect": "^3.6.5",
- "flow-enums-runtime": "^0.0.6",
- "jest-validate": "^29.7.0",
- "metro": "0.84.5",
- "metro-cache": "0.84.5",
- "metro-core": "0.84.5",
- "metro-runtime": "0.84.5",
- "yaml": "^2.6.1"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-core": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.84.5.tgz",
- "integrity": "sha512-xwm605hCi5Y6eJTTb8ZWo6pkUcoBEIyiQOfkZh5GwtDwUrP9SNhTQZhzJHrBCwwxlf3Ptl/pxWJgQ1rsNYMnrA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "flow-enums-runtime": "^0.0.6",
- "lodash.throttle": "^4.1.1",
- "metro-resolver": "0.84.5"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-file-map": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.84.5.tgz",
- "integrity": "sha512-mlm/JL8toSbSc2akpKIGmzvrVRSCgZ5vkbycI34oMLoOnLGuLyC8WTyVJ6P0hZG/usDaGwZSl/s9BCRriqjGJA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "debug": "^4.4.0",
- "fb-watchman": "^2.0.0",
- "flow-enums-runtime": "^0.0.6",
- "graceful-fs": "^4.2.4",
- "invariant": "^2.2.4",
- "jest-worker": "^29.7.0",
- "micromatch": "^4.0.4",
- "nullthrows": "^1.1.1",
- "walker": "^1.0.7"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-minify-terser": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.84.5.tgz",
- "integrity": "sha512-BJoFwCEDsYnagPqarayInv2+diCDNDdLlaof/p6s9w4gh+gc9HXYM+pDvsKGKKUumpZswNF3Z/ftTMqKl/5IBg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "flow-enums-runtime": "^0.0.6",
- "terser": "^5.15.0"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-resolver": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.84.5.tgz",
- "integrity": "sha512-VSSnepg1k6LyCwtb6eirWdAWlpKwBG8Rdtsr1mU38rMelFyWgh3/QuMSiZIZAIjwg/fsa8GhW5/FO54CAUPCEA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "flow-enums-runtime": "^0.0.6"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-runtime": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.84.5.tgz",
- "integrity": "sha512-U1m2+d1Pr+JO2/iVXBB2OfXXityz7tqwIorxfrT15IEgaHvpJBq/OHiqnOWPKJbUl3JcxjcdviZZOKk85oK4Qg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/runtime": "^7.25.0",
- "flow-enums-runtime": "^0.0.6"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-source-map": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.84.5.tgz",
- "integrity": "sha512-2BtV5L9uPc49F13Gn5wiP6bX/EncqzqTIk2VL/0F/96Vo0YEOjluT/qktQjFODfqGFsucwnh5mPEAl/2jVEfeg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/traverse": "^7.29.0",
- "@babel/types": "^7.29.0",
- "flow-enums-runtime": "^0.0.6",
- "invariant": "^2.2.4",
- "metro-symbolicate": "0.84.5",
- "nullthrows": "^1.1.1",
- "ob1": "0.84.5",
- "source-map": "^0.5.6",
- "vlq": "^1.0.0"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-symbolicate": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.84.5.tgz",
- "integrity": "sha512-rQ40zYDAkaWBN9yvjUuAD0ZpzBMZSoKyGYXnb5JrfbKjun7fTvfoLHL3KXFYenBTYZkQtlp4cKSCv/1utxFyOw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "flow-enums-runtime": "^0.0.6",
- "invariant": "^2.2.4",
- "metro-source-map": "0.84.5",
- "nullthrows": "^1.1.1",
- "source-map": "^0.5.6",
- "vlq": "^1.0.0"
- },
- "bin": {
- "metro-symbolicate": "src/index.js"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-transform-plugins": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.84.5.tgz",
- "integrity": "sha512-+InaSVGaOyt0DyRo4Y/zIdPI6CZwnbNho5LAL23tgmuGwv7fyfkF7kKfPjZcfxXBcoYdTLLFnCfCH/dHSiCqNg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/core": "^7.25.2",
- "@babel/generator": "^7.29.1",
- "@babel/template": "^7.28.6",
- "@babel/traverse": "^7.29.0",
- "flow-enums-runtime": "^0.0.6",
- "nullthrows": "^1.1.1"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro-transform-worker": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.84.5.tgz",
- "integrity": "sha512-ui1Z8x4s5RL36gMmKLaMMO7O9NNDHNdthEZSCDQHAau3JcAsTaFOK6I+2q4I/kW5u8hSEjJk9L45TXSVJw6g1A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/core": "^7.25.2",
- "@babel/generator": "^7.29.1",
- "@babel/parser": "^7.29.0",
- "@babel/types": "^7.29.0",
- "flow-enums-runtime": "^0.0.6",
- "metro": "0.84.5",
- "metro-babel-transformer": "0.84.5",
- "metro-cache": "0.84.5",
- "metro-cache-key": "0.84.5",
- "metro-minify-terser": "0.84.5",
- "metro-source-map": "0.84.5",
- "metro-transform-plugins": "0.84.5",
- "nullthrows": "^1.1.1"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/metro/node_modules/hermes-estree": {
- "version": "0.35.0",
- "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz",
- "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/@react-native/metro-config/node_modules/metro/node_modules/hermes-parser": {
- "version": "0.35.0",
- "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz",
- "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "hermes-estree": "0.35.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/mime-db": {
- "version": "1.54.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
- "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/mime-types": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
- "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "mime-db": "^1.54.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/ob1": {
- "version": "0.84.5",
- "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.84.5.tgz",
- "integrity": "sha512-aH9RkoZc7w/90HBamFxTw8ZLFr05wXS+iOnvmrgo53Ep8Pyrm5FieQSaPIVROkfFVQISeD/zo92fes26TOwe+A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "flow-enums-runtime": "^0.0.6"
- },
- "engines": {
- "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/serialize-error": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz",
- "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/@react-native/metro-config/node_modules/ws": {
- "version": "7.5.13",
- "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.13.tgz",
- "integrity": "sha512-rsKI6xDBFVf4r/x8XyChGK04QR/XHroxs/jUcoWvtEZM8TPU/X/uIY9B1CsSzYws9ZJb/6bbBu7dPhFW00CAoA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=8.3.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": "^5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- },
- "node_modules/@react-native/metro-config/node_modules/yaml": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
- "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/eemeli"
- }
- },
"node_modules/@react-native/virtualized-lists": {
"version": "0.85.3",
"resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.85.3.tgz",
@@ -6249,33 +4230,6 @@
"win32"
]
},
- "node_modules/@sideway/address": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz",
- "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==",
- "license": "BSD-3-Clause",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@hapi/hoek": "^9.0.0"
- }
- },
- "node_modules/@sideway/formula": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz",
- "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==",
- "license": "BSD-3-Clause",
- "optional": true,
- "peer": true
- },
- "node_modules/@sideway/pinpoint": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz",
- "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
- "license": "BSD-3-Clause",
- "optional": true,
- "peer": true
- },
"node_modules/@sinclair/typebox": {
"version": "0.27.10",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz",
@@ -7374,14 +5328,6 @@
"url": "https://opencollective.com/vitest"
}
},
- "node_modules/@vscode/sudo-prompt": {
- "version": "9.3.2",
- "resolved": "https://registry.npmjs.org/@vscode/sudo-prompt/-/sudo-prompt-9.3.2.tgz",
- "integrity": "sha512-gcXoCN00METUNFeQOFJ+C9xUI0DKB+0EGMVg7wbVYRHBw2Eq3fKisDZOkRdOz3kqXRKOENMfShPOmypw1/8nOw==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/@xmldom/xmldom": {
"version": "0.8.12",
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz",
@@ -7421,32 +5367,6 @@
"node": ">=6.5"
}
},
- "node_modules/accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/accepts/node_modules/negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/acorn": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
@@ -7551,115 +5471,6 @@
"license": "MIT",
"peer": true
},
- "node_modules/ansi-fragments": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/ansi-fragments/-/ansi-fragments-0.2.1.tgz",
- "integrity": "sha512-DykbNHxuXQwUDRv5ibc2b0x7uw7wmwOGLBUd5RmaQ5z8Lhx19vwvKV+FAsM5rEA6dEcHxX+/Ad5s9eF2k2bB+w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "colorette": "^1.0.7",
- "slice-ansi": "^2.0.0",
- "strip-ansi": "^5.0.0"
- }
- },
- "node_modules/ansi-fragments/node_modules/ansi-regex": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
- "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/ansi-fragments/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/ansi-fragments/node_modules/astral-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
- "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/ansi-fragments/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/ansi-fragments/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/ansi-fragments/node_modules/is-fullwidth-code-point": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
- "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/ansi-fragments/node_modules/slice-ansi": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
- "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "ansi-styles": "^3.2.0",
- "astral-regex": "^1.0.0",
- "is-fullwidth-code-point": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/ansi-fragments/node_modules/strip-ansi": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
- "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "ansi-regex": "^4.1.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -7876,24 +5687,6 @@
"node": "^18.17.0 || >=20.5.0"
}
},
- "node_modules/appdirsjs": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/appdirsjs/-/appdirsjs-1.2.8.tgz",
- "integrity": "sha512-8zl1xlxeS4a0/36CT6LOaVioPOL8TeLT1b9OHk0j9xSbzmPBuM7lUgWMSTh6SbuF8fbwjcP1rr30OCLpd1fl+A==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/codingjerk"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/codingjerk"
- }
- ],
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/arch": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz",
@@ -7969,14 +5762,6 @@
"node": ">=0.12.0"
}
},
- "node_modules/async-limiter": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
- "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -8019,201 +5804,6 @@
"npm": ">=6"
}
},
- "node_modules/babel-plugin-polyfill-corejs2": {
- "version": "0.4.17",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz",
- "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/compat-data": "^7.28.6",
- "@babel/helper-define-polyfill-provider": "^0.6.8",
- "semver": "^6.3.1"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/babel-plugin-polyfill-regenerator": {
- "version": "0.6.8",
- "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz",
- "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/helper-define-polyfill-provider": "^0.6.8"
- },
- "peerDependencies": {
- "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
- }
- },
- "node_modules/babel-plugin-react-compiler": {
- "version": "0.0.0-experimental-592953e-20240517",
- "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-0.0.0-experimental-592953e-20240517.tgz",
- "integrity": "sha512-OjG1SVaeQZaJrqkMFJatg8W/MTow8Ak5rx2SI0ETQBO1XvOk/XZGMbltNCPdFJLKghBYoBjC+Y3Ap/Xr7B01mA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/generator": "7.2.0",
- "@babel/types": "^7.19.0",
- "chalk": "4",
- "invariant": "^2.2.4",
- "pretty-format": "^24",
- "zod": "^3.22.4",
- "zod-validation-error": "^2.1.0"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/@babel/generator": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.2.0.tgz",
- "integrity": "sha512-BA75MVfRlFQG2EZgFYIwyT1r6xSkwfP2bdkY/kLZusEYWiJs4xCowab/alaEaT0wSvmVuXGqiefeBlP+7V1yKg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/types": "^7.2.0",
- "jsesc": "^2.5.1",
- "lodash": "^4.17.10",
- "source-map": "^0.5.0",
- "trim-right": "^1.0.1"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/@jest/types": {
- "version": "24.9.0",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz",
- "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^1.1.1",
- "@types/yargs": "^13.0.0"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/@types/istanbul-reports": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz",
- "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@types/istanbul-lib-coverage": "*",
- "@types/istanbul-lib-report": "*"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/@types/yargs": {
- "version": "13.0.12",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz",
- "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/ansi-regex": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
- "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/ansi-styles": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
- "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "color-convert": "^1.9.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/color-convert": {
- "version": "1.9.3",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
- "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "color-name": "1.1.3"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/color-name": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
- "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/babel-plugin-react-compiler/node_modules/jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/pretty-format": {
- "version": "24.9.0",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz",
- "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@jest/types": "^24.9.0",
- "ansi-regex": "^4.0.0",
- "ansi-styles": "^3.2.0",
- "react-is": "^16.8.4"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/babel-plugin-react-compiler/node_modules/react-is": {
- "version": "16.13.1",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
- "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/babel-plugin-syntax-hermes-parser": {
"version": "0.33.3",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.33.3.tgz",
@@ -8241,17 +5831,6 @@
"hermes-estree": "0.33.3"
}
},
- "node_modules/babel-plugin-transform-flow-enums": {
- "version": "0.0.2",
- "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-enums/-/babel-plugin-transform-flow-enums-0.0.2.tgz",
- "integrity": "sha512-g4aaCrDDOsWjbm0PUUeVnkcVd6AKJsVc/MbnPhEotEpkeJQP6b8nzewohQi7+QS8UyPehOhGWn0nOwjvWpmMvQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@babel/plugin-syntax-flow": "^7.12.1"
- }
- },
"node_modules/bail": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
@@ -8358,112 +5937,6 @@
"integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==",
"license": "MIT"
},
- "node_modules/body-parser": {
- "version": "1.20.6",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz",
- "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "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"
- },
- "engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
- }
- },
- "node_modules/body-parser/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/body-parser/node_modules/http-errors": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
- "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "depd": "~2.0.0",
- "inherits": "~2.0.4",
- "setprototypeof": "~1.2.0",
- "statuses": "~2.0.2",
- "toidentifier": "~1.0.1"
- },
- "engines": {
- "node": ">= 0.8"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/body-parser/node_modules/iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/body-parser/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/body-parser/node_modules/on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "ee-first": "1.1.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/body-parser/node_modules/statuses": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
- "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/boolean": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
@@ -8666,17 +6139,6 @@
"node": ">= 10.0.0"
}
},
- "node_modules/bytes": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/cac": {
"version": "6.7.14",
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
@@ -8829,24 +6291,6 @@
"node": ">= 0.4"
}
},
- "node_modules/call-bound": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
- "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "call-bind-apply-helpers": "^1.0.2",
- "get-intrinsic": "^1.3.0"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -8856,17 +6300,6 @@
"node": ">=6"
}
},
- "node_modules/camelcase": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
- "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/caniuse-lite": {
"version": "1.0.30001809",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz",
@@ -9195,14 +6628,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"license": "MIT"
},
- "node_modules/colorette": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
- "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -9226,14 +6651,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/command-exists": {
- "version": "1.2.9",
- "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
- "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/commander": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
@@ -9254,92 +6671,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/compressible": {
- "version": "2.0.18",
- "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
- "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "mime-db": ">= 1.43.0 < 2"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/compression": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
- "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "bytes": "3.1.2",
- "compressible": "~2.0.18",
- "debug": "2.6.9",
- "negotiator": "~0.6.4",
- "on-headers": "~1.1.0",
- "safe-buffer": "5.2.1",
- "vary": "~1.1.2"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/compression/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/compression/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/compression/node_modules/negotiator": {
- "version": "0.6.4",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
- "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/compression/node_modules/safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -9443,17 +6774,6 @@
"license": "MIT",
"peer": true
},
- "node_modules/content-type": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
@@ -9473,24 +6793,6 @@
"url": "https://opencollective.com/express"
}
},
- "node_modules/core-js-compat": {
- "version": "3.50.0",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.50.0.tgz",
- "integrity": "sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "browserslist": "^4.28.7"
- },
- "engines": {
- "node": ">=6.4.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/core-js"
- }
- },
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -10108,17 +7410,6 @@
}
}
},
- "node_modules/decamelize": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
- "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/decimal.js-light": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
@@ -10191,17 +7482,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/deepmerge": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
- "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/defaults": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
@@ -11135,20 +8415,6 @@
"node": ">=6"
}
},
- "node_modules/envinfo": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz",
- "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "bin": {
- "envinfo": "dist/cli.js"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/err-code": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
@@ -11175,25 +8441,6 @@
"stackframe": "^1.3.4"
}
},
- "node_modules/errorhandler": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.2.tgz",
- "integrity": "sha512-kNAL7hESndBCrWwS72QyV3IVOTrVmj9D062FV5BQswNL5zEdeRmz/WJFyh6Aj/plvvSOrzddkxW57HgkZcR9Fw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "accepts": "~1.3.8",
- "escape-html": "~1.0.3"
- },
- "engines": {
- "node": ">= 0.8"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
@@ -11775,38 +9022,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/fast-glob/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -11837,26 +9052,6 @@
],
"license": "BSD-3-Clause"
},
- "node_modules/fast-xml-parser": {
- "version": "4.5.7",
- "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.7.tgz",
- "integrity": "sha512-a6Qh1RMCNbSrU1+sAyAAZH3rTe+OaWJbNZIq0S+ifZciUUOQtlVxBJwoTUE2bYhysmG/RYyI5WJFIKdBahJdrQ==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
- }
- ],
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "strnum": "^1.0.5"
- },
- "bin": {
- "fxparser": "src/cli/cli.js"
- }
- },
"node_modules/fastdom": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fastdom/-/fastdom-1.0.12.tgz",
@@ -12736,17 +9931,6 @@
"node": ">= 14"
}
},
- "node_modules/human-signals": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
- "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
- "license": "Apache-2.0",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=10.17.0"
- }
- },
"node_modules/iceberg-js": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
@@ -13352,21 +10536,6 @@
"jiti": "lib/jiti-cli.mjs"
}
},
- "node_modules/joi": {
- "version": "17.13.6",
- "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.6.tgz",
- "integrity": "sha512-ImNZaq/LSysofih+xIGYfR0WUXMA9GLUNB//YTCSrZptoRmVgaNAdJyi6K1kXi9pkLEoSkoI8I4UwtNiu/D7nw==",
- "license": "BSD-3-Clause",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@hapi/hoek": "^9.3.0",
- "@hapi/topo": "^5.1.0",
- "@sideway/address": "^4.1.5",
- "@sideway/formula": "^3.0.1",
- "@sideway/pinpoint": "^2.0.0"
- }
- },
"node_modules/jpeg-js": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
@@ -13521,29 +10690,6 @@
"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz",
"integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw=="
},
- "node_modules/kleur": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/launch-editor": {
- "version": "2.14.1",
- "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz",
- "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "picocolors": "^1.1.1",
- "shell-quote": "^1.8.4"
- }
- },
"node_modules/layout-base": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz",
@@ -13679,14 +10825,6 @@
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
"license": "MIT"
},
- "node_modules/lodash.debounce": {
- "version": "4.0.8",
- "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
- "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/lodash.escaperegexp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
@@ -13731,158 +10869,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/logkitty": {
- "version": "0.7.1",
- "resolved": "https://registry.npmjs.org/logkitty/-/logkitty-0.7.1.tgz",
- "integrity": "sha512-/3ER20CTTbahrCrpYfPn7Xavv9diBROZpoXGVZDWMw4b/X4uuUwAC0ki85tgsdMRONURyIJbcOvS94QsUBYPbQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "ansi-fragments": "^0.2.1",
- "dayjs": "^1.8.15",
- "yargs": "^15.1.0"
- },
- "bin": {
- "logkitty": "bin/logkitty.js"
- }
- },
- "node_modules/logkitty/node_modules/cliui": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^6.2.0"
- }
- },
- "node_modules/logkitty/node_modules/find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/logkitty/node_modules/locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "p-locate": "^4.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/logkitty/node_modules/p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "p-try": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/logkitty/node_modules/p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "p-limit": "^2.2.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/logkitty/node_modules/wrap-ansi": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/logkitty/node_modules/y18n": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
- "license": "ISC",
- "optional": true,
- "peer": true
- },
- "node_modules/logkitty/node_modules/yargs": {
- "version": "15.4.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "cliui": "^6.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^4.1.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^4.2.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^18.1.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/logkitty/node_modules/yargs-parser": {
- "version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "license": "ISC",
- "optional": true,
- "peer": true,
- "dependencies": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/longest-streak": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
@@ -14316,17 +11302,6 @@
"url": "https://opencollective.com/unified"
}
},
- "node_modules/media-typer": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/memoize-one": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
@@ -14341,17 +11316,6 @@
"license": "MIT",
"peer": true
},
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/mermaid": {
"version": "11.17.0",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.17.0.tgz",
@@ -15381,17 +12345,6 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"license": "MIT"
},
- "node_modules/nocache": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/nocache/-/nocache-3.0.4.tgz",
- "integrity": "sha512-WDD0bdg9mbq6F4mRxEYcPWwfA1vxd0mrvKOyxI7Xj/atfRHVeutzuWByG//jfm4uPzp0y4Kj051EORCBSQMycw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=12.0.0"
- }
- },
"node_modules/node-abi": {
"version": "4.28.0",
"resolved": "https://registry.npmjs.org/node-abi/-/node-abi-4.28.0.tgz",
@@ -15526,21 +12479,6 @@
"node": ">=18"
}
},
- "node_modules/node-stream-zip": {
- "version": "1.16.0",
- "resolved": "https://registry.npmjs.org/node-stream-zip/-/node-stream-zip-1.16.0.tgz",
- "integrity": "sha512-ObaRrRoR8T68wF6suxHd7R4XQNamij6ZQHrwG7Dx1D2zeHcDNLsIOBcWrIwtDm7AsCXBguaPHgXhcjxDa2szrg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=0.12.0"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/antelle"
- }
- },
"node_modules/nopt": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz",
@@ -15606,20 +12544,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/object-inspect": {
- "version": "1.13.4",
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
- "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@@ -15649,17 +12573,6 @@
"node": ">= 0.8"
}
},
- "node_modules/on-headers": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
- "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -15685,31 +12598,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/open": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz",
- "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "is-wsl": "^1.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/open/node_modules/is-wsl": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
- "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -15831,17 +12719,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/p-try": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
- "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
@@ -16190,7 +13067,6 @@
"os": [
"darwin"
],
- "peer": true,
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
@@ -16469,21 +13345,6 @@
"node": ">=10"
}
},
- "node_modules/prompts": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
- "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.5"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -16543,24 +13404,6 @@
"node": ">=6"
}
},
- "node_modules/qs": {
- "version": "6.15.3",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
- "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
- "license": "BSD-3-Clause",
- "optional": true,
- "peer": true,
- "dependencies": {
- "es-define-property": "^1.0.1",
- "side-channel": "^1.1.1"
- },
- "engines": {
- "node": ">=0.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
@@ -16604,70 +13447,6 @@
"node": ">= 0.6"
}
},
- "node_modules/raw-body": {
- "version": "2.5.3",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
- "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "bytes": "~3.1.2",
- "http-errors": "~2.0.1",
- "iconv-lite": "~0.4.24",
- "unpipe": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/raw-body/node_modules/http-errors": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
- "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "depd": "~2.0.0",
- "inherits": "~2.0.4",
- "setprototypeof": "~1.2.0",
- "statuses": "~2.0.2",
- "toidentifier": "~1.0.1"
- },
- "engines": {
- "node": ">= 0.8"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/express"
- }
- },
- "node_modules/raw-body/node_modules/iconv-lite": {
- "version": "0.4.24",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "safer-buffer": ">= 2.1.2 < 3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/raw-body/node_modules/statuses": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
- "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -17672,75 +14451,12 @@
"redux": "^5.0.0"
}
},
- "node_modules/regenerate": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
- "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/regenerate-unicode-properties": {
- "version": "10.2.2",
- "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
- "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "regenerate": "^1.4.2"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"license": "MIT"
},
- "node_modules/regexpu-core": {
- "version": "6.4.0",
- "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz",
- "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "regenerate": "^1.4.2",
- "regenerate-unicode-properties": "^10.2.2",
- "regjsgen": "^0.8.0",
- "regjsparser": "^0.13.0",
- "unicode-match-property-ecmascript": "^2.0.0",
- "unicode-match-property-value-ecmascript": "^2.2.1"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/regjsgen": {
- "version": "0.8.0",
- "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
- "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
- "node_modules/regjsparser": {
- "version": "0.13.2",
- "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz",
- "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==",
- "license": "BSD-2-Clause",
- "optional": true,
- "peer": true,
- "dependencies": {
- "jsesc": "~3.1.0"
- },
- "bin": {
- "regjsparser": "bin/parser"
- }
- },
"node_modules/remark-gfm": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
@@ -17825,14 +14541,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/require-main-filename": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
- "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "license": "ISC",
- "optional": true,
- "peer": true
- },
"node_modules/resedit": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz",
@@ -18277,14 +14985,6 @@
"node": ">= 0.8"
}
},
- "node_modules/set-blocking": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
- "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "license": "ISC",
- "optional": true,
- "peer": true
- },
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
@@ -18395,86 +15095,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/side-channel": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
- "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "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"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-list": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
- "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "es-errors": "^1.3.0",
- "object-inspect": "^1.13.4"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-map": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
- "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "call-bound": "^1.0.2",
- "es-errors": "^1.3.0",
- "get-intrinsic": "^1.2.5",
- "object-inspect": "^1.13.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/side-channel-weakmap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
- "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "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"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
"node_modules/siginfo": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
@@ -18546,14 +15166,6 @@
"node": ">=10"
}
},
- "node_modules/sisteransi": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/slice-ansi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
@@ -18836,17 +15448,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/strip-final-newline": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -18860,20 +15461,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/strnum": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz",
- "integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==",
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/NaturalIntelligence"
- }
- ],
- "license": "MIT",
- "optional": true,
- "peer": true
- },
"node_modules/strtok3": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
@@ -19370,17 +15957,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/trim-right": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
- "integrity": "sha512-WZGXGstmCWgeevgTL54hrCuw1dyMQIzWy7ZfqRJfSmJZBwklI15egmQytFP6bPidmw3M8d5yEowl1niq4vmqZw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/trough": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
@@ -19467,21 +16043,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/type-is": {
- "version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "media-typer": "0.3.0",
- "mime-types": "~2.1.24"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -19533,54 +16094,6 @@
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"license": "MIT"
},
- "node_modules/unicode-canonical-property-names-ecmascript": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
- "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-match-property-ecmascript": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
- "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "unicode-canonical-property-names-ecmascript": "^2.0.0",
- "unicode-property-aliases-ecmascript": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-match-property-value-ecmascript": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
- "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/unicode-property-aliases-ecmascript": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
- "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/unified": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
@@ -19807,17 +16320,6 @@
"uuid": "dist-node/bin/uuid"
}
},
- "node_modules/vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 0.8"
- }
- },
"node_modules/verror": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
@@ -20111,14 +16613,6 @@
"node": ">= 8"
}
},
- "node_modules/which-module": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
- "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
- "license": "ISC",
- "optional": true,
- "peer": true
- },
"node_modules/why-is-node-running": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
@@ -20360,31 +16854,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/zod": {
- "version": "3.25.76",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
- "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "funding": {
- "url": "https://github.com/sponsors/colinhacks"
- }
- },
- "node_modules/zod-validation-error": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-2.1.0.tgz",
- "integrity": "sha512-VJh93e2wb4c3tWtGgTa0OF/dTt/zoPCPzXq4V11ZjxmEAFaPi/Zss1xIZdEB5RD8GD00U0/iVXgqkF77RV7pdQ==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">=18.0.0"
- },
- "peerDependencies": {
- "zod": "^3.18.0"
- }
- },
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
@@ -20397,7 +16866,7 @@
},
"packages/api-client": {
"name": "@d3ro/api-client",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"dependencies": {
"@d3ro/core": "*",
@@ -20414,7 +16883,7 @@
},
"packages/core": {
"name": "@d3ro/core",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"dependencies": {
"docx": "^9.6.1"
@@ -20425,7 +16894,7 @@
},
"packages/i18n": {
"name": "@d3ro/i18n",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"devDependencies": {
"@types/react": "^19.0.0"
@@ -20436,7 +16905,7 @@
},
"packages/ui": {
"name": "@d3ro/ui",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"dependencies": {
"@d3ro/core": "*"
@@ -20456,7 +16925,7 @@
},
"packages/ui-native": {
"name": "@d3ro/ui-native",
- "version": "1.2.0",
+ "version": "1.3.0",
"license": "MIT",
"devDependencies": {
"@types/react": "*"
diff --git a/package.json b/package.json
index f6f935c..2d16304 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "d3ro-voice-monorepo",
- "version": "1.2.0",
+ "version": "1.3.0",
"private": true,
"description": "D3RO Voice — 멀티플랫폼 AI 음성 어시스턴트 (Monorepo)",
"author": "D3RO",
diff --git a/packages/api-client/package.json b/packages/api-client/package.json
index fb092e8..e56a903 100644
--- a/packages/api-client/package.json
+++ b/packages/api-client/package.json
@@ -1,6 +1,6 @@
{
"name": "@d3ro/api-client",
- "version": "1.2.0",
+ "version": "1.3.0",
"private": true,
"description": "D3RO Voice API 클라이언트 — Supabase 래퍼 (web/desktop/mobile 공유)",
"license": "MIT",
diff --git a/packages/core/package.json b/packages/core/package.json
index 79f6241..f337d21 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@d3ro/core",
- "version": "1.2.0",
+ "version": "1.3.0",
"private": true,
"description": "D3RO Voice 공유 비즈니스 로직 — 타입, 에러, IPC 채널, 상수, 유틸",
"license": "MIT",
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
index 11e3c2d..d496837 100644
--- a/packages/i18n/package.json
+++ b/packages/i18n/package.json
@@ -1,6 +1,6 @@
{
"name": "@d3ro/i18n",
- "version": "1.2.0",
+ "version": "1.3.0",
"private": true,
"description": "D3RO Voice 공유 i18n — 12개 locale, 타입 안전 키, Context, 포맷 유틸",
"license": "MIT",
diff --git a/packages/ui-native/package.json b/packages/ui-native/package.json
index c89ef8f..746c3ce 100644
--- a/packages/ui-native/package.json
+++ b/packages/ui-native/package.json
@@ -1,6 +1,6 @@
{
"name": "@d3ro/ui-native",
- "version": "1.2.0",
+ "version": "1.3.0",
"private": true,
"description": "D3RO Voice React Native DS — MetalCard/PhosphorText/Led/WaveBars etc.",
"license": "MIT",
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 97ca0c6..ce578c6 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -1,6 +1,6 @@
{
"name": "@d3ro/ui",
- "version": "1.2.0",
+ "version": "1.3.0",
"private": true,
"description": "D3RO Voice 공유 UI — 디자인 시스템 컴포넌트 + 테마 토큰 + CSS 변수 맵",
"license": "MIT",
diff --git a/release/product-version.json b/release/product-version.json
index 0cbc573..ec8a3a1 100644
--- a/release/product-version.json
+++ b/release/product-version.json
@@ -1,8 +1,8 @@
{
"schemaVersion": 1,
- "version": "1.2.0",
- "androidVersionCode": 1020001,
- "iosBuildNumber": 1020001,
- "releaseDate": "2026-09-16",
+ "version": "1.3.0",
+ "androidVersionCode": 1030001,
+ "iosBuildNumber": 1030001,
+ "releaseDate": "2026-09-18",
"desktopLicensePublicKeyId": "5c52b765135ee2531c681b53cd4dc0e96fff8737f496c0b04ba8334fc81a887f"
}
diff --git a/scripts/ci/verify-sidecar-bundle.mjs b/scripts/ci/verify-sidecar-bundle.mjs
new file mode 100644
index 0000000..b7f0865
--- /dev/null
+++ b/scripts/ci/verify-sidecar-bundle.mjs
@@ -0,0 +1,57 @@
+// scans/ci/verify-sidecar-bundle.mjs
+// 패키징 전에 STT 사이드카 번들이 실제로 존재하고 필수 데이터가 들어있는지 검증한다.
+//
+// 왜 필요한가: electron-builder의 extraResources는 소스 디렉토리가 없으면 조용히
+// 건너뛴다. 그 결과 로컬 전사가 전혀 동작하지 않는 설치 파일이 배포된 이력이 있다.
+// 패키징 직전에 하드 실패시켜 같은 회귀를 막는다.
+//
+// 사용: node scripts/ci/verify-sidecar-bundle.mjs
+
+import { existsSync, statSync } from 'node:fs'
+import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+const scriptDir = path.dirname(fileURLToPath(import.meta.url))
+const repoRoot = path.resolve(scriptDir, '..', '..')
+const desktopDir = path.join(repoRoot, 'apps', 'desktop')
+const bundleDir = path.join(desktopDir, 'sidecar-dist', 'sidecar')
+const exeSuffix = process.platform === 'win32' ? '.exe' : ''
+const exePath = path.join(bundleDir, `sidecar${exeSuffix}`)
+
+/** 패키지에 반드시 포함돼야 하는 런타임 데이터 (PyInstaller 6은 _internal/ 하위) */
+const REQUIRED_RELATIVE = [
+ // faster-whisper VAD onnx — 누락하면 vad_filter=true 전사가 런타임에 실패한다.
+ ['faster_whisper', 'assets', 'silero_vad_v6.onnx'],
+]
+
+const problems = []
+
+if (!existsSync(exePath)) {
+ problems.push(
+ `사이드카 실행 파일이 없습니다: ${exePath}\n` +
+ ' 빌드: npm --prefix apps/desktop run sidecar:setup && npm --prefix apps/desktop run sidecar:build',
+ )
+}
+
+if (existsSync(bundleDir)) {
+ for (const parts of REQUIRED_RELATIVE) {
+ const candidates = [
+ path.join(bundleDir, '_internal', ...parts),
+ path.join(bundleDir, ...parts),
+ ]
+ if (!candidates.some((candidate) => existsSync(candidate))) {
+ problems.push(
+ `사이드카 번들에 필수 데이터가 없습니다: ${candidates[0]} (또는 ${candidates[1]})`,
+ )
+ }
+ }
+}
+
+if (problems.length > 0) {
+ console.error('STT 사이드카 번들 검증 실패:')
+ for (const problem of problems) console.error(`- ${problem}`)
+ process.exit(1)
+}
+
+const sizeMb = statSync(exePath).size / (1024 * 1024)
+console.log(`STT 사이드카 번들 검증 통과: ${exePath} (${sizeMb.toFixed(1)} MB)`)
diff --git a/site/package-lock.json b/site/package-lock.json
index 4c5df39..57d4b69 100644
--- a/site/package-lock.json
+++ b/site/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "d3ro-voice-site",
- "version": "1.2.0",
+ "version": "1.3.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "d3ro-voice-site",
- "version": "1.2.0",
+ "version": "1.3.0",
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
diff --git a/site/package.json b/site/package.json
index e1247c3..1c5a97d 100644
--- a/site/package.json
+++ b/site/package.json
@@ -1,7 +1,7 @@
{
"name": "d3ro-voice-site",
"private": true,
- "version": "1.2.0",
+ "version": "1.3.0",
"type": "module",
"scripts": {
"dev": "vite --port 5199 --host",