An encrypted personal cloud built on the AT Protocol.

Add TanStack Start, sharing UI, settings, and deployment infra

Migrate from Vite SPA to TanStack Start for SSR on public routes with
per-route SEO meta tags. Add sharing UI with grant create/list/revoke,
settings page with AppView URL config, user menu with avatar and portal
dropdown, and Containerfiles for appview and web. Fix auth guards across
all device routes to read from Zustand store instead of dead router context.

+1568 -223
+1
.claude/agents/product-owner.md
··· 3 3 description: Manages the crosslink issue tracker — triaging, organizing, deduplicating, and maintaining the backlog. Delegate all milestone, epic, and dependency management to this agent. 4 4 tools: Read, Glob, Grep, Bash 5 5 model: haiku 6 + permissionMode: bypassPermissions 6 7 --- 7 8 8 9 # Product Owner
+7
.containerignore
··· 1 + target/ 2 + web/node_modules/ 3 + web/dist/ 4 + web/.output/ 5 + .git/ 6 + .claude/ 7 + *.secret
+3
CHANGELOG.md
··· 12 12 - Remove bearer token authentication fallback from AppView [#26](https://issues.opake.app/issues/26.html)s 13 13 14 14 ### Added 15 + - Add web sharing UI with grant management [#149](https://issues.opake.app/issues/149.html) 16 + - Add settings page with AppView URL configuration [#277](https://issues.opake.app/issues/277.html) 17 + - Add device settings link in sidebar with account info [#276](https://issues.opake.app/issues/276.html) 15 18 - Add web file and directory moving [#253](https://issues.opake.app/issues/253.html) 16 19 - Add web metadata management for documents [#252](https://issues.opake.app/issues/252.html) 17 20 - Add error toast notifications for user feedback [#266](https://issues.opake.app/issues/266.html)
+22
Containerfile.appview
··· 1 + FROM rust:1.86-slim-bookworm AS builder 2 + 3 + RUN apt-get update && apt-get install -y pkg-config libssl-dev && rm -rf /var/lib/apt/lists/* 4 + 5 + WORKDIR /build 6 + COPY Cargo.toml Cargo.lock ./ 7 + COPY crates/ crates/ 8 + 9 + RUN cargo build --release --package opake-appview 10 + 11 + FROM debian:bookworm-slim 12 + 13 + RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* 14 + RUN groupadd -g 1000 opake && useradd -u 1000 -g opake -m opake 15 + 16 + COPY --from=builder /build/target/release/opake-appview /usr/local/bin/opake-appview 17 + 18 + USER 1000:1000 19 + EXPOSE 6100 20 + 21 + ENTRYPOINT ["opake-appview"] 22 + CMD ["run"]
+35
Containerfile.web
··· 1 + FROM rust:1.86-slim-bookworm AS wasm-builder 2 + 3 + RUN cargo install wasm-pack 4 + 5 + WORKDIR /build 6 + COPY Cargo.toml Cargo.lock ./ 7 + COPY crates/ crates/ 8 + 9 + RUN wasm-pack build crates/opake-wasm --target web --out-dir ../../web/src/wasm/opake-wasm --out-name opake 10 + 11 + FROM oven/bun:1 AS web-builder 12 + 13 + WORKDIR /build/web 14 + COPY web/package.json web/bun.lock ./ 15 + RUN bun install --frozen-lockfile 16 + 17 + COPY web/ ./ 18 + COPY --from=wasm-builder /build/web/src/wasm/opake-wasm/ ./src/wasm/opake-wasm/ 19 + 20 + RUN bun run build 21 + 22 + FROM node:22-slim 23 + 24 + RUN groupadd -g 1000 opake || true && useradd -u 1000 -g 1000 -m opake || true 25 + 26 + WORKDIR /app 27 + COPY --from=web-builder --chown=1000:1000 /build/web/dist/ ./dist/ 28 + 29 + USER 1000:1000 30 + EXPOSE 3000 31 + 32 + ENV NODE_ENV=production 33 + ENV PORT=3000 34 + 35 + CMD ["node", "dist/server/server.js"]
+351 -24
web/bun.lock
··· 6 6 "name": "opake-web", 7 7 "dependencies": { 8 8 "@phosphor-icons/react": "^2.1.10", 9 - "@tanstack/react-router": "^1.163.3", 9 + "@tanstack/react-start": "^1.166.3", 10 10 "clsx": "^2.1.1", 11 11 "comlink": "^4.4.2", 12 12 "dexie": "^4.3.0", ··· 17 17 "zustand": "^5.0.11", 18 18 }, 19 19 "devDependencies": { 20 + "@mdx-js/rollup": "^3.1.1", 20 21 "@tailwindcss/vite": "^4.2.1", 21 - "@tanstack/router-plugin": "^1.164.0", 22 22 "@testing-library/jest-dom": "^6.9.1", 23 23 "@testing-library/react": "^16.3.2", 24 + "@types/node": "^25.3.5", 24 25 "@types/react": "^19.2.14", 25 26 "@types/react-dom": "^19.2.3", 26 - "@vitejs/plugin-react": "^5.1.4", 27 27 "daisyui": "^5.5.19", 28 28 "eslint": "^10.0.3", 29 29 "eslint-config-prettier": "^10.1.8", ··· 38 38 "jiti": "^2.6.1", 39 39 "prettier": "^3.8.1", 40 40 "prettier-plugin-tailwindcss": "^0.7.2", 41 + "remark-gfm": "^4.0.1", 41 42 "tailwindcss": "^4.2.1", 42 43 "typescript": "^5.9.3", 43 44 "typescript-eslint": "^8.56.1", ··· 82 83 "@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="], 83 84 84 85 "@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A=="], 85 - 86 - "@babel/plugin-transform-react-jsx-self": ["@babel/plugin-transform-react-jsx-self@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw=="], 87 - 88 - "@babel/plugin-transform-react-jsx-source": ["@babel/plugin-transform-react-jsx-source@7.27.1", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], 89 86 90 87 "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], 91 88 ··· 179 176 180 177 "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], 181 178 179 + "@mdx-js/mdx": ["@mdx-js/mdx@3.1.1", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdx": "^2.0.0", "acorn": "^8.0.0", "collapse-white-space": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-util-scope": "^1.0.0", "estree-walker": "^3.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "markdown-extensions": "^2.0.0", "recma-build-jsx": "^1.0.0", "recma-jsx": "^1.0.0", "recma-stringify": "^1.0.0", "rehype-recma": "^1.0.0", "remark-mdx": "^3.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "source-map": "^0.7.0", "unified": "^11.0.0", "unist-util-position-from-estree": "^2.0.0", "unist-util-stringify-position": "^4.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ=="], 180 + 181 + "@mdx-js/rollup": ["@mdx-js/rollup@3.1.1", "", { "dependencies": { "@mdx-js/mdx": "^3.0.0", "@rollup/pluginutils": "^5.0.0", "source-map": "^0.7.0", "vfile": "^6.0.0" }, "peerDependencies": { "rollup": ">=2" } }, "sha512-v8satFmBB+DqDzYohnm1u2JOvxx6Hl3pUvqzJvfs2Zk/ngZ1aRUhsWpXvwPkNeGN9c2NCm/38H29ZqXQUjf8dw=="], 182 + 183 + "@oozcitak/dom": ["@oozcitak/dom@2.0.2", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/url": "^3.0.0", "@oozcitak/util": "^10.0.0" } }, "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w=="], 184 + 185 + "@oozcitak/infra": ["@oozcitak/infra@2.0.2", "", { "dependencies": { "@oozcitak/util": "^10.0.0" } }, "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA=="], 186 + 187 + "@oozcitak/url": ["@oozcitak/url@3.0.0", "", { "dependencies": { "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0" } }, "sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ=="], 188 + 189 + "@oozcitak/util": ["@oozcitak/util@10.0.0", "", {}, "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA=="], 190 + 182 191 "@phosphor-icons/react": ["@phosphor-icons/react@2.1.10", "", { "peerDependencies": { "react": ">= 16.8", "react-dom": ">= 16.8" } }, "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA=="], 183 192 184 - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], 193 + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.40", "", {}, "sha512-s3GeJKSQOwBlzdUrj4ISjJj5SfSh+aqn0wjOar4Bx95iV1ETI7F6S/5hLcfAxZ9kXDcyrAkxPlqmd1ZITttf+w=="], 194 + 195 + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], 185 196 186 197 "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="], 187 198 ··· 267 278 268 279 "@tanstack/history": ["@tanstack/history@1.161.4", "", {}, "sha512-Kp/WSt411ZWYvgXy6uiv5RmhHrz9cAml05AQPrtdAp7eUqvIDbMGPnML25OKbzR3RJ1q4wgENxDTvlGPa9+Mww=="], 269 280 270 - "@tanstack/react-router": ["@tanstack/react-router@1.163.3", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/react-store": "^0.9.1", "@tanstack/router-core": "1.163.3", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-hheBbFVb+PbxtrWp8iy6+TTRTbhx3Pn6hKo8Tv/sWlG89ZMcD1xpQWzx8ukHN9K8YWbh5rdzt4kv6u8X4kB28Q=="], 281 + "@tanstack/react-router": ["@tanstack/react-router@1.166.3", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/react-store": "^0.9.1", "@tanstack/router-core": "1.166.2", "isbot": "^5.1.22", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-5NOwAnEp+koHYaRkK5+biYiuOxnQe/7q8R7LLAJ5Ryk6hXoIimOv6gWimPxANwhCWg9spfRZCNswi8EQaidYBg=="], 282 + 283 + "@tanstack/react-start": ["@tanstack/react-start@1.166.3", "", { "dependencies": { "@tanstack/react-router": "1.166.3", "@tanstack/react-start-client": "1.166.3", "@tanstack/react-start-server": "1.166.3", "@tanstack/router-utils": "^1.161.4", "@tanstack/start-client-core": "1.166.2", "@tanstack/start-plugin-core": "1.166.3", "@tanstack/start-server-core": "1.166.2", "pathe": "^2.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "vite": ">=7.0.0" } }, "sha512-RtWJUvRSm3NHRK2NDsrpvr+R394oKWi1HFvCC1iH5F/vzNq7X9bncrCM5Onnl8uax/Fhm5uCZJw2DA0dRuxetA=="], 284 + 285 + "@tanstack/react-start-client": ["@tanstack/react-start-client@1.166.3", "", { "dependencies": { "@tanstack/react-router": "1.166.3", "@tanstack/router-core": "1.166.2", "@tanstack/start-client-core": "1.166.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-sw0LegEgIjZFuOmCRs95ZDXpTJftvGYS8DLhfZwrsrnxGIubu61ITSv3P1VShKhppbagQPG8RkM9JUTzAIKmpg=="], 286 + 287 + "@tanstack/react-start-server": ["@tanstack/react-start-server@1.166.3", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/react-router": "1.166.3", "@tanstack/router-core": "1.166.2", "@tanstack/start-client-core": "1.166.2", "@tanstack/start-server-core": "1.166.2" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-Bw9fQa54GdPnLeyG/VsurF508ZRCtu2aV/F39U8K9NOVVhr9uY9l/qVwifPVysYPVgTdMaaDDIIcDhWxcq90Yg=="], 271 288 272 289 "@tanstack/react-store": ["@tanstack/react-store@0.9.1", "", { "dependencies": { "@tanstack/store": "0.9.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-YzJLnRvy5lIEFTLWBAZmcOjK3+2AepnBv/sr6NZmiqJvq7zTQggyK99Gw8fqYdMdHPQWXjz0epFKJXC+9V2xDA=="], 273 290 274 - "@tanstack/router-core": ["@tanstack/router-core@1.163.3", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/store": "^0.9.1", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-jPptiGq/w3nuPzcMC7RNa79aU+b6OjaDzWJnBcV2UAwL4ThJamRS4h42TdhJE+oF5yH9IEnCOGQdfnbw45LbfA=="], 291 + "@tanstack/router-core": ["@tanstack/router-core@1.166.2", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/store": "^0.9.1", "cookie-es": "^2.0.0", "seroval": "^1.4.2", "seroval-plugins": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-zn3NhENOAX9ToQiX077UV2OH3aJKOvV2ZMNZZxZ3gDG3i3WqL8NfWfEgetEAfMN37/Mnt90PpotYgf7IyuoKqQ=="], 275 292 276 - "@tanstack/router-generator": ["@tanstack/router-generator@1.164.0", "", { "dependencies": { "@tanstack/router-core": "1.163.3", "@tanstack/router-utils": "1.161.4", "@tanstack/virtual-file-routes": "1.161.4", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-Uiyj+RtW0kdeqEd8NEd3Np1Z2nhJ2xgLS8U+5mTvFrm/s3xkM2LYjJHoLzc6am7sKPDsmeF9a4/NYq3R7ZJP0Q=="], 293 + "@tanstack/router-generator": ["@tanstack/router-generator@1.166.2", "", { "dependencies": { "@tanstack/router-core": "1.166.2", "@tanstack/router-utils": "1.161.4", "@tanstack/virtual-file-routes": "1.161.4", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-wbvdyP1PKKQKk4aVlGeK9S5uDy8zodTr3tEZ2gRKNavJLusXbEWqtoo42JxHFFNB6dtguehFMt8PyZPAtkgWwQ=="], 277 294 278 - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.164.0", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.163.3", "@tanstack/router-generator": "1.164.0", "@tanstack/router-utils": "1.161.4", "@tanstack/virtual-file-routes": "1.161.4", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.163.3", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-cZPsEMhqzyzmuPuDbsTAzBZaT+cj0pGjwdhjxJfPCM06Ax8v4tFR7n/Ug0UCwnNAUEmKZWN3lA9uT+TxXnk9PQ=="], 295 + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.166.3", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-syntax-typescript": "^7.27.1", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.5", "@babel/types": "^7.28.5", "@tanstack/router-core": "1.166.2", "@tanstack/router-generator": "1.166.2", "@tanstack/router-utils": "1.161.4", "@tanstack/virtual-file-routes": "1.161.4", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.166.3", "vite": ">=5.0.0 || >=6.0.0 || >=7.0.0", "vite-plugin-solid": "^2.11.10", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-yhnJRohpdKB24Fh7fW5mwgffpOcERZlXdk3i8PjXn+OYgAiG/cpuXXOJpZZ6An68vDW+Z5zBuTynXsDi2ZE4JQ=="], 279 296 280 297 "@tanstack/router-utils": ["@tanstack/router-utils@1.161.4", "", { "dependencies": { "@babel/core": "^7.28.5", "@babel/generator": "^7.28.5", "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", "ansis": "^4.1.0", "babel-dead-code-elimination": "^1.0.12", "diff": "^8.0.2", "pathe": "^2.0.3", "tinyglobby": "^0.2.15" } }, "sha512-r8TpjyIZoqrXXaf2DDyjd44gjGBoyE+/oEaaH68yLI9ySPO1gUWmQENZ1MZnmBnpUGN24NOZxdjDLc8npK0SAw=="], 281 298 299 + "@tanstack/start-client-core": ["@tanstack/start-client-core@1.166.2", "", { "dependencies": { "@tanstack/router-core": "1.166.2", "@tanstack/start-fn-stubs": "1.161.4", "@tanstack/start-storage-context": "1.166.2", "seroval": "^1.4.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-weADfq6bBWgib7Tf7J+P5zF7LHfUm8UuMhijwTfYjaDQ5za04N4PQk5msw2+VchUnSL44aQUA0WeOmoEkG1KMQ=="], 300 + 301 + "@tanstack/start-fn-stubs": ["@tanstack/start-fn-stubs@1.161.4", "", {}, "sha512-b8s6iSQ+ny0P4lGK0n3DKaL6EI7SECG0/89svDeYieVw2+MaFOJVcQo3rU3BUvmuOcIkgkE5IhdzkmzPXH6yfA=="], 302 + 303 + "@tanstack/start-plugin-core": ["@tanstack/start-plugin-core@1.166.3", "", { "dependencies": { "@babel/code-frame": "7.27.1", "@babel/core": "^7.28.5", "@babel/types": "^7.28.5", "@rolldown/pluginutils": "1.0.0-beta.40", "@tanstack/router-core": "1.166.2", "@tanstack/router-generator": "1.166.2", "@tanstack/router-plugin": "1.166.3", "@tanstack/router-utils": "1.161.4", "@tanstack/start-client-core": "1.166.2", "@tanstack/start-server-core": "1.166.2", "cheerio": "^1.0.0", "exsolve": "^1.0.7", "pathe": "^2.0.3", "picomatch": "^4.0.3", "source-map": "^0.7.6", "srvx": "^0.11.7", "tinyglobby": "^0.2.15", "ufo": "^1.5.4", "vitefu": "^1.1.1", "xmlbuilder2": "^4.0.3", "zod": "^3.24.2" }, "peerDependencies": { "vite": ">=7.0.0" } }, "sha512-d2zgfdYcalq2iEXQGY6D7k7rWiJx4PbhDeWFz9QC7yRKVQbr8pr0q284RQnQWgewOlen7gWN9ActsuPRSxuG9Q=="], 304 + 305 + "@tanstack/start-server-core": ["@tanstack/start-server-core@1.166.2", "", { "dependencies": { "@tanstack/history": "1.161.4", "@tanstack/router-core": "1.166.2", "@tanstack/start-client-core": "1.166.2", "@tanstack/start-storage-context": "1.166.2", "h3-v2": "npm:h3@2.0.1-rc.14", "seroval": "^1.4.2", "tiny-invariant": "^1.3.3" } }, "sha512-9BDZsaLyHVux5tJRRBRYa2xW2jUaKr4PbJkTCSSOnAByOGUVJy7N+790/Q1Kq/LVud+0h42vZHWSRDDywfnedQ=="], 306 + 307 + "@tanstack/start-storage-context": ["@tanstack/start-storage-context@1.166.2", "", { "dependencies": { "@tanstack/router-core": "1.166.2" } }, "sha512-c3QPApFAhiDXDZ/zLvop5InErqCrawWuO751FpItGnelOlpOAPMw5/h//1u/RnIcOv2l/ffDBCbp+N09eFPhaA=="], 308 + 282 309 "@tanstack/store": ["@tanstack/store@0.9.1", "", {}, "sha512-+qcNkOy0N1qSGsP7omVCW0SDrXtaDcycPqBDE726yryiA5eTDFpjBReaYjghVJwNf1pcPMyzIwTGlYjCSQR0Fg=="], 283 310 284 311 "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.161.4", "", {}, "sha512-42WoRePf8v690qG8yGRe/YOh+oHni9vUaUUfoqlS91U2scd3a5rkLtVsc6b7z60w3RogH0I00vdrC5AaeiZ18w=="], ··· 290 317 "@testing-library/react": ["@testing-library/react@16.3.2", "", { "dependencies": { "@babel/runtime": "^7.12.5" }, "peerDependencies": { "@testing-library/dom": "^10.0.0", "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="], 291 318 292 319 "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], 293 - 294 - "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], 295 - 296 - "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], 297 - 298 - "@types/babel__template": ["@types/babel__template@7.4.4", "", { "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A=="], 299 - 300 - "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], 301 320 302 321 "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], 303 322 323 + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], 324 + 304 325 "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], 305 326 306 327 "@types/esrecurse": ["@types/esrecurse@4.3.1", "", {}, "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw=="], 307 328 308 329 "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 330 + 331 + "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], 332 + 333 + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], 309 334 310 335 "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], 311 336 312 - "@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="], 337 + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], 338 + 339 + "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], 340 + 341 + "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], 342 + 343 + "@types/node": ["@types/node@25.3.5", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA=="], 313 344 314 345 "@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="], 315 346 316 347 "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], 348 + 349 + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], 317 350 318 351 "@types/whatwg-mimetype": ["@types/whatwg-mimetype@3.0.2", "", {}, "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA=="], 319 352 ··· 339 372 340 373 "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.56.1", "", { "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" } }, "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw=="], 341 374 342 - "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.4", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA=="], 375 + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], 343 376 344 377 "@vitest/expect": ["@vitest/expect@4.0.18", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ=="], 345 378 ··· 369 402 370 403 "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], 371 404 405 + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], 406 + 372 407 "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], 373 408 374 409 "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="], ··· 387 422 388 423 "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="], 389 424 425 + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], 426 + 390 427 "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], 391 428 392 429 "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], ··· 396 433 "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], 397 434 398 435 "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], 436 + 437 + "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], 399 438 400 439 "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], 401 440 ··· 403 442 404 443 "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], 405 444 445 + "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], 446 + 406 447 "brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], 407 448 408 449 "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], ··· 421 462 422 463 "caniuse-lite": ["caniuse-lite@1.0.30001776", "", {}, "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw=="], 423 464 465 + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], 466 + 424 467 "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], 425 468 469 + "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], 470 + 471 + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], 472 + 473 + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], 474 + 475 + "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], 476 + 477 + "cheerio": ["cheerio@1.2.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="], 478 + 479 + "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], 480 + 426 481 "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], 427 482 428 483 "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 429 484 485 + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], 486 + 430 487 "comlink": ["comlink@4.4.2", "", {}, "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g=="], 488 + 489 + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], 431 490 432 491 "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], 433 492 ··· 437 496 438 497 "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], 439 498 499 + "css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="], 500 + 501 + "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], 502 + 440 503 "css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="], 441 504 442 505 "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], ··· 452 515 "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], 453 516 454 517 "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], 518 + 519 + "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], 455 520 456 521 "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], 457 522 ··· 465 530 466 531 "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 467 532 533 + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], 534 + 468 535 "dexie": ["dexie@4.3.0", "", {}, "sha512-5EeoQpJvMKHe6zWt/FSIIuRa3CWlZeIl6zKXt+Lz7BU6RoRRLgX9dZEynRfXrkLcldKYCBiz7xekTEylnie1Ug=="], 469 536 470 537 "diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], 471 538 472 539 "dom-accessibility-api": ["dom-accessibility-api@0.6.3", "", {}, "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w=="], 473 540 541 + "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], 542 + 543 + "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], 544 + 545 + "domhandler": ["domhandler@5.0.3", "", { "dependencies": { "domelementtype": "^2.3.0" } }, "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w=="], 546 + 547 + "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], 548 + 474 549 "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], 475 550 476 551 "electron-to-chromium": ["electron-to-chromium@1.5.307", "", {}, "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg=="], 477 552 478 553 "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], 554 + 555 + "encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="], 479 556 480 557 "enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="], 481 558 ··· 496 573 "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="], 497 574 498 575 "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], 576 + 577 + "esast-util-from-estree": ["esast-util-from-estree@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "unist-util-position-from-estree": "^2.0.0" } }, "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ=="], 578 + 579 + "esast-util-from-js": ["esast-util-from-js@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "acorn": "^8.0.0", "esast-util-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw=="], 499 580 500 581 "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=="], 501 582 ··· 533 614 534 615 "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], 535 616 617 + "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], 618 + 619 + "estree-util-build-jsx": ["estree-util-build-jsx@3.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "estree-walker": "^3.0.0" } }, "sha512-8U5eiL6BTrPxp/CHbs2yMgP8ftMhR5ww1eIKoWRMlqvltHF8fZn5LRDvTKuxD3DUn+shRbLGqXemcP51oFCsGQ=="], 620 + 621 + "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], 622 + 623 + "estree-util-scope": ["estree-util-scope@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0" } }, "sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ=="], 624 + 625 + "estree-util-to-js": ["estree-util-to-js@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "astring": "^1.8.0", "source-map": "^0.7.0" } }, "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg=="], 626 + 627 + "estree-util-visit": ["estree-util-visit@2.0.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/unist": "^3.0.0" } }, "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww=="], 628 + 536 629 "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], 537 630 538 631 "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], 539 632 540 633 "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], 634 + 635 + "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], 636 + 637 + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], 541 638 542 639 "fake-indexeddb": ["fake-indexeddb@6.2.5", "", {}, "sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w=="], 543 640 ··· 593 690 594 691 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 595 692 693 + "h3-v2": ["h3@2.0.1-rc.14", "", { "dependencies": { "rou3": "^0.7.12", "srvx": "^0.11.2" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"], "bin": { "h3": "bin/h3.mjs" } }, "sha512-163qbGmTr/9rqQRNuqMqtgXnOUAkE4KTdauiC9y0E5iG1I65kte9NyfWvZw5RTDMt6eY+DtyoNzrQ9wA2BfvGQ=="], 694 + 596 695 "happy-dom": ["happy-dom@20.8.3", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-lMHQRRwIPyJ70HV0kkFT7jH/gXzSI7yDkQFe07E2flwmNDFoWUTRMKpW2sglsnpeA7b6S2TJPp98EbQxai8eaQ=="], 597 696 598 697 "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], ··· 607 706 608 707 "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 609 708 709 + "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="], 710 + 711 + "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], 712 + 713 + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], 714 + 610 715 "hermes-estree": ["hermes-estree@0.25.1", "", {}, "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw=="], 611 716 612 717 "hermes-parser": ["hermes-parser@0.25.1", "", { "dependencies": { "hermes-estree": "0.25.1" } }, "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA=="], 718 + 719 + "htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="], 720 + 721 + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], 613 722 614 723 "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], 615 724 ··· 619 728 620 729 "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], 621 730 731 + "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], 732 + 622 733 "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], 734 + 735 + "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], 736 + 737 + "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], 623 738 624 739 "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], 625 740 ··· 637 752 638 753 "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="], 639 754 755 + "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], 756 + 640 757 "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 641 758 642 759 "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], ··· 645 762 646 763 "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 647 764 765 + "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], 766 + 648 767 "is-immutable-type": ["is-immutable-type@5.0.1", "", { "dependencies": { "@typescript-eslint/type-utils": "^8.0.0", "ts-api-utils": "^2.0.0", "ts-declaration-location": "^1.0.4" }, "peerDependencies": { "eslint": "*", "typescript": ">=4.7.4" } }, "sha512-LkHEOGVZZXxGl8vDs+10k3DvP++SEoYEAJLRk6buTFi6kD7QekThV7xHS0j6gpnUCQ0zpud/gMDGiV4dQneLTg=="], 649 768 650 769 "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], ··· 654 773 "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], 655 774 656 775 "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], 776 + 777 + "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], 657 778 658 779 "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], 659 780 ··· 683 804 684 805 "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], 685 806 807 + "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], 808 + 686 809 "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], 687 810 688 811 "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], ··· 733 856 734 857 "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], 735 858 859 + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], 860 + 736 861 "lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], 737 862 738 863 "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], 739 864 740 865 "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], 866 + 867 + "markdown-extensions": ["markdown-extensions@2.0.0", "", {}, "sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q=="], 868 + 869 + "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], 741 870 742 871 "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], 743 872 873 + "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], 874 + 875 + "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], 876 + 877 + "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", "mdast-util-gfm-strikethrough": "^2.0.0", "mdast-util-gfm-table": "^2.0.0", "mdast-util-gfm-task-list-item": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], 878 + 879 + "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], 880 + 881 + "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], 882 + 883 + "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], 884 + 885 + "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], 886 + 887 + "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], 888 + 889 + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], 890 + 891 + "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], 892 + 893 + "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], 894 + 895 + "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], 896 + 897 + "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], 898 + 899 + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], 900 + 901 + "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], 902 + 903 + "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], 904 + 905 + "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], 906 + 907 + "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], 908 + 909 + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], 910 + 911 + "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], 912 + 913 + "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], 914 + 915 + "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], 916 + 917 + "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], 918 + 919 + "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], 920 + 921 + "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], 922 + 923 + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], 924 + 925 + "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], 926 + 927 + "micromark-extension-mdx-md": ["micromark-extension-mdx-md@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ=="], 928 + 929 + "micromark-extension-mdxjs": ["micromark-extension-mdxjs@3.0.0", "", { "dependencies": { "acorn": "^8.0.0", "acorn-jsx": "^5.0.0", "micromark-extension-mdx-expression": "^3.0.0", "micromark-extension-mdx-jsx": "^3.0.0", "micromark-extension-mdx-md": "^2.0.0", "micromark-extension-mdxjs-esm": "^3.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ=="], 930 + 931 + "micromark-extension-mdxjs-esm": ["micromark-extension-mdxjs-esm@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A=="], 932 + 933 + "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], 934 + 935 + "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], 936 + 937 + "micromark-factory-mdx-expression": ["micromark-factory-mdx-expression@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-position-from-estree": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ=="], 938 + 939 + "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], 940 + 941 + "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], 942 + 943 + "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], 944 + 945 + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], 946 + 947 + "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], 948 + 949 + "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], 950 + 951 + "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], 952 + 953 + "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], 954 + 955 + "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], 956 + 957 + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], 958 + 959 + "micromark-util-events-to-acorn": ["micromark-util-events-to-acorn@2.0.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "estree-util-visit": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg=="], 960 + 961 + "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], 962 + 963 + "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], 964 + 965 + "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], 966 + 967 + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], 968 + 969 + "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], 970 + 971 + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], 972 + 973 + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], 974 + 744 975 "min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="], 745 976 746 977 "minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], ··· 754 985 "node-releases": ["node-releases@2.0.36", "", {}, "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA=="], 755 986 756 987 "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], 988 + 989 + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], 757 990 758 991 "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], 759 992 ··· 775 1008 776 1009 "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], 777 1010 1011 + "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], 1012 + 1013 + "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], 1014 + 1015 + "parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="], 1016 + 1017 + "parse5-parser-stream": ["parse5-parser-stream@7.1.2", "", { "dependencies": { "parse5": "^7.0.0" } }, "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow=="], 1018 + 778 1019 "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], 779 1020 780 1021 "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], ··· 797 1038 798 1039 "pretty-format": ["pretty-format@27.5.1", "", { "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" } }, "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ=="], 799 1040 1041 + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], 1042 + 800 1043 "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], 801 1044 802 1045 "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], ··· 805 1048 806 1049 "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="], 807 1050 808 - "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], 809 - 810 1051 "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], 811 1052 812 1053 "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], 813 1054 1055 + "recma-build-jsx": ["recma-build-jsx@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-build-jsx": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew=="], 1056 + 1057 + "recma-jsx": ["recma-jsx@1.0.1", "", { "dependencies": { "acorn-jsx": "^5.0.0", "estree-util-to-js": "^2.0.0", "recma-parse": "^1.0.0", "recma-stringify": "^1.0.0", "unified": "^11.0.0" }, "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w=="], 1058 + 1059 + "recma-parse": ["recma-parse@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "esast-util-from-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-OYLsIGBB5Y5wjnSnQW6t3Xg7q3fQ7FWbw/vcXtORTnyaSFscOtABg+7Pnz6YZ6c27fG1/aN8CjfwoUEUIdwqWQ=="], 1060 + 1061 + "recma-stringify": ["recma-stringify@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-util-to-js": "^2.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g=="], 1062 + 814 1063 "redent": ["redent@3.0.0", "", { "dependencies": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" } }, "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="], 815 1064 816 1065 "refa": ["refa@0.12.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.8.0" } }, "sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g=="], ··· 823 1072 824 1073 "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="], 825 1074 1075 + "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], 1076 + 1077 + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], 1078 + 1079 + "remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="], 1080 + 1081 + "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], 1082 + 1083 + "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], 1084 + 1085 + "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], 1086 + 826 1087 "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], 827 1088 828 1089 "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=="], 1090 + 1091 + "rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="], 829 1092 830 1093 "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], 831 1094 ··· 835 1098 836 1099 "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], 837 1100 1101 + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], 1102 + 838 1103 "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], 839 1104 840 1105 "scslre": ["scslre@0.3.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.8.0", "refa": "^0.12.0", "regexp-ast-analysis": "^0.7.0" } }, "sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ=="], ··· 869 1134 870 1135 "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 871 1136 1137 + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], 1138 + 1139 + "srvx": ["srvx@0.11.8", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-2n9t0YnAXPJjinytvxccNgs7rOA5gmE7Wowt/8Dy2dx2fDC6sBhfBpbrCvjYKALlVukPS/Uq3QwkolKNa7P/2Q=="], 1140 + 872 1141 "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], 873 1142 874 1143 "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], ··· 883 1152 884 1153 "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], 885 1154 1155 + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], 1156 + 886 1157 "strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], 1158 + 1159 + "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], 1160 + 1161 + "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], 887 1162 888 1163 "tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="], 889 1164 ··· 905 1180 906 1181 "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 907 1182 1183 + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], 1184 + 1185 + "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], 1186 + 908 1187 "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], 909 1188 910 1189 "ts-declaration-location": ["ts-declaration-location@1.0.7", "", { "dependencies": { "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": ">=4.0.0" } }, "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA=="], ··· 926 1205 "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 927 1206 928 1207 "typescript-eslint": ["typescript-eslint@8.56.1", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.56.1", "@typescript-eslint/parser": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/utils": "8.56.1" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ=="], 1208 + 1209 + "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], 929 1210 930 1211 "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="], 931 1212 1213 + "undici": ["undici@7.22.0", "", {}, "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg=="], 1214 + 932 1215 "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], 933 1216 1217 + "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], 1218 + 1219 + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], 1220 + 1221 + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], 1222 + 1223 + "unist-util-position-from-estree": ["unist-util-position-from-estree@2.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ=="], 1224 + 1225 + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], 1226 + 1227 + "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], 1228 + 1229 + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], 1230 + 934 1231 "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], 935 1232 936 1233 "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], ··· 939 1236 940 1237 "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], 941 1238 1239 + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], 1240 + 1241 + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], 1242 + 942 1243 "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=="], 943 1244 944 1245 "vite-plugin-comlink": ["vite-plugin-comlink@5.3.0", "", { "dependencies": { "json5": "2.2.3", "magic-string": "0.30.17", "source-map": "^0.7.4" }, "peerDependencies": { "comlink": "^4.3.1", "vite": ">=2.9.6" } }, "sha512-grCxBC8JaCYIHPIvrM5c8Hq+XjFs3nhwSDc0SLpgY3D2LtE5j0wIZTF8zMWMzwMcrFPXWSejZI6MF6yvP/Iq9g=="], 945 1246 946 1247 "vite-plugin-wasm": ["vite-plugin-wasm@3.5.0", "", { "peerDependencies": { "vite": "^2 || ^3 || ^4 || ^5 || ^6 || ^7" } }, "sha512-X5VWgCnqiQEGb+omhlBVsvTfxikKtoOgAzQ95+BZ8gQ+VfMHIjSHr0wyvXFQCa0eKQ0fKyaL0kWcEnYqBac4lQ=="], 947 1248 1249 + "vitefu": ["vitefu@1.1.2", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw=="], 1250 + 948 1251 "vitest": ["vitest@4.0.18", "", { "dependencies": { "@vitest/expect": "4.0.18", "@vitest/mocker": "4.0.18", "@vitest/pretty-format": "4.0.18", "@vitest/runner": "4.0.18", "@vitest/snapshot": "4.0.18", "@vitest/spy": "4.0.18", "@vitest/utils": "4.0.18", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.18", "@vitest/browser-preview": "4.0.18", "@vitest/browser-webdriverio": "4.0.18", "@vitest/ui": "4.0.18", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ=="], 949 1252 950 1253 "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], 951 1254 1255 + "whatwg-encoding": ["whatwg-encoding@3.1.1", "", { "dependencies": { "iconv-lite": "0.6.3" } }, "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ=="], 1256 + 952 1257 "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], 953 1258 954 1259 "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], ··· 966 1271 "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], 967 1272 968 1273 "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], 1274 + 1275 + "xmlbuilder2": ["xmlbuilder2@4.0.3", "", { "dependencies": { "@oozcitak/dom": "^2.0.2", "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0", "js-yaml": "^4.1.1" } }, "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA=="], 969 1276 970 1277 "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], 971 1278 ··· 977 1284 978 1285 "zustand": ["zustand@5.0.11", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg=="], 979 1286 1287 + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], 1288 + 980 1289 "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], 981 1290 982 1291 "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], 983 1292 984 1293 "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], 985 1294 1295 + "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], 1296 + 986 1297 "@tailwindcss/node/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 987 1298 988 1299 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], ··· 996 1307 "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], 997 1308 998 1309 "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 1310 + 1311 + "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], 999 1312 1000 1313 "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], 1001 1314 1002 1315 "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], 1003 1316 1317 + "@types/ws/@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="], 1318 + 1004 1319 "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], 1005 1320 1006 1321 "@vitest/mocker/magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], ··· 1009 1324 1010 1325 "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1011 1326 1327 + "cheerio/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], 1328 + 1012 1329 "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 1330 + 1331 + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], 1013 1332 1014 1333 "eslint-plugin-functional/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], 1015 1334 1016 1335 "eslint-plugin-jsx-a11y/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], 1336 + 1337 + "happy-dom/@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="], 1338 + 1339 + "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], 1340 + 1341 + "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], 1342 + 1343 + "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], 1017 1344 1018 1345 "readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 1019 1346
+2
web/eslint.config.ts
··· 15 15 ignores: [ 16 16 "src/routeTree.gen.ts", 17 17 "src/wasm/**", 18 + "src/content/**/*.mdx", 19 + ".output/**", 18 20 "dist/**", 19 21 "node_modules/**", 20 22 ],
-18
web/index.html
··· 1 - <!doctype html> 2 - <html lang="en" data-theme="opake"> 3 - <head> 4 - <meta charset="UTF-8" /> 5 - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 - <title>Opake</title> 7 - <link rel="preconnect" href="https://fonts.googleapis.com" /> 8 - <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> 9 - <link 10 - href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400;1,500;1,600&family=Inter:ital,opsz,wght@0,14..32,300;0,14..32,400;0,14..32,500;0,14..32,600;1,14..32,300;1,14..32,400&display=swap" 11 - rel="stylesheet" 12 - /> 13 - </head> 14 - <body> 15 - <div id="root"></div> 16 - <script type="module" src="/src/main.tsx"></script> 17 - </body> 18 - </html>
+6 -5
web/package.json
··· 13 13 "test:watch": "vitest", 14 14 "lint": "eslint src/", 15 15 "lint:fix": "eslint src/ --fix", 16 - "format": "prettier --write 'src/**/*.{ts,tsx}'", 17 - "format:check": "prettier --check 'src/**/*.{ts,tsx}'" 16 + "format": "prettier --write 'src/**/*.{ts,tsx,mdx}'", 17 + "format:check": "prettier --check 'src/**/*.{ts,tsx,mdx}'" 18 18 }, 19 19 "dependencies": { 20 20 "@phosphor-icons/react": "^2.1.10", 21 - "@tanstack/react-router": "^1.163.3", 21 + "@tanstack/react-start": "^1.166.3", 22 22 "clsx": "^2.1.1", 23 23 "comlink": "^4.4.2", 24 24 "dexie": "^4.3.0", ··· 29 29 "zustand": "^5.0.11" 30 30 }, 31 31 "devDependencies": { 32 + "@mdx-js/rollup": "^3.1.1", 32 33 "@tailwindcss/vite": "^4.2.1", 33 - "@tanstack/router-plugin": "^1.164.0", 34 34 "@testing-library/jest-dom": "^6.9.1", 35 35 "@testing-library/react": "^16.3.2", 36 + "@types/node": "^25.3.5", 36 37 "@types/react": "^19.2.14", 37 38 "@types/react-dom": "^19.2.3", 38 - "@vitejs/plugin-react": "^5.1.4", 39 39 "daisyui": "^5.5.19", 40 40 "eslint": "^10.0.3", 41 41 "eslint-config-prettier": "^10.1.8", ··· 50 50 "jiti": "^2.6.1", 51 51 "prettier": "^3.8.1", 52 52 "prettier-plugin-tailwindcss": "^0.7.2", 53 + "remark-gfm": "^4.0.1", 53 54 "tailwindcss": "^4.2.1", 54 55 "typescript": "^5.9.3", 55 56 "typescript-eslint": "^8.56.1",
+24
web/src/client.tsx
··· 1 + // Client entry — TanStack Start hydrates the app from here. 2 + 3 + import { enableMapSet, enableArrayMethods } from "immer"; 4 + import { StrictMode, startTransition } from "react"; 5 + import { hydrateRoot } from "react-dom/client"; 6 + import { StartClient } from "@tanstack/react-start/client"; 7 + import { getCryptoWorker } from "@/lib/worker"; 8 + import { useAuthStore } from "@/stores/auth"; 9 + 10 + enableMapSet(); 11 + enableArrayMethods(); 12 + 13 + console.debug("[opake] app starting"); 14 + getCryptoWorker(); // warm up WASM worker early 15 + void useAuthStore.getState().boot(); // start session restore from IndexedDB 16 + 17 + startTransition(() => { 18 + hydrateRoot( 19 + document, 20 + <StrictMode> 21 + <StartClient /> 22 + </StrictMode>, 23 + ); 24 + });
+4
web/src/components/cabinet/FileActionMenu.tsx
··· 3 3 DotsThreeVerticalIcon, 4 4 DownloadSimpleIcon, 5 5 PencilSimpleIcon, 6 + ShareNetworkIcon, 6 7 TrashIcon, 7 8 } from "@phosphor-icons/react"; 8 9 import { DropdownMenu } from "@/components/DropdownMenu"; ··· 14 15 readonly onEditMetadata?: () => void; 15 16 readonly onRename?: () => void; 16 17 readonly onMove?: () => void; 18 + readonly onShare?: () => void; 17 19 readonly onDownload?: () => void; 18 20 readonly onDelete?: () => void; 19 21 readonly onDeleteFolder?: () => void; ··· 24 26 onEditMetadata, 25 27 onRename, 26 28 onMove, 29 + onShare, 27 30 onDownload, 28 31 onDelete, 29 32 onDeleteFolder, ··· 52 55 ] 53 56 : [ 54 57 { icon: PencilSimpleIcon, label: "Edit details", onClick: onEditMetadata }, 58 + { icon: ShareNetworkIcon, label: "Share\u2026", onClick: onShare }, 55 59 { icon: ArrowBendUpRightIcon, label: "Move to\u2026", onClick: onMove }, 56 60 { icon: DownloadSimpleIcon, label: "Download", onClick: onDownload }, 57 61 { icon: TrashIcon, label: "Delete", onClick: onDelete },
+3
web/src/components/cabinet/FileGridCard.tsx
··· 10 10 readonly onEditMetadata?: () => void; 11 11 readonly onRename?: () => void; 12 12 readonly onMove?: () => void; 13 + readonly onShare?: () => void; 13 14 readonly onDownload?: () => void; 14 15 readonly onDelete?: () => void; 15 16 readonly onDeleteFolder?: () => void; ··· 21 22 onEditMetadata, 22 23 onRename, 23 24 onMove, 25 + onShare, 24 26 onDownload, 25 27 onDelete, 26 28 onDeleteFolder, ··· 62 64 onEditMetadata={onEditMetadata} 63 65 onRename={onRename} 64 66 onMove={onMove} 67 + onShare={onShare} 65 68 onDownload={onDownload} 66 69 onDelete={onDelete} 67 70 onDeleteFolder={onDeleteFolder}
+3
web/src/components/cabinet/FileListRow.tsx
··· 10 10 readonly onEditMetadata?: () => void; 11 11 readonly onRename?: () => void; 12 12 readonly onMove?: () => void; 13 + readonly onShare?: () => void; 13 14 readonly onDownload?: () => void; 14 15 readonly onDelete?: () => void; 15 16 readonly onDeleteFolder?: () => void; ··· 21 22 onEditMetadata, 22 23 onRename, 23 24 onMove, 25 + onShare, 24 26 onDownload, 25 27 onDelete, 26 28 onDeleteFolder, ··· 59 61 onEditMetadata={onEditMetadata} 60 62 onRename={onRename} 61 63 onMove={onMove} 64 + onShare={onShare} 62 65 onDownload={onDownload} 63 66 onDelete={onDelete} 64 67 onDeleteFolder={onDeleteFolder}
+5
web/src/components/cabinet/PanelContent.tsx
··· 15 15 } from "./MetadataEditDialog"; 16 16 import { MoveDialog, type MoveDialogHandle } from "./MoveDialog"; 17 17 import { RenameDialog, type RenameDialogHandle } from "./RenameDialog"; 18 + import { ShareDialog, type ShareDialogHandle } from "./ShareDialog"; 18 19 import { useDocumentsStore } from "@/stores/documents"; 19 20 import { getCryptoWorker } from "@/lib/worker"; 20 21 import type { FileItem } from "./types"; ··· 47 48 const metadataDialogRef = useRef<MetadataEditDialogHandle>(null); 48 49 const moveDialogRef = useRef<MoveDialogHandle>(null); 49 50 const renameDialogRef = useRef<RenameDialogHandle>(null); 51 + const shareDialogRef = useRef<ShareDialogHandle>(null); 50 52 51 53 const handleDeleteFolderClick = async (item: FileItem) => { 52 54 const worker = getCryptoWorker(); ··· 98 100 onEditMetadata={() => metadataDialogRef.current?.show(item)} 99 101 onRename={() => renameDialogRef.current?.show(item.uri, item.name)} 100 102 onMove={() => void handleMoveClick(item)} 103 + onShare={() => shareDialogRef.current?.show(item.uri, item.name)} 101 104 onDownload={() => onDownload(item.uri)} 102 105 onDelete={() => deleteDialogRef.current?.show(item.uri, item.name)} 103 106 onDeleteFolder={() => void handleDeleteFolderClick(item)} ··· 114 117 onEditMetadata={() => metadataDialogRef.current?.show(item)} 115 118 onRename={() => renameDialogRef.current?.show(item.uri, item.name)} 116 119 onMove={() => void handleMoveClick(item)} 120 + onShare={() => shareDialogRef.current?.show(item.uri, item.name)} 117 121 onDownload={() => onDownload(item.uri)} 118 122 onDelete={() => deleteDialogRef.current?.show(item.uri, item.name)} 119 123 onDeleteFolder={() => void handleDeleteFolderClick(item)} ··· 127 131 <MetadataEditDialog ref={metadataDialogRef} onSave={onUpdateMetadata} /> 128 132 <MoveDialog ref={moveDialogRef} onMove={onMoveEntry} /> 129 133 <RenameDialog ref={renameDialogRef} onSave={onRenameDirectory} /> 134 + <ShareDialog ref={shareDialogRef} /> 130 135 </div> 131 136 ); 132 137 }
+206
web/src/components/cabinet/ShareDialog.tsx
··· 1 + import { forwardRef, useCallback, useImperativeHandle, useRef, useState } from "react"; 2 + import { ShareNetworkIcon } from "@phosphor-icons/react"; 3 + import { resolveRecipient, createGrant } from "@/lib/sharing"; 4 + import { useAuthStore } from "@/stores/auth"; 5 + import { IndexedDbStorage } from "@/lib/indexeddbStorage"; 6 + import { getCryptoWorker } from "@/lib/worker"; 7 + import { base64ToUint8Array } from "@/lib/encoding"; 8 + import { authenticatedXrpc } from "@/lib/api"; 9 + import { toastSuccess, toastError } from "@/stores/toast"; 10 + import type { DocumentRecord, Encryption } from "@/lib/pdsTypes"; 11 + import type { OAuthSession } from "@/lib/storageTypes"; 12 + import { MODAL_TRANSITION_MS } from "@/components/ConfirmDialog"; 13 + 14 + export interface ShareDialogHandle { 15 + readonly show: (documentUri: string, documentName: string) => void; 16 + } 17 + 18 + const storage = new IndexedDbStorage(); 19 + 20 + /** Unwrap the content key from a document's encryption envelope. */ 21 + async function unwrapContentKey( 22 + encryption: Encryption, 23 + privateKey: Uint8Array, 24 + ): Promise<Uint8Array> { 25 + if (encryption.$type !== "app.opake.document#directEncryption") { 26 + throw new Error("Keyring-encrypted documents cannot be shared via ad-hoc grants yet"); 27 + } 28 + 29 + const worker = getCryptoWorker(); 30 + const keys = encryption.envelope.keys; 31 + 32 + /* eslint-disable functional/no-loop-statements -- sequential try/catch unwrap */ 33 + for (const wk of keys) { 34 + try { 35 + return await worker.unwrapKey(wk, privateKey); 36 + } catch { 37 + // Not our key — try next 38 + } 39 + } 40 + /* eslint-enable functional/no-loop-statements */ 41 + 42 + throw new Error("No matching wrapped key found for this identity"); 43 + } 44 + 45 + export const ShareDialog = forwardRef<ShareDialogHandle>(function ShareDialog(_, ref) { 46 + const dialogRef = useRef<HTMLDialogElement>(null); 47 + const inputRef = useRef<HTMLInputElement>(null); 48 + 49 + const [documentUri, setDocumentUri] = useState<string | null>(null); 50 + const [documentName, setDocumentName] = useState(""); 51 + const [recipientHandle, setRecipientHandle] = useState(""); 52 + const [status, setStatus] = useState<"idle" | "resolving" | "sharing" | "done" | "error">("idle"); 53 + const [errorMessage, setErrorMessage] = useState(""); 54 + 55 + const session = useAuthStore((s) => s.session); 56 + 57 + const dismiss = useCallback(() => { 58 + dialogRef.current?.close(); 59 + setTimeout(() => { 60 + setDocumentUri(null); 61 + setDocumentName(""); 62 + setRecipientHandle(""); 63 + setStatus("idle"); 64 + setErrorMessage(""); 65 + }, MODAL_TRANSITION_MS); 66 + }, []); 67 + 68 + useImperativeHandle(ref, () => ({ 69 + show: (uri: string, name: string) => { 70 + setDocumentUri(uri); 71 + setDocumentName(name); 72 + setRecipientHandle(""); 73 + setStatus("idle"); 74 + setErrorMessage(""); 75 + dialogRef.current?.showModal(); 76 + // Focus the input after dialog opens 77 + setTimeout(() => inputRef.current?.focus(), 50); 78 + }, 79 + })); 80 + 81 + const handleShare = useCallback(async () => { 82 + if (!documentUri || session.status !== "active" || !recipientHandle.trim()) return; 83 + 84 + const handle = recipientHandle.trim(); 85 + 86 + setStatus("resolving"); 87 + setErrorMessage(""); 88 + 89 + try { 90 + // Resolve recipient 91 + const recipient = await resolveRecipient(handle); 92 + 93 + if (recipient.did === session.did) { 94 + throw new Error("You can't share a file with yourself"); 95 + } 96 + 97 + setStatus("sharing"); 98 + 99 + // Load identity + session for crypto 100 + const oauthSession = (await storage.loadSession(session.did)) as OAuthSession; 101 + const identity = await storage.loadIdentity(session.did); 102 + const privateKey = base64ToUint8Array(identity.private_key); 103 + 104 + // Fetch the document record to get the encryption envelope 105 + const docResponse = (await authenticatedXrpc( 106 + { 107 + pdsUrl: session.pdsUrl, 108 + lexicon: `com.atproto.repo.getRecord?repo=${encodeURIComponent(session.did)}&collection=app.opake.document&rkey=${encodeURIComponent(documentUri.split("/").at(-1) ?? "")}`, 109 + }, 110 + oauthSession, 111 + )) as { value: DocumentRecord }; 112 + 113 + const contentKey = await unwrapContentKey(docResponse.value.encryption, privateKey); 114 + 115 + await createGrant({ 116 + pdsUrl: session.pdsUrl, 117 + ownerDid: session.did, 118 + documentUri, 119 + recipientDid: recipient.did, 120 + contentKey, 121 + recipientPublicKey: recipient.publicKey, 122 + session: oauthSession, 123 + }); 124 + 125 + setStatus("done"); 126 + toastSuccess(`Shared "${documentName}" with ${handle}`); 127 + dismiss(); 128 + } catch (error) { 129 + const message = error instanceof Error ? error.message : "Failed to share"; 130 + setErrorMessage(message); 131 + setStatus("error"); 132 + toastError(message); 133 + } 134 + }, [documentUri, session, recipientHandle, documentName, dismiss]); 135 + 136 + const busy = status === "resolving" || status === "sharing"; 137 + 138 + return ( 139 + <dialog ref={dialogRef} className="modal" aria-label="Share file"> 140 + <div className="modal-box max-w-sm"> 141 + <div className="flex flex-col items-center gap-3 text-center"> 142 + <div className="bg-primary/10 flex size-11 items-center justify-center rounded-full"> 143 + <ShareNetworkIcon size={20} className="text-primary" /> 144 + </div> 145 + <h3 className="text-base-content text-sm font-semibold">Share file</h3> 146 + <p className="text-text-muted text-xs"> 147 + Share <span className="text-base-content font-medium">{documentName}</span> with another 148 + Opake user. 149 + </p> 150 + </div> 151 + 152 + <div className="mt-4"> 153 + <label className="label" htmlFor="share-recipient"> 154 + <span className="text-text-muted text-xs">Recipient handle</span> 155 + </label> 156 + <input 157 + ref={inputRef} 158 + id="share-recipient" 159 + type="text" 160 + placeholder="alice.bsky.social" 161 + value={recipientHandle} 162 + onChange={(e) => setRecipientHandle(e.target.value)} 163 + onKeyDown={(e) => { 164 + if (e.key === "Enter" && !busy && recipientHandle.trim()) { 165 + void handleShare(); 166 + } 167 + }} 168 + disabled={busy} 169 + className="input input-bordered input-sm border-base-300/50 bg-base-200/50 text-ui w-full" 170 + aria-describedby={errorMessage ? "share-error" : undefined} 171 + /> 172 + {errorMessage && ( 173 + <p id="share-error" className="text-error mt-1 text-xs" role="alert"> 174 + {errorMessage} 175 + </p> 176 + )} 177 + {status === "resolving" && ( 178 + <p className="text-text-muted mt-1 text-xs">Resolving recipient…</p> 179 + )} 180 + {status === "sharing" && <p className="text-text-muted mt-1 text-xs">Creating grant…</p>} 181 + </div> 182 + 183 + <div className="modal-action justify-center gap-2"> 184 + <button 185 + onClick={dismiss} 186 + disabled={busy} 187 + className="btn btn-ghost btn-sm rounded-lg text-xs" 188 + > 189 + Cancel 190 + </button> 191 + <button 192 + onClick={() => void handleShare()} 193 + disabled={busy || !recipientHandle.trim()} 194 + className="btn btn-primary btn-sm gap-1.5 rounded-lg text-xs" 195 + > 196 + {busy && <span className="loading loading-spinner loading-xs" />} 197 + Share 198 + </button> 199 + </div> 200 + </div> 201 + <form method="dialog" className="modal-backdrop"> 202 + <button aria-label="Close">close</button> 203 + </form> 204 + </dialog> 205 + ); 206 + });
+123 -38
web/src/components/cabinet/TopBar.tsx
··· 1 + import { useCallback, useEffect, useRef, useState } from "react"; 2 + import { createPortal } from "react-dom"; 1 3 import { 2 4 MagnifyingGlassIcon, 3 5 XIcon, ··· 9 11 SignOutIcon, 10 12 } from "@phosphor-icons/react"; 11 13 import { Link } from "@tanstack/react-router"; 14 + import { useAuthStore } from "@/stores/auth"; 15 + import { truncateDid } from "@/lib/format"; 12 16 13 17 interface TopBarProps { 14 18 readonly searchQuery: string; 15 19 readonly onSearchChange: (query: string) => void; 16 20 } 17 21 18 - function closeDropdown(e: React.MouseEvent) { 19 - e.currentTarget.closest("details")?.removeAttribute("open"); 22 + /** Fetch the user's Bluesky profile avatar URL from their PDS. */ 23 + function useAvatarUrl(pdsUrl: string | null, did: string | null): string | null { 24 + const [avatarUrl, setAvatarUrl] = useState<string | null>(null); 25 + 26 + useEffect(() => { 27 + if (!pdsUrl || !did) return; 28 + 29 + const controller = new AbortController(); 30 + 31 + fetch(`${pdsUrl}/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(did)}`, { 32 + signal: controller.signal, 33 + }) 34 + .then((res) => (res.ok ? (res.json() as Promise<{ avatar?: string }>) : null)) 35 + .then((profile) => setAvatarUrl(profile?.avatar ?? null)) 36 + .catch(() => setAvatarUrl(null)); 37 + 38 + return () => { 39 + controller.abort(); 40 + setAvatarUrl(null); 41 + }; 42 + }, [pdsUrl, did]); 43 + 44 + return avatarUrl; 20 45 } 21 46 22 47 export function TopBar({ searchQuery, onSearchChange }: TopBarProps) { 48 + const session = useAuthStore((s) => s.session); 49 + // eslint-disable-next-line @typescript-eslint/unbound-method -- Zustand actions don't use `this` 50 + const logout = useAuthStore((s) => s.logout); 51 + 52 + const handle = session.status === "active" ? session.handle : null; 53 + const did = session.status === "active" ? session.did : null; 54 + const pdsUrl = session.status === "active" ? session.pdsUrl : null; 55 + const initial = handle?.[0]?.toUpperCase() ?? "?"; 56 + const avatarUrl = useAvatarUrl(pdsUrl, did); 57 + 58 + const [menuOpen, setMenuOpen] = useState(false); 59 + const triggerRef = useRef<HTMLButtonElement>(null); 60 + const popoverRef = useRef<HTMLDivElement>(null); 61 + 62 + const closeMenu = useCallback(() => setMenuOpen(false), []); 63 + 64 + useEffect(() => { 65 + if (!menuOpen) return; 66 + function onClickOutside(e: MouseEvent) { 67 + const target = e.target as Node; 68 + if (triggerRef.current?.contains(target) || popoverRef.current?.contains(target)) { 69 + return; 70 + } 71 + setMenuOpen(false); 72 + } 73 + document.addEventListener("mousedown", onClickOutside); 74 + return () => document.removeEventListener("mousedown", onClickOutside); 75 + }, [menuOpen]); 76 + 23 77 return ( 24 78 <header className="border-base-300/50 bg-base-300/90 flex shrink-0 items-center gap-3 border-b px-5 py-2.5 backdrop-blur-[10px]"> 25 79 {/* Search */} ··· 27 81 <MagnifyingGlassIcon size={13} className="text-text-faint" /> 28 82 <input 29 83 type="text" 30 - placeholder="Search your cabinet\u2026" 84 + placeholder="Search your cabinet…" 31 85 value={searchQuery} 32 86 onChange={(e) => onSearchChange(e.target.value)} 33 87 className="text-secondary grow bg-transparent" ··· 59 113 </div> 60 114 61 115 {/* User menu */} 62 - <details className="dropdown dropdown-end"> 63 - <summary className="btn btn-ghost btn-sm gap-2 rounded-lg pl-1"> 64 - <div className="bg-accent text-caption text-primary flex size-7 items-center justify-center rounded-full font-semibold"> 65 - A 116 + <button 117 + ref={triggerRef} 118 + onClick={() => setMenuOpen((prev) => !prev)} 119 + className="btn btn-ghost btn-sm gap-2 rounded-lg pl-1" 120 + aria-expanded={menuOpen} 121 + aria-haspopup="true" 122 + > 123 + {avatarUrl ? ( 124 + <img 125 + src={avatarUrl} 126 + alt="" 127 + className="size-7 shrink-0 rounded-full object-cover" 128 + aria-hidden="true" 129 + /> 130 + ) : ( 131 + <div 132 + className="bg-accent text-caption text-primary flex size-7 items-center justify-center rounded-full font-semibold" 133 + aria-hidden="true" 134 + > 135 + {initial} 66 136 </div> 67 - <span className="text-secondary text-xs font-normal">alice.bsky.social</span> 68 - </summary> 69 - <div className="dropdown-content border-base-300/50 bg-base-100 shadow-panel-lg z-50 w-52.5 rounded-xl border"> 70 - <div className="border-base-300/50 border-b px-3.5 py-2.5"> 71 - <div className="text-ui text-base-content font-medium">alice.bsky.social</div> 72 - <div className="text-caption text-text-faint mt-0.5">did:plc:7f2ab3c4\u20268e91</div> 73 - </div> 74 - <ul className="menu p-1"> 75 - {[ 76 - { icon: UserIcon, label: "Profile & DID", to: "/cabinet/settings" as const }, 77 - { icon: LockIcon, label: "Encryption Keys", to: "/cabinet/settings" as const }, 78 - { icon: GearIcon, label: "Settings", to: "/cabinet/settings" as const }, 79 - ].map(({ icon: Icon, label, to }) => ( 80 - <li key={label}> 81 - <Link to={to} onClick={closeDropdown} className="text-secondary gap-2.5 text-xs"> 82 - <Icon size={13} /> 83 - {label} 84 - </Link> 137 + )} 138 + <span className="text-secondary text-xs font-normal">{handle ?? "Not signed in"}</span> 139 + </button> 140 + {menuOpen && 141 + createPortal( 142 + <div 143 + ref={popoverRef} 144 + className="border-base-300/50 bg-base-100 shadow-panel-lg fixed top-12 right-4 z-[9999] w-52.5 rounded-xl border" 145 + > 146 + {handle && did && ( 147 + <div className="border-base-300/50 border-b px-3.5 py-2.5"> 148 + <div className="text-ui text-base-content font-medium">{handle}</div> 149 + <div className="text-caption text-text-faint mt-0.5">{truncateDid(did)}</div> 150 + </div> 151 + )} 152 + <ul className="menu p-1"> 153 + {[ 154 + { icon: UserIcon, label: "Profile & DID", to: "/cabinet/settings" as const }, 155 + { icon: LockIcon, label: "Encryption Keys", to: "/cabinet/settings" as const }, 156 + { icon: GearIcon, label: "Settings", to: "/cabinet/settings" as const }, 157 + ].map(({ icon: Icon, label, to }) => ( 158 + <li key={label}> 159 + <Link to={to} onClick={closeMenu} className="text-secondary gap-2.5 text-xs"> 160 + <Icon size={13} /> 161 + {label} 162 + </Link> 163 + </li> 164 + ))} 165 + </ul> 166 + <div className="divider my-0.5" /> 167 + <ul className="menu p-1 pt-0"> 168 + <li> 169 + <button 170 + onClick={() => { 171 + closeMenu(); 172 + void logout(); 173 + }} 174 + className="text-error gap-2.5 text-xs" 175 + > 176 + <SignOutIcon size={13} /> 177 + Sign out 178 + </button> 85 179 </li> 86 - ))} 87 - </ul> 88 - <div className="divider my-0.5" /> 89 - <ul className="menu p-1 pt-0"> 90 - <li> 91 - <Link to="/" className="text-error gap-2.5 text-xs"> 92 - <SignOutIcon size={13} /> 93 - Sign out 94 - </Link> 95 - </li> 96 - </ul> 97 - </div> 98 - </details> 180 + </ul> 181 + </div>, 182 + document.body, 183 + )} 99 184 </header> 100 185 ); 101 186 }
+3
web/src/content/landing.mdx
··· 1 + Opake exists because privacy and collaboration should not be a tradeoff. 2 + Your files — encrypted, owned, shared on your terms — through 3 + decentralised identity, with no central authority in between.
+13
web/src/index.css
··· 154 154 font-family: var(--font-sans); 155 155 -webkit-font-smoothing: antialiased; 156 156 } 157 + 158 + /* Scoped prose — for MDX content blocks */ 159 + .prose p { 160 + margin-block: 0.75em; 161 + } 162 + .prose a { 163 + color: var(--color-primary); 164 + text-decoration: underline; 165 + text-underline-offset: 2px; 166 + } 167 + .prose strong { 168 + font-weight: 600; 169 + } 157 170 }
+23
web/src/lib/format.ts
··· 84 84 return then.toLocaleDateString("en-GB", { day: "numeric", month: "short" }); 85 85 } 86 86 87 + /** Truncate a DID for display, keeping prefix and abbreviated identifier. */ 88 + export function truncateDid(did: string): string { 89 + const lastColon = did.lastIndexOf(":"); 90 + if (lastColon === -1) return did; 91 + const prefix = did.slice(0, lastColon + 1); 92 + const id = did.slice(lastColon + 1); 93 + if (id.length <= 8) return did; 94 + return `${prefix}${id.slice(0, 4)}…${id.slice(-3)}`; 95 + } 96 + 97 + /** Format an ISO date as a short locale string (e.g. "Mar 8, 2026"). */ 98 + export function formatShortDate(iso: string): string { 99 + try { 100 + return new Date(iso).toLocaleDateString(undefined, { 101 + month: "short", 102 + day: "numeric", 103 + year: "numeric", 104 + }); 105 + } catch { 106 + return iso; 107 + } 108 + } 109 + 87 110 const DOCUMENT_COLLECTION = "app.opake.document"; 88 111 const DIRECTORY_COLLECTION = "app.opake.directory"; 89 112
+231
web/src/lib/sharing.ts
··· 1 + // Sharing helpers — resolve recipient, create/list/revoke grants. 2 + 3 + import type { WrappedKey } from "@/lib/cryptoTypes"; 4 + import type { EncryptedMetadataEnvelope } from "@/lib/pdsTypes"; 5 + import type { Session } from "@/lib/storageTypes"; 6 + import { authenticatedXrpc, authenticatedDeleteRecord, appview } from "@/lib/api"; 7 + import { resolveHandleToPds } from "@/lib/oauth"; 8 + import { getCryptoWorker } from "@/lib/worker"; 9 + import { base64ToUint8Array, uint8ArrayToBase64 } from "@/lib/encoding"; 10 + import { IndexedDbStorage } from "@/lib/indexeddbStorage"; 11 + 12 + const GRANT_COLLECTION = "app.opake.grant"; 13 + const PUBLIC_KEY_COLLECTION = "app.opake.publicKey"; 14 + 15 + // --------------------------------------------------------------------------- 16 + // Types 17 + // --------------------------------------------------------------------------- 18 + 19 + export interface GrantRecord { 20 + readonly opakeVersion: number; 21 + readonly document: string; 22 + readonly recipient: string; 23 + readonly wrappedKey: WrappedKey; 24 + readonly encryptedMetadata: EncryptedMetadataEnvelope; 25 + readonly expiresAt?: string; 26 + readonly createdAt: string; 27 + } 28 + 29 + export interface GrantEntry { 30 + readonly uri: string; 31 + readonly cid: string; 32 + readonly record: GrantRecord; 33 + } 34 + 35 + export interface RecipientInfo { 36 + readonly did: string; 37 + readonly pdsUrl: string; 38 + readonly publicKey: Uint8Array; 39 + } 40 + 41 + /** Inbox grant item from the AppView. */ 42 + export interface InboxGrantItem { 43 + readonly uri: string; 44 + readonly ownerDid: string; 45 + readonly documentUri: string; 46 + readonly createdAt: string; 47 + } 48 + 49 + // --------------------------------------------------------------------------- 50 + // Recipient resolution 51 + // --------------------------------------------------------------------------- 52 + 53 + /** Resolve a handle to a DID + PDS URL + X25519 public key. */ 54 + export async function resolveRecipient(handle: string): Promise<RecipientInfo> { 55 + const { did, pdsUrl } = await resolveHandleToPds(handle); 56 + 57 + const base = pdsUrl.replace(/\/$/, ""); 58 + const url = `${base}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(did)}&collection=${encodeURIComponent(PUBLIC_KEY_COLLECTION)}&rkey=self`; 59 + const response = await fetch(url); 60 + if (!response.ok) { 61 + throw new Error(`No Opake public key found for ${handle} (${did})`); 62 + } 63 + 64 + const record = (await response.json()) as { 65 + value?: { publicKey?: { $bytes: string } | string }; 66 + }; 67 + const raw = record.value?.publicKey; 68 + if (!raw) { 69 + throw new Error(`No encryption public key published for ${handle}`); 70 + } 71 + 72 + const b64 = typeof raw === "string" ? raw : raw.$bytes; 73 + const publicKey = base64ToUint8Array(b64); 74 + 75 + return { did, pdsUrl, publicKey }; 76 + } 77 + 78 + // --------------------------------------------------------------------------- 79 + // Grant creation 80 + // --------------------------------------------------------------------------- 81 + 82 + interface CreateGrantParams { 83 + readonly pdsUrl: string; 84 + readonly ownerDid: string; 85 + readonly documentUri: string; 86 + readonly recipientDid: string; 87 + readonly contentKey: Uint8Array; 88 + readonly recipientPublicKey: Uint8Array; 89 + readonly session: Session; 90 + } 91 + 92 + /** Wrap the content key to the recipient and create a grant record on the PDS. */ 93 + export async function createGrant(params: CreateGrantParams): Promise<string> { 94 + const worker = getCryptoWorker(); 95 + 96 + const wrappedKey = await worker.wrapKey( 97 + params.contentKey, 98 + params.recipientPublicKey, 99 + params.recipientDid, 100 + ); 101 + 102 + const metadata = { permissions: "read", note: null }; 103 + const metadataBytes = new TextEncoder().encode(JSON.stringify(metadata)); 104 + const encryptedMeta = await worker.encryptBlob(params.contentKey, metadataBytes); 105 + 106 + const record = { 107 + $type: GRANT_COLLECTION, 108 + opakeVersion: await worker.schemaVersion(), 109 + document: params.documentUri, 110 + recipient: params.recipientDid, 111 + wrappedKey, 112 + encryptedMetadata: { 113 + ciphertext: { $bytes: uint8ArrayToBase64(encryptedMeta.ciphertext) }, 114 + nonce: { $bytes: uint8ArrayToBase64(encryptedMeta.nonce) }, 115 + }, 116 + createdAt: new Date().toISOString(), 117 + }; 118 + 119 + const result = (await authenticatedXrpc( 120 + { 121 + pdsUrl: params.pdsUrl, 122 + lexicon: "com.atproto.repo.createRecord", 123 + method: "POST", 124 + body: { 125 + repo: params.ownerDid, 126 + collection: GRANT_COLLECTION, 127 + record, 128 + }, 129 + }, 130 + params.session, 131 + )) as { uri: string }; 132 + 133 + return result.uri; 134 + } 135 + 136 + // --------------------------------------------------------------------------- 137 + // Grant listing (outgoing — from own PDS) 138 + // --------------------------------------------------------------------------- 139 + 140 + interface ListRecordsResponse { 141 + readonly records: readonly { uri: string; cid: string; value: GrantRecord }[]; 142 + readonly cursor?: string; 143 + } 144 + 145 + /** List all outgoing grants from the owner's PDS. */ 146 + export async function listOutgoingGrants( 147 + pdsUrl: string, 148 + did: string, 149 + session: Session, 150 + ): Promise<GrantEntry[]> { 151 + /* eslint-disable functional/no-loop-statements, functional/no-let, functional/immutable-data, functional/prefer-immutable-types -- paginated cursor loop */ 152 + const entries: GrantEntry[] = []; 153 + let cursor: string | undefined; 154 + 155 + do { 156 + const params = new URLSearchParams({ 157 + repo: did, 158 + collection: GRANT_COLLECTION, 159 + limit: "100", 160 + }); 161 + if (cursor) params.set("cursor", cursor); 162 + 163 + const response = (await authenticatedXrpc( 164 + { pdsUrl, lexicon: `com.atproto.repo.listRecords?${params}` }, 165 + session, 166 + )) as ListRecordsResponse; 167 + 168 + for (const r of response.records) { 169 + entries.push({ uri: r.uri, cid: r.cid, record: r.value }); 170 + } 171 + 172 + cursor = response.cursor; 173 + } while (cursor); 174 + /* eslint-enable functional/no-loop-statements, functional/no-let, functional/immutable-data, functional/prefer-immutable-types */ 175 + 176 + return entries; 177 + } 178 + 179 + // --------------------------------------------------------------------------- 180 + // Grant listing (incoming — from AppView inbox) 181 + // --------------------------------------------------------------------------- 182 + 183 + interface InboxResponse { 184 + readonly grants: InboxGrantItem[]; 185 + readonly cursor?: string; 186 + } 187 + 188 + /** Fetch incoming grants from the AppView inbox. */ 189 + export async function listIncomingGrants(did: string): Promise<InboxGrantItem[]> { 190 + const storage = new IndexedDbStorage(); 191 + const config = await storage.loadConfig().catch(() => null); 192 + const appviewUrl = config?.appviewUrl; 193 + if (!appviewUrl) return []; 194 + 195 + /* eslint-disable functional/no-loop-statements, functional/no-let, functional/immutable-data, functional/prefer-immutable-types -- paginated cursor loop */ 196 + const items: InboxGrantItem[] = []; 197 + let cursor: string | undefined; 198 + 199 + do { 200 + const params = new URLSearchParams({ did, limit: "100" }); 201 + if (cursor) params.set("cursor", cursor); 202 + 203 + const response = (await appview(`/api/inbox?${params}`, { 204 + pdsUrl: "", 205 + appviewUrl, 206 + })) as InboxResponse; 207 + 208 + items.push(...response.grants); 209 + cursor = response.cursor; 210 + } while (cursor); 211 + /* eslint-enable functional/no-loop-statements, functional/no-let, functional/immutable-data, functional/prefer-immutable-types */ 212 + 213 + return items; 214 + } 215 + 216 + // --------------------------------------------------------------------------- 217 + // Grant revocation 218 + // --------------------------------------------------------------------------- 219 + 220 + /** Delete a grant record by AT-URI. */ 221 + export async function revokeGrant( 222 + pdsUrl: string, 223 + did: string, 224 + grantUri: string, 225 + session: Session, 226 + ): Promise<void> { 227 + const segments = grantUri.split("/"); 228 + const rkey = segments[segments.length - 1]; 229 + 230 + await authenticatedDeleteRecord({ pdsUrl, did, collection: GRANT_COLLECTION, rkey }, session); 231 + }
-59
web/src/main.tsx
··· 1 - import { enableMapSet, enableArrayMethods } from "immer"; 2 - import { StrictMode, useEffect } from "react"; 3 - import { createRoot } from "react-dom/client"; 4 - import { createRouter, RouterProvider } from "@tanstack/react-router"; 5 - import { routeTree } from "./routeTree.gen"; 6 - import { useAuthStore } from "@/stores/auth"; 7 - import type { RouterContext } from "@/routes/__root"; 8 - import "./index.css"; 9 - import { getCryptoWorker } from "@/lib/worker"; 10 - import { ToastContainer } from "@/components/ToastContainer"; 11 - 12 - enableMapSet(); 13 - enableArrayMethods(); 14 - 15 - console.debug("[opake] app starting"); 16 - getCryptoWorker(); // warm up WASM worker early 17 - 18 - const router = createRouter({ 19 - routeTree, 20 - context: {} as RouterContext, 21 - }); 22 - 23 - declare module "@tanstack/react-router" { 24 - interface Register { 25 - router: typeof router; 26 - } 27 - } 28 - 29 - function App() { 30 - const session = useAuthStore((s) => s.session); 31 - const identity = useAuthStore((s) => s.identity); 32 - useEffect(() => { 33 - if (session.status === "initializing") { 34 - void useAuthStore.getState().boot(); 35 - } 36 - }, [session.status]); 37 - 38 - // Don't render the router until boot resolves — route guards would see 39 - // "initializing" as not-active and redirect to login before IndexedDB loads. 40 - if (session.status === "initializing") { 41 - return null; 42 - } 43 - 44 - return ( 45 - <> 46 - <RouterProvider router={router} context={{ auth: { session, identity } }} /> 47 - <ToastContainer /> 48 - </> 49 - ); 50 - } 51 - 52 - const rootElement = document.getElementById("root"); 53 - if (!rootElement) throw new Error("Missing #root element"); 54 - 55 - createRoot(rootElement).render( 56 - <StrictMode> 57 - <App /> 58 - </StrictMode>, 59 - );
+6
web/src/mdx.d.ts
··· 1 + declare module "*.mdx" { 2 + import type { ComponentType } from "react"; 3 + // eslint-disable-next-line functional/prefer-immutable-types -- module declaration 4 + const Component: ComponentType; 5 + export default Component; 6 + }
+9
web/src/routeTree.gen.ts
··· 441 441 export const routeTree = rootRouteImport 442 442 ._addFileChildren(rootRouteChildren) 443 443 ._addFileTypes<FileRouteTypes>() 444 + 445 + import type { getRouter } from './router.tsx' 446 + import type { createStart } from '@tanstack/react-start' 447 + declare module '@tanstack/react-start' { 448 + interface Register { 449 + ssr: true 450 + router: Awaited<ReturnType<typeof getRouter>> 451 + } 452 + }
+28
web/src/router.tsx
··· 1 + // Router factory — TanStack Start resolves this as #tanstack-router-entry. 2 + // Must export `getRouter` (sync or async). 3 + 4 + import { createRouter } from "@tanstack/react-router"; 5 + import { routeTree } from "./routeTree.gen"; 6 + import type { RouterContext } from "@/routes/__root"; 7 + 8 + export function getRouter() { 9 + return createRouter({ 10 + routeTree, 11 + // Auth context is read directly from the store by routes that need it 12 + // (cabinet/route.tsx, devices/route.tsx). The router context type is 13 + // retained for type compatibility but no longer drives auth flow. 14 + context: { 15 + auth: { 16 + session: { status: "initializing" }, 17 + identity: { status: "unchecked" }, 18 + }, 19 + } satisfies RouterContext, 20 + scrollRestoration: true, 21 + }); 22 + } 23 + 24 + declare module "@tanstack/react-router" { 25 + interface Register { 26 + router: ReturnType<typeof getRouter>; 27 + } 28 + }
+61 -15
web/src/routes/__root.tsx
··· 1 - import { createRootRouteWithContext, Outlet, useRouter } from "@tanstack/react-router"; 1 + import { 2 + createRootRouteWithContext, 3 + HeadContent, 4 + Outlet, 5 + Scripts, 6 + useRouter, 7 + } from "@tanstack/react-router"; 2 8 import type { AuthSnapshot } from "@/stores/auth"; 9 + import { ToastContainer } from "@/components/ToastContainer"; 10 + import css from "@/index.css?url"; 3 11 4 12 export interface RouterContext { 5 13 auth: AuthSnapshot; 6 14 } 7 15 16 + function RootDocument({ children }: Readonly<{ children: React.ReactNode }>) { 17 + return ( 18 + <html lang="en" data-theme="opake"> 19 + <head> 20 + <meta charSet="UTF-8" /> 21 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 22 + <link rel="preconnect" href="https://fonts.googleapis.com" /> 23 + <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" /> 24 + <link 25 + href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400;1,500;1,600&family=Inter:ital,opsz,wght@0,14..32,300;0,14..32,400;0,14..32,500;0,14..32,600;1,14..32,300;1,14..32,400&display=swap" 26 + rel="stylesheet" 27 + /> 28 + <link rel="stylesheet" href={css} /> 29 + <HeadContent /> 30 + </head> 31 + <body> 32 + {children} 33 + <Scripts /> 34 + </body> 35 + </html> 36 + ); 37 + } 38 + 8 39 function RootLayout() { 9 - return <Outlet />; 40 + return ( 41 + <RootDocument> 42 + <Outlet /> 43 + <ToastContainer /> 44 + </RootDocument> 45 + ); 10 46 } 11 47 12 48 function RootError({ error }: Readonly<{ error: Error }>) { 13 49 const router = useRouter(); 14 50 15 51 return ( 16 - <div className="bg-base-300 flex min-h-screen items-center justify-center font-sans"> 17 - <div className="card card-bordered bg-base-100 max-w-md p-8 text-center"> 18 - <h1 className="text-error mb-2 text-lg font-medium">Something went wrong</h1> 19 - <p className="text-text-muted mb-6 text-sm">{error.message}</p> 20 - <button 21 - onClick={() => { 22 - void router.invalidate(); 23 - }} 24 - className="btn btn-neutral btn-sm" 25 - > 26 - Try again 27 - </button> 52 + <RootDocument> 53 + <div className="bg-base-300 flex min-h-screen items-center justify-center font-sans"> 54 + <div className="card card-bordered bg-base-100 max-w-md p-8 text-center"> 55 + <h1 className="text-error mb-2 text-lg font-medium">Something went wrong</h1> 56 + <p className="text-text-muted mb-6 text-sm">{error.message}</p> 57 + <button 58 + onClick={() => { 59 + void router.invalidate(); 60 + }} 61 + className="btn btn-neutral btn-sm" 62 + > 63 + Try again 64 + </button> 65 + </div> 28 66 </div> 29 - </div> 67 + </RootDocument> 30 68 ); 31 69 } 32 70 33 71 export const Route = createRootRouteWithContext<RouterContext>()({ 72 + head: () => ({ 73 + meta: [ 74 + { title: "Opake" }, 75 + { name: "og:site_name", content: "Opake" }, 76 + { name: "og:type", content: "website" }, 77 + { name: "twitter:card", content: "summary" }, 78 + ], 79 + }), 34 80 component: RootLayout, 35 81 errorComponent: RootError, 36 82 });
+9 -2
web/src/routes/cabinet/route.tsx
··· 3 3 import { Sidebar } from "@/components/cabinet/Sidebar"; 4 4 import { TopBar } from "@/components/cabinet/TopBar"; 5 5 import { useDocumentsStore } from "@/stores/documents"; 6 + import { useAuthStore } from "@/stores/auth"; 6 7 7 8 function CabinetLayout() { 8 9 const [searchQuery, setSearchQuery] = useState(""); ··· 24 25 } 25 26 26 27 export const Route = createFileRoute("/cabinet")({ 27 - beforeLoad: ({ context }) => { 28 - if (context.auth.session.status !== "active") { 28 + ssr: false, 29 + beforeLoad: async () => { 30 + // Wait for auth boot if still initializing (IndexedDB session restore) 31 + const state = useAuthStore.getState(); 32 + if (state.session.status === "initializing") { 33 + await state.boot(); 34 + } 35 + if (useAuthStore.getState().session.status !== "active") { 29 36 throw redirect({ to: "/devices/login" }); 30 37 } 31 38 },
+142 -35
web/src/routes/cabinet/settings.tsx
··· 1 + import { useCallback, useEffect, useState } from "react"; 1 2 import { createFileRoute } from "@tanstack/react-router"; 2 - import { 3 - UserIcon, 4 - LockIcon, 5 - ShareNetworkIcon, 6 - ShieldCheckIcon, 7 - BellIcon, 8 - CaretRightIcon, 9 - } from "@phosphor-icons/react"; 3 + import { UserIcon, GlobeIcon, FloppyDiskIcon } from "@phosphor-icons/react"; 10 4 import { PanelShell } from "@/components/cabinet/PanelShell"; 5 + import { useAuthStore } from "@/stores/auth"; 6 + import { IndexedDbStorage } from "@/lib/indexeddbStorage"; 7 + import { truncateDid } from "@/lib/format"; 8 + import { toastSuccess, toastError } from "@/stores/toast"; 9 + import type { Config } from "@/lib/storageTypes"; 10 + 11 + // --------------------------------------------------------------------------- 12 + // Helpers 13 + // --------------------------------------------------------------------------- 14 + 15 + const storage = new IndexedDbStorage(); 16 + 17 + // --------------------------------------------------------------------------- 18 + // AppView URL Section 19 + // --------------------------------------------------------------------------- 20 + 21 + function AppViewUrlSection() { 22 + const [appviewUrl, setAppviewUrl] = useState(""); 23 + const [savedUrl, setSavedUrl] = useState(""); 24 + const [saving, setSaving] = useState(false); 25 + 26 + useEffect(() => { 27 + storage 28 + .loadConfig() 29 + .then((config) => { 30 + const url = config.appviewUrl ?? ""; 31 + setAppviewUrl(url); 32 + setSavedUrl(url); 33 + }) 34 + .catch(() => { 35 + // No config yet — leave empty 36 + }); 37 + }, []); 38 + 39 + const isDirty = appviewUrl !== savedUrl; 11 40 12 - const SETTINGS_SECTIONS = [ 13 - { label: "Account & Identity", desc: "DID: did:plc:7f2ab3c4d\u20268e91f0", icon: UserIcon }, 14 - { label: "Encryption Keys", desc: "Last rotated 14 days ago \u00b7 Active", icon: LockIcon }, 15 - { 16 - label: "Sharing & Permissions", 17 - desc: "3 active collaborators", 18 - icon: ShareNetworkIcon, 19 - }, 20 - { label: "Connected Devices", desc: "2 devices linked", icon: ShieldCheckIcon }, 21 - { label: "Notifications", desc: "Email & in-app alerts", icon: BellIcon }, 22 - ]; 41 + const handleSave = useCallback(async () => { 42 + setSaving(true); 43 + try { 44 + const config: Config = await storage.loadConfig().catch( 45 + (): Config => ({ 46 + defaultDid: null, 47 + accounts: {}, 48 + appviewUrl: null, 49 + }), 50 + ); 51 + const updated: Config = { 52 + ...config, 53 + appviewUrl: appviewUrl.trim() || null, 54 + }; 55 + await storage.saveConfig(updated); 56 + setSavedUrl(appviewUrl); 57 + toastSuccess("AppView URL saved"); 58 + } catch (error) { 59 + const message = error instanceof Error ? error.message : "Failed to save"; 60 + toastError(message); 61 + } finally { 62 + setSaving(false); 63 + } 64 + }, [appviewUrl]); 65 + 66 + return ( 67 + <div className="card card-bordered border-base-300/50 bg-base-100 p-4"> 68 + <div className="mb-3 flex items-center gap-2.5"> 69 + <div className="bg-bg-stone flex size-8 shrink-0 items-center justify-center rounded-lg"> 70 + <GlobeIcon size={14} className="text-text-muted" /> 71 + </div> 72 + <div> 73 + <div className="text-ui text-base-content font-medium">AppView URL</div> 74 + <div className="text-caption text-text-muted"> 75 + The AppView endpoint used for indexing and search. 76 + </div> 77 + </div> 78 + </div> 79 + 80 + <div className="flex items-center gap-2"> 81 + <input 82 + type="url" 83 + placeholder="https://appview.opake.app" 84 + value={appviewUrl} 85 + onChange={(e) => setAppviewUrl(e.target.value)} 86 + className="input input-bordered input-sm border-base-300/50 bg-base-200/50 text-ui flex-1" 87 + aria-label="AppView URL" 88 + /> 89 + <button 90 + onClick={() => void handleSave()} 91 + disabled={!isDirty || saving} 92 + className="btn btn-primary btn-sm gap-1.5" 93 + > 94 + <FloppyDiskIcon size={13} /> 95 + {saving ? "Saving…" : "Save"} 96 + </button> 97 + </div> 98 + </div> 99 + ); 100 + } 101 + 102 + // --------------------------------------------------------------------------- 103 + // Account Section 104 + // --------------------------------------------------------------------------- 105 + 106 + function AccountSection() { 107 + const session = useAuthStore((s) => s.session); 108 + 109 + if (session.status !== "active") return null; 110 + 111 + return ( 112 + <div className="card card-bordered border-base-300/50 bg-base-100 p-4"> 113 + <div className="mb-3 flex items-center gap-2.5"> 114 + <div className="bg-bg-stone flex size-8 shrink-0 items-center justify-center rounded-lg"> 115 + <UserIcon size={14} className="text-text-muted" /> 116 + </div> 117 + <div> 118 + <div className="text-ui text-base-content font-medium">Account</div> 119 + <div className="text-caption text-text-muted">Your AT Protocol identity</div> 120 + </div> 121 + </div> 122 + 123 + <dl className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-1.5 text-xs"> 124 + <dt className="text-text-faint font-medium">Handle</dt> 125 + <dd className="text-base-content font-mono">{session.handle}</dd> 126 + 127 + <dt className="text-text-faint font-medium">DID</dt> 128 + <dd className="text-base-content font-mono" title={session.did}> 129 + {truncateDid(session.did)} 130 + </dd> 131 + 132 + <dt className="text-text-faint font-medium">PDS</dt> 133 + <dd className="text-base-content font-mono">{session.pdsUrl}</dd> 134 + </dl> 135 + </div> 136 + ); 137 + } 138 + 139 + // --------------------------------------------------------------------------- 140 + // Settings Page 141 + // --------------------------------------------------------------------------- 23 142 24 143 function SettingsPage() { 25 144 const breadcrumbs = ( ··· 40 159 <div className="text-text-muted text-xs">Manage your account, keys, and preferences.</div> 41 160 </div> 42 161 <div className="divider mt-0 mb-4" /> 43 - <div className="flex flex-col gap-1.5"> 44 - {SETTINGS_SECTIONS.map(({ label, desc, icon: Icon }) => ( 45 - <div 46 - key={label} 47 - className="card card-bordered border-base-300/50 bg-base-100 cursor-pointer p-3.5" 48 - > 49 - <div className="bg-bg-stone flex size-8 shrink-0 items-center justify-center rounded-lg"> 50 - <Icon size={14} className="text-text-muted" /> 51 - </div> 52 - <div className="flex-1"> 53 - <div className="text-ui text-base-content font-medium">{label}</div> 54 - <div className="text-caption text-text-muted">{desc}</div> 55 - </div> 56 - <CaretRightIcon size={13} className="text-text-faint" /> 57 - </div> 58 - ))} 162 + 163 + <div className="flex flex-col gap-3"> 164 + <AccountSection /> 165 + <AppViewUrlSection /> 59 166 </div> 60 167 </div> 61 168 </PanelShell>
+180 -7
web/src/routes/cabinet/shared.tsx
··· 1 + import { useCallback, useEffect, useState } from "react"; 1 2 import { createFileRoute } from "@tanstack/react-router"; 2 - import { UsersIcon } from "@phosphor-icons/react"; 3 + import { UsersIcon, ShareNetworkIcon, TrashIcon, ArrowSquareOutIcon } from "@phosphor-icons/react"; 3 4 import { PanelShell } from "@/components/cabinet/PanelShell"; 5 + import { useAuthStore } from "@/stores/auth"; 6 + import { IndexedDbStorage } from "@/lib/indexeddbStorage"; 7 + import { truncateDid, formatShortDate } from "@/lib/format"; 8 + import { 9 + listOutgoingGrants, 10 + listIncomingGrants, 11 + revokeGrant, 12 + type GrantEntry, 13 + type InboxGrantItem, 14 + } from "@/lib/sharing"; 15 + import type { OAuthSession } from "@/lib/storageTypes"; 16 + import { toastSuccess, toastError } from "@/stores/toast"; 17 + 18 + const storage = new IndexedDbStorage(); 19 + 20 + // --------------------------------------------------------------------------- 21 + // Outgoing Grants Section 22 + // --------------------------------------------------------------------------- 23 + 24 + function OutgoingGrantsSection({ 25 + grants, 26 + onRevoke, 27 + }: { 28 + readonly grants: readonly GrantEntry[]; 29 + readonly onRevoke: (uri: string) => void; 30 + }) { 31 + if (grants.length === 0) return null; 32 + 33 + return ( 34 + <section className="mb-6"> 35 + <h3 className="text-label text-text-faint mb-2 ml-1 tracking-widest uppercase"> 36 + Shared by you 37 + </h3> 38 + <div className="flex flex-col gap-1"> 39 + {grants.map((grant) => ( 40 + <div 41 + key={grant.uri} 42 + className="hover:bg-bg-hover flex items-center gap-3 rounded-xl px-3 py-2.5 transition-colors" 43 + > 44 + <div className="bg-primary/10 flex size-8 shrink-0 items-center justify-center rounded-lg"> 45 + <ShareNetworkIcon size={14} className="text-primary" /> 46 + </div> 47 + <div className="min-w-0 flex-1"> 48 + <div className="text-ui text-base-content truncate"> 49 + → {truncateDid(grant.record.recipient)} 50 + </div> 51 + <div className="text-caption text-text-faint mt-0.5"> 52 + {formatShortDate(grant.record.createdAt)} 53 + </div> 54 + </div> 55 + <button 56 + onClick={() => onRevoke(grant.uri)} 57 + className="btn btn-ghost btn-xs btn-square rounded-md" 58 + aria-label={`Revoke grant to ${grant.record.recipient}`} 59 + > 60 + <TrashIcon size={13} className="text-error" /> 61 + </button> 62 + </div> 63 + ))} 64 + </div> 65 + </section> 66 + ); 67 + } 68 + 69 + // --------------------------------------------------------------------------- 70 + // Incoming Grants Section 71 + // --------------------------------------------------------------------------- 72 + 73 + function IncomingGrantsSection({ grants }: { readonly grants: readonly InboxGrantItem[] }) { 74 + if (grants.length === 0) return null; 75 + 76 + return ( 77 + <section className="mb-6"> 78 + <h3 className="text-label text-text-faint mb-2 ml-1 tracking-widest uppercase"> 79 + Shared with you 80 + </h3> 81 + <div className="flex flex-col gap-1"> 82 + {grants.map((grant) => ( 83 + <div 84 + key={grant.uri} 85 + className="hover:bg-bg-hover flex items-center gap-3 rounded-xl px-3 py-2.5 transition-colors" 86 + > 87 + <div className="bg-success/10 flex size-8 shrink-0 items-center justify-center rounded-lg"> 88 + <UsersIcon size={14} className="text-success" /> 89 + </div> 90 + <div className="min-w-0 flex-1"> 91 + <div className="text-ui text-base-content truncate"> 92 + From {truncateDid(grant.ownerDid)} 93 + </div> 94 + <div className="text-caption text-text-faint mt-0.5"> 95 + {formatShortDate(grant.createdAt)} 96 + </div> 97 + </div> 98 + <ArrowSquareOutIcon size={13} className="text-text-faint shrink-0" /> 99 + </div> 100 + ))} 101 + </div> 102 + </section> 103 + ); 104 + } 105 + 106 + // --------------------------------------------------------------------------- 107 + // Page 108 + // --------------------------------------------------------------------------- 4 109 5 110 function SharedPage() { 111 + const session = useAuthStore((s) => s.session); 112 + const [outgoing, setOutgoing] = useState<GrantEntry[]>([]); 113 + const [incoming, setIncoming] = useState<InboxGrantItem[]>([]); 114 + const [loading, setLoading] = useState(true); 115 + 116 + const fetchGrants = useCallback(async () => { 117 + if (session.status !== "active") { 118 + setLoading(false); 119 + return; 120 + } 121 + 122 + setLoading(true); 123 + try { 124 + const oauthSession = (await storage.loadSession(session.did)) as OAuthSession; 125 + 126 + const [out, inc] = await Promise.all([ 127 + listOutgoingGrants(session.pdsUrl, session.did, oauthSession), 128 + listIncomingGrants(session.did), 129 + ]); 130 + 131 + setOutgoing(out); 132 + setIncoming(inc); 133 + } catch (error) { 134 + console.error("[shared] failed to load grants:", error); 135 + } finally { 136 + setLoading(false); 137 + } 138 + }, [session]); 139 + 140 + useEffect(() => { 141 + void fetchGrants(); 142 + }, [fetchGrants]); 143 + 144 + const handleRevoke = useCallback( 145 + async (grantUri: string) => { 146 + if (session.status !== "active") return; 147 + 148 + try { 149 + const oauthSession = (await storage.loadSession(session.did)) as OAuthSession; 150 + await revokeGrant(session.pdsUrl, session.did, grantUri, oauthSession); 151 + setOutgoing((prev) => prev.filter((g) => g.uri !== grantUri)); 152 + toastSuccess("Grant revoked"); 153 + } catch (error) { 154 + const message = error instanceof Error ? error.message : "Failed to revoke"; 155 + toastError(message); 156 + } 157 + }, 158 + [session], 159 + ); 160 + 6 161 const breadcrumbs = ( 7 162 <div className="breadcrumbs text-ui min-w-0 flex-1 overflow-hidden"> 8 163 <ul> ··· 12 167 </ul> 13 168 </div> 14 169 ); 170 + 171 + const isEmpty = outgoing.length === 0 && incoming.length === 0; 15 172 16 173 return ( 17 174 <PanelShell depth={1} breadcrumbs={breadcrumbs} footer="Shared items · Encrypted"> ··· 31 188 </div> 32 189 </div> 33 190 34 - <div className="hero py-16"> 35 - <div className="hero-content flex-col text-center"> 36 - <div className="bg-bg-sage flex size-13 items-center justify-center rounded-[14px]"> 37 - <UsersIcon size={22} className="text-success" /> 191 + <div className="p-4"> 192 + {loading ? ( 193 + <div className="flex justify-center py-12"> 194 + <span className="loading loading-spinner loading-md text-text-faint" /> 38 195 </div> 39 - <div className="text-ui text-text-muted">No shared files yet</div> 40 - </div> 196 + ) : isEmpty ? ( 197 + <div className="hero py-16"> 198 + <div className="hero-content flex-col text-center"> 199 + <div className="bg-bg-sage flex size-13 items-center justify-center rounded-[14px]"> 200 + <UsersIcon size={22} className="text-success" /> 201 + </div> 202 + <div className="text-ui text-text-muted">No shared files yet</div> 203 + <div className="text-caption text-text-faint max-w-xs"> 204 + Share files from the file action menu to grant access to other Opake users. 205 + </div> 206 + </div> 207 + </div> 208 + ) : ( 209 + <> 210 + <OutgoingGrantsSection grants={outgoing} onRevoke={(uri) => void handleRevoke(uri)} /> 211 + <IncomingGrantsSection grants={incoming} /> 212 + </> 213 + )} 41 214 </div> 42 215 </PanelShell> 43 216 );
+6 -2
web/src/routes/devices/index.tsx
··· 13 13 const MIN_CHECK_DISPLAY_MS = 2000; 14 14 15 15 export const Route = createFileRoute("/devices/")({ 16 - beforeLoad: ({ context }) => { 17 - if (context.auth.session.status !== "active") { 16 + beforeLoad: async () => { 17 + const state = useAuthStore.getState(); 18 + if (state.session.status === "initializing") { 19 + await state.boot(); 20 + } 21 + if (useAuthStore.getState().session.status !== "active") { 18 22 throw redirect({ to: "/devices/login" }); 19 23 } 20 24 },
+8 -2
web/src/routes/devices/login.tsx
··· 46 46 } 47 47 48 48 export const Route = createFileRoute("/devices/login")({ 49 - beforeLoad: ({ context }) => { 50 - if (context.auth.session.status === "active") throw redirect({ to: "/devices" }); 49 + beforeLoad: async () => { 50 + const state = useAuthStore.getState(); 51 + if (state.session.status === "initializing") { 52 + await state.boot(); 53 + } 54 + if (useAuthStore.getState().session.status === "active") { 55 + throw redirect({ to: "/devices" }); 56 + } 51 57 }, 52 58 component: LoginPage, 53 59 });
+8 -2
web/src/routes/devices/oauth-callback.tsx
··· 78 78 } 79 79 80 80 export const Route = createFileRoute("/devices/oauth-callback")({ 81 - beforeLoad: ({ context }) => { 82 - if (context.auth.session.status === "active") throw redirect({ to: "/devices" }); 81 + beforeLoad: async () => { 82 + const state = useAuthStore.getState(); 83 + if (state.session.status === "initializing") { 84 + await state.boot(); 85 + } 86 + if (useAuthStore.getState().session.status === "active") { 87 + throw redirect({ to: "/devices" }); 88 + } 83 89 }, 84 90 component: OAuthCallbackPage, 85 91 });
+6 -2
web/src/routes/devices/pair.accept.tsx
··· 18 18 // --------------------------------------------------------------------------- 19 19 20 20 export const Route = createFileRoute("/devices/pair/accept")({ 21 - beforeLoad: ({ context }) => { 22 - if (context.auth.session.status !== "active") { 21 + beforeLoad: async () => { 22 + const state = useAuthStore.getState(); 23 + if (state.session.status === "initializing") { 24 + await state.boot(); 25 + } 26 + if (useAuthStore.getState().session.status !== "active") { 23 27 throw redirect({ to: "/devices/login" }); 24 28 } 25 29 },
+6 -2
web/src/routes/devices/pair.request.tsx
··· 22 22 // --------------------------------------------------------------------------- 23 23 24 24 export const Route = createFileRoute("/devices/pair/request")({ 25 - beforeLoad: ({ context }) => { 26 - if (context.auth.session.status !== "active") { 25 + beforeLoad: async () => { 26 + const state = useAuthStore.getState(); 27 + if (state.session.status === "initializing") { 28 + await state.boot(); 29 + } 30 + if (useAuthStore.getState().session.status !== "active") { 27 31 throw redirect({ to: "/devices/login" }); 28 32 } 29 33 },
+1
web/src/routes/devices/route.tsx
··· 8 8 const MIN_CHECK_DISPLAY_MS = 2000; 9 9 10 10 export const Route = createFileRoute("/devices")({ 11 + ssr: false, 11 12 component: View, 12 13 errorComponent: ErrorView, 13 14 });
+17 -5
web/src/routes/index.tsx
··· 1 1 import { createFileRoute, Link } from "@tanstack/react-router"; 2 2 import { ArrowRightIcon } from "@phosphor-icons/react"; 3 3 import { OpakeLogo } from "@/components/OpakeLogo"; 4 + import LandingContent from "@/content/landing.mdx"; 4 5 5 6 function LandingPage() { 6 7 return ( ··· 27 28 privately kept. 28 29 </h1> 29 30 30 - <p className="text-secondary mb-10 max-w-130 text-center text-[1.05rem] leading-[1.75]"> 31 - Opake exists because privacy and collaboration should not be a tradeoff. Your files — 32 - encrypted, owned, shared on your terms — through decentralised identity, with no central 33 - authority in between. 34 - </p> 31 + <div className="prose text-secondary mb-10 max-w-130 text-center text-[1.05rem] leading-[1.75]"> 32 + <LandingContent /> 33 + </div> 35 34 36 35 <div className="flex items-center gap-3.5"> 37 36 <Link ··· 53 52 ); 54 53 } 55 54 55 + const DESCRIPTION = 56 + "Encrypted personal cloud built on the AT Protocol. Your files — encrypted, owned, shared on your terms."; 57 + 56 58 export const Route = createFileRoute("/")({ 59 + head: () => ({ 60 + meta: [ 61 + { title: "Opake — Your data, freely shared, privately kept" }, 62 + { name: "description", content: DESCRIPTION }, 63 + { name: "og:title", content: "Opake — Your data, freely shared, privately kept" }, 64 + { name: "og:description", content: DESCRIPTION }, 65 + { name: "twitter:title", content: "Opake — Your data, freely shared, privately kept" }, 66 + { name: "twitter:description", content: DESCRIPTION }, 67 + ], 68 + }), 57 69 component: LandingPage, 58 70 });
+1 -1
web/tsconfig.json
··· 17 17 "@/*": ["./src/*"] 18 18 } 19 19 }, 20 - "include": ["src", "tests"], 20 + "include": ["src"], 21 21 "references": [{ "path": "./tsconfig.node.json" }] 22 22 }
+7
web/tsconfig.test.json
··· 1 + { 2 + "extends": "./tsconfig.json", 3 + "compilerOptions": { 4 + "types": ["vite/client", "node"] 5 + }, 6 + "include": ["src", "tests"] 7 + }
+8 -4
web/vite.config.ts
··· 1 1 import { defineConfig } from "vite"; 2 - import { TanStackRouterVite } from "@tanstack/router-plugin/vite"; 2 + import { tanstackStart } from "@tanstack/react-start/plugin/vite"; 3 3 import tailwindcss from "@tailwindcss/vite"; 4 4 import { comlink } from "vite-plugin-comlink"; 5 5 import wasm from "vite-plugin-wasm"; 6 - import react from "@vitejs/plugin-react"; 6 + import mdx from "@mdx-js/rollup"; 7 + import remarkGfm from "remark-gfm"; 7 8 8 9 export default defineConfig({ 9 10 plugins: [ 10 - TanStackRouterVite({ target: "react", autoCodeSplitting: true }), 11 + // MDX must run before React transform 12 + { enforce: "pre" as const, ...mdx({ remarkPlugins: [remarkGfm] }) }, 11 13 tailwindcss(), 12 14 wasm(), 13 15 comlink(), 14 - react(), 16 + // tanstackStart() replaces TanStackRouterVite() + react() 17 + tanstackStart(), 15 18 ], 16 19 worker: { 20 + format: "es", 17 21 plugins: () => [wasm(), comlink()], 18 22 }, 19 23 resolve: {