feat: frontend support.
This commit is contained in:
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"permissions": {
|
|
||||||
"allow": [
|
|
||||||
"Bash(cat:*)",
|
|
||||||
"Bash(mkdir:*)",
|
|
||||||
"mcp__ide__getDiagnostics",
|
|
||||||
"Bash(bun run:*)",
|
|
||||||
"Bash(bun:*)",
|
|
||||||
"Bash(NODE_TLS_REJECT_UNAUTHORIZED=0 bun:*)"
|
|
||||||
],
|
|
||||||
"deny": [],
|
|
||||||
"ask": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@ cache/
|
|||||||
*.apk
|
*.apk
|
||||||
anisette/*
|
anisette/*
|
||||||
js/node_modules/*
|
js/node_modules/*
|
||||||
|
*.so
|
||||||
|
*.wasm
|
||||||
99
CLAUDE.md
99
CLAUDE.md
@@ -1,91 +1,58 @@
|
|||||||
# AGENTS.md
|
# CLAUDE.md
|
||||||
|
|
||||||
This file provides guidance to Every Agents when working with code in this repository.
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This crate provides Apple Anisette authentication in browser via WebAssembly. It uses a custom Unicorn Engine fork (https://github.com/lbr77/unicorn/tree/tci-emscripten) to emulate ARM64 Android binaries (`libstoreservicescore.so` + `libCoreADI.so`) for generating Anisette headers.
|
Rust/WASM project that emulates ARM64 Android binaries (`libstoreservicescore.so` + `libCoreADI.so`) via a custom Unicorn Engine fork to generate Apple Anisette headers in the browser or Node.js.
|
||||||
|
|
||||||
**Note**: The Android library blobs (`libstoreservicescore.so`, `libCoreADI.so`) are not included in this repository. Extract them from an APK or obtain separately.
|
The Android library blobs are not included — extract from an APK or obtain separately.
|
||||||
|
|
||||||
## Build Commands
|
## Build Commands
|
||||||
|
|
||||||
### Prerequisites
|
Must source Emscripten before building WASM:
|
||||||
|
|
||||||
- Rust nightly (edition 2024)
|
|
||||||
- Emscripten SDK (for WASM builds)
|
|
||||||
|
|
||||||
### Setup Unicorn Engine
|
|
||||||
|
|
||||||
Clone the custom Unicorn repository and checkout to the `tci-emscripten` branch:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/lbr77/unicorn.git
|
source "/Users/libr/Desktop/Life/emsdk/emsdk_env.sh"
|
||||||
cd unicorn && git checkout tci-emscripten
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Then build Unicorn for Emscripten:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash script/rebuild-unicorn.sh
|
bun run build # WASM (debug) + JS bundle
|
||||||
|
bun run release # WASM (release) + JS bundle
|
||||||
|
bun run build:js # JS bundle only (no WASM rebuild)
|
||||||
|
bun run build:glue # WASM only
|
||||||
|
bun run build:unicorn # Rebuild Unicorn (rarely needed)
|
||||||
```
|
```
|
||||||
|
|
||||||
The rebuild script handles:
|
JS bundle outputs to `dist/anisette.js`. WASM glue outputs to `dist/anisette_rs.node.{js,wasm}` and `dist/anisette_rs.{js,wasm}`.
|
||||||
- Running `emcmake cmake` with appropriate flags
|
|
||||||
- Building only `arm` and `aarch64` architectures
|
|
||||||
- Using static archives (`-DUNICORN_LEGACY_STATIC_ARCHIVE=ON`)
|
|
||||||
|
|
||||||
### Build WASM Glue
|
The `js/package.json` build script also outputs to `../dist/anisette.js` directly.
|
||||||
|
|
||||||
```bash
|
|
||||||
bash script/build-glue.sh # Debug build
|
|
||||||
bash script/build-glue.sh --release # Release build
|
|
||||||
```
|
|
||||||
|
|
||||||
Outputs:
|
|
||||||
- `test/dist/anisette_rs.js` / `.wasm` (web)
|
|
||||||
- `test/dist/anisette_rs.node.js` / `.wasm` (Node.js)
|
|
||||||
- Copies to `../../frontend/public/anisette/`
|
|
||||||
|
|
||||||
### Run Native Example
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo run --example anisette -- <libstoreservicescore.so> <libCoreADI.so> [library_path] [dsid] [apple_root_pem]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Node.js Example
|
|
||||||
|
|
||||||
```bash
|
|
||||||
node example/run-node.mjs <libstoreservicescore.so> <libCoreADI.so> [library_path] [dsid] [identifier]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
### Core Modules
|
### Rust → WASM layer (`src/`)
|
||||||
|
|
||||||
- **`adi.rs`**: ADI (Apple Device Identity) wrapper — provisioning, OTP requests
|
- `exports.rs` — all `#[no_mangle]` C FFI exports. Every new public function must also be added to `EXPORTED_FUNCTIONS` in `script/build-glue.sh` (both `WEB_EXPORTED_FUNCTIONS` and `NODE_EXPORTED_FUNCTIONS` as appropriate).
|
||||||
- **`emu.rs`**: Unicorn-based ARM64 emulator core — library loading, symbol resolution, function calls
|
- `adi.rs` — wraps the emulated ADI library calls
|
||||||
- **`exports.rs`**: C FFI exports for WASM — `anisette_*` functions
|
- `emu.rs` — Unicorn ARM64 emulator core
|
||||||
- **`device.rs`**: Device identity management — UUIDs, identifiers, persistence
|
- `idbfs.rs` — Emscripten IndexedDB FS integration (browser only)
|
||||||
- **`idbfs.rs`**: IndexedDB filesystem integration for Emscripten
|
|
||||||
- **`provisioning.rs`** / **`provisioning_wasm.rs`**: Apple provisioning protocol
|
|
||||||
|
|
||||||
### Emulator Memory Layout
|
### JS/TS layer (`js/src/`)
|
||||||
|
|
||||||
- Libraries mapped to import address space with stub hooks
|
- `anisette.ts` — main `Anisette` class. **Each `getData()` call fully reinits the WASM state** (new `WasmBridge`, re-writes VFS files, re-calls `initFromBlobs`) to work around a Unicorn emulator bug that causes illegal writes on repeated use.
|
||||||
- Stack, heap, and return addresses pre-allocated
|
- `wasm-bridge.ts` — raw pointer/length marshalling to WASM exports
|
||||||
- Import hooks dispatch to runtime stubs
|
- `wasm-loader.ts` — thin wrapper around `ModuleFactory`; caller must pass `locateFile` via `moduleOverrides` to resolve the `.wasm` path
|
||||||
|
- `provisioning.ts` — Apple provisioning HTTP flow (fetches SPIM, sends CPIM)
|
||||||
|
- `device.ts` — loads or generates `device.json`
|
||||||
|
|
||||||
### Public API (exports.rs)
|
### Key design decisions
|
||||||
|
|
||||||
- `anisette_init_from_blobs` — Initialize from library blobs
|
- `adi.pb` (provisioning state) lives in the WASM VFS. After provisioning, call `anisette.getAdiPb()` and persist it yourself — it is **not** automatically written to disk.
|
||||||
- `anisette_is_machine_provisioned` — Check provisioning state
|
- `fromSo()` accepts `init.adiPb` and `init.deviceJsonBytes` to restore a previous session into the VFS before init.
|
||||||
- `anisette_start_provisioning` / `anisette_end_provisioning` — Provisioning flow
|
- `loadWasm()` is environment-agnostic — no `node:` imports. Pass `locateFile` in `moduleOverrides`.
|
||||||
- `anisette_request_otp` — Generate OTP + machine ID headers
|
|
||||||
|
|
||||||
### Data Flow
|
### Example usage
|
||||||
|
|
||||||
1. Load Android `libstoreservicescore.so` + `libCoreADI.so`
|
```bash
|
||||||
2. Initialize device identity (`device.json`)
|
NODE_TLS_REJECT_UNAUTHORIZED=0 bun example/anisette-api.mjs \
|
||||||
3. Provision with Apple (if needed)
|
<libstoreservicescore.so> <libCoreADI.so> [library_path]
|
||||||
4. Request OTP → `X-Apple-I-MD` + `X-Apple-I-MD-M` headers
|
```
|
||||||
|
|||||||
@@ -10,14 +10,15 @@ import path from "node:path";
|
|||||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const bundlePath = path.join(__dirname, "..", "dist", "anisette.js");
|
const {Anisette, loadWasm} = await import("../js/dist");
|
||||||
|
// const bundlePath = path.join(__dirname, "..", "dist", "anisette.js");
|
||||||
|
|
||||||
const { Anisette, loadWasm } = await import(
|
// const { Anisette, loadWasm } = await import(
|
||||||
pathToFileURL(bundlePath).href
|
// pathToFileURL(bundlePath).href
|
||||||
).catch(() => {
|
// ).catch(() => {
|
||||||
console.error("Bundle not found. Run: npm run build:js");
|
// console.error("Bundle not found. Run: npm run build:js");
|
||||||
process.exit(1);
|
// process.exit(1);
|
||||||
});
|
// });
|
||||||
|
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
if (args.length < 2) {
|
if (args.length < 2) {
|
||||||
|
|||||||
24
frontend/.gitignore
vendored
Normal file
24
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
5
frontend/README.md
Normal file
5
frontend/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Vue 3 + Vite
|
||||||
|
|
||||||
|
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||||
|
|
||||||
|
Learn more about IDE Support for Vue in the [Vue Docs Scaling up Guide](https://vuejs.org/guide/scaling-up/tooling.html#ide-support).
|
||||||
197
frontend/bun.lock
Normal file
197
frontend/bun.lock
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "frontend",
|
||||||
|
"dependencies": {
|
||||||
|
"anisette-js": "file:../js",
|
||||||
|
"libcurl.js": "^0.7.4",
|
||||||
|
"vue": "^3.5.25",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^6.0.2",
|
||||||
|
"vite": "^7.3.1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
|
||||||
|
|
||||||
|
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="],
|
||||||
|
|
||||||
|
"@babel/parser": ["@babel/parser@7.29.0", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww=="],
|
||||||
|
|
||||||
|
"@babel/types": ["@babel/types@7.29.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A=="],
|
||||||
|
|
||||||
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="],
|
||||||
|
|
||||||
|
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.3", "", { "os": "android", "cpu": "arm64" }, "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg=="],
|
||||||
|
|
||||||
|
"@esbuild/android-x64": ["@esbuild/android-x64@0.27.3", "", { "os": "android", "cpu": "x64" }, "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg=="],
|
||||||
|
|
||||||
|
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w=="],
|
||||||
|
|
||||||
|
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.3", "", { "os": "linux", "cpu": "arm" }, "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.3", "", { "os": "linux", "cpu": "none" }, "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw=="],
|
||||||
|
|
||||||
|
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.3", "", { "os": "linux", "cpu": "x64" }, "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA=="],
|
||||||
|
|
||||||
|
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.3", "", { "os": "none", "cpu": "x64" }, "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw=="],
|
||||||
|
|
||||||
|
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ=="],
|
||||||
|
|
||||||
|
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.3", "", { "os": "none", "cpu": "arm64" }, "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g=="],
|
||||||
|
|
||||||
|
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q=="],
|
||||||
|
|
||||||
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="],
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||||
|
|
||||||
|
"@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.2", "", {}, "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.59.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.59.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.59.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.59.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.59.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.59.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.59.0", "", { "os": "none", "cpu": "arm64" }, "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.59.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.59.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA=="],
|
||||||
|
|
||||||
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="],
|
||||||
|
|
||||||
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@25.3.2", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q=="],
|
||||||
|
|
||||||
|
"@vitejs/plugin-vue": ["@vitejs/plugin-vue@6.0.4", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.2" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "vue": "^3.2.25" } }, "sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ=="],
|
||||||
|
|
||||||
|
"@vue/compiler-core": ["@vue/compiler-core@3.5.29", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/shared": "3.5.29", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw=="],
|
||||||
|
|
||||||
|
"@vue/compiler-dom": ["@vue/compiler-dom@3.5.29", "", { "dependencies": { "@vue/compiler-core": "3.5.29", "@vue/shared": "3.5.29" } }, "sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg=="],
|
||||||
|
|
||||||
|
"@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.29", "", { "dependencies": { "@babel/parser": "^7.29.0", "@vue/compiler-core": "3.5.29", "@vue/compiler-dom": "3.5.29", "@vue/compiler-ssr": "3.5.29", "@vue/shared": "3.5.29", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-oJZhN5XJs35Gzr50E82jg2cYdZQ78wEwvRO6Y63TvLVTc+6xICzJHP1UIecdSPPYIbkautNBanDiWYa64QSFIA=="],
|
||||||
|
|
||||||
|
"@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.29", "", { "dependencies": { "@vue/compiler-dom": "3.5.29", "@vue/shared": "3.5.29" } }, "sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw=="],
|
||||||
|
|
||||||
|
"@vue/reactivity": ["@vue/reactivity@3.5.29", "", { "dependencies": { "@vue/shared": "3.5.29" } }, "sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA=="],
|
||||||
|
|
||||||
|
"@vue/runtime-core": ["@vue/runtime-core@3.5.29", "", { "dependencies": { "@vue/reactivity": "3.5.29", "@vue/shared": "3.5.29" } }, "sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg=="],
|
||||||
|
|
||||||
|
"@vue/runtime-dom": ["@vue/runtime-dom@3.5.29", "", { "dependencies": { "@vue/reactivity": "3.5.29", "@vue/runtime-core": "3.5.29", "@vue/shared": "3.5.29", "csstype": "^3.2.3" } }, "sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg=="],
|
||||||
|
|
||||||
|
"@vue/server-renderer": ["@vue/server-renderer@3.5.29", "", { "dependencies": { "@vue/compiler-ssr": "3.5.29", "@vue/shared": "3.5.29" }, "peerDependencies": { "vue": "3.5.29" } }, "sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g=="],
|
||||||
|
|
||||||
|
"@vue/shared": ["@vue/shared@3.5.29", "", {}, "sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg=="],
|
||||||
|
|
||||||
|
"anisette-js": ["anisette-js@file:../js", { "dependencies": { "@types/node": "^25.3.2" }, "devDependencies": { "typescript": "^5.4.0" } }],
|
||||||
|
|
||||||
|
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
|
||||||
|
|
||||||
|
"entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
||||||
|
|
||||||
|
"esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
|
||||||
|
|
||||||
|
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||||
|
|
||||||
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
|
"libcurl.js": ["libcurl.js@0.7.4", "", {}, "sha512-UpvVirvATP7fD0t4rnsxVRuUpPVIo2QvWj4+5JrMsd1KSEvYkON36+COOPAl88hPlGJddk+DfZRvyF7KG7YcSA=="],
|
||||||
|
|
||||||
|
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||||
|
|
||||||
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
|
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||||
|
|
||||||
|
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||||
|
|
||||||
|
"rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="],
|
||||||
|
|
||||||
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
|
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||||
|
|
||||||
|
"vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
|
||||||
|
|
||||||
|
"vue": ["vue@3.5.29", "", { "dependencies": { "@vue/compiler-dom": "3.5.29", "@vue/compiler-sfc": "3.5.29", "@vue/runtime-dom": "3.5.29", "@vue/server-renderer": "3.5.29", "@vue/shared": "3.5.29" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
12
frontend/index.html
Normal file
12
frontend/index.html
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Anisette JS Demo</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
20
frontend/package.json
Normal file
20
frontend/package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "frontend",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"vue": "^3.5.25",
|
||||||
|
"anisette-js": "file:../js",
|
||||||
|
"libcurl.js": "^0.7.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-vue": "^6.0.2",
|
||||||
|
"vite": "^7.3.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
1527
frontend/public/libcurl_full.js
Normal file
1527
frontend/public/libcurl_full.js
Normal file
File diff suppressed because one or more lines are too long
1527
frontend/public/libcurl_full.mjs
Normal file
1527
frontend/public/libcurl_full.mjs
Normal file
File diff suppressed because one or more lines are too long
65
frontend/src/App.vue
Normal file
65
frontend/src/App.vue
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Anisette, loadWasmModule } from 'anisette-js'
|
||||||
|
import { initLibcurl } from './libcurl-init'
|
||||||
|
import { LibcurlHttpClient } from './libcurl-http'
|
||||||
|
|
||||||
|
const status = ref('Ready')
|
||||||
|
const headers = ref(null)
|
||||||
|
|
||||||
|
async function runDemo() {
|
||||||
|
try {
|
||||||
|
status.value = 'Loading libcurl...'
|
||||||
|
await initLibcurl()
|
||||||
|
const httpClient = new LibcurlHttpClient()
|
||||||
|
|
||||||
|
status.value = 'Loading WASM...'
|
||||||
|
const wasmModule = await loadWasmModule({ printErr: () => {}})
|
||||||
|
|
||||||
|
status.value = 'Loading library files...'
|
||||||
|
const [ssResp, caResp] = await Promise.all([
|
||||||
|
fetch('/libstoreservicescore.so'),
|
||||||
|
fetch('/libCoreADI.so')
|
||||||
|
])
|
||||||
|
|
||||||
|
if (!ssResp.ok || !caResp.ok) {
|
||||||
|
throw new Error('Failed to load .so files from public directory')
|
||||||
|
}
|
||||||
|
|
||||||
|
const ssBytes = new Uint8Array(await ssResp.arrayBuffer())
|
||||||
|
const caBytes = new Uint8Array(await caResp.arrayBuffer())
|
||||||
|
|
||||||
|
status.value = 'Initializing Anisette...'
|
||||||
|
const anisette = await Anisette.fromSo(ssBytes, caBytes, wasmModule, {
|
||||||
|
httpClient,
|
||||||
|
init: { libraryPath: '/anisette' }
|
||||||
|
})
|
||||||
|
console.log(anisette.getDevice())
|
||||||
|
if (!anisette.isProvisioned) {
|
||||||
|
status.value = 'Provisioning...'
|
||||||
|
await anisette.provision()
|
||||||
|
}
|
||||||
|
|
||||||
|
status.value = 'Getting headers...'
|
||||||
|
headers.value = await anisette.getData()
|
||||||
|
status.value = 'Done'
|
||||||
|
} catch (err) {
|
||||||
|
status.value = `Error: ${err.message}`
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1>Anisette JS Demo</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button @click="runDemo">Run</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>Status: {{ status }}</p>
|
||||||
|
|
||||||
|
<pre v-if="headers">{{ JSON.stringify(headers, null, 2) }}</pre>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
28
frontend/src/libcurl-http.ts
Normal file
28
frontend/src/libcurl-http.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// Libcurl-based HTTP client for browser
|
||||||
|
|
||||||
|
import type { HttpClient } from "anisette-js";
|
||||||
|
import { libcurl } from "./libcurl-init";
|
||||||
|
|
||||||
|
export class LibcurlHttpClient implements HttpClient {
|
||||||
|
async get(url: string, headers: Record<string, string>): Promise<Uint8Array> {
|
||||||
|
// @ts-ignore
|
||||||
|
const response = (await libcurl.fetch(url, { method: "GET", headers, insecure: true })) as Response;
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP GET ${url} failed: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
return new Uint8Array(await response.arrayBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
async post(
|
||||||
|
url: string,
|
||||||
|
body: string,
|
||||||
|
headers: Record<string, string>
|
||||||
|
): Promise<Uint8Array> {
|
||||||
|
// @ts-ignore
|
||||||
|
const response = (await libcurl.fetch(url, { method: "POST", body, headers, insecure: true})) as Response;
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP POST ${url} failed: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
return new Uint8Array(await response.arrayBuffer());
|
||||||
|
}
|
||||||
|
}
|
||||||
20
frontend/src/libcurl-init.ts
Normal file
20
frontend/src/libcurl-init.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { libcurl } from "../public/libcurl_full.mjs";
|
||||||
|
|
||||||
|
let initialized = false;
|
||||||
|
let initPromise: Promise<void> | null = null;
|
||||||
|
|
||||||
|
export async function initLibcurl(): Promise<void> {
|
||||||
|
if (initialized) return;
|
||||||
|
if (initPromise) return initPromise;
|
||||||
|
initPromise = (async () => {
|
||||||
|
const wsProto = location.protocol === "https:" ? "wss:" : "ws:";
|
||||||
|
let wsUrl = `${wsProto}//${location.host}/wisp/`;
|
||||||
|
libcurl.set_websocket(wsUrl);
|
||||||
|
await libcurl.load_wasm("/libcurl.wasm");
|
||||||
|
initialized = true;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return initPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { libcurl };
|
||||||
4
frontend/src/main.js
Normal file
4
frontend/src/main.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
createApp(App).mount('#app')
|
||||||
40
frontend/src/types/libcurl.d.ts
vendored
Normal file
40
frontend/src/types/libcurl.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
declare module "libcurl.js" {
|
||||||
|
interface LibcurlResponse {
|
||||||
|
ok: boolean;
|
||||||
|
status: number;
|
||||||
|
statusText: string;
|
||||||
|
headers: Headers;
|
||||||
|
raw_headers: [string, string][];
|
||||||
|
text(): Promise<string>;
|
||||||
|
json(): Promise<any>;
|
||||||
|
arrayBuffer(): Promise<ArrayBuffer>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LibcurlFetchOptions {
|
||||||
|
method?: string;
|
||||||
|
headers?: Record<string, string> | Headers;
|
||||||
|
body?: string | ArrayBuffer | Uint8Array;
|
||||||
|
redirect?: "follow" | "manual" | "error";
|
||||||
|
proxy?: string;
|
||||||
|
_libcurl_verbose?: number;
|
||||||
|
_libcurl_http_version?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Libcurl {
|
||||||
|
ready: boolean;
|
||||||
|
onload: (() => void) | null;
|
||||||
|
set_websocket(url: string): void;
|
||||||
|
load_wasm(url?: string): Promise<void>;
|
||||||
|
fetch(url: string, options?: LibcurlFetchOptions): Promise<LibcurlResponse>;
|
||||||
|
get_error_string(code: number): string;
|
||||||
|
get_cacert(): string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const libcurl: Libcurl;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "libcurl.js/bundled" {
|
||||||
|
import type { Libcurl } from "libcurl.js";
|
||||||
|
export const libcurl: Libcurl;
|
||||||
|
}
|
||||||
19
frontend/vite.config.js
Normal file
19
frontend/vite.config.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
|
optimizeDeps: {
|
||||||
|
exclude: ['anisette-js']
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
target: 'esnext'
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
"/api": "http://localhost:8080",
|
||||||
|
"/wisp": { target: "ws://localhost:8080", ws: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
0
js/.gitignore
vendored
Normal file
0
js/.gitignore
vendored
Normal file
@@ -4,12 +4,19 @@
|
|||||||
"workspaces": {
|
"workspaces": {
|
||||||
"": {
|
"": {
|
||||||
"name": "anisette-js",
|
"name": "anisette-js",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^25.3.2",
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.4.0",
|
"typescript": "^5.4.0",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
|
"@types/node": ["@types/node@25.3.2", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,24 @@
|
|||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"import": "./dist/index.js",
|
"types": "./dist/index.d.ts",
|
||||||
"types": "./dist/index.d.ts"
|
"default": "./src/browser.ts",
|
||||||
|
"node": "./src/index.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"./src/anisette_rs.js",
|
||||||
|
"./src/anisette_rs.node.js"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "bun build src/index.ts --outfile ../dist/anisette.js --target node --format esm --minify-syntax --minify-whitespace",
|
"build": "tsc && cp src/anisette_rs.js dist/ && cp src/anisette_rs.node.js dist/",
|
||||||
"build:cjs": "bun build src/index.ts --outfile dist/anisette.cjs --target node --format cjs --minify",
|
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.4.0"
|
"typescript": "^5.4.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "^25.3.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
js/src/browser.ts
Normal file
25
js/src/browser.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//@ts-expect-error no types for the Emscripten module factory
|
||||||
|
import ModuleFactory from "./anisette_rs.js";
|
||||||
|
export * from './index';
|
||||||
|
export type EmscriptenModule = any;
|
||||||
|
export interface ModuleOverrides {
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wasmUrl = new URL("./anisette_rs.wasm", import.meta.url).href;
|
||||||
|
|
||||||
|
export async function loadWasmModule(
|
||||||
|
moduleOverrides: ModuleOverrides = {}
|
||||||
|
): Promise<EmscriptenModule> {
|
||||||
|
return ModuleFactory({
|
||||||
|
...moduleOverrides,
|
||||||
|
locateFile: (filename: string) => {
|
||||||
|
if (filename.endsWith(".wasm")) return wasmUrl;
|
||||||
|
return filename;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadWasm = loadWasmModule;
|
||||||
|
export const isNodeEnvironment = () => false;
|
||||||
|
export const getWasmBinaryPath = () => wasmUrl;
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
// Public entry point — re-exports everything users need
|
// Public entry point — re-exports everything users need
|
||||||
|
|
||||||
export { Anisette } from "./anisette.js";
|
export { Anisette } from "./anisette.js";
|
||||||
export { loadWasm } from "./wasm-loader.js";
|
|
||||||
export { WasmBridge } from "./wasm-bridge.js";
|
export { WasmBridge } from "./wasm-bridge.js";
|
||||||
export { Device } from "./device.js";
|
export { Device } from "./device.js";
|
||||||
export { LibraryStore } from "./library.js";
|
export { LibraryStore } from "./library.js";
|
||||||
@@ -15,3 +14,12 @@ export type {
|
|||||||
DeviceJson,
|
DeviceJson,
|
||||||
} from "./types.js";
|
} from "./types.js";
|
||||||
export type { AnisetteOptions } from "./anisette.js";
|
export type { AnisetteOptions } from "./anisette.js";
|
||||||
|
|
||||||
|
export {
|
||||||
|
loadWasmModule,
|
||||||
|
loadWasmModule as loadWasm,
|
||||||
|
isNodeEnvironment,
|
||||||
|
getWasmBinaryPath,
|
||||||
|
type EmscriptenModule,
|
||||||
|
type ModuleOverrides,
|
||||||
|
} from "./wasm-loader.js";
|
||||||
|
|||||||
@@ -1,7 +1,94 @@
|
|||||||
// @ts-expect-error — glue file is generated, no types available
|
// Unified WASM loader with automatic environment detection
|
||||||
import ModuleFactory from "../../dist/anisette_rs.node.js";
|
// Uses import.meta.url + protocol detection to support both Node.js and Browser
|
||||||
|
//
|
||||||
|
// Browser (Vite/Webpack/etc): https://... or http://... -> anisette_rs.js + .wasm
|
||||||
|
// Node.js: file://... -> anisette_rs.node.js + .wasm
|
||||||
|
|
||||||
|
// Get the base URL from import.meta.url
|
||||||
|
const MODULE_URL = new URL(import.meta.url);
|
||||||
|
const IS_NODE = MODULE_URL.protocol === "file:";
|
||||||
|
|
||||||
|
// Determine which WASM build to use based on environment
|
||||||
|
const WASM_JS_PATH = IS_NODE
|
||||||
|
? new URL("../dist/anisette_rs.node.js", MODULE_URL)
|
||||||
|
: new URL("../dist/anisette_rs.js", MODULE_URL);
|
||||||
|
|
||||||
|
const WASM_BINARY_PATH = IS_NODE
|
||||||
|
? new URL("../dist/anisette_rs.node.wasm", MODULE_URL)
|
||||||
|
: new URL("../dist/anisette_rs.wasm", MODULE_URL);
|
||||||
|
|
||||||
|
// Module overrides type (Emscripten module configuration)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export async function loadWasm(moduleOverrides?: Record<string, any>): Promise<any> {
|
export type EmscriptenModule = any;
|
||||||
return ModuleFactory({ ...moduleOverrides });
|
|
||||||
|
export interface ModuleOverrides {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the Emscripten WASM module with automatic environment detection.
|
||||||
|
*
|
||||||
|
* This function automatically:
|
||||||
|
* - Detects Node.js vs Browser environment via import.meta.url protocol
|
||||||
|
* - Loads the appropriate WASM build (node or web)
|
||||||
|
* - Configures locateFile to find the .wasm binary
|
||||||
|
* - Initializes the module with optional overrides
|
||||||
|
*
|
||||||
|
* @param moduleOverrides - Optional Emscripten module configuration overrides
|
||||||
|
* @returns Initialized Emscripten module with all exports (_malloc, _free, _anisette_* etc.)
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* // Browser (Vue/React/Next.js) and Node.js - same code!
|
||||||
|
* const module = await loadWasmModule();
|
||||||
|
*
|
||||||
|
* // With custom overrides
|
||||||
|
* const module = await loadWasmModule({
|
||||||
|
* print: (text: string) => console.log("WASM:", text),
|
||||||
|
* printErr: (text: string) => console.error("WASM Error:", text),
|
||||||
|
* });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export async function loadWasmModule(
|
||||||
|
moduleOverrides: ModuleOverrides = {}
|
||||||
|
): Promise<EmscriptenModule> {
|
||||||
|
// Dynamic import of the appropriate Emscripten glue file
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const { default: ModuleFactory } = await import(/* @vite-ignore */ WASM_JS_PATH.href) as { default: (config: ModuleOverrides) => Promise<EmscriptenModule> };
|
||||||
|
|
||||||
|
// In browser: let Emscripten use default fetch behavior
|
||||||
|
// In Node.js: provide locateFile to resolve the .wasm path
|
||||||
|
const config: ModuleOverrides = IS_NODE
|
||||||
|
? {
|
||||||
|
...moduleOverrides,
|
||||||
|
locateFile: (filename: string) => {
|
||||||
|
if (filename.endsWith(".wasm")) {
|
||||||
|
// In Node.js, return the absolute file path
|
||||||
|
return WASM_BINARY_PATH.pathname;
|
||||||
|
}
|
||||||
|
return filename;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: moduleOverrides;
|
||||||
|
|
||||||
|
return ModuleFactory(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to check if running in Node.js environment.
|
||||||
|
* Uses the same protocol detection as the loader.
|
||||||
|
*/
|
||||||
|
export function isNodeEnvironment(): boolean {
|
||||||
|
return IS_NODE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the resolved WASM binary path (useful for debugging).
|
||||||
|
*/
|
||||||
|
export function getWasmBinaryPath(): string {
|
||||||
|
return IS_NODE ? WASM_BINARY_PATH.pathname : WASM_BINARY_PATH.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-export for backward compatibility
|
||||||
|
export { loadWasmModule as loadWasm };
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2020",
|
"target": "ES2022",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"lib": ["ES2020", "DOM"],
|
"lib": ["ES2022", "DOM"],
|
||||||
"outDir": "./dist",
|
"outDir": "./dist",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ fi
|
|||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
TARGET_DIR="${ROOT_DIR}/target/wasm32-unknown-emscripten/${BUILD_MODE}"
|
TARGET_DIR="${ROOT_DIR}/target/wasm32-unknown-emscripten/${BUILD_MODE}"
|
||||||
DIST_DIR="${ROOT_DIR}/dist"
|
DIST_DIR="${ROOT_DIR}/js/src"
|
||||||
# EMSDK_DIR="${EMSDK:-/Users/libr/Desktop/Life/emsdk}"
|
# EMSDK_DIR="${EMSDK:-/Users/libr/Desktop/Life/emsdk}"
|
||||||
UNICORN_BUILD_DIR="${UNICORN_BUILD_DIR:-${ROOT_DIR}/../unicorn/build}"
|
UNICORN_BUILD_DIR="${UNICORN_BUILD_DIR:-${ROOT_DIR}/../unicorn/build}"
|
||||||
NODE_DIST_JS="${DIST_DIR}/anisette_rs.node.js"
|
NODE_DIST_JS="${DIST_DIR}/anisette_rs.node.js"
|
||||||
@@ -66,6 +66,7 @@ emcc \
|
|||||||
-sEXPORT_ES6=1 \
|
-sEXPORT_ES6=1 \
|
||||||
-sENVIRONMENT=web \
|
-sENVIRONMENT=web \
|
||||||
-sWASM=1 \
|
-sWASM=1 \
|
||||||
|
-sSINGLE_FILE=1 \
|
||||||
-sALLOW_MEMORY_GROWTH=1 \
|
-sALLOW_MEMORY_GROWTH=1 \
|
||||||
-sINITIAL_MEMORY=268435456 \
|
-sINITIAL_MEMORY=268435456 \
|
||||||
-sWASM_BIGINT=1 \
|
-sWASM_BIGINT=1 \
|
||||||
@@ -81,6 +82,7 @@ emcc \
|
|||||||
-sEXPORT_ES6=1 \
|
-sEXPORT_ES6=1 \
|
||||||
-sENVIRONMENT=node \
|
-sENVIRONMENT=node \
|
||||||
-sWASM=1 \
|
-sWASM=1 \
|
||||||
|
-sSINGLE_FILE=1 \
|
||||||
-sALLOW_MEMORY_GROWTH=1 \
|
-sALLOW_MEMORY_GROWTH=1 \
|
||||||
-sINITIAL_MEMORY=268435456 \
|
-sINITIAL_MEMORY=268435456 \
|
||||||
-sWASM_BIGINT=1 \
|
-sWASM_BIGINT=1 \
|
||||||
@@ -91,24 +93,4 @@ emcc \
|
|||||||
|
|
||||||
echo "glue build done:"
|
echo "glue build done:"
|
||||||
echo " ${DIST_DIR}/anisette_rs.js"
|
echo " ${DIST_DIR}/anisette_rs.js"
|
||||||
echo " ${DIST_DIR}/anisette_rs.wasm"
|
echo " ${DIST_DIR}/anisette_rs.node.js"
|
||||||
echo " ${NODE_DIST_JS}"
|
|
||||||
echo " ${NODE_DIST_WASM}"
|
|
||||||
|
|
||||||
# Bundle TS API + glue into a single JS file
|
|
||||||
JS_DIR="${ROOT_DIR}/js"
|
|
||||||
if command -v bun >/dev/null 2>&1 && [[ -f "${JS_DIR}/src/index.ts" ]]; then
|
|
||||||
echo "bundling TS API..."
|
|
||||||
bun build "${JS_DIR}/src/index.ts" \
|
|
||||||
--outfile "${DIST_DIR}/anisette.js" \
|
|
||||||
--target node \
|
|
||||||
--format esm \
|
|
||||||
--minify-syntax --minify-whitespace
|
|
||||||
echo " ${DIST_DIR}/anisette.js"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy to frontend if directory exists (skip in CI if not present)
|
|
||||||
if [[ -d "${ROOT_DIR}/../../frontend/public/anisette" ]]; then
|
|
||||||
cp "${DIST_DIR}/anisette_rs.js" "${ROOT_DIR}/../../frontend/public/anisette/anisette_rs.js"
|
|
||||||
cp "${DIST_DIR}/anisette_rs.wasm" "${ROOT_DIR}/../../frontend/public/anisette/anisette_rs.wasm"
|
|
||||||
fi
|
|
||||||
|
|||||||
Reference in New Issue
Block a user