From b2801d1c04c1e5c9de7eb18ac6c2d8c0b7e308db Mon Sep 17 00:00:00 2001 From: ienaga Date: Thu, 2 Jul 2026 13:19:56 +0900 Subject: [PATCH] =?UTF-8?q?#75=20WIP:=20xbox=E3=81=B8=E3=81=AE=E6=9B=B8?= =?UTF-8?q?=E3=81=8D=E5=87=BA=E3=81=97=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/xbox-host-ci.yml | 138 ++ README.md | 11 + package-lock.json | 357 ++--- package.json | 18 +- src/index.ts | 282 +++- templates/xbox/.gitignore | 16 + templates/xbox/CMakeLists.txt | 121 ++ templates/xbox/MicrosoftGame.config | 36 + templates/xbox/README.md | 319 ++++ templates/xbox/cmake/FetchDawn.cmake | 42 + templates/xbox/cmake/FindV8.cmake | 62 + templates/xbox/js/bootstrap.js | 143 ++ templates/xbox/js/selftest.js | 515 +++++++ templates/xbox/src/AssetLoader.cpp | 106 ++ templates/xbox/src/AssetLoader.h | 35 + templates/xbox/src/EventLoop.cpp | 162 ++ templates/xbox/src/EventLoop.h | 73 + templates/xbox/src/HostContext.h | 58 + templates/xbox/src/bindings/Audio.cpp | 320 ++++ templates/xbox/src/bindings/Bindings.h | 75 + templates/xbox/src/bindings/Canvas.cpp | 171 +++ templates/xbox/src/bindings/Canvas2D.cpp | 462 ++++++ templates/xbox/src/bindings/Console.cpp | 52 + templates/xbox/src/bindings/DomShims.cpp | 221 +++ templates/xbox/src/bindings/EventTarget.cpp | 127 ++ templates/xbox/src/bindings/EventTarget.h | 17 + templates/xbox/src/bindings/Fetch.cpp | 149 ++ templates/xbox/src/bindings/Gamepad.cpp | 79 + templates/xbox/src/bindings/Image.cpp | 160 ++ templates/xbox/src/bindings/ImageSource.h | 26 + templates/xbox/src/bindings/Network.cpp | 303 ++++ templates/xbox/src/bindings/RasterCore.h | 265 ++++ templates/xbox/src/bindings/Timers.cpp | 87 ++ templates/xbox/src/bindings/Video.cpp | 235 +++ templates/xbox/src/bindings/webgpu/WebGPU.cpp | 1318 +++++++++++++++++ .../xbox/src/bindings/webgpu/WebGPUCommon.h | 119 ++ .../xbox/src/bindings/webgpu/WebGPUEnums.h | 245 +++ templates/xbox/src/gpu/DawnContext.cpp | 170 +++ templates/xbox/src/gpu/DawnContext.h | 64 + templates/xbox/src/main.cpp | 368 +++++ templates/xbox/src/platform/AudioEngine.cpp | 184 +++ templates/xbox/src/platform/AudioEngine.h | 65 + .../xbox/src/platform/GamepadManager.cpp | 83 ++ templates/xbox/src/platform/GamepadManager.h | 40 + templates/xbox/src/platform/ImageTypes.h | 17 + .../xbox/src/platform/TextRasterizer.cpp | 183 +++ templates/xbox/src/platform/TextRasterizer.h | 35 + templates/xbox/src/platform/WicDecoder.cpp | 63 + templates/xbox/src/platform/WicDecoder.h | 16 + templates/xbox/src/v8/V8Runtime.cpp | 284 ++++ templates/xbox/src/v8/V8Runtime.h | 68 + templates/xbox/src/v8/V8Util.h | 56 + templates/xbox/src/worker/WorkerRuntime.cpp | 453 ++++++ templates/xbox/src/worker/WorkerRuntime.h | 108 ++ templates/xbox/tests/check_local.sh | 56 + templates/xbox/tests/raster_test.bin | Bin 0 -> 55648 bytes templates/xbox/tests/raster_test.cpp | 279 ++++ .../xbox/tests/windows_platform_test.cpp | 187 +++ 58 files changed, 9516 insertions(+), 188 deletions(-) create mode 100644 .github/workflows/xbox-host-ci.yml create mode 100644 templates/xbox/.gitignore create mode 100644 templates/xbox/CMakeLists.txt create mode 100644 templates/xbox/MicrosoftGame.config create mode 100644 templates/xbox/README.md create mode 100644 templates/xbox/cmake/FetchDawn.cmake create mode 100644 templates/xbox/cmake/FindV8.cmake create mode 100644 templates/xbox/js/bootstrap.js create mode 100644 templates/xbox/js/selftest.js create mode 100644 templates/xbox/src/AssetLoader.cpp create mode 100644 templates/xbox/src/AssetLoader.h create mode 100644 templates/xbox/src/EventLoop.cpp create mode 100644 templates/xbox/src/EventLoop.h create mode 100644 templates/xbox/src/HostContext.h create mode 100644 templates/xbox/src/bindings/Audio.cpp create mode 100644 templates/xbox/src/bindings/Bindings.h create mode 100644 templates/xbox/src/bindings/Canvas.cpp create mode 100644 templates/xbox/src/bindings/Canvas2D.cpp create mode 100644 templates/xbox/src/bindings/Console.cpp create mode 100644 templates/xbox/src/bindings/DomShims.cpp create mode 100644 templates/xbox/src/bindings/EventTarget.cpp create mode 100644 templates/xbox/src/bindings/EventTarget.h create mode 100644 templates/xbox/src/bindings/Fetch.cpp create mode 100644 templates/xbox/src/bindings/Gamepad.cpp create mode 100644 templates/xbox/src/bindings/Image.cpp create mode 100644 templates/xbox/src/bindings/ImageSource.h create mode 100644 templates/xbox/src/bindings/Network.cpp create mode 100644 templates/xbox/src/bindings/RasterCore.h create mode 100644 templates/xbox/src/bindings/Timers.cpp create mode 100644 templates/xbox/src/bindings/Video.cpp create mode 100644 templates/xbox/src/bindings/webgpu/WebGPU.cpp create mode 100644 templates/xbox/src/bindings/webgpu/WebGPUCommon.h create mode 100644 templates/xbox/src/bindings/webgpu/WebGPUEnums.h create mode 100644 templates/xbox/src/gpu/DawnContext.cpp create mode 100644 templates/xbox/src/gpu/DawnContext.h create mode 100644 templates/xbox/src/main.cpp create mode 100644 templates/xbox/src/platform/AudioEngine.cpp create mode 100644 templates/xbox/src/platform/AudioEngine.h create mode 100644 templates/xbox/src/platform/GamepadManager.cpp create mode 100644 templates/xbox/src/platform/GamepadManager.h create mode 100644 templates/xbox/src/platform/ImageTypes.h create mode 100644 templates/xbox/src/platform/TextRasterizer.cpp create mode 100644 templates/xbox/src/platform/TextRasterizer.h create mode 100644 templates/xbox/src/platform/WicDecoder.cpp create mode 100644 templates/xbox/src/platform/WicDecoder.h create mode 100644 templates/xbox/src/v8/V8Runtime.cpp create mode 100644 templates/xbox/src/v8/V8Runtime.h create mode 100644 templates/xbox/src/v8/V8Util.h create mode 100644 templates/xbox/src/worker/WorkerRuntime.cpp create mode 100644 templates/xbox/src/worker/WorkerRuntime.h create mode 100755 templates/xbox/tests/check_local.sh create mode 100755 templates/xbox/tests/raster_test.bin create mode 100644 templates/xbox/tests/raster_test.cpp create mode 100644 templates/xbox/tests/windows_platform_test.cpp diff --git a/.github/workflows/xbox-host-ci.yml b/.github/workflows/xbox-host-ci.yml new file mode 100644 index 0000000..049bb3b --- /dev/null +++ b/.github/workflows/xbox-host-ci.yml @@ -0,0 +1,138 @@ +# Xbox ホスト (templates/xbox) の CI。 +# +# ローカルに Windows PC / GDK が無くても、GitHub Actions の windows-latest 上で +# 以下を検証する: +# 1. raster-tests : Canvas2D ソフトラスタライザの単体テスト (Linux + Windows/MSVC) +# 2. windows-platform : WIC / DirectWrite / Media Foundation / XAudio2 の +# 実 API を Windows 上で実行する smoke テスト +# 3. v8-syntax-check : V8 依存ソースを V8 ヘッダ + MSVC でコンパイル検証 +# (リンクなし。Dawn 依存の WebGPU.cpp / DawnContext.cpp / +# main.cpp と GDK 依存は対象外 → 実機セットアップ時に検証) +name: xbox-host-ci + +on: + push: + paths: + - "templates/xbox/**" + - ".github/workflows/xbox-host-ci.yml" + pull_request: + paths: + - "templates/xbox/**" + - ".github/workflows/xbox-host-ci.yml" + workflow_dispatch: + +env: + # コンパイル検証に使う V8 ヘッダのバージョン (README「V8 の用意」と揃える) + V8_TAG: 13.6.233.17 + +jobs: + raster-tests-linux: + name: RasterCore tests + V8 syntax check (Linux) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Fetch V8 headers (${{ env.V8_TAG }}) + run: | + git clone --depth 1 --branch "$V8_TAG" --filter=blob:none --sparse \ + https://github.com/v8/v8.git v8_headers + cd v8_headers && git sparse-checkout set include + - name: Run raster tests + ASan + V8 syntax check + working-directory: templates/xbox + run: tests/check_local.sh "$GITHUB_WORKSPACE/v8_headers/include" + + windows-tests: + name: RasterCore + Windows platform tests (MSVC) + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Build & run RasterCore tests + shell: cmd + working-directory: templates/xbox + run: | + cl /nologo /std:c++17 /EHsc /W3 /O1 tests\raster_test.cpp /Fe:raster_test.exe + if errorlevel 1 exit /b 1 + raster_test.exe + + - name: Build & run Windows platform tests (WIC/DirectWrite/MF/XAudio2) + shell: cmd + working-directory: templates/xbox + run: | + cl /nologo /std:c++17 /EHsc /W3 /DNOMINMAX /Isrc ^ + tests\windows_platform_test.cpp ^ + src\platform\WicDecoder.cpp ^ + src\platform\TextRasterizer.cpp ^ + src\platform\AudioEngine.cpp ^ + /Fe:platform_test.exe ^ + ole32.lib uuid.lib windowscodecs.lib d2d1.lib dwrite.lib ^ + mfplat.lib mfreadwrite.lib mfuuid.lib xaudio2.lib shlwapi.lib + if errorlevel 1 exit /b 1 + platform_test.exe + + v8-syntax-check: + name: V8-dependent sources compile check (MSVC) + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Fetch V8 headers (${{ env.V8_TAG }}) + shell: pwsh + run: | + git clone --depth 1 --branch $env:V8_TAG --filter=blob:none --sparse ` + https://github.com/v8/v8.git v8_headers + Push-Location v8_headers + git sparse-checkout set include + Pop-Location + + - name: Compile-check (no link) + shell: pwsh + working-directory: templates/xbox + run: | + $v8inc = Join-Path $env:GITHUB_WORKSPACE "v8_headers/include" + # Dawn 依存 (webgpu_cpp.h が Dawn ビルドで生成されるため CI では検証不可): + # src/main.cpp, src/gpu/DawnContext.cpp, src/bindings/webgpu/WebGPU.cpp + # GDK 依存 (GameInput.h): + # src/platform/GamepadManager.cpp (下の別ステップで warn-only) + $files = @( + "src/AssetLoader.cpp", + "src/EventLoop.cpp", + "src/v8/V8Runtime.cpp", + "src/worker/WorkerRuntime.cpp", + "src/bindings/Audio.cpp", + "src/bindings/Canvas.cpp", + "src/bindings/Canvas2D.cpp", + "src/bindings/Console.cpp", + "src/bindings/DomShims.cpp", + "src/bindings/EventTarget.cpp", + "src/bindings/Fetch.cpp", + "src/bindings/Gamepad.cpp", + "src/bindings/Image.cpp", + "src/bindings/Network.cpp", + "src/bindings/Timers.cpp", + "src/bindings/Video.cpp", + "src/platform/AudioEngine.cpp", + "src/platform/WicDecoder.cpp", + "src/platform/TextRasterizer.cpp" + ) + # CMakeLists.txt と同じ定義でコンパイルのみ実行 (/c)。 + cl /c /nologo /std:c++20 /Zc:__cplusplus /EHsc /W3 ` + /DNOMINMAX /DWIN32_LEAN_AND_MEAN /DUNICODE /D_UNICODE ` + /DV8_COMPRESS_POINTERS /DV8_ENABLE_SANDBOX ` + /Isrc /I"$v8inc" ` + @files + if ($LASTEXITCODE -ne 0) { exit 1 } + Write-Host "OK: all V8-dependent sources compiled" + + - name: Compile-check GamepadManager (GameInput.h — warn only) + shell: pwsh + working-directory: templates/xbox + continue-on-error: true + run: | + # GameInput.h は GDK 付属。Windows SDK に含まれる環境でのみ検証できる。 + cl /c /nologo /std:c++20 /EHsc /W3 /DNOMINMAX /DWIN32_LEAN_AND_MEAN ` + /Isrc src/platform/GamepadManager.cpp + if ($LASTEXITCODE -ne 0) { + Write-Warning "GameInput.h not available in this SDK (expected without GDK)" + } diff --git a/README.md b/README.md index a561434..0056023 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,17 @@ With Next2D Framework, you can efficiently develop and deploy applications optim | web | Export minfy'd JS files | | iOS | open Xcode, and Export ipa | | iOS | open Android Studio, and Export apk | +| Xbox | GDK native title (V8 + Dawn/WebGPU) | + +Xbox exports a GDK native title that runs the Next2D JavaScript on the V8 engine and +renders with Dawn (WebGPU → D3D12), packaged as a C++ executable (no Electron/WebView). +Building the GDK package requires Windows + Visual Studio 2022 + Microsoft GDK + a devkit. +On other platforms the builder scaffolds the host project and stages assets only. +See the generated `xbox/README.md` for full build steps. + +```bat +npx @next2d/builder --platform xbox --env prd --v8-root C:\path\to\v8 +``` ### Scheduled introduction diff --git a/package-lock.json b/package-lock.json index 0493e40..93bbc45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,18 @@ { "name": "@next2d/builder", - "version": "0.1.5", + "version": "0.1.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@next2d/builder", - "version": "0.1.5", + "version": "0.1.6", "license": "MIT", "dependencies": { "@electron-forge/core": "^7.11.2", - "@types/node": "^25.9.2", + "@types/node": "^26.1.0", "picocolors": "^1.1.1", - "vite": "^8.0.16" + "vite": "^8.1.2" }, "bin": { "builder": "dist/index.js" @@ -20,11 +20,11 @@ "devDependencies": { "@eslint/eslintrc": "^3.3.5", "@eslint/js": "^10.0.1", - "@typescript-eslint/eslint-plugin": "^8.60.1", - "@typescript-eslint/parser": "^8.60.1", - "eslint": "^10.4.1", + "@typescript-eslint/eslint-plugin": "^8.62.1", + "@typescript-eslint/parser": "^8.62.1", + "eslint": "^10.6.0", "eslint-plugin-unused-imports": "^4.4.1", - "globals": "^17.6.0", + "globals": "^17.7.0", "typescript": "^6.0.3" } }, @@ -618,20 +618,20 @@ } }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "license": "MIT", "optional": true, "dependencies": { @@ -639,9 +639,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "license": "MIT", "optional": true, "dependencies": { @@ -979,13 +979,13 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -1059,18 +1059,18 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.138.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.138.0.tgz", + "integrity": "sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.4.tgz", + "integrity": "sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==", "cpu": [ "arm64" ], @@ -1084,9 +1084,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.4.tgz", + "integrity": "sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==", "cpu": [ "arm64" ], @@ -1100,9 +1100,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.4.tgz", + "integrity": "sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==", "cpu": [ "x64" ], @@ -1116,9 +1116,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.4.tgz", + "integrity": "sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==", "cpu": [ "x64" ], @@ -1132,9 +1132,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.4.tgz", + "integrity": "sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==", "cpu": [ "arm" ], @@ -1148,9 +1148,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.4.tgz", + "integrity": "sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==", "cpu": [ "arm64" ], @@ -1167,9 +1167,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.4.tgz", + "integrity": "sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==", "cpu": [ "arm64" ], @@ -1186,9 +1186,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.4.tgz", + "integrity": "sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==", "cpu": [ "ppc64" ], @@ -1205,9 +1205,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.4.tgz", + "integrity": "sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==", "cpu": [ "s390x" ], @@ -1224,9 +1224,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.4.tgz", + "integrity": "sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==", "cpu": [ "x64" ], @@ -1243,9 +1243,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.4.tgz", + "integrity": "sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==", "cpu": [ "x64" ], @@ -1262,9 +1262,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.4.tgz", + "integrity": "sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==", "cpu": [ "arm64" ], @@ -1278,27 +1278,27 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.4.tgz", + "integrity": "sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==", "cpu": [ "wasm32" ], "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.4.tgz", + "integrity": "sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==", "cpu": [ "arm64" ], @@ -1312,9 +1312,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.4.tgz", + "integrity": "sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==", "cpu": [ "x64" ], @@ -1367,9 +1367,9 @@ } }, "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "license": "MIT", "optional": true, "dependencies": { @@ -1423,12 +1423,12 @@ } }, "node_modules/@types/node": { - "version": "25.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz", - "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.0.tgz", + "integrity": "sha512-O0A1G3xPGy4w7AgQdAQYUlQ+BKk2Oovw8eRpofyp5KdBZULnbe+WqaOVNrm705SHphCiG4XHsACrSmPu1f+Kgw==", "license": "MIT", "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" + "undici-types": "~8.3.0" } }, "node_modules/@types/responselike": { @@ -1451,17 +1451,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", - "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.1.tgz", + "integrity": "sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/type-utils": "8.60.1", - "@typescript-eslint/utils": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", + "@typescript-eslint/scope-manager": "8.62.1", + "@typescript-eslint/type-utils": "8.62.1", + "@typescript-eslint/utils": "8.62.1", + "@typescript-eslint/visitor-keys": "8.62.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -1474,7 +1474,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.60.1", + "@typescript-eslint/parser": "^8.62.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -1490,16 +1490,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", - "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.1.tgz", + "integrity": "sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", + "@typescript-eslint/scope-manager": "8.62.1", + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/typescript-estree": "8.62.1", + "@typescript-eslint/visitor-keys": "8.62.1", "debug": "^4.4.3" }, "engines": { @@ -1515,14 +1515,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", - "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.1.tgz", + "integrity": "sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.60.1", - "@typescript-eslint/types": "^8.60.1", + "@typescript-eslint/tsconfig-utils": "^8.62.1", + "@typescript-eslint/types": "^8.62.1", "debug": "^4.4.3" }, "engines": { @@ -1537,14 +1537,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", - "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.1.tgz", + "integrity": "sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1" + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/visitor-keys": "8.62.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1555,9 +1555,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", - "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.1.tgz", + "integrity": "sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==", "dev": true, "license": "MIT", "engines": { @@ -1572,15 +1572,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", - "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.1.tgz", + "integrity": "sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1", - "@typescript-eslint/utils": "8.60.1", + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/typescript-estree": "8.62.1", + "@typescript-eslint/utils": "8.62.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1597,9 +1597,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", - "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.1.tgz", + "integrity": "sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==", "dev": true, "license": "MIT", "engines": { @@ -1611,16 +1611,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", - "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.1.tgz", + "integrity": "sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.60.1", - "@typescript-eslint/tsconfig-utils": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/visitor-keys": "8.60.1", + "@typescript-eslint/project-service": "8.62.1", + "@typescript-eslint/tsconfig-utils": "8.62.1", + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/visitor-keys": "8.62.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1649,9 +1649,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "dev": true, "license": "MIT", "dependencies": { @@ -1678,16 +1678,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", - "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.1.tgz", + "integrity": "sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.60.1", - "@typescript-eslint/types": "8.60.1", - "@typescript-eslint/typescript-estree": "8.60.1" + "@typescript-eslint/scope-manager": "8.62.1", + "@typescript-eslint/types": "8.62.1", + "@typescript-eslint/typescript-estree": "8.62.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1702,13 +1702,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.60.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", - "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", + "version": "8.62.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.1.tgz", + "integrity": "sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/types": "8.62.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2908,11 +2908,14 @@ } }, "node_modules/eslint": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz", - "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz", + "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==", "dev": true, "license": "MIT", + "workspaces": [ + "packages/*" + ], "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -3686,9 +3689,9 @@ } }, "node_modules/globals": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", - "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", + "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", "dev": true, "license": "MIT", "engines": { @@ -4863,9 +4866,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", "funding": [ { "type": "github", @@ -5382,9 +5385,9 @@ } }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", "funding": [ { "type": "opencollective", @@ -5830,12 +5833,12 @@ } }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.4.tgz", + "integrity": "sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==", "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.138.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -5845,21 +5848,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm64": "1.1.4", + "@rolldown/binding-darwin-arm64": "1.1.4", + "@rolldown/binding-darwin-x64": "1.1.4", + "@rolldown/binding-freebsd-x64": "1.1.4", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.4", + "@rolldown/binding-linux-arm64-gnu": "1.1.4", + "@rolldown/binding-linux-arm64-musl": "1.1.4", + "@rolldown/binding-linux-ppc64-gnu": "1.1.4", + "@rolldown/binding-linux-s390x-gnu": "1.1.4", + "@rolldown/binding-linux-x64-gnu": "1.1.4", + "@rolldown/binding-linux-x64-musl": "1.1.4", + "@rolldown/binding-openharmony-arm64": "1.1.4", + "@rolldown/binding-wasm32-wasi": "1.1.4", + "@rolldown/binding-win32-arm64-msvc": "1.1.4", + "@rolldown/binding-win32-x64-msvc": "1.1.4" } }, "node_modules/run-parallel": { @@ -6582,9 +6585,9 @@ } }, "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "license": "MIT" }, "node_modules/unique-filename": { @@ -6690,15 +6693,15 @@ } }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.2.tgz", + "integrity": "sha512-6YYPbRXTxx6bRXmOn7XdnQAy5DQNHhDgtjhDHI13oe4pY93kkcdGJWxpGwOm++/Wh0QpQhDrpIoVMrmrsI5AGQ==", "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "postcss": "^8.5.16", + "rolldown": "~1.1.3", "tinyglobby": "^0.2.17" }, "bin": { @@ -6715,7 +6718,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", diff --git a/package.json b/package.json index 7afb5d6..d76c6a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@next2d/builder", - "version": "0.1.5", + "version": "0.1.6", "description": "Multi-platform builder for Next2d Framework, supporting export to various platforms such as macOS, Windows, iOS, Android and Web(HTML)", "author": "Toshiyuki Ienaga ", "license": "MIT", @@ -17,6 +17,7 @@ "JavaScript for iOS", "JavaScript for Android", "JavaScript for Steam", + "JavaScript for Xbox", "Multi-platform builder" ], "scripts": { @@ -24,7 +25,8 @@ "release": "tsc" }, "files": [ - "dist" + "dist", + "templates" ], "repository": { "type": "git", @@ -35,18 +37,18 @@ }, "dependencies": { "@electron-forge/core": "^7.11.2", - "@types/node": "^25.9.2", + "@types/node": "^26.1.0", "picocolors": "^1.1.1", - "vite": "^8.0.16" + "vite": "^8.1.2" }, "devDependencies": { "@eslint/eslintrc": "^3.3.5", "@eslint/js": "^10.0.1", - "@typescript-eslint/eslint-plugin": "^8.60.1", - "@typescript-eslint/parser": "^8.60.1", - "eslint": "^10.4.1", + "@typescript-eslint/eslint-plugin": "^8.62.1", + "@typescript-eslint/parser": "^8.62.1", + "eslint": "^10.6.0", "eslint-plugin-unused-imports": "^4.4.1", - "globals": "^17.6.0", + "globals": "^17.7.0", "typescript": "^6.0.3" } } diff --git a/src/index.ts b/src/index.ts index d0ba55e..1c2d0b5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,8 @@ import pc from "picocolors"; import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; import cp from "child_process"; import { loadConfigFromFile } from "vite"; import { api } from "@electron-forge/core"; @@ -23,6 +25,7 @@ let hasHelp: boolean = false; let preview: boolean = false; let open: boolean = false; let build: boolean = false; +let v8Root: string = ""; for (let idx: number = 0; idx < process.argv.length; ++idx) { @@ -53,6 +56,11 @@ for (let idx: number = 0; idx < process.argv.length; ++idx) { environment = process.argv[++idx]; break; + case "--v8-root": + // Xbox ビルドで使う prebuilt V8 monolith のパス (環境変数 V8_ROOT より優先) + v8Root = process.argv[++idx] || ""; + break; + default: break; @@ -78,7 +86,7 @@ if (!platform || !environment) { const echoHelp = (): void => { console.log(); - console.log(pc.green("`--platform` can be specified for macOS, Windows, iOS, Android, and Web")); + console.log(pc.green("`--platform` can be specified for macOS, Windows, iOS, Android, Xbox, and Web")); console.log(pc.green("It is not case sensitive.")); console.log(); console.log("For build example:"); @@ -87,6 +95,9 @@ const echoHelp = (): void => console.log("For preview example:"); console.log("npx @next2d/builder --preview --platform web --env prd"); console.log(); + console.log("For Xbox build example (prebuilt V8 monolith path):"); + console.log("npx @next2d/builder --platform xbox --env prd --v8-root C:\\path\\to\\v8"); + console.log(); process.exit(1); }; @@ -108,6 +119,7 @@ switch (platform) { case "steam:linux": case "ios": case "android": + case "xbox": case "web": break; @@ -573,6 +585,270 @@ const buildNative = async (): Promise => }); }; +/** + * @type {string} + * @constant + */ +const XBOX_DIR_NAME: string = "xbox"; + +/** + * @description builder パッケージに同梱したテンプレートの絶対パスを取得 + * Get the absolute path of a template shipped with the builder package + * + * @param {string} name + * @return {string} + * @method + * @public + */ +const getTemplateDir = (name: string): string => +{ + // dist/index.js から見た templates/ (パッケージルート直下) + const currentDir: string = path.dirname(fileURLToPath(import.meta.url)); + return path.resolve(currentDir, "..", "templates", name); +}; + +/** + * @type {string} + * @constant + */ +const XBOX_CONFIG_NAME: string = "MicrosoftGame.config"; + +/** + * @description ゲーム側の `xbox/` へスキャフォールドしないテンプレート内ファイル。 + * - MicrosoftGame.config : ゲーム固有設定 (injectGameConfig が注入) + * - tests / .v8_headers : builder リポジトリ側の開発用テスト・キャッシュ + * - build 系 : テンプレート内でビルドした場合の生成物 + * Template entries excluded from the per-game `xbox/` scaffold. + * + * @type {Set} + * @constant + */ +const XBOX_SCAFFOLD_EXCLUDES: Set = new Set([ + XBOX_CONFIG_NAME, + "tests", + ".v8_headers", + "build", + "out", + "_deps", + "CMakeCache.txt", + "CMakeFiles" +]); + +/** + * @description XboxホストのC++/CMake一式をbuilder同梱テンプレートから毎回更新する。 + * ホストは「エンジン相当」でユーザーは編集しない前提のため、常に最新へ上書きする。 + * ただし各ゲーム固有の `MicrosoftGame.config` は対象外(injectGameConfigが扱う)。 + * Refresh the Xbox host (C++/CMake) from the builder's bundled template on every build. + * The host is engine-like and not user-edited, so it is always overwritten, + * excluding the per-game `MicrosoftGame.config` (handled by injectGameConfig). + * + * @return {Promise} + * @method + * @public + */ +const refreshXboxHost = (): Promise => +{ + return new Promise((resolve, reject): void => + { + const projectDir: string = `${process.cwd()}/${XBOX_DIR_NAME}`; + const templateDir: string = getTemplateDir("xbox"); + if (!fs.existsSync(templateDir)) { + return reject(`Xbox template not found: ${templateDir}`); + } + + try { + fs.cpSync(templateDir, projectDir, { + "recursive": true, + // ゲーム固有設定と builder 側の開発用ファイルはスキャフォールドしない + "filter": (src: string): boolean => !XBOX_SCAFFOLD_EXCLUDES.has(path.basename(src)) + }); + console.log(pc.green(`Successfully refreshed \`${XBOX_DIR_NAME}\` host.`)); + resolve(); + } catch (error) { + reject(`Failed to refresh ${XBOX_DIR_NAME} host. ${error}`); + } + }); +}; + +/** + * @description ゲームルートの `MicrosoftGame.config` をXboxホストへ注入する。 + * capacitor.config.json と同様、各ゲーム固有の設定(TitleId/StoreId/名称/ロゴ)は + * プロジェクトルートで管理し、それをホストのビルド対象へ配置する。 + * 未配置ならbuilder同梱の既定をフォールバックとして使い、作成を促す。 + * Inject the game-root `MicrosoftGame.config` into the Xbox host. + * + * @return {Promise} + * @method + * @public + */ +const injectGameConfig = (): Promise => +{ + return new Promise((resolve, reject): void => + { + const rootConfig: string = `${process.cwd()}/${XBOX_CONFIG_NAME}`; + const hostConfig: string = `${process.cwd()}/${XBOX_DIR_NAME}/${XBOX_CONFIG_NAME}`; + + try { + if (fs.existsSync(rootConfig)) { + fs.cpSync(rootConfig, hostConfig); + console.log(pc.green(`Applied project \`${XBOX_CONFIG_NAME}\`.`)); + } else { + // フォールバック: builder同梱の既定を配置し、ルートへの作成を促す + fs.cpSync(`${getTemplateDir("xbox")}/${XBOX_CONFIG_NAME}`, hostConfig); + console.log(pc.yellow(`\`${XBOX_CONFIG_NAME}\` not found at project root. Using the default template.`)); + console.log(pc.yellow(`Create \`${XBOX_CONFIG_NAME}\` at your project root (like capacitor.config.json) to set TitleId/StoreId/logos.`)); + } + resolve(); + } catch (error) { + reject(`Failed to apply ${XBOX_CONFIG_NAME}. ${error}`); + } + }); +}; + +/** + * @description ビルド済みWeb資材(JS/HTML/アセット)をXboxホストのassetsへ配置 + * Deploy built web resources (JS/HTML/assets) into the Xbox host assets + * + * @return {Promise} + * @method + * @public + */ +const copyXboxResources = (): Promise => +{ + return new Promise((resolve, reject): void => + { + const assetsDir: string = `${process.cwd()}/${XBOX_DIR_NAME}/assets/app`; + + try { + // reset + fs.rmSync(assetsDir, { "recursive": true, "force": true }); + fs.mkdirSync(assetsDir, { "recursive": true }); + + // copy built web resources (JS/HTML/assets) + fs.cpSync(`${$buildDir}/`, assetsDir, { "recursive": true }); + + console.log(pc.green("Successfully copy built resources to Xbox host.")); + resolve(); + } catch (error) { + reject(`Failed to copy built resources to Xbox host. ${error}`); + } + }); +}; + +/** + * @description Xbox(GDKネイティブ)用アプリの書き出し関数。 + * V8にNext2DのJSを載せ、Dawn(WebGPU/D3D12)で描画するC++ホストをビルドする。 + * Export function for Xbox (GDK native) apps. + * Builds a C++ host that runs Next2D's JS on V8 and renders via Dawn (WebGPU/D3D12). + * + * @return {Promise} + * @method + * @public + */ +const buildXbox = async (): Promise => +{ + // C++/CMake ホストを最新へ更新 (ゲーム固有設定は除外) + await refreshXboxHost(); + // ゲームルートの MicrosoftGame.config を注入 + await injectGameConfig(); + // ビルド済みWeb資材を配置 + await copyXboxResources(); + + /** + * GDK ビルドは Windows + Visual Studio + Microsoft GDK が必須。 + * それ以外の環境ではスキャフォールドと資材配置のみを行い、手順を案内する。 + * The GDK build requires Windows + Visual Studio + Microsoft GDK. + * On other platforms we only scaffold and copy assets, then guide the next steps. + */ + if (process.platform !== "win32") { + console.log(); + console.log(pc.yellow("Xbox host project has been generated and assets were copied.")); + console.log(pc.yellow("The GDK build must run on Windows with Visual Studio and the Microsoft GDK installed.")); + console.log(pc.yellow(`See ${XBOX_DIR_NAME}/README.md for the build steps.`)); + console.log(); + return; + } + + /** + * @type {string} + * GDK の対象コンソール世代。既定は Xbox Series X|S (Scarlett)。 + * 必要に応じて `Gaming.Xbox.XboxOne.x64` / `Gaming.Desktop.x64` を選択。 + */ + const gdkArch: string = process.env.NEXT2D_XBOX_ARCH || "Gaming.Xbox.Scarlett.x64"; + const cmakeBuildDir: string = `${process.cwd()}/${$outDir}/${platformDir}/build`; + + /** + * prebuilt V8 monolith のパス。優先順: `--v8-root` 引数 > 環境変数 V8_ROOT。 + * どちらも無い場合は CMake (FindV8.cmake) が明確なエラーで停止するため、 + * ここでは事前に分かりやすく案内だけする。 + */ + const resolvedV8Root: string = v8Root + ? path.resolve(process.cwd(), v8Root) + : process.env.V8_ROOT || ""; + + if (!resolvedV8Root) { + console.log(pc.red("V8 path is not specified.")); + console.log(pc.red("Pass `--v8-root ` (or set the V8_ROOT environment variable):")); + console.log(pc.red(" npx @next2d/builder --platform xbox --env prd --v8-root C:\\path\\to\\v8")); + console.log(pc.red(`The path must contain \`include/v8.h\` and \`lib/v8_monolith.lib\`. See ${XBOX_DIR_NAME}/README.md.`)); + return; + } + if (!fs.existsSync(`${resolvedV8Root}/include/v8.h`)) { + console.log(pc.red(`\`include/v8.h\` not found under: ${resolvedV8Root}`)); + console.log(pc.red(`Check the \`--v8-root\` path. See ${XBOX_DIR_NAME}/README.md for how to prepare the prebuilt V8.`)); + return; + } + + const configure = (): Promise => + { + return new Promise((resolve, reject): void => + { + const stream = cp.spawn("cmake", [ + "-S", `${process.cwd()}/${XBOX_DIR_NAME}`, + "-B", cmakeBuildDir, + "-G", "Visual Studio 17 2022", + "-A", gdkArch, + "-D", `V8_ROOT=${resolvedV8Root}` + ], { "stdio": "inherit" }); + + stream.on("close", (code: number): void => + { + if (code !== 0) { + return reject("Failed to configure the Xbox (CMake/GDK) project."); + } + resolve(); + }); + }); + }; + + await configure(); + + if (open || preview) { + // Visual Studio ソリューションを開く (実機/エミュレータでの実行はVS側で行う) + cp.spawn("cmake", [ + "--open", cmakeBuildDir + ], { "stdio": "inherit" }); + return; + } + + // フルビルド (Release パッケージまで) + const stream = cp.spawn("cmake", [ + "--build", cmakeBuildDir, + "--config", "Release" + ], { "stdio": "inherit" }); + + stream.on("close", (code: number): void => + { + if (code !== 0) { + console.log(pc.red("Export of the Xbox (GDK) package failed.")); + return; + } + console.log(); + console.log(pc.green(`Finished building the Xbox (GDK) package for ${gdkArch}.`)); + console.log(); + }); +}; + /** * @description ビルドの実行関数 * Build Execution Functions @@ -615,8 +891,8 @@ const multiBuild = async (): Promise => } break; - case "open:ios": - await openNative(); + case "xbox": + await buildXbox(); break; case "web": diff --git a/templates/xbox/.gitignore b/templates/xbox/.gitignore new file mode 100644 index 0000000..219531a --- /dev/null +++ b/templates/xbox/.gitignore @@ -0,0 +1,16 @@ +# ビルド成果物 +build/ +out/ +*.exe +*.pdb +*.msixvc +*.xvc + +# builder が assets/app にビルド済みWeb資材(vite出力)を配置する (生成物) +assets/ + +# Dawn / CMake の取得キャッシュ +_deps/ +CMakeCache.txt +CMakeFiles/ +.v8_headers/ diff --git a/templates/xbox/CMakeLists.txt b/templates/xbox/CMakeLists.txt new file mode 100644 index 0000000..7dfb170 --- /dev/null +++ b/templates/xbox/CMakeLists.txt @@ -0,0 +1,121 @@ +# ============================================================================= +# Next2D Xbox (GDK native) host +# +# V8 に Next2D の JavaScript を載せ、Dawn(WebGPU/D3D12) で描画する +# ネイティブタイトルをビルドする CMake プロジェクト。 +# +# 対象トリプレット (builder が -A に渡す): +# - Gaming.Xbox.Scarlett.x64 : Xbox Series X|S +# - Gaming.Xbox.XboxOne.x64 : Xbox One +# - Gaming.Desktop.x64 : PC(GDK) / ローカル検証 +# +# 必須: +# - Windows + Visual Studio 2022 (v143) +# - Microsoft GDK (June 2024 以降) +# - V8 の prebuilt ライブラリ (V8_ROOT で指定) +# - Dawn (FetchContent で取得・ビルド) +# ============================================================================= +cmake_minimum_required(VERSION 3.26) + +project(Next2DXboxHost LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# GDK target platform (Gaming.Xbox.* / Gaming.Desktop.*) の判定 +if(NOT DEFINED CMAKE_GENERATOR_PLATFORM) + message(WARNING "CMAKE_GENERATOR_PLATFORM が未設定です。builder は -A Gaming.Xbox.Scarlett.x64 等を渡します。") +endif() + +string(FIND "${CMAKE_GENERATOR_PLATFORM}" "Gaming.Xbox" _is_console) +if(_is_console GREATER_EQUAL 0) + set(NEXT2D_IS_CONSOLE ON) +else() + set(NEXT2D_IS_CONSOLE OFF) +endif() + +# ----------------------------------------------------------------------------- +# 依存: Dawn (WebGPU 実装 / D3D12 バックエンド) +# ----------------------------------------------------------------------------- +include(cmake/FetchDawn.cmake) + +# ----------------------------------------------------------------------------- +# 依存: V8 (prebuilt) +# V8 はソースからのビルドが重いため prebuilt を前提とする。 +# cmake -D V8_ROOT= で monolith ビルド (v8_monolith.lib) を指す。 +# ----------------------------------------------------------------------------- +include(cmake/FindV8.cmake) + +# ----------------------------------------------------------------------------- +# ソース +# ----------------------------------------------------------------------------- +file(GLOB_RECURSE NEXT2D_HOST_SOURCES + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h" +) + +add_executable(Next2DXboxHost WIN32 ${NEXT2D_HOST_SOURCES}) + +target_include_directories(Next2DXboxHost PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/src" +) + +target_link_libraries(Next2DXboxHost PRIVATE + v8::v8 + dawn::webgpu_dawn +) + +# WebGPU / D3D12 / GameInput / WIC / XAudio2 / GDK +target_link_libraries(Next2DXboxHost PRIVATE + d3d12 + dxgi + dxguid + windowscodecs # WIC: 画像デコード +) + +if(NEXT2D_IS_CONSOLE) + # コンソール用ライブラリ (GDK が提供) + target_link_libraries(Next2DXboxHost PRIVATE + xgameruntime + gameinput + xaudio2_9 # コンソールは XAudio2.9 + ) + target_compile_definitions(Next2DXboxHost PRIVATE NEXT2D_XBOX_CONSOLE=1) +else() + # PC(GDK) / ローカル検証 + target_link_libraries(Next2DXboxHost PRIVATE + xgameruntime + gameinput + xaudio2 + ) + target_compile_definitions(Next2DXboxHost PRIVATE NEXT2D_XBOX_CONSOLE=0) +endif() + +# V8 のプラットフォーム要件 +target_compile_definitions(Next2DXboxHost PRIVATE + V8_COMPRESS_POINTERS + V8_ENABLE_SANDBOX + NOMINMAX + WIN32_LEAN_AND_MEAN + UNICODE + _UNICODE +) + +# ----------------------------------------------------------------------------- +# ランタイム資材のステージング +# builder が assets/app に JS/HTML/アセットを配置する。 +# js/bootstrap.js はブラウザ相当グローバルを整える起動スクリプト。 +# ----------------------------------------------------------------------------- +add_custom_command(TARGET Next2DXboxHost POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/assets" + "$/assets" + COMMAND ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/js" + "$/js" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_CURRENT_SOURCE_DIR}/MicrosoftGame.config" + "$/MicrosoftGame.config" + COMMENT "Staging Next2D assets, bootstrap JS and MicrosoftGame.config" +) diff --git a/templates/xbox/MicrosoftGame.config b/templates/xbox/MicrosoftGame.config new file mode 100644 index 0000000..f1976b6 --- /dev/null +++ b/templates/xbox/MicrosoftGame.config @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + 00000000000000000000000000000000 + 00000000 + 0000000000000000 + + + + + + diff --git a/templates/xbox/README.md b/templates/xbox/README.md new file mode 100644 index 0000000..211b7c2 --- /dev/null +++ b/templates/xbox/README.md @@ -0,0 +1,319 @@ +Next2D Xbox Host (GDK native / V8 + Dawn WebGPU) +================================================ + +このディレクトリは `@next2d/builder --platform xbox` が生成する **Xbox GDK ネイティブタイトル** の +ホストプロジェクトです。Next2D の JavaScript を **V8** で実行し、描画を **Dawn(WebGPU → D3D12)** で行う +C++ 実行ファイルをビルドします。Electron や WebView は使用しません。 + +> **重要 / 検証ステータス** +> 本テンプレートは *アーキテクチャ的に整合したフルスキャフォールド* です。Xbox GDK タイトルの +> コンパイル・実機動作確認には **Windows + Visual Studio 2022 + Microsoft GDK + 開発機(devkit)** が +> 必須で、macOS/Linux では検証できません。GDK/実機が揃った段階で、下記「実機検証で残る作業」に沿って +> 最終調整(WebGPU バインディングの Next2D レンダラ実使用箇所への追従、画像アップロード経路、 +> ストア設定値)を行ってください。 + +--- + +## アーキテクチャ + +``` +┌──────────────────────────────────────────────────────────────┐ +│ Next2D アプリ (assets/app/*.js ← builder が vite build で生成) │ +│ WebGPU / Canvas / rAF / fetch / Gamepad / WebAudio を使用 │ +└───────────────▲──────────────────────────────────────────────┘ + │ グローバル(navigator.gpu, requestAnimationFrame, ...) +┌───────────────┴──────────────────────────────────────────────┐ +│ js/bootstrap.js TextEncoder/TextDecoder/URL 等の JS 層補完 │ +└───────────────▲──────────────────────────────────────────────┘ + │ V8 バインディング (src/bindings/*) +┌───────────────┴──────────────────────────────────────────────┐ +│ C++ ホスト (src/) │ +│ ├─ v8/V8Runtime Isolate/Context/ESM/マイクロタスク │ +│ ├─ EventLoop setTimeout/rAF/performance.now │ +│ ├─ bindings/ console/timers/fetch/Image/Audio/ │ +│ │ WebGPU(navigator.gpu)/Gamepad/DOM │ +│ ├─ gpu/DawnContext Instance/Adapter/Device/Surface(D3D12) │ +│ ├─ platform/GamepadManager GameInput → W3C Gamepad │ +│ ├─ platform/AudioEngine XAudio2 + Media Foundation デコード │ +│ └─ main.cpp GDK 初期化 / ウィンドウ / ゲームループ │ +└──────────────────────────────────────────────────────────────┘ +``` + +ゲームループ(`main.cpp`)は毎フレーム: +1. Win32 メッセージ処理 +2. ゲームパッド入力ポーリング(GameInput) +3. メインの タイマー実行 → マイクロタスク +4. メインの `requestAnimationFrame`(アプリのロジック/描画コマンド生成) +5. **Worker のメッセージ配送 + rAF**(レンダラ worker がここで WebGPU コマンドを submit) +6. V8 プラットフォームタスク + Dawn `ProcessEvents` +7. サーフェス Present + +### ランタイム環境の要点 + +- **jitless V8**: Xbox/Switch のリテールは JIT(動的コード生成)を禁止するため、V8 は + `--jitless`(Ignition インタプリタのみ)で起動する。WebAssembly も無効。 +- **Worker / OffscreenCanvas**: player はレンダラを Worker + `transferControlToOffscreen` + で動かす。本ホストは **「同一 Isolate 内の別 Context を協調スケジューリングする単一スレッド + Worker モデル」** を採用(`src/worker/WorkerRuntime`)。`postMessage` は V8 の + ValueSerializer による structured clone、OffscreenCanvas は transfer 対応。GPU を 1 スレッドに + 保てるため Dawn のスレッド安全性問題を避けられる。真のマルチスレッド化は将来の最適化。 +- **Canvas 2D**: `src/bindings/Canvas2D.cpp` にソフトウェア実装。パス/変換/`isPointInPath` + (ヒット判定の中核)・`fill`/`stroke`・`getImageData` を実装。実グリフ描画(`fillText` の見た目)は + 未実装(`measureText` は近似メトリクス)。 +- **グラフィックスバックエンド抽象化**: `HostContext::backend`(WebGPU/WebGL2)で切替。Xbox は + WebGPU。`navigator.gpu` はバックエンドが WebGPU のときのみ公開し、WebGL2(Switch)では + player が `canvas.getContext('webgl2')` へ自動フォールバックする。canvas の getContext は + `src/bindings/Canvas.cpp` が backend に応じて `2d`/`webgpu`/`webgl2` をディスパッチ。 + +--- + +## 必要環境 + +| 項目 | 内容 | +|------|------| +| OS | Windows 11 | +| IDE | Visual Studio 2022 (v143, C++ Game development workload) | +| GDK | Microsoft GDK (June 2024 以降) — ID@Xbox 登録が必要 | +| CMake | 3.26 以降 | +| V8 | prebuilt monolith (`v8_monolith.lib` + `include/`) | +| Dawn | CMake の FetchContent で自動取得(ネットワーク必須) | +| devkit | Xbox 開発機(実機検証用) | + +--- + +## V8 の用意 + +V8 はソースビルドが非常に重いため prebuilt monolith を使います。自前ビルド例: + +```bat +:: depot_tools 導入後 +fetch v8 +cd v8 +gn gen out\x64.release --args="v8_monolithic=true v8_use_external_startup_data=false is_component_build=false target_cpu=\"x64\" is_debug=false v8_enable_pointer_compression=true v8_enable_sandbox=true" +ninja -C out\x64.release v8_monolith +``` + +生成物を次の構成で配置し、builder の `--v8-root` 引数 (または環境変数 `V8_ROOT`) で指すこと: + +``` +/include/v8.h ... +/lib/v8_monolith.lib +``` + +> V8 のビルドフラグ(pointer compression / sandbox)は `CMakeLists.txt` の +> `V8_COMPRESS_POINTERS` / `V8_ENABLE_SANDBOX` と一致させてください。不一致は ABI 崩れの原因になります。 + +--- + +## ビルド手順 + +builder 経由(推奨): + +```bat +:: プロジェクトルートで。V8 は --v8-root で指定する (環境変数 V8_ROOT でも可) +npx @next2d/builder --platform xbox --env prd --v8-root C:\path\to\v8 +``` + +builder は次を行います: +1. Web 資材を `vite build` で生成 +2. `xbox/assets/app` へ資材を配置 +3. CMake で GDK 構成(`-A Gaming.Xbox.Scarlett.x64`)を生成 +4. `cmake --build`(Release)でパッケージ生成 + +対象世代を変える場合は環境変数 `NEXT2D_XBOX_ARCH` を設定: +`Gaming.Xbox.Scarlett.x64`(既定) / `Gaming.Xbox.XboxOne.x64` / `Gaming.Desktop.x64` + +Visual Studio で開いて実行/デバッグ: + +```bat +npx @next2d/builder --platform xbox --env prd --open +``` + +CMake を直接叩く場合: + +```bat +cmake -S xbox -B out/xbox/build -G "Visual Studio 17 2022" -A Gaming.Xbox.Scarlett.x64 -D V8_ROOT=%V8_ROOT% +cmake --build out/xbox/build --config Release +``` + +--- + +## テスト + +Windows / GDK が無い環境でも検証できるよう、テストは 3 層に分かれています。 + +> 1・2 は **builder リポジトリ (Next2D/builder) 側**の開発用テストで、ゲーム側の +> `xbox/` にはコピーされません (`tests/` はスキャフォールド対象外)。 +> ゲーム側で実行するのは 3 のセルフテストのみです。 + +### 1. どこでも実行できる単体テスト (`tests/raster_test.cpp`) + +Canvas2D のソフトラスタライザ純ロジック (`src/bindings/RasterCore.h` — +塗り/winding/クリップ/色解析/曲線フラット化) は V8/Windows 非依存で、 +macOS / Linux でそのまま実行できます: + +```bash +# raster テスト + ASan + V8 ヘッダに対する構文チェックを一括実行 +tests/check_local.sh +``` + +構文チェックは実 V8 ヘッダ (pin: `13.6.233.17`) に対して、Windows API 非依存の +V8 バインディング 12 ファイルをコンパイル検証します (API 削除・型不整合を検出)。 + +### 2. GitHub Actions (windows-latest) — `.github/workflows/xbox-host-ci.yml` + +ローカルに Windows が無くても、push すると CI が Windows 上で実行します: + +- **RasterCore 単体テスト** を MSVC でビルド・実行 (Linux では gcc + ASan/UBSan でも) +- **Windows 実 API smoke テスト** (`tests/windows_platform_test.cpp`): + WIC の PNG デコード、DirectWrite/Direct2D のグリフラスタライズ (英/日)、 + Media Foundation の音声デコード、XAudio2 初期化 — を実際に実行して検証 +- **V8 依存ソースのコンパイル検証** (V8 ヘッダ + MSVC、リンクなし) + +CI 対象外 (実機セットアップ時に検証): Dawn 依存の `WebGPU.cpp` / `DawnContext.cpp` / +`main.cpp` (webgpu_cpp.h が Dawn ビルドで生成されるため) と GDK 依存の `GamepadManager.cpp`。 + +### 3. 実機セルフテスト (`js/selftest.js`) + +ビルドが通ったら最初に実行するテスト。アプリの代わりに全バインディングを +実機/PC 上で網羅実行し、TAP 形式で結果を出して自動終了します: + +```bat +Next2DXboxHost.exe --selftest +``` + +検証内容: タイマー/rAF/イベント、WIC 画像デコード、Canvas2D (fill/clip/ +isPointInPath/fillText/drawImage)、Blob/URL/XHR、Worker (blob URL + +structured clone + OffscreenCanvas 転送)、**WebGPU 実行経路** (バッファ読み戻し、 +copyExternalImageToTexture、**dynamic offset 付き描画**、**stencil8 マスク描画**)、 +音声 (decodeAudioData/gain/ended)、ゲームパッド、クリップボード。 +終了コード 0 = 全パス。 + +--- + +## WebGPU バインディングの実装範囲 + +`src/bindings/webgpu/` は **Next2D の WebGPU レンダラが使用するコア経路** を実装しています: + +- `navigator.gpu` : `requestAdapter` / `getPreferredCanvasFormat` +- `GPUAdapter` : `requestDevice` +- `GPUDevice` : `createBuffer` / `createTexture` / `createSampler` / `createShaderModule` / + `createBindGroupLayout` / `createBindGroup` / `createPipelineLayout` / + `createRenderPipeline` / `createCommandEncoder` / `queue` / `limits` +- `GPUQueue` : `submit` / `writeBuffer` / `writeTexture` / `copyExternalImageToTexture` + (画像/テキスト/動画のテクスチャ化。CPUで flipY/premultiply を適用し WriteTexture へ) +- `GPUCommandEncoder` : `beginRenderPass` / `finish` / `copyBufferToBuffer` / + `copyTextureToTexture` / `copyTextureToBuffer`(ピクセル読み戻し) +- `GPURenderPassEncoder` : `setPipeline` / `setBindGroup`(**dynamic offsets 対応**) / + `setVertexBuffer` / `setIndexBuffer` / `setViewport` / `setScissorRect` / + `setStencilReference`(**マスク処理**) / `setBlendConstant` / `draw` / `drawIndexed` / + `drawIndirect`(コンテナ一括描画) / `end` +- ステンシル: `depthStencil`(`stencilFront`/`stencilBack`/`stencilReadMask`/`stencilWriteMask`、 + `stencil8` フォーマット)と `depthStencilAttachment`(`stencilLoadOp`/`stencilStoreOp`/ + `stencilClearValue`)を解析。Next2D のマスク/クリップ描画の中核。 +- `GPUBuffer` : `getMappedRange` / `mapAsync`(READ) / `unmap` / `destroy` +- `GPUTexture` : `createView` / `destroy` +- `canvas.getContext('webgpu')` : `configure` / `getCurrentTexture` + +> player(`@next2d/player`) の実 API 使用を突合済み。**コンピュートシェーダ / render bundle / +> indexed draw / importExternalTexture は player が未使用**のため未実装で問題なし。 +- 定数: `GPUBufferUsage` / `GPUTextureUsage` / `GPUShaderStage` / `GPUColorWrite` / `GPUMapMode` + +WebGPU IDL は約 40 型・数百メソッドあり、全面手書きは現実的でないため、上記コアを実装し +拡張ポイントを `«EXTEND»` コメントで明示しています。追加が必要になった API は同じラップ +パターン(`WrapWith` / `Unwrap` / ディスクリプタ解析ヘルパー)で追記してください。 + +--- + +## Next2D 機能カバレッジ (player 突合) + +player の実 API 使用を全面調査し、以下を実装済み。**この環境(macOS/GDK・devkit 無)では +コンパイル・実機検証は不可**のため、下表は「コード上の対応状況」であり実機での見た目・挙動は +別途検証が必要です。 + +| 機能 | 状態 | 備考 | +|------|------|------| +| WebGPU コア描画 (shape/mask/blend/pipeline) | ✅ 実装 | 初期化 limits も対応 | +| マスク/クリップ (ステンシル + `setStencilReference` + dynamic offset) | ✅ 実装 | `stencil8`/`stencilFront`/`stencilBack` を解析 | +| コンテナ一括描画 (`drawIndirect`) | ✅ 実装 | | +| 画像/テキスト/動画のテクスチャ化 (`copyExternalImageToTexture`) | ✅ 実装 | flipY/premultiply を CPU 適用 | +| フィルタ (`copyTextureToTexture` + fragment shader) | ✅ 実装 | コンピュート不使用のため対応可 | +| ピクセル読み戻し (`copyTextureToBuffer`+`mapAsync`) | ✅ 実装 | BitmapData/getPixels | +| ヒット判定 (Canvas2D `isPointInPath`) | ✅ 実装 | ソフトラスタ | +| **テキスト表示** (`fillText`/`measureText`/`clip`) | ⚠️ 実装(要検証) | DirectWrite/Direct2D。`clip` は矩形近似(TextField 枠に十分)。ベースライン/合字/絵文字は実機検証 | +| アセット読込 (`XMLHttpRequest`) | ✅ 実装 | assets/blob から同期読込 | +| Worker/OffscreenCanvas (レンダラ) | ✅ 実装(要検証) | 協調シングルスレッド + `?worker&inline`(blob) 対応 | +| `Blob`/`URL.createObjectURL`/`ImageData` | ✅ 実装 | | +| ゲームパッド入力 | ✅ 実装 | `navigator.getGamepads` ポーリング | +| サウンド (WebAudio 最小) | ⚠️ 実装(要検証) | decode/play/stop/音量/ミュート/`ended` 発火に対応。AnalyserNode 等は未対応 | +| ポインタ/キーボード/ホイール入力 | ⚠️ 実装(要検証) | `WndProc`→`DispatchEvent`。配送先canvasの特定は実機検証 | +| 動画 (HTMLVideoElement) | ⚠️ 実装(要検証) | `Video.cpp`(Media Foundation)。フレーム精度/音声同期は実機検証 | +| クリップボード (`navigator.clipboard`) | ✅ 実装 | Win32 クリップボード (readText/writeText) | + +> player が使用する API はコード上すべて実装済み。残る唯一の障壁は +> **GDK/devkit を要するコンパイル・実機検証**(この環境では物理的に不可)。 + +## 実機検証で残る作業 + +0. **まずセルフテスト**: ビルドが通ったら `Next2DXboxHost.exe --selftest` を実行。 + 全バインディングを一括検証し、失敗箇所が TAP 形式で列挙されます (上記「テスト」参照)。 +1. **Dawn / V8 API 追従**: `FetchDawn.cmake` の `DAWN_TAG` と V8 バージョンで + webgpu_cpp のシンボル名(例 `ShaderSourceWGSL` / `Limits` / `GetLimits` の戻り値型)が + 異なる場合があります。該当タグの `webgpu_cpp.h` に合わせて調整してください。 +2. **テキストの見た目**: `Canvas2D.cpp` の `fillText`/`measureText` は DirectWrite/Direct2D で + 実装済みですが、ベースライン位置・`textAlign`/`textBaseline`・合字/絵文字は実機で要検証。 +3. **ポインタ/キーボード入力**: `main.cpp` の `WndProc` でマウス/キー/ホイールを + `DispatchEvent` 経由で window/document/主要canvas へ配送済み。ただし player がリスナを + 登録する canvas インスタンスの特定はヒューリスティック(最後に生成した canvas)のため、 + 実機で正しい配送先か検証・調整してください。キーボードは window 登録で信頼できます。 +4. **動画 (任意機能)**: `