diff --git a/.github/workflows/generate-swift-docs.yml b/.github/workflows/generate-swift-docs.yml new file mode 100644 index 000000000..3c4ec7201 --- /dev/null +++ b/.github/workflows/generate-swift-docs.yml @@ -0,0 +1,71 @@ +name: Generate Swift Docs + +on: + push: + branches: [main] + paths: + - "Sources/**" + - "Package.swift" + - "scripts/swift-docs/**" + - ".github/workflows/generate-swift-docs.yml" + schedule: + - cron: "0 3 * * *" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + name: Generate TypeDoc JSON + runs-on: macos-15 + steps: + - name: Checkout supabase-swift + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "22" + + - name: Install swift-docs dependencies + run: npm ci + working-directory: scripts/swift-docs + + - name: Generate TypeDoc JSON + run: | + npx tsx src/index.ts \ + ../../ \ + spec.json \ + supabase-swift-categories.json + working-directory: scripts/swift-docs + + - name: Prepare Pages artifact + run: | + mkdir -p site/v2 + cp scripts/swift-docs/spec.json site/v2/spec.json + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + with: + path: site/ + + deploy: + name: Deploy to GitHub Pages + runs-on: ubuntu-latest + needs: build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 diff --git a/scripts/swift-docs/.gitignore b/scripts/swift-docs/.gitignore new file mode 100644 index 000000000..c2658d7d1 --- /dev/null +++ b/scripts/swift-docs/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/scripts/swift-docs/package-lock.json b/scripts/swift-docs/package-lock.json new file mode 100644 index 000000000..ac1ac2590 --- /dev/null +++ b/scripts/swift-docs/package-lock.json @@ -0,0 +1,1831 @@ +{ + "name": "@supabase/swift-docs", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@supabase/swift-docs", + "version": "0.1.0", + "devDependencies": { + "@types/node": "^22", + "tsx": "^4.19.2", + "typescript": "^5.7.2", + "vitest": "^4.1.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", + "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "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==", + "dev": true, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "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==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "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==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.0.tgz", + "integrity": "sha512-QWlFW2wf3nTjC13/DqRnBpR4ZO36VJH/JVBkA/vcnmbTBNQIlnObqyqZE1tUR7+Ni23Lda8R1BxMfbXRpCUx5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", + "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", + "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", + "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", + "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.9", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", + "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "@vitest/utils": "4.1.9", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", + "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", + "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.9", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/obug": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rolldown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "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" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/tsx": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", + "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", + "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9", + "@vitest/coverage-istanbul": "4.1.9", + "@vitest/coverage-v8": "4.1.9", + "@vitest/ui": "4.1.9", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "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", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/scripts/swift-docs/package.json b/scripts/swift-docs/package.json new file mode 100644 index 000000000..e761d1861 --- /dev/null +++ b/scripts/swift-docs/package.json @@ -0,0 +1,18 @@ +{ + "name": "@supabase/swift-docs", + "private": true, + "type": "module", + "version": "0.1.0", + "description": "Generates TypeDoc-compatible JSON from a Swift SDK using Symbol Graph.", + "scripts": { + "extract": "tsx src/index.ts", + "test": "vitest run", + "typecheck": "tsc --noEmit" + }, + "devDependencies": { + "@types/node": "^22", + "tsx": "^4.19.2", + "typescript": "^5.7.2", + "vitest": "^4.1.0" + } +} diff --git a/scripts/swift-docs/src/doc-comment.ts b/scripts/swift-docs/src/doc-comment.ts new file mode 100644 index 000000000..c75b3af63 --- /dev/null +++ b/scripts/swift-docs/src/doc-comment.ts @@ -0,0 +1,62 @@ +import type { TypeDocComment, CommentContent, CommentBlockTag } from "./typedoc-types.js"; + +const PARAM_RE = /^-\s+Parameter\s+(\w+):\s*(.*)/i; +const RETURNS_RE = /^-\s+Returns:\s*(.*)/i; +const THROWS_RE = /^-\s+Throws:\s*(.*)/i; +const DASH_TAG_RE = /^-\s+(\w+):\s*(.*)/i; +const DOCC_PARAM_RE = /^@Parameter\s+(\w+)\s+(.*)/i; +const DOCC_RETURNS_RE = /^@Returns\s+(.*)/i; +const DOCC_THROWS_RE = /^@Throws\s+(.*)/i; +const DOCC_TAG_RE = /^@(\w+)\s+(.*)/i; + +function isTagLine(t: string): boolean { + return ( + PARAM_RE.test(t) || RETURNS_RE.test(t) || THROWS_RE.test(t) || + DASH_TAG_RE.test(t) || DOCC_PARAM_RE.test(t) || DOCC_RETURNS_RE.test(t) || + DOCC_THROWS_RE.test(t) || DOCC_TAG_RE.test(t) + ); +} + +function parseTagLine(t: string): CommentBlockTag | null { + let m: RegExpExecArray | null; + + m = PARAM_RE.exec(t) ?? DOCC_PARAM_RE.exec(t); + if (m) return { tag: "@param", name: m[1], content: [{ kind: "text", text: m[2] }] }; + + m = RETURNS_RE.exec(t) ?? DOCC_RETURNS_RE.exec(t); + if (m) return { tag: "@returns", content: [{ kind: "text", text: m[1] }] }; + + m = THROWS_RE.exec(t) ?? DOCC_THROWS_RE.exec(t); + if (m) return { tag: "@throws", content: [{ kind: "text", text: m[1] }] }; + + m = DASH_TAG_RE.exec(t); + if (m) return { tag: `@${m[1].toLowerCase()}`, content: [{ kind: "text", text: m[2] }] }; + + m = DOCC_TAG_RE.exec(t); + if (m) return { tag: `@${m[1].toLowerCase()}`, content: [{ kind: "text", text: m[2] }] }; + + return null; +} + +export function parseDocComment(lines: Array<{ text: string }>): TypeDocComment { + const summary: CommentContent[] = []; + const blockTags: CommentBlockTag[] = []; + let inTags = false; + + for (const { text: raw } of lines) { + const t = raw.trim(); + + if (!inTags && !isTagLine(t)) { + if (t) summary.push({ kind: "text", text: t }); + continue; + } + + if (!isTagLine(t)) continue; // non-tag lines after first tag are inter-tag content, not summary + + inTags = true; + const tag = parseTagLine(t); + if (tag) blockTags.push(tag); + } + + return blockTags.length > 0 ? { summary, blockTags } : { summary }; +} diff --git a/scripts/swift-docs/src/extract.ts b/scripts/swift-docs/src/extract.ts new file mode 100644 index 000000000..a8620b2c6 --- /dev/null +++ b/scripts/swift-docs/src/extract.ts @@ -0,0 +1,40 @@ +import { spawnSync } from "node:child_process"; +import { existsSync, readdirSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import type { SymbolGraph } from "./symbol-graph.js"; + +export function extractSymbolGraphs(sdkRoot: string): SymbolGraph[] { + const result = spawnSync( + "swift", + ["package", "dump-symbol-graph", "--minimum-access-level", "public"], + { cwd: sdkRoot, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] } + ); + + if (result.error) { + throw new Error(`Failed to spawn swift: ${result.error.message}`); + } + + // dump-symbol-graph exits non-zero when test-only targets can't load on the + // current platform (e.g. iOS test targets on macOS). We tolerate this as long + // as the primary module symbol graphs were produced. + const buildDir = join(sdkRoot, ".build"); + const archDir = readdirSync(buildDir).find( + d => existsSync(join(buildDir, d, "symbolgraph")) + ); + + if (!archDir) { + // No symbol graphs at all — surface the original error + throw new Error(`swift package dump-symbol-graph failed:\n${result.stderr}`); + } + + const symbolgraphDir = join(buildDir, archDir, "symbolgraph"); + const graphs: SymbolGraph[] = []; + for (const file of readdirSync(symbolgraphDir)) { + // Skip cross-module extension graphs (e.g. Auth@Foundation.symbols.json) + if (!file.endsWith(".symbols.json") || file.includes("@")) continue; + const raw = readFileSync(join(symbolgraphDir, file), "utf8"); + graphs.push(JSON.parse(raw) as SymbolGraph); + } + + return graphs; +} diff --git a/scripts/swift-docs/src/index.ts b/scripts/swift-docs/src/index.ts new file mode 100644 index 000000000..feac6feec --- /dev/null +++ b/scripts/swift-docs/src/index.ts @@ -0,0 +1,35 @@ +import { resolve } from "node:path"; +import { readFileSync, writeFileSync } from "node:fs"; +import { extractSymbolGraphs } from "./extract.js"; +import { transformSymbolGraph } from "./transform.js"; + +const sdkRoot = process.argv[2]; +const outputPath = process.argv[3] ?? "typedoc.json"; +const categoriesPath = process.argv[4]; + +if (!sdkRoot) { + console.error("Usage: tsx src/index.ts [output.json] [categories.json]"); + process.exit(1); +} + +const root = resolve(sdkRoot); +const out = resolve(outputPath); + +let categoryMap: Record = {}; +if (categoriesPath) { + const resolved = resolve(categoriesPath); + categoryMap = JSON.parse(readFileSync(resolved, "utf8")) as Record; + console.error(`Loaded ${Object.keys(categoryMap).length} category mapping(s) from ${resolved}.`); +} + +console.error(`Extracting symbol graphs from ${root}...`); +const graphs = extractSymbolGraphs(root); +console.error(`Found ${graphs.length} symbol graph(s).`); + +// Use the umbrella module name if present; otherwise the first graph's name. +const umbrella = graphs.find(g => g.module.name === "Supabase") ?? graphs[0]; +const moduleName = umbrella?.module.name ?? "Module"; +const project = transformSymbolGraph(graphs, moduleName, categoryMap); + +writeFileSync(out, JSON.stringify(project, null, 2), "utf8"); +console.error(`Written to ${out}`); diff --git a/scripts/swift-docs/src/symbol-graph.ts b/scripts/swift-docs/src/symbol-graph.ts new file mode 100644 index 000000000..2bb13e423 --- /dev/null +++ b/scripts/swift-docs/src/symbol-graph.ts @@ -0,0 +1,55 @@ +export interface DeclarationFragment { + kind: string; + spelling: string; + preciseIdentifier?: string; +} + +export interface DocCommentLine { + range?: { + start: { line: number; character: number }; + end: { line: number; character: number }; + }; + text: string; +} + +export interface FunctionParameter { + name: string; + declarationFragments: DeclarationFragment[]; +} + +export interface SwiftGenericParameter { + name: string; + index: number; + depth: number; +} + +export interface SymbolGraphSymbol { + identifier: { precise: string; interfaceLanguage: string }; + kind: { identifier: string; displayName: string }; + names: { title: string }; + docComment?: { lines: DocCommentLine[] }; + declarationFragments?: DeclarationFragment[]; + functionSignature?: { + parameters: FunctionParameter[]; + returns: DeclarationFragment[]; + }; + accessLevel: string; + location?: { uri: string; position: { line: number; character: number } }; + swiftGenerics?: { parameters?: SwiftGenericParameter[] }; +} + +export interface Relationship { + kind: string; + source: string; + target: string; + targetFallback?: string; +} + +export interface SymbolGraph { + module: { + name: string; + platform: { operatingSystem: { name: string }; architecture: string }; + }; + symbols: SymbolGraphSymbol[]; + relationships: Relationship[]; +} diff --git a/scripts/swift-docs/src/transform.ts b/scripts/swift-docs/src/transform.ts new file mode 100644 index 000000000..711653272 --- /dev/null +++ b/scripts/swift-docs/src/transform.ts @@ -0,0 +1,292 @@ +import type { + SymbolGraph, SymbolGraphSymbol, DeclarationFragment, +} from "./symbol-graph.js"; +import type { + TypeDocProject, TypeDocDeclaration, TypeDocSignature, + TypeDocParameter, TypeDocType, TypeDocKind, CommentBlockTag, +} from "./typedoc-types.js"; +import { KIND_STRING } from "./typedoc-types.js"; +import { parseDocComment } from "./doc-comment.js"; + +const KIND_MAP: Record = { + "swift.class": 128, "swift.struct": 128, "swift.protocol": 256, + "swift.enum": 8, "swift.enum.case": 16, "swift.init": 512, + "swift.method": 2048, "swift.property": 1024, + "swift.func": 64, "swift.typealias": 2097152, "swift.var": 32, +}; + +export function parseTypeString(text: string, genericParams: Set = new Set()): TypeDocType { + const t = text.trim(); + if (t.endsWith("?")) { + return { + type: "union", + types: [parseTypeString(t.slice(0, -1), genericParams), { type: "intrinsic", name: "undefined" }], + }; + } + if (t.startsWith("Optional<") && t.endsWith(">")) { + return { + type: "union", + types: [parseTypeString(t.slice(9, -1), genericParams), { type: "intrinsic", name: "undefined" }], + }; + } + if (t.startsWith("[") && t.endsWith("]")) { + const inner = t.slice(1, -1).trim(); + const colonIdx = findTopLevelColon(inner); + if (colonIdx >= 0) { + return { + type: "reference", name: "Dictionary", + typeArguments: [ + parseTypeString(inner.slice(0, colonIdx).trim(), genericParams), + parseTypeString(inner.slice(colonIdx + 1).trim(), genericParams), + ], + }; + } + return { type: "array", elementType: parseTypeString(inner, genericParams) }; + } + if (t.startsWith("Array<") && t.endsWith(">")) { + return { type: "array", elementType: parseTypeString(t.slice(6, -1), genericParams) }; + } + if (genericParams.has(t)) return { type: "typeParameter", name: t }; + // Emit all simple identifiers as "intrinsic" so the docs renderer displays + // them as-is. Using "reference" triggers TypeDoc's id-lookup path which + // collapses to "nameOnly" when there is no id to dereference — and the + // ReturnTypeDetails component skips nameOnly types entirely. + return { type: "intrinsic", name: t }; +} + +function findTopLevelColon(s: string): number { + let depth = 0; + for (let i = 0; i < s.length; i++) { + const c = s[i]; + if (c === "<" || c === "[" || c === "(") depth++; + else if (c === ">" || c === "]" || c === ")") depth--; + else if (c === ":" && depth === 0) return i; + } + return -1; +} + +function parseFragments(frags: DeclarationFragment[] | undefined, gp: Set): TypeDocType { + if (!frags?.length) return { type: "intrinsic", name: "Void" }; + return parseTypeString(frags.map(f => f.spelling).join("").trim(), gp); +} + +// Extract only the type portion from Swift declaration fragments. +// Strips the prefix up to and including the first ": " colon separator, then +// strips any trailing computed-property accessor block ("{ get }", "{ get set }", +// "? { get }", etc.). +// +// Examples: +// param: [label, ": ", typeId] → [typeId] +// array: [var, " ", name, ": [", typeId, "]"] → [{text,"["}, typeId, "]"] +// optional: [var, " ", name, ": ", typeId, "? { ", get, " }"] → [typeId, "?"] +export function paramTypeFragments(frags: DeclarationFragment[]): DeclarationFragment[] { + // Step 1: find and strip up to the ": " colon separator. + let typeFrags: DeclarationFragment[] = frags; + for (let i = 0; i < frags.length; i++) { + const f = frags[i]; + if (f.kind === "text" && f.spelling.trimStart().startsWith(":")) { + const afterColon = f.spelling.trimStart().replace(/^:\s*/, ""); + typeFrags = afterColon + ? [{ kind: "text", spelling: afterColon }, ...frags.slice(i + 1)] + : frags.slice(i + 1); + break; + } + } + + // Step 2: strip trailing computed accessor block starting at "{". + const result: DeclarationFragment[] = []; + for (const f of typeFrags) { + if (f.kind === "text") { + const braceIdx = f.spelling.indexOf("{"); + if (braceIdx >= 0) { + const before = f.spelling.slice(0, braceIdx).trimEnd(); + if (before) result.push({ kind: "text", spelling: before }); + break; + } + } else if (f.kind === "keyword" && (f.spelling === "get" || f.spelling === "set")) { + break; + } + result.push(f); + } + return result.length > 0 ? result : typeFrags; +} + +function baseName(title: string): string { + const i = title.indexOf("("); + return i >= 0 ? title.slice(0, i) : title; +} + +function buildSignature( + sym: SymbolGraphSymbol, + genericParams: Set, + getId: () => number, +): TypeDocSignature { + const fs = sym.functionSignature!; + const name = baseName(sym.names.title); + const comment = sym.docComment?.lines.length + ? parseDocComment(sym.docComment.lines) : undefined; + + const paramDocs = new Map(); + for (const tag of comment?.blockTags ?? []) { + if (tag.tag === "@param" && tag.name) { + paramDocs.set(tag.name, tag.content.map(c => c.text).join("")); + } + } + + const parameters: TypeDocParameter[] = (fs.parameters ?? []).map(p => { + const docText = paramDocs.get(p.name); + return { + id: getId(), + name: p.name, + kind: 32768 as const, + kindString: "Parameter" as const, + flags: {} as Record, + ...(docText && { comment: { summary: [{ kind: "text" as const, text: docText }] } }), + type: parseFragments(paramTypeFragments(p.declarationFragments ?? []), genericParams), + }; + }); + + const hasComment = comment && (comment.summary.length > 0 || (comment.blockTags?.length ?? 0) > 0); + + return { + id: getId(), + name, + kind: 4096 as const, + kindString: "Call signature" as const, + flags: {}, + ...(hasComment && { comment }), + ...(parameters.length > 0 && { parameters }), + type: parseFragments(fs.returns, genericParams), + }; +} + +export function transformSymbolGraph( + graphs: SymbolGraph[], + moduleName: string, + categoryMap: Record = {}, +): TypeDocProject { + let nextId = 1; + const getId = () => nextId++; + + const allSymbols = new Map(); + const allRelationships: SymbolGraph["relationships"] = []; + const symbolModule = new Map(); + for (const g of graphs) { + for (const s of g.symbols) { + allSymbols.set(s.identifier.precise, s); + symbolModule.set(s.identifier.precise, g.module.name); + } + allRelationships.push(...g.relationships); + } + + const memberOf = new Map(); + const conformsTo = new Map(); + const inheritsFrom = new Map(); + for (const rel of allRelationships) { + if (rel.kind === "memberOf") memberOf.set(rel.source, rel.target); + else if (rel.kind === "conformsTo") { + const list = conformsTo.get(rel.source) ?? []; + list.push(rel.target); + conformsTo.set(rel.source, list); + } else if (rel.kind === "inheritsFrom") inheritsFrom.set(rel.source, rel.target); + } + + const publicSymbols = [...allSymbols.values()].filter( + s => s.accessLevel === "public" || s.accessLevel === "open" + ); + + const declarations = new Map(); + for (const sym of publicSymbols) { + const kind = KIND_MAP[sym.kind.identifier]; + if (!kind) continue; + + const precise = sym.identifier.precise; + const genericParams = new Set(sym.swiftGenerics?.parameters?.map(p => p.name) ?? []); + const isCallable = kind === 2048 || kind === 64 || kind === 512; + + const decl: TypeDocDeclaration = { + id: getId(), + name: baseName(sym.names.title), + variant: "declaration", + kind, + kindString: KIND_STRING[kind], + flags: {}, + }; + + if (!isCallable && sym.docComment?.lines.length) { + const comment = parseDocComment(sym.docComment.lines); + if (comment.summary.length || comment.blockTags?.length) decl.comment = comment; + } + + if (sym.location) { + const uri = sym.location.uri; + decl.sources = [{ + fileName: uri.startsWith("file://") ? uri.slice("file://".length) : uri, + line: sym.location.position.line, + character: sym.location.position.character, + }]; + } + + if (isCallable && sym.functionSignature) { + decl.signatures = [buildSignature(sym, genericParams, getId)]; + } + + if ((kind === 1024 || kind === 32) && sym.declarationFragments) { + decl.type = parseFragments(paramTypeFragments(sym.declarationFragments), genericParams); + } + + if (genericParams.size > 0) { + decl.typeParameter = [...genericParams].map(name => ({ + id: getId(), name, kind: 131072 as const, kindString: "Type parameter" as const, + flags: {} as Record, + })); + } + + const conforms = conformsTo.get(precise); + if (conforms?.length) { + decl.implementedTypes = conforms.map(p => ({ + type: "reference" as const, + name: allSymbols.get(p)?.names.title ?? p, + })); + } + + const superclass = inheritsFrom.get(precise); + if (superclass) { + decl.extendedTypes = [{ + type: "reference", + name: allSymbols.get(superclass)?.names.title ?? superclass, + }]; + } + + const modName = symbolModule.get(precise); + const category = modName ? categoryMap[modName] : undefined; + if (category) { + const tag: CommentBlockTag = { tag: "@category", content: [{ kind: "text", text: category }] }; + if (decl.comment) { + decl.comment = { ...decl.comment, blockTags: [...(decl.comment.blockTags ?? []), tag] }; + } else { + decl.comment = { summary: [], blockTags: [tag] }; + } + } + + declarations.set(precise, decl); + } + + const rootChildren: TypeDocDeclaration[] = []; + for (const sym of publicSymbols) { + const decl = declarations.get(sym.identifier.precise); + if (!decl) continue; + const parentPrecise = memberOf.get(sym.identifier.precise); + if (parentPrecise) { + const parent = declarations.get(parentPrecise); + if (parent) { + parent.children = parent.children ?? []; + parent.children.push(decl); + continue; + } + } + rootChildren.push(decl); + } + + return { id: 0, name: moduleName, kind: 1, kindString: "Project", flags: {}, children: rootChildren }; +} diff --git a/scripts/swift-docs/src/typedoc-types.ts b/scripts/swift-docs/src/typedoc-types.ts new file mode 100644 index 000000000..71301d8e8 --- /dev/null +++ b/scripts/swift-docs/src/typedoc-types.ts @@ -0,0 +1,92 @@ +export type TypeDocKind = + | 1 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096 | 32768 | 131072 | 2097152; + +export const KIND_STRING: Record = { + 1: "Project", 8: "Enum", 16: "EnumMember", 32: "Variable", + 64: "Function", 128: "Class", 256: "Interface", 512: "Constructor", + 1024: "Property", 2048: "Method", 4096: "Call signature", + 32768: "Parameter", 131072: "Type parameter", 2097152: "Type alias", +}; + +export interface CommentContent { kind: "text" | "code"; text: string } + +export interface CommentBlockTag { + tag: string; + name?: string; + content: CommentContent[]; +} + +export interface TypeDocComment { + summary: CommentContent[]; + blockTags?: CommentBlockTag[]; +} + +export interface TypeDocType { + type: "intrinsic" | "reference" | "array" | "union" | "typeParameter"; + name?: string; + elementType?: TypeDocType; + types?: TypeDocType[]; + typeArguments?: TypeDocType[]; +} + +export interface TypeDocSource { + fileName: string; + line: number; + character: number; +} + +export interface TypeDocTypeParameter { + id: number; + name: string; + kind: 131072; + kindString: "Type parameter"; + flags: Record; +} + +export interface TypeDocParameter { + id: number; + name: string; + kind: 32768; + kindString: "Parameter"; + flags: Record; + comment?: TypeDocComment; + type?: TypeDocType; +} + +export interface TypeDocSignature { + id: number; + name: string; + kind: 4096; + kindString: "Call signature"; + flags: Record; + comment?: TypeDocComment; + parameters?: TypeDocParameter[]; + type?: TypeDocType; + typeParameter?: TypeDocTypeParameter[]; +} + +export interface TypeDocDeclaration { + id: number; + name: string; + variant: "declaration"; + kind: TypeDocKind; + kindString: string; + flags: Record; + comment?: TypeDocComment; + sources?: TypeDocSource[]; + children?: TypeDocDeclaration[]; + signatures?: TypeDocSignature[]; + type?: TypeDocType; + extendedTypes?: TypeDocType[]; + implementedTypes?: TypeDocType[]; + typeParameter?: TypeDocTypeParameter[]; +} + +export interface TypeDocProject { + id: 0; + name: string; + kind: 1; + kindString: "Project"; + flags: Record; + children: TypeDocDeclaration[]; +} diff --git a/scripts/swift-docs/supabase-swift-categories.json b/scripts/swift-docs/supabase-swift-categories.json new file mode 100644 index 000000000..28652edfd --- /dev/null +++ b/scripts/swift-docs/supabase-swift-categories.json @@ -0,0 +1,8 @@ +{ + "Auth": "Authentication", + "PostgREST": "Database", + "Realtime": "Realtime", + "Storage": "Storage", + "Functions": "Edge Functions", + "Supabase": "Client" +} diff --git a/scripts/swift-docs/test/doc-comment.test.ts b/scripts/swift-docs/test/doc-comment.test.ts new file mode 100644 index 000000000..56dd8ddcb --- /dev/null +++ b/scripts/swift-docs/test/doc-comment.test.ts @@ -0,0 +1,102 @@ +import { describe, it, expect } from "vitest"; +import { parseDocComment } from "../src/doc-comment"; + +describe("parseDocComment", () => { + it("returns empty summary for empty input", () => { + expect(parseDocComment([])).toEqual({ summary: [] }); + }); + + it("parses a plain single-line summary", () => { + const result = parseDocComment([{ text: "Signs in the user." }]); + expect(result.summary).toEqual([{ kind: "text", text: "Signs in the user." }]); + expect(result.blockTags).toBeUndefined(); + }); + + it("accumulates multiple plain lines into summary", () => { + const result = parseDocComment([ + { text: "First line." }, + { text: "Second line." }, + ]); + expect(result.summary).toEqual([ + { kind: "text", text: "First line." }, + { kind: "text", text: "Second line." }, + ]); + }); + + it("stops summary at first tag line; blank separator is discarded", () => { + const result = parseDocComment([ + { text: "Signs in the user." }, + { text: "" }, + { text: "- Parameter email: The email." }, + ]); + expect(result.summary).toEqual([{ kind: "text", text: "Signs in the user." }]); + }); + + it("parses - Parameter tag (traditional Swift style)", () => { + const result = parseDocComment([ + { text: "Does something." }, + { text: "- Parameter email: The user email." }, + ]); + expect(result.blockTags).toEqual([ + { tag: "@param", name: "email", content: [{ kind: "text", text: "The user email." }] }, + ]); + }); + + it("parses - Returns tag", () => { + const result = parseDocComment([ + { text: "Does something." }, + { text: "- Returns: The session." }, + ]); + expect(result.blockTags).toEqual([ + { tag: "@returns", content: [{ kind: "text", text: "The session." }] }, + ]); + }); + + it("parses - Throws tag", () => { + const result = parseDocComment([{ text: "- Throws: AuthError on failure." }]); + expect(result.blockTags).toEqual([ + { tag: "@throws", content: [{ kind: "text", text: "AuthError on failure." }] }, + ]); + }); + + it("parses DocC @Parameter style", () => { + const result = parseDocComment([{ text: "@Parameter email The user email." }]); + expect(result.blockTags).toEqual([ + { tag: "@param", name: "email", content: [{ kind: "text", text: "The user email." }] }, + ]); + }); + + it("parses DocC @Returns style", () => { + const result = parseDocComment([{ text: "@Returns The session." }]); + expect(result.blockTags).toEqual([ + { tag: "@returns", content: [{ kind: "text", text: "The session." }] }, + ]); + }); + + it("passes unknown dash-tags through with lowercased tag name", () => { + const result = parseDocComment([{ text: "- Note: Be careful here." }]); + expect(result.blockTags).toEqual([ + { tag: "@note", content: [{ kind: "text", text: "Be careful here." }] }, + ]); + }); + + it("parses multiple params and returns together", () => { + const result = parseDocComment([ + { text: "Signs in." }, + { text: "" }, + { text: "- Parameter email: The email." }, + { text: "- Parameter password: The password." }, + { text: "- Returns: The session." }, + ]); + expect(result.blockTags).toHaveLength(3); + expect(result.blockTags![0]).toEqual({ + tag: "@param", name: "email", content: [{ kind: "text", text: "The email." }], + }); + expect(result.blockTags![1]).toEqual({ + tag: "@param", name: "password", content: [{ kind: "text", text: "The password." }], + }); + expect(result.blockTags![2]).toEqual({ + tag: "@returns", content: [{ kind: "text", text: "The session." }], + }); + }); +}); diff --git a/scripts/swift-docs/test/fixtures/sample.symbols.json b/scripts/swift-docs/test/fixtures/sample.symbols.json new file mode 100644 index 000000000..672a8955f --- /dev/null +++ b/scripts/swift-docs/test/fixtures/sample.symbols.json @@ -0,0 +1,121 @@ +{ + "module": { + "name": "Auth", + "platform": { "operatingSystem": { "name": "macosx" }, "architecture": "arm64" } + }, + "symbols": [ + { + "identifier": { "precise": "s:4Auth10AuthClientC", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.class", "displayName": "Class" }, + "names": { "title": "AuthClient" }, + "docComment": { "lines": [{ "text": "Manages authentication state." }] }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthClient.swift", "position": { "line": 10, "character": 0 } } + }, + { + "identifier": { "precise": "s:4Auth10AuthClientC6signInSSSSF", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.method", "displayName": "Instance Method" }, + "names": { "title": "signIn(email:password:)" }, + "docComment": { + "lines": [ + { "text": "Signs in a user." }, + { "text": "" }, + { "text": "- Parameter email: The user's email address." }, + { "text": "- Parameter password: The user's password." }, + { "text": "- Returns: The authenticated session." } + ] + }, + "functionSignature": { + "parameters": [ + { "name": "email", "declarationFragments": [{ "kind": "typeIdentifier", "spelling": "String" }] }, + { "name": "password", "declarationFragments": [{ "kind": "typeIdentifier", "spelling": "String" }] } + ], + "returns": [{ "kind": "typeIdentifier", "spelling": "AuthSession" }] + }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthClient.swift", "position": { "line": 20, "character": 4 } } + }, + { + "identifier": { "precise": "s:4Auth11AuthSessionV", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.struct", "displayName": "Structure" }, + "names": { "title": "AuthSession" }, + "docComment": { "lines": [{ "text": "Represents an active user session." }] }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthSession.swift", "position": { "line": 5, "character": 0 } } + }, + { + "identifier": { "precise": "s:4Auth11AuthSessionV5tokenSSvp", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.property", "displayName": "Instance Property" }, + "names": { "title": "token" }, + "declarationFragments": [{ "kind": "typeIdentifier", "spelling": "String" }], + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthSession.swift", "position": { "line": 7, "character": 4 } } + }, + { + "identifier": { "precise": "s:4Auth9AuthErrorO", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.enum", "displayName": "Enumeration" }, + "names": { "title": "AuthError" }, + "docComment": { "lines": [{ "text": "Errors thrown by authentication operations." }] }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthError.swift", "position": { "line": 3, "character": 0 } } + }, + { + "identifier": { "precise": "s:4Auth9AuthErrorO12invalidEmailyA2CmF", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.enum.case", "displayName": "Case" }, + "names": { "title": "invalidEmail" }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthError.swift", "position": { "line": 5, "character": 4 } } + }, + { + "identifier": { "precise": "s:4Auth9AuthErrorO12networkErroryA2CmF", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.enum.case", "displayName": "Case" }, + "names": { "title": "networkError" }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthError.swift", "position": { "line": 6, "character": 4 } } + }, + { + "identifier": { "precise": "s:4Auth15AuthProviderableP", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.protocol", "displayName": "Protocol" }, + "names": { "title": "AuthProviderable" }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthProviderable.swift", "position": { "line": 1, "character": 0 } } + }, + { + "identifier": { "precise": "s:4Auth14BaseAuthClientC", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.class", "displayName": "Class" }, + "names": { "title": "BaseAuthClient" }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/BaseAuthClient.swift", "position": { "line": 1, "character": 0 } } + }, + { + "identifier": { "precise": "s:4Auth10AuthClientC7signOutyyF", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.method", "displayName": "Instance Method" }, + "names": { "title": "signOut()" }, + "functionSignature": { + "parameters": [] + }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthClient.swift", "position": { "line": 30, "character": 4 } } + }, + { + "identifier": { "precise": "s:4Auth10AuthClientC8refreshySSSSF", "interfaceLanguage": "swift" }, + "kind": { "identifier": "swift.method", "displayName": "Instance Method" }, + "names": { "title": "refresh(token:)" }, + "functionSignature": { + "returns": [{ "kind": "typeIdentifier", "spelling": "AuthSession" }] + }, + "accessLevel": "public", + "location": { "uri": "file:///Sources/Auth/AuthClient.swift", "position": { "line": 35, "character": 4 } } + } + ], + "relationships": [ + { "kind": "memberOf", "source": "s:4Auth10AuthClientC6signInSSSSF", "target": "s:4Auth10AuthClientC" }, + { "kind": "memberOf", "source": "s:4Auth11AuthSessionV5tokenSSvp", "target": "s:4Auth11AuthSessionV" }, + { "kind": "memberOf", "source": "s:4Auth9AuthErrorO12invalidEmailyA2CmF", "target": "s:4Auth9AuthErrorO" }, + { "kind": "memberOf", "source": "s:4Auth9AuthErrorO12networkErroryA2CmF", "target": "s:4Auth9AuthErrorO" }, + { "kind": "conformsTo", "source": "s:4Auth10AuthClientC", "target": "s:4Auth15AuthProviderableP" }, + { "kind": "inheritsFrom", "source": "s:4Auth10AuthClientC", "target": "s:4Auth14BaseAuthClientC" }, + { "kind": "memberOf", "source": "s:4Auth10AuthClientC7signOutyyF", "target": "s:4Auth10AuthClientC" }, + { "kind": "memberOf", "source": "s:4Auth10AuthClientC8refreshySSSSF", "target": "s:4Auth10AuthClientC" } + ] +} diff --git a/scripts/swift-docs/test/transform.test.ts b/scripts/swift-docs/test/transform.test.ts new file mode 100644 index 000000000..41954f541 --- /dev/null +++ b/scripts/swift-docs/test/transform.test.ts @@ -0,0 +1,302 @@ +import { describe, it, expect } from "vitest"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import { transformSymbolGraph, parseTypeString, paramTypeFragments } from "../src/transform"; +import type { SymbolGraph } from "../src/symbol-graph"; +import type { TypeDocDeclaration } from "../src/typedoc-types"; + +const here = dirname(fileURLToPath(import.meta.url)); +const fixture = JSON.parse( + readFileSync(join(here, "fixtures", "sample.symbols.json"), "utf8") +) as SymbolGraph; + +// --------------------------------------------------------------------------- +// Type string parser +// --------------------------------------------------------------------------- + +describe("parseTypeString", () => { + it("maps String to intrinsic", () => { + expect(parseTypeString("String")).toEqual({ type: "intrinsic", name: "String" }); + }); + it("maps Bool to intrinsic", () => { + expect(parseTypeString("Bool")).toEqual({ type: "intrinsic", name: "Bool" }); + }); + it("maps T? to union with undefined", () => { + expect(parseTypeString("String?")).toEqual({ + type: "union", + types: [{ type: "intrinsic", name: "String" }, { type: "intrinsic", name: "undefined" }], + }); + }); + it("maps Optional", () => { + expect(parseTypeString("Optional")).toEqual({ + type: "union", + types: [{ type: "intrinsic", name: "String" }, { type: "intrinsic", name: "undefined" }], + }); + }); + it("maps [T] to array", () => { + expect(parseTypeString("[String]")).toEqual({ + type: "array", + elementType: { type: "intrinsic", name: "String" }, + }); + }); + it("maps Array", () => { + expect(parseTypeString("Array")).toEqual({ + type: "array", + elementType: { type: "intrinsic", name: "String" }, + }); + }); + it("maps [K: V] to Dictionary reference", () => { + expect(parseTypeString("[String: Int]")).toEqual({ + type: "reference", + name: "Dictionary", + typeArguments: [{ type: "intrinsic", name: "String" }, { type: "intrinsic", name: "Int" }], + }); + }); + it("maps a known generic param to typeParameter", () => { + expect(parseTypeString("T", new Set(["T"]))).toEqual({ type: "typeParameter", name: "T" }); + }); + it("maps an unknown named type to intrinsic", () => { + expect(parseTypeString("AuthSession")).toEqual({ type: "intrinsic", name: "AuthSession" }); + }); +}); + +// --------------------------------------------------------------------------- +// paramTypeFragments +// --------------------------------------------------------------------------- + +describe("paramTypeFragments", () => { + it("strips external label and colon from a simple parameter", () => { + const frags = [ + { kind: "identifier", spelling: "uid" }, + { kind: "text", spelling: ": " }, + { kind: "typeIdentifier", spelling: "UUID" }, + ]; + expect(paramTypeFragments(frags)).toEqual([{ kind: "typeIdentifier", spelling: "UUID" }]); + }); + + it("strips var/name prefix from a property declaration, plain type", () => { + // var name: String + const frags = [ + { kind: "keyword", spelling: "var" }, + { kind: "text", spelling: " " }, + { kind: "identifier", spelling: "name" }, + { kind: "text", spelling: ": " }, + { kind: "typeIdentifier", spelling: "String" }, + ]; + expect(paramTypeFragments(frags)).toEqual([{ kind: "typeIdentifier", spelling: "String" }]); + }); + + it("preserves array bracket when colon and bracket are bundled", () => { + // var tags: [String] + const frags = [ + { kind: "keyword", spelling: "var" }, + { kind: "text", spelling: " " }, + { kind: "identifier", spelling: "tags" }, + { kind: "text", spelling: ": [" }, + { kind: "typeIdentifier", spelling: "String" }, + { kind: "text", spelling: "]" }, + ]; + expect(paramTypeFragments(frags)).toEqual([ + { kind: "text", spelling: "[" }, + { kind: "typeIdentifier", spelling: "String" }, + { kind: "text", spelling: "]" }, + ]); + }); + + it("strips accessor block from optional computed property", () => { + // nonisolated var currentSession: Session? { get } + const frags = [ + { kind: "attribute", spelling: "nonisolated" }, + { kind: "text", spelling: " " }, + { kind: "keyword", spelling: "var" }, + { kind: "text", spelling: " " }, + { kind: "identifier", spelling: "currentSession" }, + { kind: "text", spelling: ": " }, + { kind: "typeIdentifier", spelling: "Session" }, + { kind: "text", spelling: "? { " }, + { kind: "keyword", spelling: "get" }, + { kind: "text", spelling: " }" }, + ]; + expect(paramTypeFragments(frags)).toEqual([ + { kind: "typeIdentifier", spelling: "Session" }, + { kind: "text", spelling: "?" }, + ]); + }); + + it("returns frags unchanged when no colon separator found", () => { + const frags = [{ kind: "typeIdentifier", spelling: "Bool" }]; + expect(paramTypeFragments(frags)).toEqual(frags); + }); +}); + +// --------------------------------------------------------------------------- +// Full transform +// --------------------------------------------------------------------------- + +describe("transformSymbolGraph", () => { + const result = transformSymbolGraph([fixture], "Auth"); + + it("produces a Project root with the module name", () => { + expect(result.id).toBe(0); + expect(result.kind).toBe(1); + expect(result.kindString).toBe("Project"); + expect(result.name).toBe("Auth"); + }); + + it("emits AuthClient as a Class (kind 128)", () => { + const cls = result.children.find(c => c.name === "AuthClient"); + expect(cls).toBeDefined(); + expect(cls!.kind).toBe(128); + expect(cls!.kindString).toBe("Class"); + }); + + it("puts signIn as a Method child of AuthClient", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + const method = cls.children?.find(c => c.name === "signIn"); + expect(method).toBeDefined(); + expect(method!.kind).toBe(2048); + expect(method!.kindString).toBe("Method"); + }); + + it("attaches doc comment summary to AuthClient", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + expect(cls.comment?.summary).toEqual([ + { kind: "text", text: "Manages authentication state." }, + ]); + }); + + it("attaches signIn Call signature with @param and @returns block tags", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + const method = cls.children?.find(c => c.name === "signIn")!; + const sig = method.signatures?.[0]; + expect(sig).toBeDefined(); + expect(sig!.kind).toBe(4096); + expect(sig!.comment?.blockTags?.find(t => t.tag === "@param" && t.name === "email")).toBeDefined(); + expect(sig!.comment?.blockTags?.find(t => t.tag === "@returns")).toBeDefined(); + }); + + it("maps signIn parameters with String types", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + const sig = cls.children?.find(c => c.name === "signIn")!.signatures![0]!; + expect(sig.parameters).toHaveLength(2); + expect(sig.parameters![0].name).toBe("email"); + expect(sig.parameters![0].type).toEqual({ type: "intrinsic", name: "String" }); + expect(sig.parameters![1].name).toBe("password"); + expect(sig.parameters![1].type).toEqual({ type: "intrinsic", name: "String" }); + }); + + it("maps signIn return type as intrinsic to AuthSession", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + const sig = cls.children?.find(c => c.name === "signIn")!.signatures![0]!; + expect(sig.type).toEqual({ type: "intrinsic", name: "AuthSession" }); + }); + + it("emits AuthSession as a Class (swift.struct maps to 128)", () => { + const s = result.children.find(c => c.name === "AuthSession"); + expect(s).toBeDefined(); + expect(s!.kind).toBe(128); + }); + + it("puts token property (kind 1024) inside AuthSession", () => { + const s = result.children.find(c => c.name === "AuthSession")!; + const prop = s.children?.find(c => c.name === "token"); + expect(prop).toBeDefined(); + expect(prop!.kind).toBe(1024); + }); + + it("emits AuthError as Enum (kind 8)", () => { + const e = result.children.find(c => c.name === "AuthError"); + expect(e).toBeDefined(); + expect(e!.kind).toBe(8); + }); + + it("puts invalidEmail as EnumMember (kind 16) inside AuthError", () => { + const e = result.children.find(c => c.name === "AuthError")!; + const member = e.children?.find(c => c.name === "invalidEmail"); + expect(member).toBeDefined(); + expect(member!.kind).toBe(16); + }); + + it("emits AuthProviderable as Interface (kind 256)", () => { + const p = result.children.find(c => c.name === "AuthProviderable"); + expect(p).toBeDefined(); + expect(p!.kind).toBe(256); + }); + + it("adds AuthProviderable to AuthClient.implementedTypes from conformsTo", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + expect(cls.implementedTypes).toEqual([{ type: "reference", name: "AuthProviderable" }]); + }); + + it("attaches source file and line to AuthClient", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + expect(cls.sources?.[0].fileName).toContain("AuthClient.swift"); + expect(cls.sources?.[0].line).toBe(10); + }); + + it("populates extendedTypes on AuthClient from inheritsFrom relationship", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + expect(cls.extendedTypes).toEqual([{ type: "reference", name: "BaseAuthClient" }]); + }); + + it("maps signOut (no returns field) to Void return type with no parameters", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + const sig = cls.children?.find(c => c.name === "signOut")!.signatures![0]!; + expect(sig.type).toEqual({ type: "intrinsic", name: "Void" }); + expect(sig.parameters).toBeUndefined(); + }); + + it("maps refresh (no parameters field) to no parameters on signature", () => { + const cls = result.children.find(c => c.name === "AuthClient")!; + const sig = cls.children?.find(c => c.name === "refresh")!.signatures![0]!; + expect(sig.parameters).toBeUndefined(); + expect(sig.type).toEqual({ type: "intrinsic", name: "AuthSession" }); + }); + + it("assigns unique integer ids to every node", () => { + const allIds: number[] = []; + function collect(decl: TypeDocDeclaration) { + allIds.push(decl.id); + for (const child of decl.children ?? []) collect(child); + for (const sig of decl.signatures ?? []) { + allIds.push(sig.id); + for (const param of sig.parameters ?? []) allIds.push(param.id); + } + } + allIds.push(result.id); + for (const child of result.children) collect(child); + expect(new Set(allIds).size).toBe(allIds.length); + }); +}); + +// --------------------------------------------------------------------------- +// Category injection via categoryMap +// --------------------------------------------------------------------------- + +describe("transformSymbolGraph with categoryMap", () => { + const mapped = transformSymbolGraph([fixture], "Auth", { Auth: "Authentication" }); + + it("injects @category on a top-level class with an existing doc comment", () => { + const cls = mapped.children.find(c => c.name === "AuthClient")!; + const tag = cls.comment?.blockTags?.find(t => t.tag === "@category"); + expect(tag).toBeDefined(); + expect(tag!.content[0].text).toBe("Authentication"); + expect(cls.comment?.summary).toEqual([{ kind: "text", text: "Manages authentication state." }]); + }); + + it("injects @category on a callable member with no prior comment", () => { + const cls = mapped.children.find(c => c.name === "AuthClient")!; + const method = cls.children?.find(c => c.name === "signIn")!; + const tag = method.comment?.blockTags?.find(t => t.tag === "@category"); + expect(tag).toBeDefined(); + expect(tag!.content[0].text).toBe("Authentication"); + expect(method.comment?.summary).toEqual([]); + }); + + it("does not inject @category when module is absent from map", () => { + const none = transformSymbolGraph([fixture], "Auth", {}); + const cls = none.children.find(c => c.name === "AuthClient")!; + expect(cls.comment?.blockTags?.find(t => t.tag === "@category")).toBeUndefined(); + }); +}); diff --git a/scripts/swift-docs/tsconfig.json b/scripts/swift-docs/tsconfig.json new file mode 100644 index 000000000..a201c8775 --- /dev/null +++ b/scripts/swift-docs/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "noEmit": true, + "types": ["node"], + "resolveJsonModule": true + }, + "include": ["src", "test"] +} diff --git a/scripts/swift-docs/vitest.config.ts b/scripts/swift-docs/vitest.config.ts new file mode 100644 index 000000000..8b5840aca --- /dev/null +++ b/scripts/swift-docs/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["test/**/*.test.ts"], + }, +});