concept-collection / stan-remote-sampling
Stan remote sampling: compile via stan-wasm-wasi, sample via wasm-exec
Jeremy Magland <jmagland@flatironinstitute.org> committed commit 9ca368255f29 Browse files
15 changed files+2698−0
.github/workflows/deploy.ymladded+40−0View file
@@ -0,0 +1,40 @@
1+name: Deploy to GitHub Pages
2+
3+on:
4+ push:
5+ branches: [main]
6+ workflow_dispatch:
7+
8+permissions:
9+ contents: read
10+ pages: write
11+ id-token: write
12+
13+concurrency:
14+ group: pages
15+ cancel-in-progress: true
16+
17+jobs:
18+ build:
19+ runs-on: ubuntu-latest
20+ steps:
21+ - uses: actions/checkout@v4
22+ - uses: actions/setup-node@v4
23+ with:
24+ node-version: 22
25+ cache: npm
26+ - run: npm ci
27+ - run: npm run build
28+ - uses: actions/upload-pages-artifact@v3
29+ with:
30+ path: dist
31+
32+ deploy:
33+ needs: build
34+ runs-on: ubuntu-latest
35+ environment:
36+ name: github-pages
37+ url: ${{ steps.deployment.outputs.page_url }}
38+ steps:
39+ - id: deployment
40+ uses: actions/deploy-pages@v4
.gitignoreadded+5−0View file
@@ -0,0 +1,5 @@
1+node_modules/
2+dist/
3+*.tsbuildinfo
4+# local prototype artifacts (tinystan/oneTBB checkouts, local server state)
5+wasi-prototype/
README.mdadded+48−0View file
@@ -0,0 +1,48 @@
1+# stan-remote-sampling
2+
3+Compile and sample Stan models from the browser, with all computation remote:
4+
5+- **Compile**: the Stan program is sent to
6+ [stan-wasm-wasi](https://stan-wasm-wasi.fly.dev) (a
7+ [stan-playground](https://github.com/flatironinstitute/stan-playground)
8+ compilation server variant), which returns the model compiled as a **pure
9+ WASI command module** — a sandboxed `.wasm` that runs one MCMC chain per
10+ invocation.
11+- **Sample**: each chain is submitted as one job to a
12+ [wasm-exec](https://github.com/magland/wasm-exec) service (default
13+ `https://wasm-exec.fly.dev`), which relays it to attached workers. Progress
14+ streams back live (SSE stderr events → per-chain progress bars); draws
15+ come back as CSV (SSE stdout events) and are merged across chains.
16+
17+Live app: https://concept-collection.github.io/stan-remote-sampling/
18+
19+You need a wasm-exec **client key** (`ck_...`) to sample; it is stored in
20+localStorage.
21+
22+## Notes
23+
24+- Chains share a seed and get distinct chain ids (CmdStan convention), so a
25+ multi-chain run is statistically equivalent to CmdStan's.
26+- First compile after server idle pays a ~30 s machine cold start; compiled
27+ models are cached by program hash, so recompiles are instant.
28+- wasm-exec jobs run with no filesystem and no network, a 256 MiB memory cap,
29+ and a wall-clock timeout. Draw output is bounded by wasm-exec's 10 MiB
30+ per-stream cap.
31+
32+## Development
33+
34+```bash
35+npm install
36+npm run dev # http://localhost:3000
37+```
38+
39+Deployment to GitHub Pages is automatic on push to `main`
40+(`.github/workflows/deploy.yml`).
41+
42+## History
43+
44+Built in stages: (1) in-browser sampling with one web worker per chain via
45+tinystan (since removed — see git history), (2) a prototype proving Stan
46+models compile to pure-WASI modules (now the
47+[stan-wasm-wasi](https://github.com/magland/stan-wasm-wasi) repo), (3) this
48+remote-only app.
index.htmladded+12−0View file
@@ -0,0 +1,12 @@
1+<!doctype html>
2+<html lang="en">
3+ <head>
4+ <meta charset="UTF-8" />
5+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+ <title>Stan Remote Sampling</title>
7+ </head>
8+ <body>
9+ <div id="root"></div>
10+ <script type="module" src="/src/main.tsx"></script>
11+ </body>
12+</html>
package-lock.jsonadded+1888−0View file
@@ -0,0 +1,1888 @@
1+{
2+ "name": "stan-remote-sampling",
3+ "version": "0.1.0",
4+ "lockfileVersion": 3,
5+ "requires": true,
6+ "packages": {
7+ "": {
8+ "name": "stan-remote-sampling",
9+ "version": "0.1.0",
10+ "dependencies": {
11+ "react": "^18.3.1",
12+ "react-dom": "^18.3.1"
13+ },
14+ "devDependencies": {
15+ "@types/react": "^18.3.12",
16+ "@types/react-dom": "^18.3.1",
17+ "@vitejs/plugin-react": "^4.3.4",
18+ "typescript": "^5.6.3",
19+ "vite": "^6.0.0"
20+ }
21+ },
22+ "node_modules/@babel/code-frame": {
23+ "version": "7.29.7",
24+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
25+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
26+ "dev": true,
27+ "license": "MIT",
28+ "dependencies": {
29+ "@babel/helper-validator-identifier": "^7.29.7",
30+ "js-tokens": "^4.0.0",
31+ "picocolors": "^1.1.1"
32+ },
33+ "engines": {
34+ "node": ">=6.9.0"
35+ }
36+ },
37+ "node_modules/@babel/compat-data": {
38+ "version": "7.29.7",
39+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
40+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
41+ "dev": true,
42+ "license": "MIT",
43+ "engines": {
44+ "node": ">=6.9.0"
45+ }
46+ },
47+ "node_modules/@babel/core": {
48+ "version": "7.29.7",
49+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
50+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
51+ "dev": true,
52+ "license": "MIT",
53+ "dependencies": {
54+ "@babel/code-frame": "^7.29.7",
55+ "@babel/generator": "^7.29.7",
56+ "@babel/helper-compilation-targets": "^7.29.7",
57+ "@babel/helper-module-transforms": "^7.29.7",
58+ "@babel/helpers": "^7.29.7",
59+ "@babel/parser": "^7.29.7",
60+ "@babel/template": "^7.29.7",
61+ "@babel/traverse": "^7.29.7",
62+ "@babel/types": "^7.29.7",
63+ "@jridgewell/remapping": "^2.3.5",
64+ "convert-source-map": "^2.0.0",
65+ "debug": "^4.1.0",
66+ "gensync": "^1.0.0-beta.2",
67+ "json5": "^2.2.3",
68+ "semver": "^6.3.1"
69+ },
70+ "engines": {
71+ "node": ">=6.9.0"
72+ },
73+ "funding": {
74+ "type": "opencollective",
75+ "url": "https://opencollective.com/babel"
76+ }
77+ },
78+ "node_modules/@babel/generator": {
79+ "version": "7.29.7",
80+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
81+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
82+ "dev": true,
83+ "license": "MIT",
84+ "dependencies": {
85+ "@babel/parser": "^7.29.7",
86+ "@babel/types": "^7.29.7",
87+ "@jridgewell/gen-mapping": "^0.3.12",
88+ "@jridgewell/trace-mapping": "^0.3.28",
89+ "jsesc": "^3.0.2"
90+ },
91+ "engines": {
92+ "node": ">=6.9.0"
93+ }
94+ },
95+ "node_modules/@babel/helper-compilation-targets": {
96+ "version": "7.29.7",
97+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
98+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
99+ "dev": true,
100+ "license": "MIT",
101+ "dependencies": {
102+ "@babel/compat-data": "^7.29.7",
103+ "@babel/helper-validator-option": "^7.29.7",
104+ "browserslist": "^4.24.0",
105+ "lru-cache": "^5.1.1",
106+ "semver": "^6.3.1"
107+ },
108+ "engines": {
109+ "node": ">=6.9.0"
110+ }
111+ },
112+ "node_modules/@babel/helper-globals": {
113+ "version": "7.29.7",
114+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
115+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
116+ "dev": true,
117+ "license": "MIT",
118+ "engines": {
119+ "node": ">=6.9.0"
120+ }
121+ },
122+ "node_modules/@babel/helper-module-imports": {
123+ "version": "7.29.7",
124+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
125+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
126+ "dev": true,
127+ "license": "MIT",
128+ "dependencies": {
129+ "@babel/traverse": "^7.29.7",
130+ "@babel/types": "^7.29.7"
131+ },
132+ "engines": {
133+ "node": ">=6.9.0"
134+ }
135+ },
136+ "node_modules/@babel/helper-module-transforms": {
137+ "version": "7.29.7",
138+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
139+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
140+ "dev": true,
141+ "license": "MIT",
142+ "dependencies": {
143+ "@babel/helper-module-imports": "^7.29.7",
144+ "@babel/helper-validator-identifier": "^7.29.7",
145+ "@babel/traverse": "^7.29.7"
146+ },
147+ "engines": {
148+ "node": ">=6.9.0"
149+ },
150+ "peerDependencies": {
151+ "@babel/core": "^7.0.0"
152+ }
153+ },
154+ "node_modules/@babel/helper-plugin-utils": {
155+ "version": "7.29.7",
156+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
157+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
158+ "dev": true,
159+ "license": "MIT",
160+ "engines": {
161+ "node": ">=6.9.0"
162+ }
163+ },
164+ "node_modules/@babel/helper-string-parser": {
165+ "version": "7.29.7",
166+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
167+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
168+ "dev": true,
169+ "license": "MIT",
170+ "engines": {
171+ "node": ">=6.9.0"
172+ }
173+ },
174+ "node_modules/@babel/helper-validator-identifier": {
175+ "version": "7.29.7",
176+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
177+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
178+ "dev": true,
179+ "license": "MIT",
180+ "engines": {
181+ "node": ">=6.9.0"
182+ }
183+ },
184+ "node_modules/@babel/helper-validator-option": {
185+ "version": "7.29.7",
186+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
187+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
188+ "dev": true,
189+ "license": "MIT",
190+ "engines": {
191+ "node": ">=6.9.0"
192+ }
193+ },
194+ "node_modules/@babel/helpers": {
195+ "version": "7.29.7",
196+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
197+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
198+ "dev": true,
199+ "license": "MIT",
200+ "dependencies": {
201+ "@babel/template": "^7.29.7",
202+ "@babel/types": "^7.29.7"
203+ },
204+ "engines": {
205+ "node": ">=6.9.0"
206+ }
207+ },
208+ "node_modules/@babel/parser": {
209+ "version": "7.29.7",
210+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
211+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
212+ "dev": true,
213+ "license": "MIT",
214+ "dependencies": {
215+ "@babel/types": "^7.29.7"
216+ },
217+ "bin": {
218+ "parser": "bin/babel-parser.js"
219+ },
220+ "engines": {
221+ "node": ">=6.0.0"
222+ }
223+ },
224+ "node_modules/@babel/plugin-transform-react-jsx-self": {
225+ "version": "7.29.7",
226+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
227+ "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
228+ "dev": true,
229+ "license": "MIT",
230+ "dependencies": {
231+ "@babel/helper-plugin-utils": "^7.29.7"
232+ },
233+ "engines": {
234+ "node": ">=6.9.0"
235+ },
236+ "peerDependencies": {
237+ "@babel/core": "^7.0.0-0"
238+ }
239+ },
240+ "node_modules/@babel/plugin-transform-react-jsx-source": {
241+ "version": "7.29.7",
242+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
243+ "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
244+ "dev": true,
245+ "license": "MIT",
246+ "dependencies": {
247+ "@babel/helper-plugin-utils": "^7.29.7"
248+ },
249+ "engines": {
250+ "node": ">=6.9.0"
251+ },
252+ "peerDependencies": {
253+ "@babel/core": "^7.0.0-0"
254+ }
255+ },
256+ "node_modules/@babel/template": {
257+ "version": "7.29.7",
258+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
259+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
260+ "dev": true,
261+ "license": "MIT",
262+ "dependencies": {
263+ "@babel/code-frame": "^7.29.7",
264+ "@babel/parser": "^7.29.7",
265+ "@babel/types": "^7.29.7"
266+ },
267+ "engines": {
268+ "node": ">=6.9.0"
269+ }
270+ },
271+ "node_modules/@babel/traverse": {
272+ "version": "7.29.7",
273+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
274+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
275+ "dev": true,
276+ "license": "MIT",
277+ "dependencies": {
278+ "@babel/code-frame": "^7.29.7",
279+ "@babel/generator": "^7.29.7",
280+ "@babel/helper-globals": "^7.29.7",
281+ "@babel/parser": "^7.29.7",
282+ "@babel/template": "^7.29.7",
283+ "@babel/types": "^7.29.7",
284+ "debug": "^4.3.1"
285+ },
286+ "engines": {
287+ "node": ">=6.9.0"
288+ }
289+ },
290+ "node_modules/@babel/types": {
291+ "version": "7.29.7",
292+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
293+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
294+ "dev": true,
295+ "license": "MIT",
296+ "dependencies": {
297+ "@babel/helper-string-parser": "^7.29.7",
298+ "@babel/helper-validator-identifier": "^7.29.7"
299+ },
300+ "engines": {
301+ "node": ">=6.9.0"
302+ }
303+ },
304+ "node_modules/@esbuild/aix-ppc64": {
305+ "version": "0.25.12",
306+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
307+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
308+ "cpu": [
309+ "ppc64"
310+ ],
311+ "dev": true,
312+ "license": "MIT",
313+ "optional": true,
314+ "os": [
315+ "aix"
316+ ],
317+ "engines": {
318+ "node": ">=18"
319+ }
320+ },
321+ "node_modules/@esbuild/android-arm": {
322+ "version": "0.25.12",
323+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
324+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
325+ "cpu": [
326+ "arm"
327+ ],
328+ "dev": true,
329+ "license": "MIT",
330+ "optional": true,
331+ "os": [
332+ "android"
333+ ],
334+ "engines": {
335+ "node": ">=18"
336+ }
337+ },
338+ "node_modules/@esbuild/android-arm64": {
339+ "version": "0.25.12",
340+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
341+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
342+ "cpu": [
343+ "arm64"
344+ ],
345+ "dev": true,
346+ "license": "MIT",
347+ "optional": true,
348+ "os": [
349+ "android"
350+ ],
351+ "engines": {
352+ "node": ">=18"
353+ }
354+ },
355+ "node_modules/@esbuild/android-x64": {
356+ "version": "0.25.12",
357+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
358+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
359+ "cpu": [
360+ "x64"
361+ ],
362+ "dev": true,
363+ "license": "MIT",
364+ "optional": true,
365+ "os": [
366+ "android"
367+ ],
368+ "engines": {
369+ "node": ">=18"
370+ }
371+ },
372+ "node_modules/@esbuild/darwin-arm64": {
373+ "version": "0.25.12",
374+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
375+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
376+ "cpu": [
377+ "arm64"
378+ ],
379+ "dev": true,
380+ "license": "MIT",
381+ "optional": true,
382+ "os": [
383+ "darwin"
384+ ],
385+ "engines": {
386+ "node": ">=18"
387+ }
388+ },
389+ "node_modules/@esbuild/darwin-x64": {
390+ "version": "0.25.12",
391+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
392+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
393+ "cpu": [
394+ "x64"
395+ ],
396+ "dev": true,
397+ "license": "MIT",
398+ "optional": true,
399+ "os": [
400+ "darwin"
401+ ],
402+ "engines": {
403+ "node": ">=18"
404+ }
405+ },
406+ "node_modules/@esbuild/freebsd-arm64": {
407+ "version": "0.25.12",
408+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
409+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
410+ "cpu": [
411+ "arm64"
412+ ],
413+ "dev": true,
414+ "license": "MIT",
415+ "optional": true,
416+ "os": [
417+ "freebsd"
418+ ],
419+ "engines": {
420+ "node": ">=18"
421+ }
422+ },
423+ "node_modules/@esbuild/freebsd-x64": {
424+ "version": "0.25.12",
425+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
426+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
427+ "cpu": [
428+ "x64"
429+ ],
430+ "dev": true,
431+ "license": "MIT",
432+ "optional": true,
433+ "os": [
434+ "freebsd"
435+ ],
436+ "engines": {
437+ "node": ">=18"
438+ }
439+ },
440+ "node_modules/@esbuild/linux-arm": {
441+ "version": "0.25.12",
442+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
443+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
444+ "cpu": [
445+ "arm"
446+ ],
447+ "dev": true,
448+ "license": "MIT",
449+ "optional": true,
450+ "os": [
451+ "linux"
452+ ],
453+ "engines": {
454+ "node": ">=18"
455+ }
456+ },
457+ "node_modules/@esbuild/linux-arm64": {
458+ "version": "0.25.12",
459+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
460+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
461+ "cpu": [
462+ "arm64"
463+ ],
464+ "dev": true,
465+ "license": "MIT",
466+ "optional": true,
467+ "os": [
468+ "linux"
469+ ],
470+ "engines": {
471+ "node": ">=18"
472+ }
473+ },
474+ "node_modules/@esbuild/linux-ia32": {
475+ "version": "0.25.12",
476+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
477+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
478+ "cpu": [
479+ "ia32"
480+ ],
481+ "dev": true,
482+ "license": "MIT",
483+ "optional": true,
484+ "os": [
485+ "linux"
486+ ],
487+ "engines": {
488+ "node": ">=18"
489+ }
490+ },
491+ "node_modules/@esbuild/linux-loong64": {
492+ "version": "0.25.12",
493+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
494+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
495+ "cpu": [
496+ "loong64"
497+ ],
498+ "dev": true,
499+ "license": "MIT",
500+ "optional": true,
501+ "os": [
502+ "linux"
503+ ],
504+ "engines": {
505+ "node": ">=18"
506+ }
507+ },
508+ "node_modules/@esbuild/linux-mips64el": {
509+ "version": "0.25.12",
510+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
511+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
512+ "cpu": [
513+ "mips64el"
514+ ],
515+ "dev": true,
516+ "license": "MIT",
517+ "optional": true,
518+ "os": [
519+ "linux"
520+ ],
521+ "engines": {
522+ "node": ">=18"
523+ }
524+ },
525+ "node_modules/@esbuild/linux-ppc64": {
526+ "version": "0.25.12",
527+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
528+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
529+ "cpu": [
530+ "ppc64"
531+ ],
532+ "dev": true,
533+ "license": "MIT",
534+ "optional": true,
535+ "os": [
536+ "linux"
537+ ],
538+ "engines": {
539+ "node": ">=18"
540+ }
541+ },
542+ "node_modules/@esbuild/linux-riscv64": {
543+ "version": "0.25.12",
544+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
545+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
546+ "cpu": [
547+ "riscv64"
548+ ],
549+ "dev": true,
550+ "license": "MIT",
551+ "optional": true,
552+ "os": [
553+ "linux"
554+ ],
555+ "engines": {
556+ "node": ">=18"
557+ }
558+ },
559+ "node_modules/@esbuild/linux-s390x": {
560+ "version": "0.25.12",
561+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
562+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
563+ "cpu": [
564+ "s390x"
565+ ],
566+ "dev": true,
567+ "license": "MIT",
568+ "optional": true,
569+ "os": [
570+ "linux"
571+ ],
572+ "engines": {
573+ "node": ">=18"
574+ }
575+ },
576+ "node_modules/@esbuild/linux-x64": {
577+ "version": "0.25.12",
578+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
579+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
580+ "cpu": [
581+ "x64"
582+ ],
583+ "dev": true,
584+ "license": "MIT",
585+ "optional": true,
586+ "os": [
587+ "linux"
588+ ],
589+ "engines": {
590+ "node": ">=18"
591+ }
592+ },
593+ "node_modules/@esbuild/netbsd-arm64": {
594+ "version": "0.25.12",
595+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
596+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
597+ "cpu": [
598+ "arm64"
599+ ],
600+ "dev": true,
601+ "license": "MIT",
602+ "optional": true,
603+ "os": [
604+ "netbsd"
605+ ],
606+ "engines": {
607+ "node": ">=18"
608+ }
609+ },
610+ "node_modules/@esbuild/netbsd-x64": {
611+ "version": "0.25.12",
612+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
613+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
614+ "cpu": [
615+ "x64"
616+ ],
617+ "dev": true,
618+ "license": "MIT",
619+ "optional": true,
620+ "os": [
621+ "netbsd"
622+ ],
623+ "engines": {
624+ "node": ">=18"
625+ }
626+ },
627+ "node_modules/@esbuild/openbsd-arm64": {
628+ "version": "0.25.12",
629+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
630+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
631+ "cpu": [
632+ "arm64"
633+ ],
634+ "dev": true,
635+ "license": "MIT",
636+ "optional": true,
637+ "os": [
638+ "openbsd"
639+ ],
640+ "engines": {
641+ "node": ">=18"
642+ }
643+ },
644+ "node_modules/@esbuild/openbsd-x64": {
645+ "version": "0.25.12",
646+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
647+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
648+ "cpu": [
649+ "x64"
650+ ],
651+ "dev": true,
652+ "license": "MIT",
653+ "optional": true,
654+ "os": [
655+ "openbsd"
656+ ],
657+ "engines": {
658+ "node": ">=18"
659+ }
660+ },
661+ "node_modules/@esbuild/openharmony-arm64": {
662+ "version": "0.25.12",
663+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
664+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
665+ "cpu": [
666+ "arm64"
667+ ],
668+ "dev": true,
669+ "license": "MIT",
670+ "optional": true,
671+ "os": [
672+ "openharmony"
673+ ],
674+ "engines": {
675+ "node": ">=18"
676+ }
677+ },
678+ "node_modules/@esbuild/sunos-x64": {
679+ "version": "0.25.12",
680+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
681+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
682+ "cpu": [
683+ "x64"
684+ ],
685+ "dev": true,
686+ "license": "MIT",
687+ "optional": true,
688+ "os": [
689+ "sunos"
690+ ],
691+ "engines": {
692+ "node": ">=18"
693+ }
694+ },
695+ "node_modules/@esbuild/win32-arm64": {
696+ "version": "0.25.12",
697+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
698+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
699+ "cpu": [
700+ "arm64"
701+ ],
702+ "dev": true,
703+ "license": "MIT",
704+ "optional": true,
705+ "os": [
706+ "win32"
707+ ],
708+ "engines": {
709+ "node": ">=18"
710+ }
711+ },
712+ "node_modules/@esbuild/win32-ia32": {
713+ "version": "0.25.12",
714+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
715+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
716+ "cpu": [
717+ "ia32"
718+ ],
719+ "dev": true,
720+ "license": "MIT",
721+ "optional": true,
722+ "os": [
723+ "win32"
724+ ],
725+ "engines": {
726+ "node": ">=18"
727+ }
728+ },
729+ "node_modules/@esbuild/win32-x64": {
730+ "version": "0.25.12",
731+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
732+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
733+ "cpu": [
734+ "x64"
735+ ],
736+ "dev": true,
737+ "license": "MIT",
738+ "optional": true,
739+ "os": [
740+ "win32"
741+ ],
742+ "engines": {
743+ "node": ">=18"
744+ }
745+ },
746+ "node_modules/@jridgewell/gen-mapping": {
747+ "version": "0.3.13",
748+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
749+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
750+ "dev": true,
751+ "license": "MIT",
752+ "dependencies": {
753+ "@jridgewell/sourcemap-codec": "^1.5.0",
754+ "@jridgewell/trace-mapping": "^0.3.24"
755+ }
756+ },
757+ "node_modules/@jridgewell/remapping": {
758+ "version": "2.3.5",
759+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
760+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
761+ "dev": true,
762+ "license": "MIT",
763+ "dependencies": {
764+ "@jridgewell/gen-mapping": "^0.3.5",
765+ "@jridgewell/trace-mapping": "^0.3.24"
766+ }
767+ },
768+ "node_modules/@jridgewell/resolve-uri": {
769+ "version": "3.1.2",
770+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
771+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
772+ "dev": true,
773+ "license": "MIT",
774+ "engines": {
775+ "node": ">=6.0.0"
776+ }
777+ },
778+ "node_modules/@jridgewell/sourcemap-codec": {
779+ "version": "1.5.5",
780+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
781+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
782+ "dev": true,
783+ "license": "MIT"
784+ },
785+ "node_modules/@jridgewell/trace-mapping": {
786+ "version": "0.3.31",
787+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
788+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
789+ "dev": true,
790+ "license": "MIT",
791+ "dependencies": {
792+ "@jridgewell/resolve-uri": "^3.1.0",
793+ "@jridgewell/sourcemap-codec": "^1.4.14"
794+ }
795+ },
796+ "node_modules/@rolldown/pluginutils": {
797+ "version": "1.0.0-beta.27",
798+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz",
799+ "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==",
800+ "dev": true,
801+ "license": "MIT"
802+ },
803+ "node_modules/@rollup/rollup-android-arm-eabi": {
804+ "version": "4.62.2",
805+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
806+ "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",
807+ "cpu": [
808+ "arm"
809+ ],
810+ "dev": true,
811+ "license": "MIT",
812+ "optional": true,
813+ "os": [
814+ "android"
815+ ]
816+ },
817+ "node_modules/@rollup/rollup-android-arm64": {
818+ "version": "4.62.2",
819+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz",
820+ "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==",
821+ "cpu": [
822+ "arm64"
823+ ],
824+ "dev": true,
825+ "license": "MIT",
826+ "optional": true,
827+ "os": [
828+ "android"
829+ ]
830+ },
831+ "node_modules/@rollup/rollup-darwin-arm64": {
832+ "version": "4.62.2",
833+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz",
834+ "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==",
835+ "cpu": [
836+ "arm64"
837+ ],
838+ "dev": true,
839+ "license": "MIT",
840+ "optional": true,
841+ "os": [
842+ "darwin"
843+ ]
844+ },
845+ "node_modules/@rollup/rollup-darwin-x64": {
846+ "version": "4.62.2",
847+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz",
848+ "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==",
849+ "cpu": [
850+ "x64"
851+ ],
852+ "dev": true,
853+ "license": "MIT",
854+ "optional": true,
855+ "os": [
856+ "darwin"
857+ ]
858+ },
859+ "node_modules/@rollup/rollup-freebsd-arm64": {
860+ "version": "4.62.2",
861+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz",
862+ "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==",
863+ "cpu": [
864+ "arm64"
865+ ],
866+ "dev": true,
867+ "license": "MIT",
868+ "optional": true,
869+ "os": [
870+ "freebsd"
871+ ]
872+ },
873+ "node_modules/@rollup/rollup-freebsd-x64": {
874+ "version": "4.62.2",
875+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz",
876+ "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==",
877+ "cpu": [
878+ "x64"
879+ ],
880+ "dev": true,
881+ "license": "MIT",
882+ "optional": true,
883+ "os": [
884+ "freebsd"
885+ ]
886+ },
887+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
888+ "version": "4.62.2",
889+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz",
890+ "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==",
891+ "cpu": [
892+ "arm"
893+ ],
894+ "dev": true,
895+ "libc": [
896+ "glibc"
897+ ],
898+ "license": "MIT",
899+ "optional": true,
900+ "os": [
901+ "linux"
902+ ]
903+ },
904+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
905+ "version": "4.62.2",
906+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz",
907+ "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==",
908+ "cpu": [
909+ "arm"
910+ ],
911+ "dev": true,
912+ "libc": [
913+ "musl"
914+ ],
915+ "license": "MIT",
916+ "optional": true,
917+ "os": [
918+ "linux"
919+ ]
920+ },
921+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
922+ "version": "4.62.2",
923+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz",
924+ "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==",
925+ "cpu": [
926+ "arm64"
927+ ],
928+ "dev": true,
929+ "libc": [
930+ "glibc"
931+ ],
932+ "license": "MIT",
933+ "optional": true,
934+ "os": [
935+ "linux"
936+ ]
937+ },
938+ "node_modules/@rollup/rollup-linux-arm64-musl": {
939+ "version": "4.62.2",
940+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz",
941+ "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==",
942+ "cpu": [
943+ "arm64"
944+ ],
945+ "dev": true,
946+ "libc": [
947+ "musl"
948+ ],
949+ "license": "MIT",
950+ "optional": true,
951+ "os": [
952+ "linux"
953+ ]
954+ },
955+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
956+ "version": "4.62.2",
957+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz",
958+ "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==",
959+ "cpu": [
960+ "loong64"
961+ ],
962+ "dev": true,
963+ "libc": [
964+ "glibc"
965+ ],
966+ "license": "MIT",
967+ "optional": true,
968+ "os": [
969+ "linux"
970+ ]
971+ },
972+ "node_modules/@rollup/rollup-linux-loong64-musl": {
973+ "version": "4.62.2",
974+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz",
975+ "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==",
976+ "cpu": [
977+ "loong64"
978+ ],
979+ "dev": true,
980+ "libc": [
981+ "musl"
982+ ],
983+ "license": "MIT",
984+ "optional": true,
985+ "os": [
986+ "linux"
987+ ]
988+ },
989+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
990+ "version": "4.62.2",
991+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz",
992+ "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==",
993+ "cpu": [
994+ "ppc64"
995+ ],
996+ "dev": true,
997+ "libc": [
998+ "glibc"
999+ ],
1000+ "license": "MIT",
1001+ "optional": true,
1002+ "os": [
1003+ "linux"
1004+ ]
1005+ },
1006+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
1007+ "version": "4.62.2",
1008+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz",
1009+ "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==",
1010+ "cpu": [
1011+ "ppc64"
1012+ ],
1013+ "dev": true,
1014+ "libc": [
1015+ "musl"
1016+ ],
1017+ "license": "MIT",
1018+ "optional": true,
1019+ "os": [
1020+ "linux"
1021+ ]
1022+ },
1023+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
1024+ "version": "4.62.2",
1025+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz",
1026+ "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==",
1027+ "cpu": [
1028+ "riscv64"
1029+ ],
1030+ "dev": true,
1031+ "libc": [
1032+ "glibc"
1033+ ],
1034+ "license": "MIT",
1035+ "optional": true,
1036+ "os": [
1037+ "linux"
1038+ ]
1039+ },
1040+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
1041+ "version": "4.62.2",
1042+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz",
1043+ "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==",
1044+ "cpu": [
1045+ "riscv64"
1046+ ],
1047+ "dev": true,
1048+ "libc": [
1049+ "musl"
1050+ ],
1051+ "license": "MIT",
1052+ "optional": true,
1053+ "os": [
1054+ "linux"
1055+ ]
1056+ },
1057+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
1058+ "version": "4.62.2",
1059+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz",
1060+ "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==",
1061+ "cpu": [
1062+ "s390x"
1063+ ],
1064+ "dev": true,
1065+ "libc": [
1066+ "glibc"
1067+ ],
1068+ "license": "MIT",
1069+ "optional": true,
1070+ "os": [
1071+ "linux"
1072+ ]
1073+ },
1074+ "node_modules/@rollup/rollup-linux-x64-gnu": {
1075+ "version": "4.62.2",
1076+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",
1077+ "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",
1078+ "cpu": [
1079+ "x64"
1080+ ],
1081+ "dev": true,
1082+ "libc": [
1083+ "glibc"
1084+ ],
1085+ "license": "MIT",
1086+ "optional": true,
1087+ "os": [
1088+ "linux"
1089+ ]
1090+ },
1091+ "node_modules/@rollup/rollup-linux-x64-musl": {
1092+ "version": "4.62.2",
1093+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",
1094+ "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",
1095+ "cpu": [
1096+ "x64"
1097+ ],
1098+ "dev": true,
1099+ "libc": [
1100+ "musl"
1101+ ],
1102+ "license": "MIT",
1103+ "optional": true,
1104+ "os": [
1105+ "linux"
1106+ ]
1107+ },
1108+ "node_modules/@rollup/rollup-openbsd-x64": {
1109+ "version": "4.62.2",
1110+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz",
1111+ "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==",
1112+ "cpu": [
1113+ "x64"
1114+ ],
1115+ "dev": true,
1116+ "license": "MIT",
1117+ "optional": true,
1118+ "os": [
1119+ "openbsd"
1120+ ]
1121+ },
1122+ "node_modules/@rollup/rollup-openharmony-arm64": {
1123+ "version": "4.62.2",
1124+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz",
1125+ "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==",
1126+ "cpu": [
1127+ "arm64"
1128+ ],
1129+ "dev": true,
1130+ "license": "MIT",
1131+ "optional": true,
1132+ "os": [
1133+ "openharmony"
1134+ ]
1135+ },
1136+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
1137+ "version": "4.62.2",
1138+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz",
1139+ "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==",
1140+ "cpu": [
1141+ "arm64"
1142+ ],
1143+ "dev": true,
1144+ "license": "MIT",
1145+ "optional": true,
1146+ "os": [
1147+ "win32"
1148+ ]
1149+ },
1150+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
1151+ "version": "4.62.2",
1152+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz",
1153+ "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==",
1154+ "cpu": [
1155+ "ia32"
1156+ ],
1157+ "dev": true,
1158+ "license": "MIT",
1159+ "optional": true,
1160+ "os": [
1161+ "win32"
1162+ ]
1163+ },
1164+ "node_modules/@rollup/rollup-win32-x64-gnu": {
1165+ "version": "4.62.2",
1166+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz",
1167+ "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==",
1168+ "cpu": [
1169+ "x64"
1170+ ],
1171+ "dev": true,
1172+ "license": "MIT",
1173+ "optional": true,
1174+ "os": [
1175+ "win32"
1176+ ]
1177+ },
1178+ "node_modules/@rollup/rollup-win32-x64-msvc": {
1179+ "version": "4.62.2",
1180+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz",
1181+ "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==",
1182+ "cpu": [
1183+ "x64"
1184+ ],
1185+ "dev": true,
1186+ "license": "MIT",
1187+ "optional": true,
1188+ "os": [
1189+ "win32"
1190+ ]
1191+ },
1192+ "node_modules/@types/babel__core": {
1193+ "version": "7.20.5",
1194+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
1195+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
1196+ "dev": true,
1197+ "license": "MIT",
1198+ "dependencies": {
1199+ "@babel/parser": "^7.20.7",
1200+ "@babel/types": "^7.20.7",
1201+ "@types/babel__generator": "*",
1202+ "@types/babel__template": "*",
1203+ "@types/babel__traverse": "*"
1204+ }
1205+ },
1206+ "node_modules/@types/babel__generator": {
1207+ "version": "7.27.0",
1208+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
1209+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
1210+ "dev": true,
1211+ "license": "MIT",
1212+ "dependencies": {
1213+ "@babel/types": "^7.0.0"
1214+ }
1215+ },
1216+ "node_modules/@types/babel__template": {
1217+ "version": "7.4.4",
1218+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
1219+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
1220+ "dev": true,
1221+ "license": "MIT",
1222+ "dependencies": {
1223+ "@babel/parser": "^7.1.0",
1224+ "@babel/types": "^7.0.0"
1225+ }
1226+ },
1227+ "node_modules/@types/babel__traverse": {
1228+ "version": "7.28.0",
1229+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
1230+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
1231+ "dev": true,
1232+ "license": "MIT",
1233+ "dependencies": {
1234+ "@babel/types": "^7.28.2"
1235+ }
1236+ },
1237+ "node_modules/@types/estree": {
1238+ "version": "1.0.9",
1239+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
1240+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
1241+ "dev": true,
1242+ "license": "MIT"
1243+ },
1244+ "node_modules/@types/prop-types": {
1245+ "version": "15.7.15",
1246+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
1247+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
1248+ "dev": true,
1249+ "license": "MIT"
1250+ },
1251+ "node_modules/@types/react": {
1252+ "version": "18.3.31",
1253+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz",
1254+ "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==",
1255+ "dev": true,
1256+ "license": "MIT",
1257+ "dependencies": {
1258+ "@types/prop-types": "*",
1259+ "csstype": "^3.2.2"
1260+ }
1261+ },
1262+ "node_modules/@types/react-dom": {
1263+ "version": "18.3.7",
1264+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz",
1265+ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==",
1266+ "dev": true,
1267+ "license": "MIT",
1268+ "peerDependencies": {
1269+ "@types/react": "^18.0.0"
1270+ }
1271+ },
1272+ "node_modules/@vitejs/plugin-react": {
1273+ "version": "4.7.0",
1274+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
1275+ "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==",
1276+ "dev": true,
1277+ "license": "MIT",
1278+ "dependencies": {
1279+ "@babel/core": "^7.28.0",
1280+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
1281+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
1282+ "@rolldown/pluginutils": "1.0.0-beta.27",
1283+ "@types/babel__core": "^7.20.5",
1284+ "react-refresh": "^0.17.0"
1285+ },
1286+ "engines": {
1287+ "node": "^14.18.0 || >=16.0.0"
1288+ },
1289+ "peerDependencies": {
1290+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1291+ }
1292+ },
1293+ "node_modules/baseline-browser-mapping": {
1294+ "version": "2.10.41",
1295+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.41.tgz",
1296+ "integrity": "sha512-WwS7MHhqGHHlaVsqRZnhvCEMS0owDX+SxRlve7JkuH7My1Ara3ZriTmCQupPfYjxMZ8I/tgxtJYr2t7taHaH4A==",
1297+ "dev": true,
1298+ "license": "Apache-2.0",
1299+ "bin": {
1300+ "baseline-browser-mapping": "dist/cli.cjs"
1301+ },
1302+ "engines": {
1303+ "node": ">=6.0.0"
1304+ }
1305+ },
1306+ "node_modules/browserslist": {
1307+ "version": "4.28.4",
1308+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
1309+ "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==",
1310+ "dev": true,
1311+ "funding": [
1312+ {
1313+ "type": "opencollective",
1314+ "url": "https://opencollective.com/browserslist"
1315+ },
1316+ {
1317+ "type": "tidelift",
1318+ "url": "https://tidelift.com/funding/github/npm/browserslist"
1319+ },
1320+ {
1321+ "type": "github",
1322+ "url": "https://github.com/sponsors/ai"
1323+ }
1324+ ],
1325+ "license": "MIT",
1326+ "dependencies": {
1327+ "baseline-browser-mapping": "^2.10.38",
1328+ "caniuse-lite": "^1.0.30001799",
1329+ "electron-to-chromium": "^1.5.376",
1330+ "node-releases": "^2.0.48",
1331+ "update-browserslist-db": "^1.2.3"
1332+ },
1333+ "bin": {
1334+ "browserslist": "cli.js"
1335+ },
1336+ "engines": {
1337+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
1338+ }
1339+ },
1340+ "node_modules/caniuse-lite": {
1341+ "version": "1.0.30001800",
1342+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz",
1343+ "integrity": "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==",
1344+ "dev": true,
1345+ "funding": [
1346+ {
1347+ "type": "opencollective",
1348+ "url": "https://opencollective.com/browserslist"
1349+ },
1350+ {
1351+ "type": "tidelift",
1352+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
1353+ },
1354+ {
1355+ "type": "github",
1356+ "url": "https://github.com/sponsors/ai"
1357+ }
1358+ ],
1359+ "license": "CC-BY-4.0"
1360+ },
1361+ "node_modules/convert-source-map": {
1362+ "version": "2.0.0",
1363+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
1364+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
1365+ "dev": true,
1366+ "license": "MIT"
1367+ },
1368+ "node_modules/csstype": {
1369+ "version": "3.2.3",
1370+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
1371+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
1372+ "dev": true,
1373+ "license": "MIT"
1374+ },
1375+ "node_modules/debug": {
1376+ "version": "4.4.3",
1377+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
1378+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
1379+ "dev": true,
1380+ "license": "MIT",
1381+ "dependencies": {
1382+ "ms": "^2.1.3"
1383+ },
1384+ "engines": {
1385+ "node": ">=6.0"
1386+ },
1387+ "peerDependenciesMeta": {
1388+ "supports-color": {
1389+ "optional": true
1390+ }
1391+ }
1392+ },
1393+ "node_modules/electron-to-chromium": {
1394+ "version": "1.5.384",
1395+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.384.tgz",
1396+ "integrity": "sha512-g6KAKY1vkYsADvSPWvdJsuYT0ixdcu6lUtD9P/wJKGBEDlZVXh2AX42j1mPqqaQPDluWjara9ziQ7xqAeXCt5A==",
1397+ "dev": true,
1398+ "license": "ISC"
1399+ },
1400+ "node_modules/esbuild": {
1401+ "version": "0.25.12",
1402+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
1403+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
1404+ "dev": true,
1405+ "hasInstallScript": true,
1406+ "license": "MIT",
1407+ "bin": {
1408+ "esbuild": "bin/esbuild"
1409+ },
1410+ "engines": {
1411+ "node": ">=18"
1412+ },
1413+ "optionalDependencies": {
1414+ "@esbuild/aix-ppc64": "0.25.12",
1415+ "@esbuild/android-arm": "0.25.12",
1416+ "@esbuild/android-arm64": "0.25.12",
1417+ "@esbuild/android-x64": "0.25.12",
1418+ "@esbuild/darwin-arm64": "0.25.12",
1419+ "@esbuild/darwin-x64": "0.25.12",
1420+ "@esbuild/freebsd-arm64": "0.25.12",
1421+ "@esbuild/freebsd-x64": "0.25.12",
1422+ "@esbuild/linux-arm": "0.25.12",
1423+ "@esbuild/linux-arm64": "0.25.12",
1424+ "@esbuild/linux-ia32": "0.25.12",
1425+ "@esbuild/linux-loong64": "0.25.12",
1426+ "@esbuild/linux-mips64el": "0.25.12",
1427+ "@esbuild/linux-ppc64": "0.25.12",
1428+ "@esbuild/linux-riscv64": "0.25.12",
1429+ "@esbuild/linux-s390x": "0.25.12",
1430+ "@esbuild/linux-x64": "0.25.12",
1431+ "@esbuild/netbsd-arm64": "0.25.12",
1432+ "@esbuild/netbsd-x64": "0.25.12",
1433+ "@esbuild/openbsd-arm64": "0.25.12",
1434+ "@esbuild/openbsd-x64": "0.25.12",
1435+ "@esbuild/openharmony-arm64": "0.25.12",
1436+ "@esbuild/sunos-x64": "0.25.12",
1437+ "@esbuild/win32-arm64": "0.25.12",
1438+ "@esbuild/win32-ia32": "0.25.12",
1439+ "@esbuild/win32-x64": "0.25.12"
1440+ }
1441+ },
1442+ "node_modules/escalade": {
1443+ "version": "3.2.0",
1444+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
1445+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
1446+ "dev": true,
1447+ "license": "MIT",
1448+ "engines": {
1449+ "node": ">=6"
1450+ }
1451+ },
1452+ "node_modules/fdir": {
1453+ "version": "6.5.0",
1454+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
1455+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
1456+ "dev": true,
1457+ "license": "MIT",
1458+ "engines": {
1459+ "node": ">=12.0.0"
1460+ },
1461+ "peerDependencies": {
1462+ "picomatch": "^3 || ^4"
1463+ },
1464+ "peerDependenciesMeta": {
1465+ "picomatch": {
1466+ "optional": true
1467+ }
1468+ }
1469+ },
1470+ "node_modules/fsevents": {
1471+ "version": "2.3.3",
1472+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
1473+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
1474+ "dev": true,
1475+ "hasInstallScript": true,
1476+ "license": "MIT",
1477+ "optional": true,
1478+ "os": [
1479+ "darwin"
1480+ ],
1481+ "engines": {
1482+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1483+ }
1484+ },
1485+ "node_modules/gensync": {
1486+ "version": "1.0.0-beta.2",
1487+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
1488+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
1489+ "dev": true,
1490+ "license": "MIT",
1491+ "engines": {
1492+ "node": ">=6.9.0"
1493+ }
1494+ },
1495+ "node_modules/js-tokens": {
1496+ "version": "4.0.0",
1497+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
1498+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
1499+ "license": "MIT"
1500+ },
1501+ "node_modules/jsesc": {
1502+ "version": "3.1.0",
1503+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
1504+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
1505+ "dev": true,
1506+ "license": "MIT",
1507+ "bin": {
1508+ "jsesc": "bin/jsesc"
1509+ },
1510+ "engines": {
1511+ "node": ">=6"
1512+ }
1513+ },
1514+ "node_modules/json5": {
1515+ "version": "2.2.3",
1516+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
1517+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
1518+ "dev": true,
1519+ "license": "MIT",
1520+ "bin": {
1521+ "json5": "lib/cli.js"
1522+ },
1523+ "engines": {
1524+ "node": ">=6"
1525+ }
1526+ },
1527+ "node_modules/loose-envify": {
1528+ "version": "1.4.0",
1529+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
1530+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
1531+ "license": "MIT",
1532+ "dependencies": {
1533+ "js-tokens": "^3.0.0 || ^4.0.0"
1534+ },
1535+ "bin": {
1536+ "loose-envify": "cli.js"
1537+ }
1538+ },
1539+ "node_modules/lru-cache": {
1540+ "version": "5.1.1",
1541+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
1542+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
1543+ "dev": true,
1544+ "license": "ISC",
1545+ "dependencies": {
1546+ "yallist": "^3.0.2"
1547+ }
1548+ },
1549+ "node_modules/ms": {
1550+ "version": "2.1.3",
1551+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1552+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1553+ "dev": true,
1554+ "license": "MIT"
1555+ },
1556+ "node_modules/nanoid": {
1557+ "version": "3.3.15",
1558+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
1559+ "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
1560+ "dev": true,
1561+ "funding": [
1562+ {
1563+ "type": "github",
1564+ "url": "https://github.com/sponsors/ai"
1565+ }
1566+ ],
1567+ "license": "MIT",
1568+ "bin": {
1569+ "nanoid": "bin/nanoid.cjs"
1570+ },
1571+ "engines": {
1572+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1573+ }
1574+ },
1575+ "node_modules/node-releases": {
1576+ "version": "2.0.50",
1577+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz",
1578+ "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==",
1579+ "dev": true,
1580+ "license": "MIT",
1581+ "engines": {
1582+ "node": ">=18"
1583+ }
1584+ },
1585+ "node_modules/picocolors": {
1586+ "version": "1.1.1",
1587+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1588+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1589+ "dev": true,
1590+ "license": "ISC"
1591+ },
1592+ "node_modules/picomatch": {
1593+ "version": "4.0.5",
1594+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
1595+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
1596+ "dev": true,
1597+ "license": "MIT",
1598+ "engines": {
1599+ "node": ">=12"
1600+ },
1601+ "funding": {
1602+ "url": "https://github.com/sponsors/jonschlinkert"
1603+ }
1604+ },
1605+ "node_modules/postcss": {
1606+ "version": "8.5.16",
1607+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
1608+ "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
1609+ "dev": true,
1610+ "funding": [
1611+ {
1612+ "type": "opencollective",
1613+ "url": "https://opencollective.com/postcss/"
1614+ },
1615+ {
1616+ "type": "tidelift",
1617+ "url": "https://tidelift.com/funding/github/npm/postcss"
1618+ },
1619+ {
1620+ "type": "github",
1621+ "url": "https://github.com/sponsors/ai"
1622+ }
1623+ ],
1624+ "license": "MIT",
1625+ "dependencies": {
1626+ "nanoid": "^3.3.12",
1627+ "picocolors": "^1.1.1",
1628+ "source-map-js": "^1.2.1"
1629+ },
1630+ "engines": {
1631+ "node": "^10 || ^12 || >=14"
1632+ }
1633+ },
1634+ "node_modules/react": {
1635+ "version": "18.3.1",
1636+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
1637+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
1638+ "license": "MIT",
1639+ "dependencies": {
1640+ "loose-envify": "^1.1.0"
1641+ },
1642+ "engines": {
1643+ "node": ">=0.10.0"
1644+ }
1645+ },
1646+ "node_modules/react-dom": {
1647+ "version": "18.3.1",
1648+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
1649+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
1650+ "license": "MIT",
1651+ "dependencies": {
1652+ "loose-envify": "^1.1.0",
1653+ "scheduler": "^0.23.2"
1654+ },
1655+ "peerDependencies": {
1656+ "react": "^18.3.1"
1657+ }
1658+ },
1659+ "node_modules/react-refresh": {
1660+ "version": "0.17.0",
1661+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
1662+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
1663+ "dev": true,
1664+ "license": "MIT",
1665+ "engines": {
1666+ "node": ">=0.10.0"
1667+ }
1668+ },
1669+ "node_modules/rollup": {
1670+ "version": "4.62.2",
1671+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
1672+ "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",
1673+ "dev": true,
1674+ "license": "MIT",
1675+ "dependencies": {
1676+ "@types/estree": "1.0.9"
1677+ },
1678+ "bin": {
1679+ "rollup": "dist/bin/rollup"
1680+ },
1681+ "engines": {
1682+ "node": ">=18.0.0",
1683+ "npm": ">=8.0.0"
1684+ },
1685+ "optionalDependencies": {
1686+ "@rollup/rollup-android-arm-eabi": "4.62.2",
1687+ "@rollup/rollup-android-arm64": "4.62.2",
1688+ "@rollup/rollup-darwin-arm64": "4.62.2",
1689+ "@rollup/rollup-darwin-x64": "4.62.2",
1690+ "@rollup/rollup-freebsd-arm64": "4.62.2",
1691+ "@rollup/rollup-freebsd-x64": "4.62.2",
1692+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.2",
1693+ "@rollup/rollup-linux-arm-musleabihf": "4.62.2",
1694+ "@rollup/rollup-linux-arm64-gnu": "4.62.2",
1695+ "@rollup/rollup-linux-arm64-musl": "4.62.2",
1696+ "@rollup/rollup-linux-loong64-gnu": "4.62.2",
1697+ "@rollup/rollup-linux-loong64-musl": "4.62.2",
1698+ "@rollup/rollup-linux-ppc64-gnu": "4.62.2",
1699+ "@rollup/rollup-linux-ppc64-musl": "4.62.2",
1700+ "@rollup/rollup-linux-riscv64-gnu": "4.62.2",
1701+ "@rollup/rollup-linux-riscv64-musl": "4.62.2",
1702+ "@rollup/rollup-linux-s390x-gnu": "4.62.2",
1703+ "@rollup/rollup-linux-x64-gnu": "4.62.2",
1704+ "@rollup/rollup-linux-x64-musl": "4.62.2",
1705+ "@rollup/rollup-openbsd-x64": "4.62.2",
1706+ "@rollup/rollup-openharmony-arm64": "4.62.2",
1707+ "@rollup/rollup-win32-arm64-msvc": "4.62.2",
1708+ "@rollup/rollup-win32-ia32-msvc": "4.62.2",
1709+ "@rollup/rollup-win32-x64-gnu": "4.62.2",
1710+ "@rollup/rollup-win32-x64-msvc": "4.62.2",
1711+ "fsevents": "~2.3.2"
1712+ }
1713+ },
1714+ "node_modules/scheduler": {
1715+ "version": "0.23.2",
1716+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
1717+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
1718+ "license": "MIT",
1719+ "dependencies": {
1720+ "loose-envify": "^1.1.0"
1721+ }
1722+ },
1723+ "node_modules/semver": {
1724+ "version": "6.3.1",
1725+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
1726+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
1727+ "dev": true,
1728+ "license": "ISC",
1729+ "bin": {
1730+ "semver": "bin/semver.js"
1731+ }
1732+ },
1733+ "node_modules/source-map-js": {
1734+ "version": "1.2.1",
1735+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1736+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1737+ "dev": true,
1738+ "license": "BSD-3-Clause",
1739+ "engines": {
1740+ "node": ">=0.10.0"
1741+ }
1742+ },
1743+ "node_modules/tinyglobby": {
1744+ "version": "0.2.17",
1745+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
1746+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
1747+ "dev": true,
1748+ "license": "MIT",
1749+ "dependencies": {
1750+ "fdir": "^6.5.0",
1751+ "picomatch": "^4.0.4"
1752+ },
1753+ "engines": {
1754+ "node": ">=12.0.0"
1755+ },
1756+ "funding": {
1757+ "url": "https://github.com/sponsors/SuperchupuDev"
1758+ }
1759+ },
1760+ "node_modules/typescript": {
1761+ "version": "5.9.3",
1762+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
1763+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
1764+ "dev": true,
1765+ "license": "Apache-2.0",
1766+ "bin": {
1767+ "tsc": "bin/tsc",
1768+ "tsserver": "bin/tsserver"
1769+ },
1770+ "engines": {
1771+ "node": ">=14.17"
1772+ }
1773+ },
1774+ "node_modules/update-browserslist-db": {
1775+ "version": "1.2.3",
1776+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
1777+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
1778+ "dev": true,
1779+ "funding": [
1780+ {
1781+ "type": "opencollective",
1782+ "url": "https://opencollective.com/browserslist"
1783+ },
1784+ {
1785+ "type": "tidelift",
1786+ "url": "https://tidelift.com/funding/github/npm/browserslist"
1787+ },
1788+ {
1789+ "type": "github",
1790+ "url": "https://github.com/sponsors/ai"
1791+ }
1792+ ],
1793+ "license": "MIT",
1794+ "dependencies": {
1795+ "escalade": "^3.2.0",
1796+ "picocolors": "^1.1.1"
1797+ },
1798+ "bin": {
1799+ "update-browserslist-db": "cli.js"
1800+ },
1801+ "peerDependencies": {
1802+ "browserslist": ">= 4.21.0"
1803+ }
1804+ },
1805+ "node_modules/vite": {
1806+ "version": "6.4.3",
1807+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
1808+ "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
1809+ "dev": true,
1810+ "license": "MIT",
1811+ "dependencies": {
1812+ "esbuild": "^0.25.0",
1813+ "fdir": "^6.4.4",
1814+ "picomatch": "^4.0.2",
1815+ "postcss": "^8.5.3",
1816+ "rollup": "^4.34.9",
1817+ "tinyglobby": "^0.2.13"
1818+ },
1819+ "bin": {
1820+ "vite": "bin/vite.js"
1821+ },
1822+ "engines": {
1823+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
1824+ },
1825+ "funding": {
1826+ "url": "https://github.com/vitejs/vite?sponsor=1"
1827+ },
1828+ "optionalDependencies": {
1829+ "fsevents": "~2.3.3"
1830+ },
1831+ "peerDependencies": {
1832+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
1833+ "jiti": ">=1.21.0",
1834+ "less": "*",
1835+ "lightningcss": "^1.21.0",
1836+ "sass": "*",
1837+ "sass-embedded": "*",
1838+ "stylus": "*",
1839+ "sugarss": "*",
1840+ "terser": "^5.16.0",
1841+ "tsx": "^4.8.1",
1842+ "yaml": "^2.4.2"
1843+ },
1844+ "peerDependenciesMeta": {
1845+ "@types/node": {
1846+ "optional": true
1847+ },
1848+ "jiti": {
1849+ "optional": true
1850+ },
1851+ "less": {
1852+ "optional": true
1853+ },
1854+ "lightningcss": {
1855+ "optional": true
1856+ },
1857+ "sass": {
1858+ "optional": true
1859+ },
1860+ "sass-embedded": {
1861+ "optional": true
1862+ },
1863+ "stylus": {
1864+ "optional": true
1865+ },
1866+ "sugarss": {
1867+ "optional": true
1868+ },
1869+ "terser": {
1870+ "optional": true
1871+ },
1872+ "tsx": {
1873+ "optional": true
1874+ },
1875+ "yaml": {
1876+ "optional": true
1877+ }
1878+ }
1879+ },
1880+ "node_modules/yallist": {
1881+ "version": "3.1.1",
1882+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
1883+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
1884+ "dev": true,
1885+ "license": "ISC"
1886+ }
1887+ }
1888+}
package.jsonadded+22−0View file
@@ -0,0 +1,22 @@
1+{
2+ "name": "stan-remote-sampling",
3+ "private": true,
4+ "version": "0.1.0",
5+ "type": "module",
6+ "scripts": {
7+ "dev": "vite",
8+ "build": "tsc -b && vite build",
9+ "preview": "vite preview"
10+ },
11+ "dependencies": {
12+ "react": "^18.3.1",
13+ "react-dom": "^18.3.1"
14+ },
15+ "devDependencies": {
16+ "@types/react": "^18.3.12",
17+ "@types/react-dom": "^18.3.1",
18+ "@vitejs/plugin-react": "^4.3.4",
19+ "typescript": "^5.6.3",
20+ "vite": "^6.0.0"
21+ }
22+}
src/App.tsxadded+339−0View file
@@ -0,0 +1,339 @@
1+import { useCallback, useRef, useState } from "react";
2+import compileStanProgram, {
3+ DEFAULT_WASI_SERVER_URL,
4+} from "./compileStanProgram";
5+import runRemoteChains from "./runRemoteChains";
6+import {
7+ ChainStatusMap,
8+ defaultSamplingOpts,
9+ MultiChainResult,
10+ SamplingOpts,
11+} from "./types";
12+
13+const DEFAULT_STAN_PROGRAM = `data {
14+ int<lower=0> N;
15+ array[N] int<lower=0, upper=1> y;
16+}
17+parameters {
18+ real<lower=0, upper=1> theta;
19+}
20+model {
21+ theta ~ beta(1, 1);
22+ y ~ bernoulli(theta);
23+}
24+`;
25+
26+const DEFAULT_DATA_JSON = `{
27+ "N": 10,
28+ "y": [0, 1, 0, 0, 0, 0, 0, 0, 0, 1]
29+}
30+`;
31+
32+const mean = (xs: number[]) => xs.reduce((a, b) => a + b, 0) / xs.length;
33+const std = (xs: number[]) => {
34+ const m = mean(xs);
35+ return Math.sqrt(mean(xs.map((x) => (x - m) ** 2)));
36+};
37+
38+function App() {
39+ const [stanProgram, setStanProgram] = useState(DEFAULT_STAN_PROGRAM);
40+ const [dataJson, setDataJson] = useState(DEFAULT_DATA_JSON);
41+
42+ const [wasiServerUrl, setWasiServerUrl] = useState(
43+ () => localStorage.getItem("wasiServerUrl") ?? DEFAULT_WASI_SERVER_URL,
44+ );
45+ const [wasmExecUrl, setWasmExecUrl] = useState(
46+ () => localStorage.getItem("wasmExecUrl") ?? "https://wasm-exec.fly.dev",
47+ );
48+ const [wasmExecKey, setWasmExecKey] = useState(
49+ () => localStorage.getItem("wasmExecKey") ?? "",
50+ );
51+
52+ const [compileStatus, setCompileStatus] = useState("");
53+ const [wasiWasmUrl, setWasiWasmUrl] = useState<string | undefined>(undefined);
54+ const [compiling, setCompiling] = useState(false);
55+
56+ const [samplingOpts, setSamplingOpts] =
57+ useState<SamplingOpts>(defaultSamplingOpts);
58+ const [sampling, setSampling] = useState(false);
59+ const [chainStatus, setChainStatus] = useState<ChainStatusMap>({});
60+ const [samplingError, setSamplingError] = useState("");
61+ const [result, setResult] = useState<MultiChainResult | undefined>(undefined);
62+ const cancelRef = useRef<(() => void) | undefined>(undefined);
63+
64+ const handleCompile = useCallback(async () => {
65+ setCompiling(true);
66+ setResult(undefined);
67+ setWasiWasmUrl(undefined);
68+ const { artifactUrl } = await compileStanProgram(
69+ wasiServerUrl,
70+ stanProgram,
71+ setCompileStatus,
72+ );
73+ setWasiWasmUrl(artifactUrl);
74+ setCompiling(false);
75+ }, [wasiServerUrl, stanProgram]);
76+
77+ const handleSample = useCallback(async () => {
78+ if (!wasiWasmUrl) return;
79+ setSampling(true);
80+ setSamplingError("");
81+ setResult(undefined);
82+ setChainStatus({});
83+
84+ try {
85+ // fetch the compiled WASI module from the compile server (browser
86+ // HTTP cache makes repeat samples cheap)
87+ const resp = await fetch(wasiWasmUrl);
88+ if (!resp.ok)
89+ throw new Error(`failed to fetch ${wasiWasmUrl}: ${resp.statusText}`);
90+ const moduleBytes = new Uint8Array(await resp.arrayBuffer());
91+
92+ const run = runRemoteChains(
93+ wasmExecUrl,
94+ wasmExecKey,
95+ moduleBytes,
96+ dataJson,
97+ samplingOpts,
98+ (chainId, status) =>
99+ setChainStatus((prev) => ({ ...prev, [chainId]: status })),
100+ );
101+ cancelRef.current = run.cancel;
102+ setResult(await run.result);
103+ } catch (e) {
104+ setSamplingError(`${e}`);
105+ } finally {
106+ cancelRef.current = undefined;
107+ setSampling(false);
108+ }
109+ }, [wasiWasmUrl, wasmExecUrl, wasmExecKey, dataJson, samplingOpts]);
110+
111+ const handleCancel = useCallback(() => {
112+ cancelRef.current?.();
113+ cancelRef.current = undefined;
114+ setSampling(false);
115+ setSamplingError("canceled");
116+ }, []);
117+
118+ const setOpt = (key: keyof SamplingOpts, value: number) =>
119+ setSamplingOpts((prev) => ({ ...prev, [key]: value }));
120+
121+ return (
122+ <div style={{ fontFamily: "sans-serif", margin: 20, maxWidth: 1100 }}>
123+ <h2>Stan Remote Sampling</h2>
124+ <p style={{ color: "#555", fontSize: 14 }}>
125+ The Stan program is compiled to a pure-WASI module by{" "}
126+ <code>stan-wasm-wasi</code>; sampling runs one chain per parallel job
127+ on <code>wasm-exec</code> workers.
128+ </p>
129+
130+ <div style={{ display: "flex", gap: 20 }}>
131+ <div style={{ flex: 2 }}>
132+ <h3>Stan program</h3>
133+ <textarea
134+ value={stanProgram}
135+ onChange={(e) => setStanProgram(e.target.value)}
136+ rows={16}
137+ style={{ width: "100%", fontFamily: "monospace" }}
138+ spellCheck={false}
139+ />
140+ </div>
141+ <div style={{ flex: 1 }}>
142+ <h3>data.json</h3>
143+ <textarea
144+ value={dataJson}
145+ onChange={(e) => setDataJson(e.target.value)}
146+ rows={16}
147+ style={{ width: "100%", fontFamily: "monospace" }}
148+ spellCheck={false}
149+ />
150+ </div>
151+ </div>
152+
153+ <h3>Compile</h3>
154+ <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
155+ <label>
156+ Compile server:{" "}
157+ <input
158+ value={wasiServerUrl}
159+ onChange={(e) => {
160+ setWasiServerUrl(e.target.value);
161+ localStorage.setItem("wasiServerUrl", e.target.value);
162+ }}
163+ style={{ width: 280 }}
164+ />
165+ </label>
166+ <button onClick={handleCompile} disabled={compiling || sampling}>
167+ Compile
168+ </button>
169+ <span>{compileStatus}</span>
170+ </div>
171+
172+ <h3>Sample</h3>
173+ <div
174+ style={{
175+ display: "flex",
176+ gap: 15,
177+ alignItems: "center",
178+ flexWrap: "wrap",
179+ fontSize: 14,
180+ marginBottom: 10,
181+ }}
182+ >
183+ <label>
184+ wasm-exec server:{" "}
185+ <input
186+ value={wasmExecUrl}
187+ onChange={(e) => {
188+ setWasmExecUrl(e.target.value);
189+ localStorage.setItem("wasmExecUrl", e.target.value);
190+ }}
191+ style={{ width: 200 }}
192+ />
193+ </label>
194+ <label>
195+ Client key (ck_...):{" "}
196+ <input
197+ value={wasmExecKey}
198+ onChange={(e) => {
199+ setWasmExecKey(e.target.value);
200+ localStorage.setItem("wasmExecKey", e.target.value);
201+ }}
202+ style={{ width: 220 }}
203+ />
204+ </label>
205+ </div>
206+
207+ <div style={{ display: "flex", gap: 15, alignItems: "center", flexWrap: "wrap" }}>
208+ <label>
209+ Chains (parallel jobs):{" "}
210+ <input
211+ type="number"
212+ min={1}
213+ max={16}
214+ value={samplingOpts.num_chains}
215+ onChange={(e) => setOpt("num_chains", parseInt(e.target.value) || 1)}
216+ style={{ width: 60 }}
217+ />
218+ </label>
219+ <label>
220+ Warmup:{" "}
221+ <input
222+ type="number"
223+ min={0}
224+ value={samplingOpts.num_warmup}
225+ onChange={(e) => setOpt("num_warmup", parseInt(e.target.value) || 0)}
226+ style={{ width: 80 }}
227+ />
228+ </label>
229+ <label>
230+ Samples:{" "}
231+ <input
232+ type="number"
233+ min={1}
234+ value={samplingOpts.num_samples}
235+ onChange={(e) => setOpt("num_samples", parseInt(e.target.value) || 1)}
236+ style={{ width: 80 }}
237+ />
238+ </label>
239+ <button
240+ onClick={handleSample}
241+ disabled={sampling || compiling || !wasiWasmUrl || !wasmExecKey}
242+ >
243+ Sample
244+ </button>
245+ {sampling && <button onClick={handleCancel}>Cancel</button>}
246+ </div>
247+
248+ {Object.keys(chainStatus).length > 0 && (
249+ <div style={{ marginTop: 15 }}>
250+ {Array.from({ length: samplingOpts.num_chains }, (_, i) => i + 1).map(
251+ (chainId) => {
252+ const s = chainStatus[chainId];
253+ let text = "submitted / queued...";
254+ let pct = 0;
255+ if (s === "done") {
256+ text = "done";
257+ pct = 100;
258+ } else if (s === "error") {
259+ text = "error";
260+ } else if (s) {
261+ pct = Math.round((s.iteration / s.totalIterations) * 100);
262+ text = `${s.iteration} / ${s.totalIterations} ${s.warmup ? "(warmup)" : "(sampling)"}`;
263+ }
264+ return (
265+ <div
266+ key={chainId}
267+ style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 4 }}
268+ >
269+ <span style={{ width: 70 }}>Chain {chainId}</span>
270+ <div style={{ width: 300, height: 14, background: "#eee", border: "1px solid #ccc" }}>
271+ <div
272+ style={{
273+ width: `${pct}%`,
274+ height: "100%",
275+ background: s === "error" ? "#c33" : "#4a90d9",
276+ }}
277+ />
278+ </div>
279+ <span style={{ fontSize: 13, color: "#555" }}>{text}</span>
280+ </div>
281+ );
282+ },
283+ )}
284+ </div>
285+ )}
286+
287+ {samplingError && (
288+ <pre style={{ color: "#c33", whiteSpace: "pre-wrap" }}>{samplingError}</pre>
289+ )}
290+
291+ {result && (
292+ <div style={{ marginTop: 15 }}>
293+ <h3>Results</h3>
294+ <p style={{ fontSize: 14 }}>
295+ {result.numChains} chains, {result.draws[0]?.length ?? 0} total draws,{" "}
296+ {result.computeTimeSec.toFixed(2)} sec
297+ </p>
298+ <table style={{ borderCollapse: "collapse" }}>
299+ <thead>
300+ <tr>
301+ {["parameter", "mean", "std"].map((h) => (
302+ <th
303+ key={h}
304+ style={{ border: "1px solid #ccc", padding: "4px 12px", textAlign: "left" }}
305+ >
306+ {h}
307+ </th>
308+ ))}
309+ </tr>
310+ </thead>
311+ <tbody>
312+ {result.paramNames.map((name, p) => (
313+ <tr key={name}>
314+ <td style={{ border: "1px solid #ccc", padding: "4px 12px" }}>{name}</td>
315+ <td style={{ border: "1px solid #ccc", padding: "4px 12px" }}>
316+ {mean(result.draws[p]).toFixed(4)}
317+ </td>
318+ <td style={{ border: "1px solid #ccc", padding: "4px 12px" }}>
319+ {std(result.draws[p]).toFixed(4)}
320+ </td>
321+ </tr>
322+ ))}
323+ </tbody>
324+ </table>
325+ {result.consoleText.trim() && (
326+ <details style={{ marginTop: 10 }}>
327+ <summary>Console output</summary>
328+ <pre style={{ fontSize: 12, background: "#f6f6f6", padding: 10 }}>
329+ {result.consoleText}
330+ </pre>
331+ </details>
332+ )}
333+ </div>
334+ )}
335+ </div>
336+ );
337+}
338+
339+export default App;
src/compileStanProgram.tsadded+59−0View file
@@ -0,0 +1,59 @@
1+// Same compilation mechanism as stan-playground: POST the program to the
2+// compilation server, then download the compiled artifact. The server
3+// (stan-wasm-wasi) produces a pure-WASI command module at
4+// /download/{model_id}/main.wasm, runnable on wasm-exec workers.
5+
6+export const DEFAULT_WASI_SERVER_URL = "https://stan-wasm-wasi.fly.dev";
7+
8+const compileStanProgram = async (
9+ serverUrl: string,
10+ stanProgram: string,
11+ onStatus: (s: string) => void,
12+): Promise<{ artifactUrl?: string }> => {
13+ try {
14+ onStatus("compiling...");
15+
16+ const compileURL = `${serverUrl}/compile`;
17+ const runCompile = await fetch(compileURL, {
18+ method: "POST",
19+ headers: {
20+ "Content-Type": "text/plain",
21+ Authorization: "Bearer 1234",
22+ },
23+ body: stanProgram,
24+ });
25+ if (!runCompile.ok) {
26+ onStatus(`failed to compile: ${await messageOrStatus(runCompile)}`);
27+ return {};
28+ }
29+ const compileResp = await runCompile.json();
30+ const artifactUrl = `${serverUrl}/download/${compileResp.model_id}/main.wasm`;
31+
32+ // download to make sure it is there
33+ onStatus("checking download of main.wasm");
34+ const downloadCheck = await fetch(artifactUrl, { method: "HEAD" });
35+ if (!downloadCheck.ok) {
36+ onStatus(
37+ `failed to download main.wasm: ${await messageOrStatus(downloadCheck)}`,
38+ );
39+ return {};
40+ }
41+
42+ onStatus("compiled");
43+ return { artifactUrl };
44+ } catch (e) {
45+ onStatus(`failed to compile: ${e}`);
46+ return {};
47+ }
48+};
49+
50+const messageOrStatus = async (response: Response) => {
51+ try {
52+ const j = await response.json();
53+ return j?.message ?? response.statusText;
54+ } catch {
55+ return response.statusText;
56+ }
57+};
58+
59+export default compileStanProgram;
src/main.tsxadded+9−0View file
@@ -0,0 +1,9 @@
1+import { StrictMode } from "react";
2+import { createRoot } from "react-dom/client";
3+import App from "./App";
4+
5+createRoot(document.getElementById("root")!).render(
6+ <StrictMode>
7+ <App />
8+ </StrictMode>,
9+);
src/mergeChains.tsadded+20−0View file
@@ -0,0 +1,20 @@
1+import type { ChainResult, MultiChainResult } from "./types";
2+
3+export const mergeChainResults = (
4+ chainResults: ChainResult[],
5+ computeTimeSec: number,
6+): MultiChainResult => {
7+ const sorted = [...chainResults].sort((a, b) => a.chainId - b.chainId);
8+ const paramNames = sorted[0].paramNames;
9+ // concatenate draws across chains, per parameter
10+ const draws = paramNames.map((_, p) => sorted.flatMap((c) => c.draws[p]));
11+ return {
12+ paramNames,
13+ draws,
14+ numChains: sorted.length,
15+ computeTimeSec,
16+ consoleText: sorted
17+ .map((c) => `--- chain ${c.chainId} ---\n${c.consoleText}`)
18+ .join("\n"),
19+ };
20+};
src/parseProgress.tsadded+14−0View file
@@ -0,0 +1,14 @@
1+import type { ChainProgress } from "./types";
2+
3+// Stan progress lines for a single chain look like:
4+// "Iteration: 800 / 2000 [ 40%] (Warmup)"
5+export const parseProgress = (msg: string): ChainProgress | undefined => {
6+ if (!msg.startsWith("Iteration:")) return undefined;
7+ const cleaned = msg.replace(/\[|\]/g, "");
8+ const parts = cleaned.split(/\s+/);
9+ const iteration = parseInt(parts[1]);
10+ const totalIterations = parseInt(parts[3]);
11+ const warmup = parts[5] === "(Warmup)";
12+ if (isNaN(iteration) || isNaN(totalIterations)) return undefined;
13+ return { iteration, totalIterations, warmup };
14+};
src/runRemoteChains.tsadded+168−0View file
@@ -0,0 +1,168 @@
1+import { mergeChainResults } from "./mergeChains";
2+import { parseProgress } from "./parseProgress";
3+import type {
4+ ChainResult,
5+ ChainStatus,
6+ MultiChainResult,
7+ SamplingOpts,
8+} from "./types";
9+
10+// Runs chains remotely on a wasm-exec service: one POST /v1/execute job per
11+// chain, module = the model compiled as a pure-WASI command module (see
12+// wasi-prototype/). The driver's argv: data_json seed chain_id num_warmup
13+// num_samples init_radius refresh. Progress arrives as SSE stderr events,
14+// draws as CSV on SSE stdout events.
15+
16+type SseEvent = { event: string; data: any };
17+
18+async function* sseEvents(body: ReadableStream<Uint8Array>): AsyncGenerator<SseEvent> {
19+ const reader = body.getReader();
20+ const decoder = new TextDecoder();
21+ let buf = "";
22+ while (true) {
23+ const { done, value } = await reader.read();
24+ if (done) break;
25+ buf += decoder.decode(value, { stream: true });
26+ let sep;
27+ while ((sep = buf.indexOf("\n\n")) !== -1) {
28+ const block = buf.slice(0, sep);
29+ buf = buf.slice(sep + 2);
30+ let event = "message";
31+ let data = "";
32+ for (const line of block.split("\n")) {
33+ if (line.startsWith("event:")) event = line.slice(6).trim();
34+ else if (line.startsWith("data:")) data += line.slice(5).trim();
35+ }
36+ if (data) yield { event, data: JSON.parse(data) };
37+ }
38+ }
39+}
40+
41+const runOneRemoteChain = async (
42+ serverUrl: string,
43+ clientKey: string,
44+ moduleBytes: Uint8Array,
45+ args: string[],
46+ chainId: number,
47+ signal: AbortSignal,
48+ onStatus: (chainId: number, status: ChainStatus) => void,
49+): Promise<ChainResult> => {
50+ const form = new FormData();
51+ // POC: send the module bytes with every job. The server caches by sha256,
52+ // so this could be optimized to module_sha256 with a 404 fallback.
53+ form.append("module", new Blob([moduleBytes as BlobPart]), "main-wasi.wasm");
54+ form.append("args", JSON.stringify(args));
55+ form.append("timeout_ms", "300000");
56+
57+ const resp = await fetch(`${serverUrl}/v1/execute`, {
58+ method: "POST",
59+ headers: { Authorization: `Bearer ${clientKey}` },
60+ body: form,
61+ signal,
62+ });
63+ if (!resp.ok || !resp.body) {
64+ let message = resp.statusText;
65+ try {
66+ message = (await resp.json()).message ?? message;
67+ } catch {
68+ // keep statusText
69+ }
70+ throw new Error(`chain ${chainId}: submit failed: ${message}`);
71+ }
72+
73+ let stdoutText = "";
74+ let stderrText = "";
75+ let stderrLineBuf = "";
76+ let exitCode: number | undefined;
77+
78+ for await (const { event, data } of sseEvents(resp.body)) {
79+ if (event === "stdout") {
80+ stdoutText += atob(data.b64);
81+ } else if (event === "stderr") {
82+ const text = atob(data.b64);
83+ stderrText += text;
84+ stderrLineBuf += text;
85+ let nl;
86+ while ((nl = stderrLineBuf.indexOf("\n")) !== -1) {
87+ const line = stderrLineBuf.slice(0, nl).trim();
88+ stderrLineBuf = stderrLineBuf.slice(nl + 1);
89+ const progress = parseProgress(line);
90+ if (progress) onStatus(chainId, progress);
91+ }
92+ } else if (event === "exit") {
93+ exitCode = data.exitCode;
94+ } else if (event === "error") {
95+ throw new Error(`chain ${chainId}: ${data.code}: ${data.message}`);
96+ }
97+ }
98+
99+ if (exitCode === undefined)
100+ throw new Error(`chain ${chainId}: stream ended without exit event`);
101+ if (exitCode !== 0)
102+ throw new Error(
103+ `chain ${chainId}: module exited with code ${exitCode}\n${stderrText.slice(-1000)}`,
104+ );
105+
106+ // stdout: header line of param names, then one CSV row per draw
107+ const lines = stdoutText.trim().split("\n");
108+ const paramNames = lines[0].split(",");
109+ const rows = lines.slice(1).map((l) => l.split(",").map(Number));
110+ const draws = paramNames.map((_, p) => rows.map((r) => r[p]));
111+ return { chainId, paramNames, draws, consoleText: stderrText };
112+};
113+
114+const runRemoteChains = (
115+ serverUrl: string,
116+ clientKey: string,
117+ moduleBytes: Uint8Array,
118+ data: string,
119+ opts: SamplingOpts,
120+ onChainStatus: (chainId: number, status: ChainStatus) => void,
121+): { result: Promise<MultiChainResult>; cancel: () => void } => {
122+ const seed = opts.seed ?? Math.floor(Math.random() * Math.pow(2, 32));
123+ const refresh = Math.max(
124+ 10,
125+ Math.floor((opts.num_warmup + opts.num_samples) / 100) * 10,
126+ );
127+ const startTimeSec = performance.now() / 1000;
128+ const abort = new AbortController();
129+
130+ const chainPromises = Array.from({ length: opts.num_chains }, (_, i) => {
131+ const chainId = i + 1;
132+ const args = [
133+ data,
134+ String(seed),
135+ String(chainId),
136+ String(opts.num_warmup),
137+ String(opts.num_samples),
138+ String(opts.init_radius),
139+ String(refresh),
140+ ];
141+ return runOneRemoteChain(
142+ serverUrl.replace(/\/$/, ""),
143+ clientKey,
144+ moduleBytes,
145+ args,
146+ chainId,
147+ abort.signal,
148+ onChainStatus,
149+ ).then(
150+ (r) => {
151+ onChainStatus(chainId, "done");
152+ return r;
153+ },
154+ (e) => {
155+ onChainStatus(chainId, "error");
156+ throw e;
157+ },
158+ );
159+ });
160+
161+ const result = Promise.all(chainPromises).then((chainResults) =>
162+ mergeChainResults(chainResults, performance.now() / 1000 - startTimeSec),
163+ );
164+
165+ return { result, cancel: () => abort.abort() };
166+};
167+
168+export default runRemoteChains;
src/types.tsadded+43−0View file
@@ -0,0 +1,43 @@
1+export type SamplingOpts = {
2+ num_chains: number;
3+ num_warmup: number;
4+ num_samples: number;
5+ init_radius: number;
6+ seed: number | undefined;
7+};
8+
9+export const defaultSamplingOpts: SamplingOpts = {
10+ num_chains: 4,
11+ num_warmup: 1000,
12+ num_samples: 1000,
13+ init_radius: 2.0,
14+ seed: undefined,
15+};
16+
17+export type ChainProgress = {
18+ iteration: number;
19+ totalIterations: number;
20+ warmup: boolean;
21+};
22+
23+// Result of one chain (one wasm-exec job)
24+export type ChainResult = {
25+ chainId: number;
26+ paramNames: string[];
27+ draws: number[][]; // draws[paramIndex][drawIndex]
28+ consoleText: string;
29+};
30+
31+export type MultiChainResult = {
32+ paramNames: string[];
33+ draws: number[][]; // draws[paramIndex][drawIndex], chains concatenated
34+ numChains: number;
35+ computeTimeSec: number;
36+ consoleText: string;
37+};
38+
39+export type ChainStatus = ChainProgress | "done" | "error";
40+
41+export type ChainStatusMap = {
42+ [chainId: number]: ChainStatus;
43+};
tsconfig.jsonadded+20−0View file
@@ -0,0 +1,20 @@
1+{
2+ "compilerOptions": {
3+ "target": "ES2020",
4+ "useDefineForClassFields": true,
5+ "lib": ["ES2020", "DOM", "DOM.Iterable", "WebWorker"],
6+ "module": "ESNext",
7+ "skipLibCheck": true,
8+ "moduleResolution": "bundler",
9+ "allowImportingTsExtensions": true,
10+ "isolatedModules": true,
11+ "moduleDetection": "force",
12+ "noEmit": true,
13+ "jsx": "react-jsx",
14+ "strict": true,
15+ "noUnusedLocals": true,
16+ "noUnusedParameters": true,
17+ "noFallthroughCasesInSwitch": true
18+ },
19+ "include": ["src"]
20+}
vite.config.tsadded+11−0View file
@@ -0,0 +1,11 @@
1+import react from "@vitejs/plugin-react";
2+import { defineConfig } from "vite";
3+
4+export default defineConfig(({ command }) => ({
5+ plugins: [react()],
6+ // served at https://concept-collection.github.io/stan-remote-sampling/
7+ base: command === "build" ? "/stan-remote-sampling/" : "/",
8+ server: {
9+ port: 3000,
10+ },
11+}));