Client side atproto account migrator in your web browser, along with services for backups and adversarial migrations.

Frontend in Svelte

Changed files
+8655 -6516
Dockerfiles
ProductionComposes
admin_cli
cron-worker
lexicon_types
packages
shared
web
web-ui
+3 -2
.dockerignore
··· 1 - target 1 + */target 2 2 **/target 3 3 **/.idea 4 4 .idea 5 - .env 5 + .env 6 + */node_modules
+4 -82
Cargo.lock
··· 229 229 checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" 230 230 231 231 [[package]] 232 - name = "askama" 233 - version = "0.14.0" 234 - source = "registry+https://github.com/rust-lang/crates.io-index" 235 - checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4" 236 - dependencies = [ 237 - "askama_derive", 238 - "itoa", 239 - "percent-encoding", 240 - "serde", 241 - "serde_json", 242 - ] 243 - 244 - [[package]] 245 - name = "askama_derive" 246 - version = "0.14.0" 247 - source = "registry+https://github.com/rust-lang/crates.io-index" 248 - checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f" 249 - dependencies = [ 250 - "askama_parser", 251 - "basic-toml", 252 - "memchr", 253 - "proc-macro2", 254 - "quote", 255 - "rustc-hash", 256 - "serde", 257 - "serde_derive", 258 - "syn 2.0.106", 259 - ] 260 - 261 - [[package]] 262 - name = "askama_parser" 263 - version = "0.14.0" 264 - source = "registry+https://github.com/rust-lang/crates.io-index" 265 - checksum = "d6ab5630b3d5eaf232620167977f95eb51f3432fc76852328774afbd242d4358" 266 - dependencies = [ 267 - "memchr", 268 - "serde", 269 - "serde_derive", 270 - "winnow 0.7.13", 271 - ] 272 - 273 - [[package]] 274 232 name = "async-compression" 275 233 version = "0.4.32" 276 234 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 511 469 checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" 512 470 513 471 [[package]] 514 - name = "basic-toml" 515 - version = "0.1.10" 516 - source = "registry+https://github.com/rust-lang/crates.io-index" 517 - checksum = "ba62675e8242a4c4e806d12f11d136e626e6c8361d6b829310732241652a178a" 518 - dependencies = [ 519 - "serde", 520 - ] 521 - 522 - [[package]] 523 472 name = "bigdecimal" 524 473 version = "0.4.8" 525 474 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1017 966 dependencies = [ 1018 967 "chrono", 1019 968 "once_cell", 1020 - "winnow 0.6.26", 969 + "winnow", 1021 970 ] 1022 971 1023 972 [[package]] ··· 1027 976 "apalis", 1028 977 "apalis-cron", 1029 978 "apalis-sql", 1030 - "chrono", 1031 979 "dotenvy", 1032 980 "log", 1033 - "serde", 1034 981 "shared", 1035 982 "tokio", 1036 983 "tower", 1037 - "tracing", 1038 984 "tracing-subscriber", 1039 985 ] 1040 986 ··· 2934 2880 "dotenvy", 2935 2881 "env_logger", 2936 2882 "jacquard", 2937 - "jacquard-api", 2938 2883 "jacquard-common", 2939 2884 "lexicon_types_crate", 2940 2885 "log", 2941 2886 "reqwest", 2887 + "rust-s3", 2888 + "shared", 2889 + "sqlx", 2942 2890 "tokio", 2943 2891 ] 2944 2892 ··· 5324 5272 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 5325 5273 5326 5274 [[package]] 5327 - name = "vite-rust" 5328 - version = "0.2.4" 5329 - source = "registry+https://github.com/rust-lang/crates.io-index" 5330 - checksum = "4ae9a5cd007a52d1227f4de7488c9da0a46b7946b3e9803025677e797ffe6f41" 5331 - dependencies = [ 5332 - "hex", 5333 - "log", 5334 - "md-5", 5335 - "regex", 5336 - "reqwest", 5337 - "serde", 5338 - "serde_json", 5339 - ] 5340 - 5341 - [[package]] 5342 5275 name = "walkdir" 5343 5276 version = "2.5.0" 5344 5277 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5470 5403 "anyhow", 5471 5404 "apalis", 5472 5405 "apalis-sql", 5473 - "askama", 5474 5406 "async-compression", 5475 5407 "atproto-identity", 5476 5408 "axum", ··· 5500 5432 "tower_governor", 5501 5433 "tracing", 5502 5434 "tracing-subscriber", 5503 - "vite-rust", 5504 5435 ] 5505 5436 5506 5437 [[package]] ··· 6066 5997 version = "0.6.26" 6067 5998 source = "registry+https://github.com/rust-lang/crates.io-index" 6068 5999 checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" 6069 - dependencies = [ 6070 - "memchr", 6071 - ] 6072 - 6073 - [[package]] 6074 - name = "winnow" 6075 - version = "0.7.13" 6076 - source = "registry+https://github.com/rust-lang/crates.io-index" 6077 - checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" 6078 6000 dependencies = [ 6079 6001 "memchr", 6080 6002 ]
+18
Dockerfiles/web-ui.Dockerfile
··· 1 + FROM node:24-slim as builder 2 + ENV PNPM_HOME="/pnpm" 3 + ENV PATH="$PNPM_HOME:$PATH" 4 + RUN corepack enable 5 + WORKDIR /app 6 + 7 + COPY ./web-ui /app 8 + RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install 9 + RUN pnpm run build 10 + 11 + FROM node:24-alpine3.22 as web-ui 12 + WORKDIR /app 13 + 14 + COPY --from=builder /app/build /app/build 15 + COPY --from=builder /app/package*.json /app/ 16 + RUN npm install --omit=dev 17 + 18 + CMD ["node", "build"]
-10
Dockerfiles/web.Dockerfile
··· 1 - FROM node:24-alpine3.21 as node_builder 2 - WORKDIR /app 3 - 4 - COPY ./web/ui-code/ /app 5 - #Not a fan of the --force here 6 - RUN npm install --force 7 - RUN npm run build 8 - 9 1 FROM rust:1.90.0-bookworm AS builder 10 2 WORKDIR /app 11 3 COPY . /app 12 4 RUN cargo build --bin web --release 13 5 14 6 FROM rust:1.90.0-bookworm AS web 15 - COPY --from=node_builder /app/dist /app/vite/dist 16 - COPY ./web/public /app/public 17 7 COPY --from=builder /app/target/release/web /usr/local/bin/web 18 8 19 9 CMD ["web"]
+16
ProductionComposes/Caddyfile
··· 1 + :80 { 2 + @xrpc { 3 + path /xrpc/* 4 + path /.well-known/* 5 + } 6 + 7 + # Handle /xrpc/ routes - forward to the API backend (web service) 8 + handle @xrpc { 9 + reverse_proxy localhost:3000 10 + } 11 + 12 + # Handle all other routes - forward to the web-ui frontend 13 + handle /* { 14 + reverse_proxy localhost:3001 15 + } 16 + }
+34 -2
ProductionComposes/compose.web.yml
··· 1 1 services: 2 2 web: 3 3 image: fatfingers23/moover_web:latest 4 - network_mode: host 5 4 restart: unless-stopped 5 + network_mode: host 6 6 ports: 7 7 - "3000:3000" 8 8 env_file: ··· 11 11 environment: 12 12 - VITE_DIST=/app/vite/dist 13 13 - PUBLIC_ASSETS=/app/public 14 + web-ui: 15 + image: fatfingers23/moover_ui:latest 16 + network_mode: host 17 + restart: unless-stopped 18 + ports: 19 + - "3001:3001" 20 + environment: 21 + - PORT=3001 22 + - PUBLIC_XRPC_BASE=pdsmoover.com 23 + caddy: 24 + image: caddy:2-alpine 25 + network_mode: host 26 + restart: unless-stopped 27 + ports: 28 + - "80:80" 29 + - "443:443" 30 + volumes: 31 + - ./Caddyfile:/etc/caddy/Caddyfile 32 + - caddy_data:/data 33 + - caddy_config:/config 34 + depends_on: 35 + - web 36 + - web-ui 14 37 #Only a single one of these instances should be running 15 38 #it's what triggers daily backups 16 39 cron-worker: 17 40 image: fatfingers23/moover_cron_worker:latest 18 41 network_mode: host 42 + restart: unless-stopped 19 43 build: 20 44 context: . 21 45 dockerfile: ./Dockerfiles/cron-worker.Dockerfile 22 46 env_file: 23 47 - path: .env 24 - required: true 48 + required: true 49 + 50 + networks: 51 + moover_network: 52 + driver: bridge 53 + 54 + volumes: 55 + caddy_data: 56 + caddy_config:
+2
ProductionComposes/compose.workers.yml
··· 11 11 worker-two: 12 12 image: fatfingers23/moover_worker:latest 13 13 network_mode: host 14 + restart: unless-stopped 14 15 env_file: 15 16 - path: .env 16 17 required: true ··· 19 20 worker-three: 20 21 image: fatfingers23/moover_worker:latest 21 22 network_mode: host 23 + restart: unless-stopped 22 24 env_file: 23 25 - path: .env 24 26 required: true
+6 -3
README.md
··· 3 3 A series of web tools to help users: migrate to a new PDS, find missing blobs, have free backups, and restore from 4 4 backups in the event of emergency. [pdsmoover.com](https://pdsmoover.com) 5 5 6 - ![cow](./web/public/halloween_moover.webp) 6 + ![cow](./web-ui/src/lib/assets/moo.webp) 7 7 8 8 A little light on documentation as I come off of about a week long crunch but. 9 9 ··· 23 23 - [cron-worker](./cron-worker) - Very simple binary to tick every hour telling the main worker to check for repos that 24 24 need an update 25 25 - [Dockerfiles](./Dockerfiles) - Dockerfiles for all the services in the repo 26 - - [lexicon_types](./lexicon_types) - TypeScript types for PDS MOOver lexicons (not currently in use) 26 + - [packages/lexicons](./packages/lexicons) - TypeScript types for PDS MOOver lexicons 27 + - [packages/moover](./packages/moover) - Frontend logic that handles all the atproto processes. Also published as a node 28 + module 29 + at [@pds-moover/moover](https://www.npmjs.com/package/@pds-moover/moover) 27 30 - [lexicon_types_crate](./lexicon_types_crate) - Rust lexicon types 28 31 - [lexicons](./lexicons) - JSON Lexicons 29 32 - [ProductionComposes](./ProductionComposes) - What I use to run PDS MOOver in production. One instance of web behind a 30 33 load balancer, one worker node currently with 3 instances on that one server. All can scale horizontally 31 34 - [shared](./shared) - Shared code between all the services 32 35 - [web](./web) - The web frontend that servers XRPC endpoints and the frontend 33 - - [web-ui](./web/ui-code) - JS code to handle everything to do with atproto such as Migrations, missing blobs, signing 36 + - [web-ui](./web-ui) - Svelte frontend. 34 37 plc ops, restores, etc 35 38 - [worker](./worker) - What acutally handles all the backing up, but the actual logic is 36 39 in [./shared/src/jobs/](./shared/src/jobs/)
+4 -2
admin_cli/Cargo.toml
··· 12 12 env_logger = "0.11" 13 13 tokio = { version = "1", features = ["macros", "rt-multi-thread"] } 14 14 jacquard-common.workspace = true 15 - jacquard-api.workspace = true 16 15 jacquard.workspace = true 17 16 lexicon_types_crate.workspace = true 18 17 reqwest.workspace = true 19 - base64 = "0.22" 18 + base64 = "0.22" 19 + shared = { path = "../shared" } 20 + sqlx = { version = "0.8.6", features = ["runtime-tokio", "postgres"] } 21 + rust-s3.workspace = true
+57
admin_cli/src/main.rs
··· 11 11 use log; 12 12 use reqwest::header; 13 13 use reqwest::header::{HeaderMap, HeaderValue}; 14 + use s3::creds::Credentials; 15 + use s3::{Bucket, Region}; 16 + use sqlx::PgPool; 14 17 use std::env; 15 18 16 19 fn init_logging() { ··· 52 55 }, 53 56 /// Trigger an instance-wide backup job (no parameters) 54 57 RequestInstanceBackup, 58 + /// Verify all backups in S3 against the database 59 + VerifyBackups, 55 60 } 56 61 57 62 #[derive(Debug, Subcommand)] ··· 219 224 } 220 225 Err(err) => { 221 226 log::error!("Instance backup request failed: {}", err); 227 + } 228 + } 229 + } 230 + Commands::VerifyBackups => { 231 + //Not really a part of the cli per say. But I needed it and is a good place as any 232 + log::info!("Verifying backups in S3..."); 233 + 234 + // Get database URL from environment 235 + let database_url = 236 + env::var("DATABASE_URL").context("DATABASE_URL environment variable not set")?; 237 + 238 + // Connect to database 239 + let pool = PgPool::connect(&database_url) 240 + .await 241 + .context("Failed to connect to database")?; 242 + 243 + // Setup S3 client 244 + let region_name = env::var("S3_REGION")?; 245 + let endpoint = env::var("S3_ENDPOINT")?; 246 + let region = Region::Custom { 247 + region: region_name, 248 + endpoint, 249 + }; 250 + let bucket = Bucket::new( 251 + env::var("S3_BUCKET_NAME")?.as_str(), 252 + region, 253 + Credentials::new( 254 + Some(env::var("S3_ACCESS_KEY")?.as_str()), 255 + Some(env::var("S3_SECRET_KEY")?.as_str()), 256 + None, 257 + None, 258 + None, 259 + )?, 260 + )?; 261 + 262 + // Call the verify_backups function 263 + match shared::jobs::verify_backups::verify_backups(&pool, &bucket).await { 264 + Ok(missing_blobs) => { 265 + if missing_blobs.is_empty() { 266 + log::info!("✓ All backups verified successfully! No missing blobs found."); 267 + } else { 268 + log::error!("✗ Found {} missing blobs:", missing_blobs.len()); 269 + for missing in &missing_blobs { 270 + println!( 271 + "Missing: DID={}, CID/REV={}, TYPE={:?}, PATH={}", 272 + missing.did, missing.cid_or_rev, missing.blob_type, missing.s3_path 273 + ); 274 + } 275 + } 276 + } 277 + Err(err) => { 278 + log::error!("Failed to verify backups: {}", err); 222 279 } 223 280 } 224 281 }
+2 -2
compose.dev.yml
··· 1 1 services: 2 2 postgres: 3 - image: postgres:latest 3 + image: postgres:18 4 4 restart: unless-stopped 5 5 environment: 6 6 POSTGRES_USER: ${DB_USER} ··· 9 9 ports: 10 10 - "5432:5432" 11 11 volumes: 12 - - moove-postgres-data:/var/lib/postgresql/data 12 + - moove-postgres-data:/var/lib/postgresql 13 13 extra_hosts: 14 14 - "host.docker.internal:host-gateway" 15 15 networks:
+14
compose.selfhost.yml
··· 16 16 - PUBLIC_ASSETS=/app/public 17 17 depends_on: 18 18 - postgres 19 + web-ui: 20 + # image: fatfingers23/moover_web_ui:latest 21 + build: 22 + context: . 23 + dockerfile: ./Dockerfiles/web-ui.Dockerfile 24 + ports: 25 + - "3001:3001" 26 + env_file: 27 + - path: .env 28 + required: false 29 + environment: 30 + - PORT=3001 31 + networks: 32 + - moover-network 19 33 cron-worker: 20 34 image: fatfingers23/moover_cron_worker:latest 21 35 # build:
-3
cron-worker/Cargo.toml
··· 9 9 apalis-sql.workspace = true 10 10 apalis-cron.workspace = true 11 11 dotenvy.workspace = true 12 - tracing.workspace = true 13 12 tracing-subscriber.workspace = true 14 13 shared.workspace = true 15 14 log.workspace = true 16 - serde.workspace = true 17 - chrono.workspace = true 18 15 tower = { version = "0.5.2", features = ["load-shed"] } 19 16 tokio.workspace = true
+11 -3
justfile
··· 3 3 docker buildx build \ 4 4 --platform linux/arm64,linux/amd64 \ 5 5 --tag fatfingers23/moover_web:latest \ 6 - --tag fatfingers23/moover_web:0.0.1 \ 6 + --tag fatfingers23/moover_web:0.0.2 \ 7 7 --file Dockerfiles/web.Dockerfile \ 8 8 --push . 9 9 # Worker 10 10 docker buildx build \ 11 11 --platform linux/arm64,linux/amd64 \ 12 12 --tag fatfingers23/moover_worker:latest \ 13 - --tag fatfingers23/moover_worker:0.0.1 \ 13 + --tag fatfingers23/moover_worker:0.0.2 \ 14 14 --file Dockerfiles/worker.Dockerfile \ 15 15 --push . 16 16 #cron worker 17 17 docker buildx build \ 18 18 --platform linux/arm64,linux/amd64 \ 19 19 --tag fatfingers23/moover_cron_worker:latest \ 20 - --tag fatfingers23/moover_cron_worker:0.0.1 \ 20 + --tag fatfingers23/moover_cron_worker:0.0.2 \ 21 21 --file Dockerfiles/cron-worker.Dockerfile \ 22 + --push . 23 + 24 + #web ui 25 + docker buildx build \ 26 + --platform linux/arm64,linux/amd64 \ 27 + --tag fatfingers23/moover_ui:latest \ 28 + --tag fatfingers23/moover_ui:0.0.2 \ 29 + --file Dockerfiles/web-ui.Dockerfile \ 22 30 --push .
lexicon_types/.gitignore packages/lexicons/.gitignore
lexicon_types/bun.lock packages/lexicons/bun.lock
+2 -2
lexicon_types/lex.config.js packages/lexicons/lex.config.js
··· 2 2 import {defineLexiconConfig} from '@atcute/lex-cli'; 3 3 4 4 export default defineLexiconConfig({ 5 - files: ['../lexicons/**/*.json'], 6 - outdir: 'src/' 5 + files: ['../../lexicons/**/*.json'], 6 + outdir: 'lib/lexicons' 7 7 });
-31
lexicon_types/package.json
··· 1 - { 2 - "name": "@pds-moover/lexicon_types", 3 - "version": "0.0.0", 4 - "type": "module", 5 - "main": "./dist/lexicon_types.umd.cjs", 6 - "module": "./dist/lexicon_types.js", 7 - "types": "./dist/index.d.ts", 8 - "exports": { 9 - ".": { 10 - "import": "./dist/lexicon_types.js", 11 - "require": "./dist/lexicon_types.umd.cjs" 12 - } 13 - }, 14 - "scripts": { 15 - "dev": "vite", 16 - "build": "tsc && vite build", 17 - "preview": "vite preview" 18 - }, 19 - "devDependencies": { 20 - "@atcute/lex-cli": "^2.2.2", 21 - "@types/node": "^24.7.2", 22 - "typescript": "~5.9.3", 23 - "vite": "^7.1.7", 24 - "vite-plugin-dts": "^4.5.4" 25 - }, 26 - "dependencies": { 27 - "@atproto/lexicon": "^0.5.1", 28 - "@atproto/xrpc": "^0.7.5", 29 - "cid": "multiformats/cid" 30 - } 31 - }
lexicon_types/src/index.ts packages/lexicons/lib/lexicons/index.ts
lexicon_types/src/types/app/bsky/actor/defs.ts packages/lexicons/lib/lexicons/types/app/bsky/actor/defs.ts
lexicon_types/src/types/app/bsky/actor/getPreferences.ts packages/lexicons/lib/lexicons/types/app/bsky/actor/getPreferences.ts
lexicon_types/src/types/app/bsky/feed/postgate.ts packages/lexicons/lib/lexicons/types/app/bsky/feed/postgate.ts
lexicon_types/src/types/app/bsky/feed/threadgate.ts packages/lexicons/lib/lexicons/types/app/bsky/feed/threadgate.ts
lexicon_types/src/types/com/atproto/sync/getBlob.ts packages/lexicons/lib/lexicons/types/com/atproto/sync/getBlob.ts
lexicon_types/src/types/com/atproto/sync/getRepo.ts packages/lexicons/lib/lexicons/types/com/atproto/sync/getRepo.ts
lexicon_types/src/types/com/pdsmoover/admin/removePds.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/admin/removePds.ts
lexicon_types/src/types/com/pdsmoover/admin/removeRepo.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/admin/removeRepo.ts
-26
lexicon_types/src/types/com/pdsmoover/admin/requestBackup.ts
··· 1 - import type {} from "@atcute/lexicons"; 2 - import * as v from "@atcute/lexicons/validations"; 3 - import type {} from "@atcute/lexicons/ambient"; 4 - 5 - const _mainSchema = /*#__PURE__*/ v.procedure( 6 - "com.pdsmoover.admin.requestBackup", 7 - { 8 - params: null, 9 - input: null, 10 - output: null, 11 - }, 12 - ); 13 - 14 - type main$schematype = typeof _mainSchema; 15 - 16 - export interface mainSchema extends main$schematype {} 17 - 18 - export const mainSchema = _mainSchema as mainSchema; 19 - 20 - export interface $params {} 21 - 22 - declare module "@atcute/lexicons/ambient" { 23 - interface XRPCProcedures { 24 - "com.pdsmoover.admin.requestBackup": mainSchema; 25 - } 26 - }
lexicon_types/src/types/com/pdsmoover/admin/requestInstanceBackup.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/admin/requestInstanceBackup.ts
lexicon_types/src/types/com/pdsmoover/admin/requestPdsBackup.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/admin/requestPdsBackup.ts
lexicon_types/src/types/com/pdsmoover/admin/requestRepoBackup.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/admin/requestRepoBackup.ts
lexicon_types/src/types/com/pdsmoover/admin/signUpPds.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/admin/signUpPds.ts
lexicon_types/src/types/com/pdsmoover/backup/describeServer.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/backup/describeServer.ts
lexicon_types/src/types/com/pdsmoover/backup/getRepoStatus.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/backup/getRepoStatus.ts
lexicon_types/src/types/com/pdsmoover/backup/removeRepo.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/backup/removeRepo.ts
lexicon_types/src/types/com/pdsmoover/backup/requestBackup.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/backup/requestBackup.ts
lexicon_types/src/types/com/pdsmoover/backup/signUp.ts packages/lexicons/lib/lexicons/types/com/pdsmoover/backup/signUp.ts
-32
lexicon_types/tsconfig.json
··· 1 - { 2 - "compilerOptions": { 3 - "target": "ES2022", 4 - "useDefineForClassFields": true, 5 - "module": "ESNext", 6 - "lib": [ 7 - "ES2022", 8 - "DOM", 9 - "DOM.Iterable" 10 - ], 11 - "types": [ 12 - "vite/client" 13 - ], 14 - "skipLibCheck": true, 15 - /* Bundler mode */ 16 - "moduleResolution": "bundler", 17 - "allowImportingTsExtensions": true, 18 - "verbatimModuleSyntax": true, 19 - "moduleDetection": "force", 20 - "noEmit": true, 21 - /* Linting */ 22 - "strict": true, 23 - "noUnusedLocals": false, 24 - "noUnusedParameters": true, 25 - "erasableSyntaxOnly": true, 26 - "noFallthroughCasesInSwitch": true, 27 - "noUncheckedSideEffectImports": true 28 - }, 29 - "include": [ 30 - "src" 31 - ] 32 - }
-15
lexicon_types/vite.config.ts
··· 1 - import {resolve} from 'path'; 2 - import {defineConfig} from 'vite'; 3 - import dts from 'vite-plugin-dts'; 4 - 5 - // https://vitejs.dev/guide/build.html#library-mode 6 - export default defineConfig({ 7 - build: { 8 - lib: { 9 - entry: resolve(__dirname, 'src/index.ts'), 10 - name: 'lexicon_types', 11 - fileName: 'lexicon_types', 12 - }, 13 - }, 14 - plugins: [dts()], 15 - });
+1
packages/lexicons/lib/index.ts
··· 1 + export * from './lexicons/index.js'
+27
packages/lexicons/package.json
··· 1 + { 2 + "type": "module", 3 + "name": "@pds-moover/lexicons", 4 + "description": "PDS MOOver Lexicon Types", 5 + "version": "1.0.1", 6 + "license": "MIT", 7 + "repository": "https://tangled.org/@baileytownsend.dev/pds-moover", 8 + "files": [ 9 + "dist" 10 + ], 11 + "exports": { 12 + ".": "./dist/index.js", 13 + "./types/*": "./dist/lexicons/types/sh/tangled/*.js" 14 + }, 15 + "scripts": { 16 + "build": "tsc", 17 + "generate": "rm -r ./lib/lexicons/; lex-cli generate -c ./lex.config.js", 18 + "prepublishOnly": "rm -rf dist; npm run build" 19 + }, 20 + "devDependencies": { 21 + "@atcute/lex-cli": "^2.2.2" 22 + }, 23 + "dependencies": { 24 + "@atproto/lexicon": "^0.5.1", 25 + "@atproto/xrpc": "^0.7.5" 26 + } 27 + }
+25
packages/lexicons/tsconfig.json
··· 1 + { 2 + "compilerOptions": { 3 + "outDir": "dist/", 4 + "esModuleInterop": true, 5 + "skipLibCheck": true, 6 + "target": "ESNext", 7 + "allowJs": true, 8 + "resolveJsonModule": true, 9 + "moduleDetection": "force", 10 + "isolatedModules": true, 11 + "verbatimModuleSyntax": true, 12 + "strict": true, 13 + "noImplicitOverride": true, 14 + "noUnusedLocals": true, 15 + "noUnusedParameters": true, 16 + "noFallthroughCasesInSwitch": true, 17 + "module": "NodeNext", 18 + "sourceMap": true, 19 + "declaration": true, 20 + "declarationMap": true 21 + }, 22 + "include": [ 23 + "lib" 24 + ] 25 + }
+1
packages/moover/.npmrc
··· 1 + @atpkgs:registry=https://registry.atpkgs.easrng.net/
+15
packages/moover/README.md
··· 1 + # @pds-moover/moover 2 + 3 + ![cow](./images/moo.webp) 4 + 5 + This is the core logic that runs [PDS MOOver](https://pdsmoover.com). With this you should be able to create your own 6 + "PDS MOOver" without having 7 + to figure out the atproto logic. 8 + 9 + - [lib/atprotoUtils.js](./lib/atprotoUtils.js) - Helpers for atproto actions 10 + - [Migrator](./lib/pdsmoover.js) - For handling regular migrations 11 + - [BackupService](./lib/backup.js) - For signing up for backups, request a back up, and remove backups to a PDS MOOver 12 + instance 13 + - [MissingBlobs](./lib/missingBlobs.js) - Finds missing blobs on your old PDS and uploads them to your new PDS 14 + - [PlcOps](./lib/plc-ops.js) - Helpers for manual PCL operations 15 + - [Restore](./lib/restore.js) - Handles a recovery and restores the at proto from the backup
packages/moover/images/moo.webp

This is a binary file and will not be displayed.

+13
packages/moover/index.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"/> 5 + <link rel="icon" type="image/svg+xml" href="/vite.svg"/> 6 + <meta name="viewport" content="width=device-width, initial-scale=1.0"/> 7 + <title>moover</title> 8 + </head> 9 + <body> 10 + <div id="app"></div> 11 + <script type="module" src="/lib/main.js"></script> 12 + </body> 13 + </html>
+81
packages/moover/lib/atprotoUtils.js
··· 1 + import { 2 + CompositeDidDocumentResolver, CompositeHandleResolver, 3 + DohJsonHandleResolver, 4 + PlcDidDocumentResolver, WebDidDocumentResolver, 5 + WellKnownHandleResolver 6 + } from '@atcute/identity-resolver'; 7 + 8 + const handleResolver = new CompositeHandleResolver({ 9 + strategy: 'race', 10 + methods: { 11 + dns: new DohJsonHandleResolver({ 12 + dohUrl: 'https://mozilla.cloudflare-dns.com/dns-query', 13 + }), 14 + http: new WellKnownHandleResolver(), 15 + }, 16 + }); 17 + 18 + const docResolver = new CompositeDidDocumentResolver({ 19 + methods: { 20 + plc: new PlcDidDocumentResolver(), 21 + web: new WebDidDocumentResolver(), 22 + }, 23 + }); 24 + 25 + /** 26 + * Cleans the handle of @ and some other unicode characters that used to show up when copied from the profile 27 + * @param handle {string} 28 + * @returns {string} 29 + */ 30 + const cleanHandle = (handle) => 31 + handle.replace('@', '').trim().replace( 32 + /[\u202A\u202C\u200E\u200F\u2066-\u2069]/g, 33 + '', 34 + ); 35 + 36 + 37 + /** 38 + * Convince helper to resolve a handle to a did and then find the PDS url from the did document. 39 + * 40 + * @param handle 41 + * @returns {Promise<{usersDid: string, pds: string}>} 42 + */ 43 + async function handleAndPDSResolver(handle) { 44 + let usersDid = null; 45 + if (handle.startsWith('did:')) { 46 + usersDid = handle; 47 + } else { 48 + const cleanedHandle = cleanHandle(handle); 49 + usersDid = await handleResolver.resolve(cleanedHandle); 50 + } 51 + const didDoc = await docResolver.resolve(usersDid); 52 + 53 + let pds; 54 + try { 55 + pds = didDoc.service?.filter((s) => 56 + s.type === 'AtprotoPersonalDataServer' 57 + )[0].serviceEndpoint; 58 + } catch (error) { 59 + throw new Error('Could not find a PDS in the DID document.'); 60 + } 61 + return {usersDid, pds}; 62 + } 63 + 64 + 65 + /** 66 + * Fetches the DID Web from the .well-known/did.json endpoint of the server. 67 + * Legacy and was helpful if the web ui and server are on the same domain, not as useful now 68 + * @param baseUrl 69 + * @returns {Promise<*>} 70 + */ 71 + async function fetchPDSMooverDIDWeb(baseUrl) { 72 + const response = await fetch(`${baseUrl}/.well-known/did.json`); 73 + if (!response.ok) { 74 + throw new Error(`Failed to fetch DID document: ${response.status}`); 75 + } 76 + const didDoc = await response.json(); 77 + return didDoc.id; 78 + } 79 + 80 + 81 + export {handleResolver, docResolver, cleanHandle, handleAndPDSResolver, fetchPDSMooverDIDWeb};
+276
packages/moover/lib/backup.js
··· 1 + import {Client, CredentialManager, ok} from '@atcute/client'; 2 + import {handleAndPDSResolver} from './atprotoUtils.js'; 3 + //Shows as unused, but is used in the return types 4 + import {ComPdsmooverBackupDescribeServer} from '@pds-moover/lexicons'; 5 + 6 + /** 7 + * JSDoc type-only import to avoid runtime import errors in the browser. 8 + * @typedef {import('@atcute/lexicons').InferXRPCBodyOutput} InferXRPCBodyOutput 9 + */ 10 + 11 + 12 + /** 13 + * Logic to sign up and manage backups for pdsmoover.com (or your own selfhosted instance) 14 + */ 15 + class BackupService { 16 + /** 17 + * 18 + * @param backupDidWeb {string} - The did:web for the xrpc service for backups, defaults to did:web:pdsmoover.com 19 + */ 20 + constructor(backupDidWeb = 'did:web:pdsmoover.com') { 21 + /** 22 + * 23 + * @type {Client} 24 + */ 25 + this.atCuteClient = null; 26 + /** 27 + * 28 + * @type {CredentialManager} 29 + */ 30 + this.atCuteCredentialManager = null; 31 + 32 + /** 33 + * The did:web for the xrpc service for backups, defaults to pdsmoover.com 34 + * @type {string} 35 + */ 36 + this.backupDidWeb = backupDidWeb; 37 + } 38 + 39 + 40 + /** 41 + * Logs in and returns the backup status. 42 + * To use the rest of the BackupService, it is assumed that this has ran first, 43 + * and the user has successfully signed up. A successful login is a returned null if the user has not signed up. 44 + * or the backup status if they are 45 + * 46 + * If the server requires 2FA, 47 + * it will throw with error.error === 'AuthFactorTokenRequired'. 48 + * @param identifier {string} handle or did 49 + * @param password {string} 50 + * @param {function|null} onStatus - a function that takes a string used to update the UI. 51 + * Like (status) => console.log(status) 52 + * @param twoFactorCode {string|null} 53 + * 54 + * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>|null>} 55 + */ 56 + async loginAndStatus(identifier, password, onStatus = null, twoFactorCode = null) { 57 + let {pds} = await handleAndPDSResolver(identifier); 58 + 59 + 60 + const manager = new CredentialManager({ 61 + service: pds 62 + }); 63 + 64 + 65 + const rpc = new Client({ 66 + handler: manager, 67 + proxy: { 68 + did: this.backupDidWeb, 69 + serviceId: '#repo_backup' 70 + } 71 + }); 72 + 73 + try { 74 + if (onStatus) onStatus('Signing in…'); 75 + 76 + let loginInput = { 77 + identifier, 78 + password, 79 + 80 + }; 81 + if (twoFactorCode) { 82 + loginInput.code = twoFactorCode; 83 + } 84 + await manager.login(loginInput); 85 + 86 + 87 + // Make the client/manager available regardless of repo status so we can sign up if needed. 88 + this.atCuteClient = rpc; 89 + this.atCuteCredentialManager = manager; 90 + 91 + if (onStatus) onStatus('Checking backup status'); 92 + const result = await rpc.get('com.pdsmoover.backup.getRepoStatus', { 93 + params: { 94 + did: manager.session.did.toString() 95 + } 96 + }); 97 + if (result.ok) { 98 + return result.data; 99 + } else { 100 + switch (result.data.error) { 101 + case 'RepoNotFound': 102 + return null; 103 + default: 104 + throw result.data.error; 105 + } 106 + 107 + } 108 + } catch (err) { 109 + throw err; 110 + } 111 + } 112 + 113 + /** 114 + * Signs the user up for backups with the service 115 + * @param onStatus 116 + * @returns {Promise<void>} 117 + */ 118 + async signUp(onStatus = null) { 119 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 120 + throw new Error('Not signed in'); 121 + } 122 + if (onStatus) onStatus('Creating backup registration…'); 123 + await ok( 124 + this.atCuteClient.post('com.pdsmoover.backup.signUp', { 125 + as: null, 126 + }) 127 + ); 128 + if (onStatus) onStatus('Backup registration complete'); 129 + //No return if successful 130 + } 131 + 132 + /** 133 + * Requests a PLC token to be sent to the user's email, needed to add a new rotation key 134 + * @returns {Promise<void>} 135 + */ 136 + async requestAPlcToken() { 137 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 138 + throw new Error('Not signed in'); 139 + } 140 + const rpc = new Client({ 141 + handler: this.atCuteCredentialManager, 142 + }); 143 + 144 + let response = await rpc.post('com.atproto.identity.requestPlcOperationSignature', { 145 + as: null, 146 + }); 147 + if (!response.ok) { 148 + throw new Error(response.data?.message || 'Failed to request PLC token'); 149 + } 150 + } 151 + 152 + /** 153 + * Adds a new rotation to the users did document. Assumes you are already signed in. 154 + * 155 + * WARNING: This will overwrite any existing rotation keys with the new one at the top, and the PDS key as the second one 156 + * @param plcToken {string} - PLC token from the user's email that was sent from requestAPlcToken 157 + * @param rotationKey {string} - The new rotation key to add to the user's did document 158 + * @returns {Promise<void>} 159 + */ 160 + async addANewRotationKey(plcToken, rotationKey) { 161 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 162 + throw new Error('Not signed in'); 163 + } 164 + 165 + 166 + const rpc = new Client({ 167 + handler: this.atCuteCredentialManager, 168 + }); 169 + 170 + let getDidCredentials = await rpc.get('com.atproto.identity.getRecommendedDidCredentials'); 171 + 172 + if (getDidCredentials.ok) { 173 + const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? []; 174 + const updatedRotationKeys = [rotationKey, ...pdsProvidedRotationKeys]; 175 + 176 + const credentials = { 177 + ...getDidCredentials.data, 178 + rotationKeys: updatedRotationKeys, 179 + }; 180 + 181 + const signDocRes = await rpc.post('com.atproto.identity.signPlcOperation', { 182 + input: { 183 + token: plcToken, 184 + ...credentials, 185 + } 186 + }); 187 + 188 + if (signDocRes.ok) { 189 + const submitDocRes = await rpc.post('com.atproto.identity.submitPlcOperation', { 190 + input: signDocRes.data, 191 + as: null, 192 + }); 193 + 194 + if (!submitDocRes.ok) { 195 + throw new Error(submitDocRes.data?.message || 'Failed to submit PLC operation'); 196 + } 197 + 198 + } else { 199 + throw new Error(signDocRes.data?.message || 'Failed to sign PLC operation'); 200 + } 201 + 202 + 203 + } else { 204 + throw new Error(getDidCredentials.data?.message || 'Failed to get status'); 205 + } 206 + } 207 + 208 + 209 + /** 210 + * 211 + * Gets the current status of the user's backup repository. 212 + * 213 + * @param onStatus {function|null} - a function that takes a string used to update the UI. 214 + * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>>} 215 + */ 216 + async getUsersRepoStatus(onStatus = null) { 217 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 218 + throw new Error('Not signed in'); 219 + } 220 + if (onStatus) onStatus('Refreshing backup status…'); 221 + const result = await this.atCuteClient.get('com.pdsmoover.backup.getRepoStatus', { 222 + params: {did: this.atCuteCredentialManager.session.did.toString()} 223 + }); 224 + if (result.ok) { 225 + return result.data; 226 + } else { 227 + throw new Error(result.data?.message || 'Failed to get status'); 228 + } 229 + } 230 + 231 + /** 232 + * Requests a backup to be run immediately for the signed-in user. Usually does, depend on the server's backup queue 233 + * @param onStatus 234 + * @returns {Promise<boolean>} 235 + */ 236 + async runBackupNow(onStatus = null) { 237 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 238 + throw new Error('Not signed in'); 239 + } 240 + if (onStatus) onStatus('Requesting backup…'); 241 + const res = await this.atCuteClient.post('com.pdsmoover.backup.requestBackup', {as: null, data: {}}); 242 + if (res.ok) { 243 + if (onStatus) onStatus('Backup requested.'); 244 + return true; 245 + } else { 246 + const err = res.data; 247 + if (err?.error === 'Timeout') { 248 + throw {error: 'Timeout', message: err?.message || 'Please wait a few minutes before requesting again.'}; 249 + } 250 + throw new Error(err?.message || 'Failed to request backup'); 251 + } 252 + } 253 + 254 + /** 255 + * Remove (delete) the signed-in user's backup repository. this also deletes all the user's backup data. 256 + * @param onStatus 257 + * @returns {Promise<boolean>} 258 + */ 259 + async removeRepo(onStatus = null) { 260 + if (!this.atCuteClient || !this.atCuteCredentialManager) { 261 + throw new Error('Not signed in'); 262 + } 263 + if (onStatus) onStatus('Deleting backup repository…'); 264 + const res = await this.atCuteClient.post('com.pdsmoover.backup.removeRepo', {as: null, data: {}}); 265 + if (res.ok) { 266 + if (onStatus) onStatus('Backup repository deleted.'); 267 + return true; 268 + } else { 269 + const err = res.data; 270 + throw new Error(err?.message || 'Failed to delete backup repository'); 271 + } 272 + } 273 + } 274 + 275 + 276 + export {BackupService};
+17
packages/moover/lib/main.js
··· 1 + import {Migrator} from './pdsmoover.js'; 2 + import {MissingBlobs} from './missingBlobs.js'; 3 + import {BackupService} from './backup.js'; 4 + import {PlcOps} from './plc-ops.js'; 5 + import {Restore} from './restore.js'; 6 + import {handleAndPDSResolver} from './atprotoUtils.js'; 7 + 8 + export { 9 + Migrator, 10 + MissingBlobs, 11 + BackupService, 12 + PlcOps, 13 + Restore, 14 + handleAndPDSResolver, 15 + 16 + } 17 +
+193
packages/moover/lib/missingBlobs.js
··· 1 + import {AtpAgent} from '@atproto/api'; 2 + import {handleAndPDSResolver} from './atprotoUtils.js'; 3 + 4 + 5 + /** 6 + * Class to help find missing blobs from the did's previous PDS and import them into the current PDS 7 + */ 8 + class MissingBlobs { 9 + 10 + constructor() { 11 + /** 12 + * The user's current PDS agent 13 + * @type {AtpAgent} 14 + */ 15 + this.currentPdsAgent = null; 16 + /** 17 + * The user's old PDS agent 18 + * @type {AtpAgent} 19 + */ 20 + this.oldPdsAgent = null; 21 + /** 22 + * the user's did 23 + * @type {string|null} 24 + */ 25 + this.did = null; 26 + /** 27 + * The user's current PDS url 28 + * @type {null} 29 + */ 30 + this.currentPdsUrl = null; 31 + /** 32 + * A list of the missing cids blobs from the old PDS. In this case if a retry upload fails it gets put in this array for the ui 33 + * @type {string[]} 34 + */ 35 + this.missingBlobs = []; 36 + 37 + } 38 + 39 + /** 40 + * Logs the user into the current PDS and gets the account status 41 + * @param handle {string} 42 + * @param password {string} 43 + * @param twoFactorCode {string|null} 44 + * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 45 + */ 46 + async currentAgentLogin( 47 + handle, 48 + password, 49 + twoFactorCode = null, 50 + ) { 51 + let {usersDid, pds} = await handleAndPDSResolver(handle); 52 + this.did = usersDid; 53 + this.currentPdsUrl = pds; 54 + const agent = new AtpAgent({ 55 + service: pds, 56 + }); 57 + 58 + if (twoFactorCode === null) { 59 + await agent.login({identifier: usersDid, password}); 60 + } else { 61 + await agent.login({identifier: usersDid, password: password, authFactorToken: twoFactorCode}); 62 + } 63 + 64 + this.currentPdsAgent = agent; 65 + 66 + const result = await agent.com.atproto.server.checkAccountStatus(); 67 + const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 68 + limit: 10, 69 + }); 70 + return {accountStatus: result.data, missingBlobsCount: missingBlobs.data.blobs.length}; 71 + } 72 + 73 + /** 74 + * Logs into the old PDS and gets the account status. 75 + * Does not need a handle 76 + * since it is assumed the user has already logged in with the current PDS and we are using their did 77 + * @param password {string} 78 + * @param twoFactorCode {string|null} 79 + * @param pdsUrl {string|null} - If you know the url of the old PDS you can pass it in here. If not it will be guessed at from plc ops 80 + * @returns {Promise<void>} 81 + */ 82 + async oldAgentLogin( 83 + password, 84 + twoFactorCode = null, 85 + pdsUrl = null, 86 + ) { 87 + let oldPds = null; 88 + 89 + if (pdsUrl === null) { 90 + const response = await fetch(`https://plc.directory/${this.did}/log`); 91 + let auditLog = await response.json(); 92 + auditLog = auditLog.reverse(); 93 + let debugCount = 0; 94 + for (const entry of auditLog) { 95 + console.log(`Loop: ${debugCount++}`); 96 + console.log(entry); 97 + if (entry.services) { 98 + if (entry.services.atproto_pds) { 99 + if (entry.services.atproto_pds.type === 'AtprotoPersonalDataServer') { 100 + const pds = entry.services.atproto_pds.endpoint; 101 + console.log(`Found PDS: ${pds}`); 102 + if (pds.toLowerCase() !== this.currentPdsUrl.toLowerCase()) { 103 + oldPds = pds; 104 + break; 105 + } 106 + } 107 + } 108 + } 109 + } 110 + if (oldPds === null) { 111 + throw new Error('Could not find your old PDS'); 112 + } 113 + } else { 114 + oldPds = pdsUrl; 115 + } 116 + 117 + const agent = new AtpAgent({ 118 + service: oldPds, 119 + }); 120 + 121 + if (twoFactorCode === null) { 122 + await agent.login({identifier: this.did, password}); 123 + } else { 124 + await agent.login({identifier: this.did, password: password, authFactorToken: twoFactorCode}); 125 + } 126 + this.oldPdsAgent = agent; 127 + } 128 + 129 + /** 130 + * Gets the missing blobs from the old PDS and uploads them to the current PDS 131 + * @param statusUpdateHandler {function} - A function to update the status of the migration. This is useful for showing the user the progress of the migration 132 + * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 133 + */ 134 + async migrateMissingBlobs(statusUpdateHandler) { 135 + if (this.currentPdsAgent === null) { 136 + throw new Error('Current PDS agent is not set'); 137 + } 138 + if (this.oldPdsAgent === null) { 139 + throw new Error('Old PDS agent is not set'); 140 + } 141 + statusUpdateHandler('Starting to import blobs...'); 142 + 143 + let totalMissingBlobs = 0; 144 + let missingBlobCursor = undefined; 145 + let missingUploadedBlobs = 0; 146 + 147 + do { 148 + 149 + const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 150 + cursor: missingBlobCursor, 151 + limit: 1000, 152 + }); 153 + totalMissingBlobs += missingBlobs.data.blobs.length; 154 + 155 + for (const recordBlob of missingBlobs.data.blobs) { 156 + try { 157 + 158 + const blobRes = await this.oldPdsAgent.com.atproto.sync.getBlob({ 159 + did: this.did, 160 + cid: recordBlob.cid, 161 + }); 162 + let result = await this.currentPdsAgent.com.atproto.repo.uploadBlob(blobRes.data, { 163 + encoding: blobRes.headers['content-type'], 164 + }); 165 + 166 + if (result.status === 429) { 167 + statusUpdateHandler(`You are being rate limited. Will need to try again later to get the rest of the blobs. Migrated blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 168 + } 169 + 170 + if (missingUploadedBlobs % 2 === 0) { 171 + statusUpdateHandler(`Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs} (The total may increase as we find more)`); 172 + } 173 + missingUploadedBlobs++; 174 + } catch (error) { 175 + console.error(error); 176 + this.missingBlobs.push(recordBlob.cid); 177 + } 178 + } 179 + missingBlobCursor = missingBlobs.data.cursor; 180 + } while (missingBlobCursor); 181 + 182 + const accountStatus = await this.currentPdsAgent.com.atproto.server.checkAccountStatus(); 183 + const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 184 + limit: 10, 185 + }); 186 + return {accountStatus: accountStatus.data, missingBlobsCount: missingBlobs.data.blobs.length}; 187 + 188 + 189 + } 190 + 191 + } 192 + 193 + export {MissingBlobs};
+389
packages/moover/lib/pdsmoover.js
··· 1 + import {docResolver, cleanHandle, handleResolver} from './atprotoUtils.js'; 2 + import {AtpAgent} from '@atproto/api'; 3 + 4 + 5 + function safeStatusUpdate(statusUpdateHandler, status) { 6 + if (statusUpdateHandler) { 7 + statusUpdateHandler(status); 8 + } 9 + } 10 + 11 + /** 12 + * Handles normal PDS Migrations between two PDSs that are both up. 13 + * On pdsmoover.com this is the logic for the MOOver 14 + */ 15 + class Migrator { 16 + constructor() { 17 + /** @type {AtpAgent} */ 18 + this.oldAgent = null; 19 + /** @type {AtpAgent} */ 20 + this.newAgent = null; 21 + /** @type {[string]} */ 22 + this.missingBlobs = []; 23 + //State for reruns 24 + /** @type {boolean} */ 25 + this.createNewAccount = true; 26 + /** @type {boolean} */ 27 + this.migrateRepo = true; 28 + /** @type {boolean} */ 29 + this.migrateBlobs = true; 30 + /** @type {boolean} */ 31 + this.migrateMissingBlobs = true; 32 + /** @type {boolean} */ 33 + this.migratePrefs = true; 34 + /** @type {boolean} */ 35 + this.migratePlcRecord = true; 36 + } 37 + 38 + /** 39 + * This migrator is pretty cut and dry and makes a few assumptions 40 + * 1. You are using the same password between each account 41 + * 2. If this command fails for something like oauth 2fa code it throws an error and expects the same values when ran again. 42 + * 3. You can control which "actions" happen by setting the class variables to false. 43 + * 4. Each instance of the class is assumed to be for a single migration 44 + * @param {string} oldHandle - The handle you use on your old pds, something like alice.bsky.social 45 + * @param {string} password - Your password for your current login. Has to be your real password, no app password. When setting up a new account we reuse it as well for that account 46 + * @param {string} newPdsUrl - The new URL for your pds. Like https://coolnewpds.com 47 + * @param {string} newEmail - The email you want to use on the new pds (can be the same as the previous one as long as it's not already being used on the new pds) 48 + * @param {string} newHandle - The new handle you want, like alice.bsky.social, or if you already have a domain name set as a handle can use it myname.com. 49 + * @param {string|null} inviteCode - The invite code you got from the PDS you are migrating to. If null does not include one 50 + * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. Like (status) => console.log(status) 51 + * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 52 + */ 53 + async migrate(oldHandle, password, newPdsUrl, newEmail, newHandle, inviteCode, statusUpdateHandler = null, twoFactorCode = null) { 54 + //Leaving this logic that either sets the agent to bsky.social, or the PDS since it's what I found worked best for migrations. 55 + // handleAndPDSResolver should be able to handle it, but there have been edge cases and this was what worked best 56 + oldHandle = cleanHandle(oldHandle); 57 + let oldAgent; 58 + let usersDid; 59 + //If it's a bsky handle just go with the entryway and let it sort everything 60 + if (oldHandle.endsWith('.bsky.social')) { 61 + oldAgent = new AtpAgent({service: 'https://bsky.social'}); 62 + const publicAgent = new AtpAgent({service: 'https://public.api.bsky.app'}); 63 + const resolveIdentityFromEntryway = await publicAgent.com.atproto.identity.resolveHandle({handle: oldHandle}); 64 + usersDid = resolveIdentityFromEntryway.data.did; 65 + 66 + } else { 67 + //Resolves the did and finds the did document for the old PDS 68 + safeStatusUpdate(statusUpdateHandler, 'Resolving old PDS'); 69 + usersDid = await handleResolver.resolve(oldHandle); 70 + const didDoc = await docResolver.resolve(usersDid); 71 + safeStatusUpdate(statusUpdateHandler, 'Resolving did document and finding your current PDS URL'); 72 + 73 + let oldPds; 74 + try { 75 + oldPds = didDoc.service.filter(s => s.type === 'AtprotoPersonalDataServer')[0].serviceEndpoint; 76 + } catch (error) { 77 + console.error(error); 78 + throw new Error('Could not find a PDS in the DID document.'); 79 + } 80 + 81 + oldAgent = new AtpAgent({ 82 + service: oldPds, 83 + }); 84 + 85 + } 86 + 87 + safeStatusUpdate(statusUpdateHandler, 'Logging you in to the old PDS'); 88 + //Login to the old PDS 89 + if (twoFactorCode === null) { 90 + await oldAgent.login({identifier: oldHandle, password}); 91 + } else { 92 + await oldAgent.login({identifier: oldHandle, password: password, authFactorToken: twoFactorCode}); 93 + } 94 + 95 + safeStatusUpdate(statusUpdateHandler, 'Checking that the new PDS is an actual PDS (if the url is wrong this takes a while to error out)'); 96 + const newAgent = new AtpAgent({service: newPdsUrl}); 97 + const newHostDesc = await newAgent.com.atproto.server.describeServer(); 98 + if (this.createNewAccount) { 99 + const newHostWebDid = newHostDesc.data.did; 100 + 101 + safeStatusUpdate(statusUpdateHandler, 'Creating a new account on the new PDS'); 102 + 103 + const createAuthResp = await oldAgent.com.atproto.server.getServiceAuth({ 104 + aud: newHostWebDid, 105 + lxm: 'com.atproto.server.createAccount', 106 + }); 107 + const serviceJwt = createAuthResp.data.token; 108 + 109 + let createAccountRequest = { 110 + did: usersDid, 111 + handle: newHandle, 112 + email: newEmail, 113 + password: password, 114 + }; 115 + if (inviteCode) { 116 + createAccountRequest.inviteCode = inviteCode; 117 + } 118 + const createNewAccount = await newAgent.com.atproto.server.createAccount( 119 + createAccountRequest, 120 + { 121 + headers: {authorization: `Bearer ${serviceJwt}`}, 122 + encoding: 'application/json', 123 + }); 124 + 125 + if (createNewAccount.data.did !== usersDid.toString()) { 126 + throw new Error('Did not create the new account with the same did as the old account'); 127 + } 128 + } 129 + safeStatusUpdate(statusUpdateHandler, 'Logging in with the new account'); 130 + 131 + await newAgent.login({ 132 + identifier: usersDid, 133 + password: password, 134 + }); 135 + 136 + if (this.migrateRepo) { 137 + safeStatusUpdate(statusUpdateHandler, 'Migrating your repo'); 138 + const repoRes = await oldAgent.com.atproto.sync.getRepo({did: usersDid}); 139 + await newAgent.com.atproto.repo.importRepo(repoRes.data, { 140 + encoding: 'application/vnd.ipld.car', 141 + }); 142 + } 143 + 144 + let newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus(); 145 + 146 + if (this.migrateBlobs) { 147 + safeStatusUpdate(statusUpdateHandler, 'Migrating your blobs'); 148 + 149 + let blobCursor = undefined; 150 + let uploadedBlobs = 0; 151 + do { 152 + safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`); 153 + 154 + const listedBlobs = await oldAgent.com.atproto.sync.listBlobs({ 155 + did: usersDid, 156 + cursor: blobCursor, 157 + limit: 100, 158 + }); 159 + 160 + for (const cid of listedBlobs.data.cids) { 161 + try { 162 + const blobRes = await oldAgent.com.atproto.sync.getBlob({ 163 + did: usersDid, 164 + cid, 165 + }); 166 + await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 167 + encoding: blobRes.headers['content-type'], 168 + }); 169 + uploadedBlobs++; 170 + if (uploadedBlobs % 10 === 0) { 171 + safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`); 172 + } 173 + } catch (error) { 174 + console.error(error); 175 + } 176 + } 177 + blobCursor = listedBlobs.data.cursor; 178 + } while (blobCursor); 179 + } 180 + 181 + if (this.migrateMissingBlobs) { 182 + newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus(); 183 + if (newAccountStatus.data.expectedBlobs !== newAccountStatus.data.importedBlobs) { 184 + let totalMissingBlobs = newAccountStatus.data.expectedBlobs - newAccountStatus.data.importedBlobs; 185 + safeStatusUpdate(statusUpdateHandler, 'Looks like there are some missing blobs. Going to try and upload them now.'); 186 + //Probably should be shared between main blob uploader, but eh 187 + let missingBlobCursor = undefined; 188 + let missingUploadedBlobs = 0; 189 + do { 190 + safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 191 + 192 + const missingBlobs = await newAgent.com.atproto.repo.listMissingBlobs({ 193 + cursor: missingBlobCursor, 194 + limit: 100, 195 + }); 196 + 197 + for (const recordBlob of missingBlobs.data.blobs) { 198 + try { 199 + 200 + const blobRes = await oldAgent.com.atproto.sync.getBlob({ 201 + did: usersDid, 202 + cid: recordBlob.cid, 203 + }); 204 + await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 205 + encoding: blobRes.headers['content-type'], 206 + }); 207 + if (missingUploadedBlobs % 10 === 0) { 208 + safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 209 + } 210 + missingUploadedBlobs++; 211 + } catch (error) { 212 + //TODO silently logging prob should list them so user can manually download 213 + console.error(error); 214 + this.missingBlobs.push(recordBlob.cid); 215 + } 216 + } 217 + missingBlobCursor = missingBlobs.data.cursor; 218 + } while (missingBlobCursor); 219 + 220 + } 221 + } 222 + if (this.migratePrefs) { 223 + const prefs = await oldAgent.app.bsky.actor.getPreferences(); 224 + await newAgent.app.bsky.actor.putPreferences(prefs.data); 225 + } 226 + 227 + this.oldAgent = oldAgent; 228 + this.newAgent = newAgent; 229 + 230 + if (this.migratePlcRecord) { 231 + await oldAgent.com.atproto.identity.requestPlcOperationSignature(); 232 + safeStatusUpdate(statusUpdateHandler, 'Please check your email for a PLC token'); 233 + } 234 + } 235 + 236 + /** 237 + * Sign and submits the PLC operation to officially migrate the account 238 + * @param {string} token - the PLC token sent in the email. If you're just wanting to run this rerun migrate with all the flags set as false except for migratePlcRecord 239 + * @param additionalRotationKeysToAdd {string[]} - additional rotation keys to add in addition to the ones provided by the new PDS. 240 + * @returns {Promise<void>} 241 + */ 242 + async signPlcOperation(token, additionalRotationKeysToAdd = []) { 243 + const getDidCredentials = 244 + await this.newAgent.com.atproto.identity.getRecommendedDidCredentials(); 245 + const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? []; 246 + // Prepend any additional rotation keys (e.g., user-added keys, newly created key) so they appear above the new PDS rotation key 247 + const rotationKeys = [...(additionalRotationKeysToAdd || []), ...pdsProvidedRotationKeys]; 248 + if (!rotationKeys) { 249 + throw new Error('No rotation key provided from the new PDS'); 250 + } 251 + const credentials = { 252 + ...getDidCredentials.data, 253 + rotationKeys: rotationKeys, 254 + }; 255 + 256 + 257 + const plcOp = await this.oldAgent.com.atproto.identity.signPlcOperation({ 258 + token: token, 259 + ...credentials, 260 + }); 261 + 262 + await this.newAgent.com.atproto.identity.submitPlcOperation({ 263 + operation: plcOp.data.operation, 264 + }); 265 + 266 + await this.newAgent.com.atproto.server.activateAccount(); 267 + await this.oldAgent.com.atproto.server.deactivateAccount({}); 268 + } 269 + 270 + /** 271 + * Using this method assumes the Migrator class was constructed new and this was called. 272 + * Find the user's previous PDS from the PLC op logs, 273 + * logs in and deactivates their old account if it was found still active. 274 + * 275 + * @param oldHandle {string} 276 + * @param oldPassword {string} 277 + * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. 278 + * Like (status) => console.log(status) 279 + * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 280 + * @returns {Promise<void>} 281 + */ 282 + async deactivateOldAccount(oldHandle, oldPassword, statusUpdateHandler = null, twoFactorCode = null) { 283 + //Leaving this logic that either sets the agent to bsky.social, or the PDS since it's what I found worked best for migrations. 284 + // handleAndPDSResolver should be able to handle it, but there have been edge cases and this was what worked best oldHandle = cleanHandle(oldHandle); 285 + let usersDid; 286 + //If it's a bsky handle just go with the entryway and let it sort everything 287 + if (oldHandle.endsWith('.bsky.social')) { 288 + const publicAgent = new AtpAgent({service: 'https://public.api.bsky.app'}); 289 + const resolveIdentityFromEntryway = await publicAgent.com.atproto.identity.resolveHandle({handle: oldHandle}); 290 + usersDid = resolveIdentityFromEntryway.data.did; 291 + } else { 292 + //Resolves the did and finds the did document for the old PDS 293 + safeStatusUpdate(statusUpdateHandler, 'Resolving did from handle'); 294 + usersDid = await handleResolver.resolve(oldHandle); 295 + } 296 + 297 + const didDoc = await docResolver.resolve(usersDid); 298 + let currentPds; 299 + try { 300 + currentPds = didDoc.service.filter(s => s.type === 'AtprotoPersonalDataServer')[0].serviceEndpoint; 301 + } catch (error) { 302 + console.error(error); 303 + throw new Error('Could not find a PDS in the DID document.'); 304 + } 305 + 306 + const plcLogRequest = await fetch(`https://plc.directory/${usersDid}/log`); 307 + const plcLog = await plcLogRequest.json(); 308 + let pdsBeforeCurrent = ''; 309 + for (const log of plcLog) { 310 + try { 311 + const pds = log.services.atproto_pds.endpoint; 312 + if (pds.toLowerCase() === currentPds.toLowerCase()) { 313 + console.log('Found the PDS before the current one'); 314 + break; 315 + } 316 + pdsBeforeCurrent = pds; 317 + } catch (e) { 318 + console.log(e); 319 + } 320 + } 321 + if (pdsBeforeCurrent === '') { 322 + throw new Error('Could not find the PDS before the current one'); 323 + } 324 + 325 + let oldAgent = new AtpAgent({service: pdsBeforeCurrent}); 326 + safeStatusUpdate(statusUpdateHandler, `Logging you in to the old PDS: ${pdsBeforeCurrent}`); 327 + //Login to the old PDS 328 + if (twoFactorCode === null) { 329 + await oldAgent.login({identifier: oldHandle, password: oldPassword}); 330 + } else { 331 + await oldAgent.login({identifier: oldHandle, password: oldPassword, authFactorToken: twoFactorCode}); 332 + } 333 + safeStatusUpdate(statusUpdateHandler, 'Checking this isn\'t your current PDS'); 334 + if (pdsBeforeCurrent === currentPds) { 335 + throw new Error('This is your current PDS. Login to your old account username and password'); 336 + } 337 + 338 + let currentAccountStatus = await oldAgent.com.atproto.server.checkAccountStatus(); 339 + if (!currentAccountStatus.data.activated) { 340 + safeStatusUpdate(statusUpdateHandler, 'All good. Your old account is not activated.'); 341 + } 342 + safeStatusUpdate(statusUpdateHandler, 'Deactivating your OLD account'); 343 + await oldAgent.com.atproto.server.deactivateAccount({}); 344 + safeStatusUpdate(statusUpdateHandler, 'Successfully deactivated your OLD account'); 345 + } 346 + 347 + /** 348 + * Signs the logged-in user in this.newAgent for backups with PDS MOOver. This is usually called after migrate and signPlcOperation are successful 349 + * 350 + * @param {string} didWeb 351 + * @returns {Promise<void>} 352 + */ 353 + async signUpForBackupsFromMigration(didWeb = 'did:web:pdsmoover.com') { 354 + 355 + //Manually grabbing the jwt and making a call with fetch cause for the life of me I could not figure out 356 + //how you used @atproto/api to make a call for proxying 357 + const url = `${this.newAgent.serviceUrl.origin}/xrpc/com.pdsmoover.backup.signUp`; 358 + 359 + const accessJwt = this.newAgent?.session?.accessJwt; 360 + if (!accessJwt) { 361 + throw new Error('Missing access token for authorization'); 362 + } 363 + 364 + const res = await fetch(url, { 365 + method: 'POST', 366 + headers: { 367 + 'Authorization': `Bearer ${accessJwt}`, 368 + 'Content-Type': 'application/json', 369 + 'Accept': 'application/json', 370 + 'atproto-proxy': `${didWeb}#repo_backup`, 371 + }, 372 + body: JSON.stringify({}), 373 + }); 374 + 375 + if (!res.ok) { 376 + let bodyText = ''; 377 + try { 378 + bodyText = await res.text(); 379 + } catch { 380 + } 381 + throw new Error(`Backup signup failed: ${res.status} ${res.statusText}${bodyText ? ` - ${bodyText}` : ''}`); 382 + } 383 + 384 + //No return the success is all that is needed, if there's an error it will throw 385 + } 386 + } 387 + 388 + export {Migrator}; 389 +
+292
packages/moover/lib/plc-ops.js
··· 1 + /** 2 + * JSDoc type-only import to avoid runtime import errors in the browser. 3 + * @typedef {import('@atcute/did-plc').defs} defs 4 + * @typedef {import('@atcute/did-plc').normalizeOp} normalizeOp 5 + * @typedef {import('@atcute/did-plc').Operation} Operation 6 + * @typedef {import('@atcute/did-plc').CompatibleOperation} CompatibleOperation 7 + * @typedef {import('@atcute/did-plc').IndexedEntryLog} IndexedEntryLog 8 + * @typedef {import('@atcute/did-plc').IndexedEntry} IndexedEntry 9 + */ 10 + 11 + import {defs, normalizeOp} from '@atcute/did-plc'; 12 + import {P256PrivateKey, parsePrivateMultikey, Secp256k1PrivateKey, Secp256k1PrivateKeyExportable} from '@atcute/crypto'; 13 + import * as CBOR from '@atcute/cbor'; 14 + import {fromBase16, toBase64Url} from '@atcute/multibase'; 15 + 16 + 17 + // Helper to base64url-encode JSON 18 + const jsonToB64Url = (obj) => { 19 + const enc = new TextEncoder(); 20 + const json = JSON.stringify(obj); 21 + return toBase64Url(enc.encode(json)); 22 + }; 23 + 24 + /** 25 + * Class to help with various PLC operations 26 + */ 27 + class PlcOps { 28 + /** 29 + * 30 + * @param plcDirectoryUrl {string} - The url of the plc directory, defaults to https://plc.directory 31 + */ 32 + constructor(plcDirectoryUrl = 'https://plc.directory') { 33 + /** 34 + * The url of the plc directory 35 + * @type {string} 36 + */ 37 + this.plcDirectoryUrl = plcDirectoryUrl; 38 + } 39 + 40 + /** 41 + * Gets the current rotation keys for a user via their last PlC operation 42 + * @param did 43 + * @returns {Promise<string[]>} 44 + */ 45 + async getCurrentRotationKeysForUser(did) { 46 + const logs = await this.getPlcAuditLogs(did); 47 + const {lastOperation} = this.getLastPlcOp(logs); 48 + return lastOperation.rotationKeys || []; 49 + } 50 + 51 + /** 52 + * Gets the last PlC operation for a user from the plc directory 53 + * @param did 54 + * @returns {Promise<{lastOperation: Operation, base: any}>} 55 + */ 56 + async getLastPlcOpFromPlc(did) { 57 + const logs = await this.getPlcAuditLogs(did); 58 + return this.getLastPlcOp(logs); 59 + } 60 + 61 + /** 62 + * 63 + * @param logs {IndexedEntryLog} 64 + * @returns {{lastOperation: Operation, base: IndexedEntry}} 65 + */ 66 + getLastPlcOp(logs) { 67 + const lastOp = logs.at(-1); 68 + return {lastOperation: normalizeOp(lastOp.operation), base: lastOp}; 69 + } 70 + 71 + 72 + /** 73 + * Gets the plc audit logs for a user from the plc directory 74 + * @param did 75 + * @returns {Promise<IndexedEntryLog>} 76 + */ 77 + async getPlcAuditLogs(did) { 78 + const response = await fetch(`${this.plcDirectoryUrl}/${did}/log/audit`); 79 + if (!response.ok) { 80 + throw new Error(`got response ${response.status}`); 81 + } 82 + 83 + const json = await response.json(); 84 + return defs.indexedEntryLog.parse(json); 85 + } 86 + 87 + /** 88 + * Creates a new secp256k1 key that can be used for either rotation or verification key 89 + * @returns {Promise<{privateKey: string, publicKey: `did:key:${string}`}>} 90 + */ 91 + async createANewSecp256k1() { 92 + let keypair = await Secp256k1PrivateKeyExportable.createKeypair(); 93 + let publicKey = await keypair.exportPublicKey('did'); 94 + let privateKey = await keypair.exportPrivateKey('multikey'); 95 + return { 96 + privateKey, 97 + publicKey 98 + }; 99 + } 100 + 101 + 102 + /** 103 + * Signs a new operation with the provided signing key, and information and submits it to the plc directory 104 + * @param did {string} - The user's did 105 + * @param signingRotationKey { P256PrivateKey|Secp256k1PrivateKey} - The keypair to sign the op with 106 + * @param alsoKnownAs {string[]} 107 + * @param rotationKeys {string[]} 108 + * @param pds {string} 109 + * @param verificationKey {string} - The public verification key 110 + * @param prev {string} - The previous valid operation's cid. 111 + * @returns {Promise<void>} 112 + */ 113 + async signAndPublishNewOp(did, signingRotationKey, alsoKnownAs, rotationKeys, pds, verificationKey, prev) { 114 + 115 + const rotationKeysToUse = [...new Set(rotationKeys)]; 116 + if (!rotationKeysToUse) { 117 + throw new Error('No rotation keys were found to be added to the PLC'); 118 + } 119 + 120 + if (rotationKeysToUse.length > 5) { 121 + throw new Error('You can only add up to 5 rotation keys to the PLC'); 122 + } 123 + 124 + const operation = { 125 + type: 'plc_operation', 126 + prev, 127 + alsoKnownAs, 128 + rotationKeys: rotationKeysToUse, 129 + services: { 130 + atproto_pds: { 131 + type: 'AtprotoPersonalDataServer', 132 + endpoint: pds 133 + } 134 + }, 135 + verificationMethods: { 136 + atproto: verificationKey 137 + } 138 + }; 139 + const opBytes = CBOR.encode(operation); 140 + const sigBytes = await signingRotationKey.sign(opBytes); 141 + 142 + const signature = toBase64Url(sigBytes); 143 + 144 + const signedOperation = { 145 + ...operation, 146 + sig: signature, 147 + }; 148 + 149 + await this.pushPlcOperation(did, signedOperation); 150 + } 151 + 152 + /** 153 + * Takes a multi or hex based private key and returns a keypair 154 + * @param privateKeyString {string} 155 + * @param type {string} - secp256k1 or p256, needed if the private key is hex based, can be assumed if it's a multikey 156 + * @returns {Promise<{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}>} 157 + */ 158 + async getKeyPair(privateKeyString, type = 'secp256k1') { 159 + const HEX_REGEX = /^[0-9a-f]+$/i; 160 + const MULTIKEY_REGEX = /^z[a-km-zA-HJ-NP-Z1-9]+$/; 161 + let keypair = undefined; 162 + 163 + if (HEX_REGEX.test(privateKeyString)) { 164 + const privateKeyBytes = fromBase16(privateKeyString); 165 + 166 + switch (type) { 167 + case 'p256': { 168 + keypair = await P256PrivateKey.importRaw(privateKeyBytes); 169 + break; 170 + } 171 + case 'secp256k1': { 172 + keypair = await Secp256k1PrivateKey.importRaw(privateKeyBytes); 173 + break; 174 + } 175 + default: { 176 + throw new Error(`unsupported "${type}" type`); 177 + } 178 + } 179 + } else if (MULTIKEY_REGEX.test(privateKeyString)) { 180 + 181 + const match = parsePrivateMultikey(privateKeyString); 182 + const privateKeyBytes = match.privateKeyBytes; 183 + 184 + switch (match.type) { 185 + case 'p256': { 186 + keypair = await P256PrivateKey.importRaw(privateKeyBytes); 187 + console.log(keypair); 188 + break; 189 + } 190 + case 'secp256k1': { 191 + keypair = await Secp256k1PrivateKey.importRaw(privateKeyBytes); 192 + break; 193 + } 194 + default: { 195 + throw new Error(`unsupported "${type}" type`); 196 + } 197 + } 198 + } else { 199 + throw new Error('unknown input format'); 200 + } 201 + return { 202 + type: 'private_key', 203 + didPublicKey: await keypair.exportPublicKey('did'), 204 + keypair: keypair, 205 + }; 206 + } 207 + 208 + /** 209 + * Submits a new operation to the plc directory 210 + * @param did {string} - The user's did 211 + * @param operation 212 + * @returns {Promise<void>} 213 + */ 214 + async pushPlcOperation(did, operation) { 215 + const response = await fetch(`${this.plcDirectoryUrl}/${did}`, { 216 + method: 'post', 217 + headers: { 218 + 'content-type': 'application/json', 219 + }, 220 + body: JSON.stringify(operation), 221 + }); 222 + 223 + const headers = response.headers; 224 + if (!response.ok) { 225 + const type = headers.get('content-type'); 226 + 227 + if (type?.includes('application/json')) { 228 + const json = await response.json(); 229 + if (typeof json === 'object' && json !== null && typeof json.message === 'string') { 230 + throw new Error(json.message); 231 + } 232 + } 233 + 234 + throw new Error(`got http ${response.status} from plc`); 235 + } 236 + }; 237 + 238 + 239 + /** 240 + * Creates a new service auth token for a user. This is what is used to create a new account on a PDS for your did 241 + * 242 + * @param iss The user's did 243 + * @param aud The did:web, if it's a PDS it's usually from /xrpc/com.atproto.server.describeServer 244 + * @param keypair The keypair to sign with only supporting ES256K atm 245 + * @param lxm The lxm which is usually com.atproto.server.createAccount for creating a new account 246 + * @returns {Promise<string>} 247 + */ 248 + async createANewServiceAuthToken(iss, aud, keypair, lxm) { 249 + 250 + 251 + // Compute iat/exp defaults (60s window like reference: MINUTE/1e3) 252 + const iat = Math.floor(Date.now() / 1e3); 253 + const exp = iat + 60; 254 + 255 + // Generate a 16-byte hex jti 256 + const jti = (() => { 257 + const bytes = new Uint8Array(16); 258 + // crypto in browser or node; fall back safely 259 + (globalThis.crypto || window.crypto).getRandomValues(bytes); 260 + return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); 261 + })(); 262 + 263 + 264 + // Build header and payload (omit undefined fields) 265 + // Just defaulting to ES256K since p256 was not importing on firefox 266 + const header = {typ: 'JWT', alg: 'ES256K'}; 267 + const payload = {}; 268 + payload.iat = iat; 269 + payload.iss = iss; 270 + payload.aud = aud; 271 + payload.exp = exp; 272 + payload.lxm = lxm; 273 + payload.jti = jti; 274 + 275 + const headerB64 = jsonToB64Url(header); 276 + const payloadB64 = jsonToB64Url(payload); 277 + const toSignStr = `${headerB64}.${payloadB64}`; 278 + 279 + // Sign 280 + const toSignBytes = new TextEncoder().encode(toSignStr); 281 + const sigBytes = await keypair.sign(toSignBytes); 282 + 283 + // Return compact JWS 284 + const sigB64 = toBase64Url(sigBytes); 285 + return `${toSignStr}.${sigB64}`; 286 + } 287 + 288 + 289 + } 290 + 291 + 292 + export {PlcOps};
+331
packages/moover/lib/restore.js
··· 1 + /** 2 + * @typedef {import('@atcute/did-plc').Operation} Operation 3 + */ 4 + import {P256PrivateKey, Secp256k1PrivateKey} from '@atcute/crypto'; 5 + import {handleAndPDSResolver} from './atprotoUtils.js'; 6 + import {PlcOps} from './plc-ops.js'; 7 + import {normalizeOp} from '@atcute/did-plc'; 8 + import {AtpAgent} from '@atproto/api'; 9 + import {Secp256k1PrivateKeyExportable} from '@atcute/crypto'; 10 + import * as CBOR from '@atcute/cbor'; 11 + import {toBase64Url} from '@atcute/multibase'; 12 + 13 + class Restore { 14 + 15 + /** 16 + * 17 + * @param pdsMooverInstance {string} - The url of the pds moover instance to restore from. Defaults to https://pdsmover.com 18 + */ 19 + constructor(pdsMooverInstance = 'https://pdsmover.com') { 20 + /** 21 + * If you want to use a different plc directory create your own instance of the plc ops class and pass it in here 22 + * @type {PlcOps} */ 23 + this.plcOps = new PlcOps(); 24 + 25 + /** 26 + * This is the base url for the pds moover instance used to restore the files from a backup. 27 + * @type {string} 28 + */ 29 + this.pdsMooverInstance = pdsMooverInstance 30 + 31 + /** 32 + * To keep it simple, only uses secp256k for the temp verification key that is used to create the new account on the new PDS 33 + * and is temporarily assigned to the user's account on PLC 34 + * @type {null|Secp256k1PrivateKeyExportable} 35 + */ 36 + this.tempVerificationKeypair = null; 37 + 38 + /** @type {AtpAgent} */ 39 + this.atpAgent = null; 40 + 41 + /** 42 + * The keypair that is used to sign the plc operation 43 + * @type {null|{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}} 44 + */ 45 + this.recoveryRotationKeyPair = null; 46 + 47 + /** 48 + * If this is true we are just restoring the repo and blobs. Ideally for rerunning a restore process after account recovery 49 + * @type {boolean} 50 + */ 51 + this.RestoreFromBackup = true; 52 + 53 + /** 54 + * If set to true then it will do the account recovery. Writes a temp key to the did doc, 55 + * create a new account on the new pds, and then submit a new plc op for the pds to have control (finishes the migration, can always restore the backup later) 56 + * @type {boolean} 57 + */ 58 + this.AccountRecovery = true; 59 + } 60 + 61 + /** 62 + * Recovers an account with the users rotation key and restores the repo from a PDS MOOver backup 63 + * This method can fail, and the account was still recovered, it's best to check the PLC logs to see where an account stands before reruns 64 + * @param rotationKey {string} - The users private rotation key, can be a multi key or hex key 65 + * @param rotationKeyType {string} - The type of the key, secp256k1 or p256. Required if the key is in hex format, defaults to secp256k1 66 + * @param currentHandleOrDid {string} - The users current handle or did, if they don't have a DNS record it will have to be their did for success 67 + * @param newPDS {string} - The new PDS url, like https://coolnewpds.com 68 + * @param newHandle {string} - Can be the users DNS handle if it is already setup with their did, if not it's bob.mypds.com 69 + * @param newPassword {string} - The new password for the new account 70 + * @param newEmail {string} - The new email for the new account 71 + * @param inviteCode {string|null} - The invite code for the new PDS if it requires one 72 + * @param cidToRestoreTo {string|null} - The cid of the plc op to restore to, used mostly to revert a fraudulent plc op. Want to give it the last valid operations cid 73 + * @param onStatus {function|null} - A function that takes a string used to update the UI. Like (status) => console.log(status) 74 + * @returns {Promise<void>} If there is a failure during restoring the back up (after the status Success! Restoring your repo...) then your account is most likely 75 + * recovered and future runs need to have the RestoreFromBackup flag set to true and AccountRecovery set to false. 76 + */ 77 + async recover( 78 + rotationKey, 79 + rotationKeyType = 'secp256k1', 80 + currentHandleOrDid, 81 + newPDS, 82 + newHandle, 83 + newPassword, 84 + newEmail, 85 + inviteCode, 86 + cidToRestoreTo = null, 87 + onStatus = null) { 88 + 89 + if (onStatus) onStatus('Resolving your handle...'); 90 + 91 + let {usersDid} = await handleAndPDSResolver(currentHandleOrDid); 92 + 93 + if (onStatus) onStatus('Checking that the new PDS is an actual PDS (if the url is wrong, this takes a while to error out)'); 94 + this.atpAgent = new AtpAgent({service: newPDS}); 95 + const newHostDesc = await this.atpAgent.com.atproto.server.describeServer(); 96 + 97 + 98 + //Check to see if the user already has a repo on the new PDS, if they do no reason to try and restore via the plc operations 99 + try { 100 + await this.atpAgent.com.atproto.repo.describeRepo({repo: usersDid.toString()}); 101 + //If we got this far and there is a repo on the new PDS with the users did, we can just move on and restore the files. 102 + //We do not want to mess with the plc ops if we dont have to 103 + this.AccountRecovery = false; 104 + 105 + } catch (error) { 106 + console.error(error); 107 + let parsedError = error.error; 108 + if (parsedError === 'RepoDeactivated') { 109 + //Ideally should mean they already have a repo on the new PDS and we just need to restore the files 110 + this.AccountRecovery = false; 111 + } 112 + //This is the error we want to see, anything else throw 113 + if (parsedError !== 'RepoNotFound') { 114 + throw error; 115 + } 116 + } 117 + 118 + //We need to double check that the new handle has not been taken, if it has we need to throw an error 119 + //We care a bit more because we do not want any unnecessary plc ops to be created 120 + try { 121 + let resolveHandle = await this.atpAgent.com.atproto.identity.resolveHandle({handle: newHandle}); 122 + if (resolveHandle.data.did === usersDid.toString()) { 123 + //This was originally setting the AccountRecovery to false, which works if it is resolved via .well-known, but not dns 124 + //The idea was to check and see if the handle has been taken. just leaving for now since it does that check and if the user owns the handle 125 + //their did should be set anyhow 126 + 127 + } else { 128 + //There is a repo with that name and it's not the users did, 129 + throw new Error('The new handle is already taken, please select a different handle'); 130 + } 131 + } catch (error) { 132 + // Going to silently log this and just assume the handle has not been taken. 133 + console.error(error); 134 + if (error.message.startsWith('The new handle')) { 135 + //it's not our custom error, so we can just throw it 136 + throw error; 137 + } 138 + 139 + } 140 + 141 + if (this.AccountRecovery) { 142 + 143 + if (onStatus) onStatus('Validating your private rotation key is in the correct format...'); 144 + 145 + this.recoveryRotationKeyPair = await this.plcOps.getKeyPair(rotationKey, rotationKeyType); 146 + 147 + 148 + if (onStatus) onStatus('Resolving PlC operation logs...'); 149 + 150 + /** @type {Operation} */ 151 + let baseOpForSigning = null; 152 + let opPrevCid = null; 153 + 154 + //This is for reversals against a rogue plc op and you want to restore to a specific cid in the audit log 155 + if (cidToRestoreTo) { 156 + let auditLogs = await this.plcOps.getPlcAuditLogs(usersDid); 157 + for (const log of auditLogs) { 158 + if (log.cid === cidToRestoreTo) { 159 + baseOpForSigning = normalizeOp(log.operation); 160 + opPrevCid = log.cid; 161 + break; 162 + } 163 + } 164 + if (!baseOpForSigning) { 165 + throw new Error('Could not find the cid in the audit logs'); 166 + } 167 + } else { 168 + let {lastOperation, base} = await this.plcOps.getLastPlcOpFromPlc(usersDid); 169 + opPrevCid = base.cid; 170 + baseOpForSigning = lastOperation; 171 + } 172 + 173 + if (onStatus) onStatus('Preparing to switch to a temp atproto key...'); 174 + if (this.tempVerificationKeypair == null) { 175 + if (onStatus) onStatus('Creating a new temp atproto key...'); 176 + this.tempVerificationKeypair = await Secp256k1PrivateKeyExportable.createKeypair(); 177 + } 178 + //Just defaulting to the user's recovery key for now. Advance cases will be something else 179 + //Maybe just a new ui to edit the PLC doc in a limited capacity, but sinc ethis is a temp plc op i don't think it's needed 180 + let tempRotationKeys = [this.recoveryRotationKeyPair.didPublicKey]; 181 + 182 + if (onStatus) onStatus('Modifying the PLC OP for recovery...'); 183 + //A temp plc op for control of the atproto key to create a serviceAuth and new account on the new PDS 184 + await this.plcOps.signAndPublishNewOp( 185 + usersDid, 186 + this.recoveryRotationKeyPair.keypair, 187 + baseOpForSigning.alsoKnownAs, 188 + tempRotationKeys, 189 + newPDS, 190 + await this.tempVerificationKeypair.exportPublicKey('did'), 191 + opPrevCid); 192 + 193 + 194 + if (onStatus) onStatus('Creating your new account on the new PDS...'); 195 + let serviceAuthToken = await this.plcOps.createANewServiceAuthToken(usersDid, newHostDesc.data.did, this.tempVerificationKeypair, 'com.atproto.server.createAccount'); 196 + 197 + let createAccountRequest = { 198 + did: usersDid, 199 + handle: newHandle, 200 + email: newEmail, 201 + password: newPassword, 202 + }; 203 + if (inviteCode) { 204 + createAccountRequest.inviteCode = inviteCode; 205 + } 206 + const _ = await this.atpAgent.com.atproto.server.createAccount( 207 + createAccountRequest, 208 + { 209 + headers: {authorization: `Bearer ${serviceAuthToken}`}, 210 + encoding: 'application/json', 211 + }); 212 + } 213 + 214 + await this.atpAgent.login({ 215 + identifier: usersDid, 216 + password: newPassword, 217 + }); 218 + 219 + if (this.AccountRecovery) { 220 + //Moving the user offically to the new PDS 221 + if (onStatus) onStatus('Signing the papers...'); 222 + let {base} = await this.plcOps.getLastPlcOpFromPlc(usersDid); 223 + await this.signRestorePlcOperation(usersDid, [this.recoveryRotationKeyPair.didPublicKey], base.cid); 224 + } 225 + 226 + if (this.RestoreFromBackup) { 227 + if (onStatus) onStatus('Success! Restoring your repo...'); 228 + const pdsMoover = new AtpAgent({service: this.pdsMooverInstance}); 229 + const repoRes = await pdsMoover.com.atproto.sync.getRepo({did: usersDid}); 230 + await this.atpAgent.com.atproto.repo.importRepo(repoRes.data, { 231 + encoding: 'application/vnd.ipld.car', 232 + }); 233 + 234 + if (onStatus) onStatus('Restoring your blobs...'); 235 + 236 + //Using the missing endpoint to findout what's missing then the PDS MOOver endpoint to restore 237 + let totalMissingBlobs = 0; 238 + let missingBlobCursor = undefined; 239 + let missingUploadedBlobs = 0; 240 + 241 + do { 242 + 243 + const missingBlobs = await this.atpAgent.com.atproto.repo.listMissingBlobs({ 244 + cursor: missingBlobCursor, 245 + limit: 1000, 246 + }); 247 + totalMissingBlobs += missingBlobs.data.blobs.length; 248 + 249 + for (const recordBlob of missingBlobs.data.blobs) { 250 + try { 251 + 252 + const blobRes = await pdsMoover.com.atproto.sync.getBlob({ 253 + did: usersDid, 254 + cid: recordBlob.cid, 255 + }); 256 + let result = await this.atpAgent.com.atproto.repo.uploadBlob(blobRes.data, { 257 + encoding: blobRes.headers['content-type'], 258 + }); 259 + 260 + 261 + if (missingUploadedBlobs % 2 === 0) { 262 + if (onStatus) onStatus(`Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs} (The total may increase as we find more)`); 263 + } 264 + missingUploadedBlobs++; 265 + } catch (error) { 266 + console.error(error); 267 + } 268 + } 269 + missingBlobCursor = missingBlobs.data.cursor; 270 + } while (missingBlobCursor); 271 + } 272 + const accountStatus = await this.atpAgent.com.atproto.server.checkAccountStatus(); 273 + if (!accountStatus.data.activated) { 274 + if (onStatus) onStatus('Activating your account...'); 275 + await this.atpAgent.com.atproto.server.activateAccount(); 276 + } 277 + 278 + } 279 + 280 + 281 + /** 282 + * This method signs the plc operation over to the new PDS and activates the account 283 + * Assumes you have already created a new account during the recovery process and logged in 284 + * Uses the recommended did doc from the PDS as a base and adds the users rotation key to the rotation keys array 285 + * 286 + * @param usersDid 287 + * @param additionalRotationKeysToAdd 288 + * @param prevCid 289 + * @returns {Promise<void>} 290 + */ 291 + async signRestorePlcOperation(usersDid, additionalRotationKeysToAdd = [], prevCid) { 292 + const getDidCredentials = 293 + await this.atpAgent.com.atproto.identity.getRecommendedDidCredentials(); 294 + 295 + const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? []; 296 + //Puts the provided rotation keys above the pds pro 297 + const rotationKeys = [...new Set([...(additionalRotationKeysToAdd || []), ...pdsProvidedRotationKeys])]; 298 + if (!rotationKeys) { 299 + throw new Error('No rotation keys were found to be added to the PLC'); 300 + } 301 + 302 + if (rotationKeys.length > 5) { 303 + throw new Error('You can only add up to 5 rotation keys to the PLC'); 304 + } 305 + 306 + 307 + const plcOpToSubmit = { 308 + type: 'plc_operation', 309 + ...getDidCredentials.data, 310 + prev: prevCid, 311 + rotationKeys: rotationKeys, 312 + }; 313 + 314 + 315 + const opBytes = CBOR.encode(plcOpToSubmit); 316 + const sigBytes = await this.recoveryRotationKeyPair.keypair.sign(opBytes); 317 + 318 + const signature = toBase64Url(sigBytes); 319 + 320 + const signedOperation = { 321 + ...plcOpToSubmit, 322 + sig: signature, 323 + }; 324 + 325 + await this.plcOps.pushPlcOperation(usersDid, signedOperation); 326 + await this.atpAgent.com.atproto.server.activateAccount(); 327 + 328 + } 329 + } 330 + 331 + export {Restore};
+59
packages/moover/package.json
··· 1 + { 2 + "name": "@pds-moover/moover", 3 + "version": "1.0.4", 4 + "description": "Utilities for ATProto PDS migrations and recovery", 5 + "repository": { 6 + "type": "git", 7 + "url": "https://tangled.org/@baileytownsend.dev/pds-moover", 8 + "directory": "packages/moover" 9 + }, 10 + "keywords": [ 11 + "atproto", 12 + "pds", 13 + "migration", 14 + "recovery", 15 + "PDS MOOver" 16 + ], 17 + "author": "Bailey Townsend", 18 + "license": "MIT", 19 + "type": "module", 20 + "files": [ 21 + "dist", 22 + "types" 23 + ], 24 + "main": "./dist/pds-moover.umd.cjs", 25 + "module": "./dist/pds-moover.js", 26 + "types": "./types/main.d.ts", 27 + "exports": { 28 + ".": { 29 + "types": "./types/main.d.ts", 30 + "import": "./dist/pds-moover.js", 31 + "require": "./dist/pds-moover.umd.cjs" 32 + } 33 + }, 34 + "scripts": { 35 + "dev": "vite", 36 + "build": "vite build", 37 + "gen:types": "tsc -p .", 38 + "preview": "vite preview" 39 + }, 40 + "devDependencies": { 41 + "eslint": "^9.34.0", 42 + "eslint-plugin-import": "^2.32.0", 43 + "vite": "^7.1.7" 44 + }, 45 + "dependencies": { 46 + "@atcute/cbor": "^2.2.7", 47 + "@atcute/client": "^4.0.4", 48 + "@atcute/crypto": "^2.2.5", 49 + "@atcute/did-plc": "^0.1.7", 50 + "@atcute/identity-resolver": "^1.1.3", 51 + "@atcute/lexicons": "^1.2.2", 52 + "@atcute/multibase": "^1.1.6", 53 + "@atproto/api": "^0.16.7", 54 + "@pds-moover/lexicons": "^1.0.0", 55 + "alpinejs": "^3.15.0", 56 + "vite-plugin-full-reload": "^1.2.0", 57 + "vite-rs-plugin": "1.0.1" 58 + } 59 + }
+30
packages/moover/tsconfig.json
··· 1 + { 2 + "include": [ 3 + "lib/**/*" 4 + ], 5 + "exclude": [ 6 + "node_modules", 7 + "dist", 8 + "types" 9 + ], 10 + "compilerOptions": { 11 + "allowJs": true, 12 + "checkJs": false, 13 + "declaration": true, 14 + "emitDeclarationOnly": true, 15 + "declarationMap": true, 16 + "outDir": "types", 17 + // or use "declarationDir": "types" 18 + 19 + "target": "ES2022", 20 + "lib": [ 21 + "ES2022" 22 + ], 23 + // add "DOM" if you use browser APIs 24 + "skipLibCheck": true, 25 + "module": "ESNext", 26 + "moduleResolution": "Bundler", 27 + // or "NodeNext" depending on your setup 28 + "rootDir": "lib" 29 + } 30 + }
+28
packages/moover/types/atprotoUtils.d.ts
··· 1 + export const handleResolver: CompositeHandleResolver; 2 + export const docResolver: CompositeDidDocumentResolver<"plc" | "web">; 3 + /** 4 + * Cleans the handle of @ and some other unicode characters that used to show up when copied from the profile 5 + * @param handle {string} 6 + * @returns {string} 7 + */ 8 + export function cleanHandle(handle: string): string; 9 + /** 10 + * Convince helper to resolve a handle to a did and then find the PDS url from the did document. 11 + * 12 + * @param handle 13 + * @returns {Promise<{usersDid: string, pds: string}>} 14 + */ 15 + export function handleAndPDSResolver(handle: any): Promise<{ 16 + usersDid: string; 17 + pds: string; 18 + }>; 19 + /** 20 + * Fetches the DID Web from the .well-known/did.json endpoint of the server. 21 + * Legacy and was helpful if the web ui and server are on the same domain, not as useful now 22 + * @param baseUrl 23 + * @returns {Promise<*>} 24 + */ 25 + export function fetchPDSMooverDIDWeb(baseUrl: any): Promise<any>; 26 + import { CompositeHandleResolver } from '@atcute/identity-resolver'; 27 + import { CompositeDidDocumentResolver } from '@atcute/identity-resolver'; 28 + //# sourceMappingURL=atprotoUtils.d.ts.map
+1
packages/moover/types/atprotoUtils.d.ts.map
··· 1 + {"version":3,"file":"atprotoUtils.d.ts","sourceRoot":"","sources":["../lib/atprotoUtils.js"],"names":[],"mappings":"AAOA,qDAQG;AAEH,sEAKG;AAEH;;;;GAIG;AACH,oCAHkB,MAAM,GACX,MAAM,CAMd;AAGL;;;;;GAKG;AACH,mDAFa,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,CAAC,CAqBpD;AAGD;;;;;GAKG;AACH,oDAFa,OAAO,CAAC,GAAC,CAAC,CAStB;wCAxEM,2BAA2B;6CAA3B,2BAA2B"}
+94
packages/moover/types/backup.d.ts
··· 1 + /** 2 + * JSDoc type-only import to avoid runtime import errors in the browser. 3 + */ 4 + export type InferXRPCBodyOutput = any; 5 + /** 6 + * JSDoc type-only import to avoid runtime import errors in the browser. 7 + * @typedef {import('@atcute/lexicons').InferXRPCBodyOutput} InferXRPCBodyOutput 8 + */ 9 + /** 10 + * Logic to sign up and manage backups for pdsmoover.com (or your own selfhosted instance) 11 + */ 12 + export class BackupService { 13 + /** 14 + * 15 + * @param backupDidWeb {string} - The did:web for the xrpc service for backups, defaults to did:web:pdsmoover.com 16 + */ 17 + constructor(backupDidWeb?: string); 18 + /** 19 + * 20 + * @type {Client} 21 + */ 22 + atCuteClient: Client; 23 + /** 24 + * 25 + * @type {CredentialManager} 26 + */ 27 + atCuteCredentialManager: CredentialManager; 28 + /** 29 + * The did:web for the xrpc service for backups, defaults to pdsmoover.com 30 + * @type {string} 31 + */ 32 + backupDidWeb: string; 33 + /** 34 + * Logs in and returns the backup status. 35 + * To use the rest of the BackupService, it is assumed that this has ran first, 36 + * and the user has successfully signed up. A successful login is a returned null if the user has not signed up. 37 + * or the backup status if they are 38 + * 39 + * If the server requires 2FA, 40 + * it will throw with error.error === 'AuthFactorTokenRequired'. 41 + * @param identifier {string} handle or did 42 + * @param password {string} 43 + * @param {function|null} onStatus - a function that takes a string used to update the UI. 44 + * Like (status) => console.log(status) 45 + * @param twoFactorCode {string|null} 46 + * 47 + * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>|null>} 48 + */ 49 + loginAndStatus(identifier: string, password: string, onStatus?: Function | null, twoFactorCode?: string | null): Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema["output"]> | null>; 50 + /** 51 + * Signs the user up for backups with the service 52 + * @param onStatus 53 + * @returns {Promise<void>} 54 + */ 55 + signUp(onStatus?: any): Promise<void>; 56 + /** 57 + * Requests a PLC token to be sent to the user's email, needed to add a new rotation key 58 + * @returns {Promise<void>} 59 + */ 60 + requestAPlcToken(): Promise<void>; 61 + /** 62 + * Adds a new rotation to the users did document. Assumes you are already signed in. 63 + * 64 + * WARNING: This will overwrite any existing rotation keys with the new one at the top, and the PDS key as the second one 65 + * @param plcToken {string} - PLC token from the user's email that was sent from requestAPlcToken 66 + * @param rotationKey {string} - The new rotation key to add to the user's did document 67 + * @returns {Promise<void>} 68 + */ 69 + addANewRotationKey(plcToken: string, rotationKey: string): Promise<void>; 70 + /** 71 + * 72 + * Gets the current status of the user's backup repository. 73 + * 74 + * @param onStatus {function|null} - a function that takes a string used to update the UI. 75 + * @returns {Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']>>} 76 + */ 77 + getUsersRepoStatus(onStatus?: Function | null): Promise<InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema["output"]>>; 78 + /** 79 + * Requests a backup to be run immediately for the signed-in user. Usually does, depend on the server's backup queue 80 + * @param onStatus 81 + * @returns {Promise<boolean>} 82 + */ 83 + runBackupNow(onStatus?: any): Promise<boolean>; 84 + /** 85 + * Remove (delete) the signed-in user's backup repository. this also deletes all the user's backup data. 86 + * @param onStatus 87 + * @returns {Promise<boolean>} 88 + */ 89 + removeRepo(onStatus?: any): Promise<boolean>; 90 + } 91 + import { Client } from '@atcute/client'; 92 + import { CredentialManager } from '@atcute/client'; 93 + import { ComPdsmooverBackupDescribeServer } from '@pds-moover/lexicons'; 94 + //# sourceMappingURL=backup.d.ts.map
+1
packages/moover/types/backup.d.ts.map
··· 1 + {"version":3,"file":"backup.d.ts","sourceRoot":"","sources":["../lib/backup.js"],"names":[],"mappings":";;;;AAKA;;;GAGG;AAGH;;GAEG;AACH;IACI;;;OAGG;IACH,2BAFwB,MAAM,EAmB7B;IAhBG;;;OAGG;IACH,cAFU,MAAM,CAEQ;IACxB;;;OAGG;IACH,yBAFU,iBAAiB,CAEQ;IAEnC;;;OAGG;IACH,cAFU,MAAM,CAEgB;IAIpC;;;;;;;;;;;;;;;OAeG;IACH,2BARsB,MAAM,YACR,MAAM,aACf,WAAS,IAAI,kBAEC,MAAM,GAAC,IAAI,GAEvB,OAAO,CAAC,mBAAmB,CAAC,gCAAgC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,GAAC,IAAI,CAAC,CAyDpG;IAED;;;;OAIG;IACH,wBAFa,OAAO,CAAC,IAAI,CAAC,CAczB;IAED;;;OAGG;IACH,oBAFa,OAAO,CAAC,IAAI,CAAC,CAgBzB;IAED;;;;;;;OAOG;IACH,6BAJoB,MAAM,eACH,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAgDzB;IAGD;;;;;;OAMG;IACH,8BAHoB,WAAS,IAAI,GACpB,OAAO,CAAC,mBAAmB,CAAC,gCAAgC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAe/F;IAED;;;;OAIG;IACH,8BAFa,OAAO,CAAC,OAAO,CAAC,CAkB5B;IAED;;;;OAIG;IACH,4BAFa,OAAO,CAAC,OAAO,CAAC,CAe5B;CACJ;uBAhR2C,gBAAgB;kCAAhB,gBAAgB;iDAGb,sBAAsB"}
+8
packages/moover/types/main.d.ts
··· 1 + import { Migrator } from './pdsmoover.js'; 2 + import { MissingBlobs } from './missingBlobs.js'; 3 + import { BackupService } from './backup.js'; 4 + import { PlcOps } from './plc-ops.js'; 5 + import { Restore } from './restore.js'; 6 + import { handleAndPDSResolver } from './atprotoUtils.js'; 7 + export { Migrator, MissingBlobs, BackupService, PlcOps, Restore, handleAndPDSResolver }; 8 + //# sourceMappingURL=main.d.ts.map
+1
packages/moover/types/main.d.ts.map
··· 1 + {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../lib/main.js"],"names":[],"mappings":"yBAAuB,gBAAgB;6BACZ,mBAAmB;8BAClB,aAAa;uBACpB,cAAc;wBACb,cAAc;qCACD,mBAAmB"}
+62
packages/moover/types/missingBlobs.d.ts
··· 1 + /** 2 + * Class to help find missing blobs from the did's previous PDS and import them into the current PDS 3 + */ 4 + export class MissingBlobs { 5 + /** 6 + * The user's current PDS agent 7 + * @type {AtpAgent} 8 + */ 9 + currentPdsAgent: AtpAgent; 10 + /** 11 + * The user's old PDS agent 12 + * @type {AtpAgent} 13 + */ 14 + oldPdsAgent: AtpAgent; 15 + /** 16 + * the user's did 17 + * @type {string|null} 18 + */ 19 + did: string | null; 20 + /** 21 + * The user's current PDS url 22 + * @type {null} 23 + */ 24 + currentPdsUrl: any; 25 + /** 26 + * A list of the missing cids blobs from the old PDS. In this case if a retry upload fails it gets put in this array for the ui 27 + * @type {string[]} 28 + */ 29 + missingBlobs: string[]; 30 + /** 31 + * Logs the user into the current PDS and gets the account status 32 + * @param handle {string} 33 + * @param password {string} 34 + * @param twoFactorCode {string|null} 35 + * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 36 + */ 37 + currentAgentLogin(handle: string, password: string, twoFactorCode?: string | null): Promise<{ 38 + accountStatus: OutputSchema; 39 + missingBlobsCount: number; 40 + }>; 41 + /** 42 + * Logs into the old PDS and gets the account status. 43 + * Does not need a handle 44 + * since it is assumed the user has already logged in with the current PDS and we are using their did 45 + * @param password {string} 46 + * @param twoFactorCode {string|null} 47 + * @param pdsUrl {string|null} - If you know the url of the old PDS you can pass it in here. If not it will be guessed at from plc ops 48 + * @returns {Promise<void>} 49 + */ 50 + oldAgentLogin(password: string, twoFactorCode?: string | null, pdsUrl?: string | null): Promise<void>; 51 + /** 52 + * Gets the missing blobs from the old PDS and uploads them to the current PDS 53 + * @param statusUpdateHandler {function} - A function to update the status of the migration. This is useful for showing the user the progress of the migration 54 + * @returns {Promise<{accountStatus: OutputSchema, missingBlobsCount: number}>} 55 + */ 56 + migrateMissingBlobs(statusUpdateHandler: Function): Promise<{ 57 + accountStatus: OutputSchema; 58 + missingBlobsCount: number; 59 + }>; 60 + } 61 + import { AtpAgent } from '@atproto/api'; 62 + //# sourceMappingURL=missingBlobs.d.ts.map
+1
packages/moover/types/missingBlobs.d.ts.map
··· 1 + {"version":3,"file":"missingBlobs.d.ts","sourceRoot":"","sources":["../lib/missingBlobs.js"],"names":[],"mappings":"AAIA;;GAEG;AACH;IAGQ;;;OAGG;IACH,iBAFU,QAAQ,CAES;IAC3B;;;OAGG;IACH,aAFU,QAAQ,CAEK;IACvB;;;OAGG;IACH,KAFU,MAAM,GAAC,IAAI,CAEN;IACf;;;OAGG;IACH,mBAAyB;IACzB;;;OAGG;IACH,cAFU,MAAM,EAAE,CAEI;IAI1B;;;;;;OAMG;IACH,0BALkB,MAAM,YACJ,MAAM,kBACD,MAAM,GAAC,IAAI,GACvB,OAAO,CAAC;QAAC,aAAa,EAAE,YAAY,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAC,CAAC,CA2B7E;IAED;;;;;;;;OAQG;IACH,wBALoB,MAAM,kBACD,MAAM,GAAC,IAAI,WAClB,MAAM,GAAC,IAAI,GAChB,OAAO,CAAC,IAAI,CAAC,CA+CzB;IAED;;;;OAIG;IACH,oDAFa,OAAO,CAAC;QAAC,aAAa,EAAE,YAAY,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAC,CAAC,CAyD7E;CAEJ;yBA9LsB,cAAc"}
+69
packages/moover/types/pdsmoover.d.ts
··· 1 + /** 2 + * Handles normal PDS Migrations between two PDSs that are both up. 3 + * On pdsmoover.com this is the logic for the MOOver 4 + */ 5 + export class Migrator { 6 + /** @type {AtpAgent} */ 7 + oldAgent: AtpAgent; 8 + /** @type {AtpAgent} */ 9 + newAgent: AtpAgent; 10 + /** @type {[string]} */ 11 + missingBlobs: [string]; 12 + /** @type {boolean} */ 13 + createNewAccount: boolean; 14 + /** @type {boolean} */ 15 + migrateRepo: boolean; 16 + /** @type {boolean} */ 17 + migrateBlobs: boolean; 18 + /** @type {boolean} */ 19 + migrateMissingBlobs: boolean; 20 + /** @type {boolean} */ 21 + migratePrefs: boolean; 22 + /** @type {boolean} */ 23 + migratePlcRecord: boolean; 24 + /** 25 + * This migrator is pretty cut and dry and makes a few assumptions 26 + * 1. You are using the same password between each account 27 + * 2. If this command fails for something like oauth 2fa code it throws an error and expects the same values when ran again. 28 + * 3. You can control which "actions" happen by setting the class variables to false. 29 + * 4. Each instance of the class is assumed to be for a single migration 30 + * @param {string} oldHandle - The handle you use on your old pds, something like alice.bsky.social 31 + * @param {string} password - Your password for your current login. Has to be your real password, no app password. When setting up a new account we reuse it as well for that account 32 + * @param {string} newPdsUrl - The new URL for your pds. Like https://coolnewpds.com 33 + * @param {string} newEmail - The email you want to use on the new pds (can be the same as the previous one as long as it's not already being used on the new pds) 34 + * @param {string} newHandle - The new handle you want, like alice.bsky.social, or if you already have a domain name set as a handle can use it myname.com. 35 + * @param {string|null} inviteCode - The invite code you got from the PDS you are migrating to. If null does not include one 36 + * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. Like (status) => console.log(status) 37 + * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 38 + */ 39 + migrate(oldHandle: string, password: string, newPdsUrl: string, newEmail: string, newHandle: string, inviteCode: string | null, statusUpdateHandler?: Function | null, twoFactorCode?: string | null): Promise<void>; 40 + /** 41 + * Sign and submits the PLC operation to officially migrate the account 42 + * @param {string} token - the PLC token sent in the email. If you're just wanting to run this rerun migrate with all the flags set as false except for migratePlcRecord 43 + * @param additionalRotationKeysToAdd {string[]} - additional rotation keys to add in addition to the ones provided by the new PDS. 44 + * @returns {Promise<void>} 45 + */ 46 + signPlcOperation(token: string, additionalRotationKeysToAdd?: string[]): Promise<void>; 47 + /** 48 + * Using this method assumes the Migrator class was constructed new and this was called. 49 + * Find the user's previous PDS from the PLC op logs, 50 + * logs in and deactivates their old account if it was found still active. 51 + * 52 + * @param oldHandle {string} 53 + * @param oldPassword {string} 54 + * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. 55 + * Like (status) => console.log(status) 56 + * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 57 + * @returns {Promise<void>} 58 + */ 59 + deactivateOldAccount(oldHandle: string, oldPassword: string, statusUpdateHandler?: Function | null, twoFactorCode?: string | null): Promise<void>; 60 + /** 61 + * Signs the logged-in user in this.newAgent for backups with PDS MOOver. This is usually called after migrate and signPlcOperation are successful 62 + * 63 + * @param {string} didWeb 64 + * @returns {Promise<void>} 65 + */ 66 + signUpForBackupsFromMigration(didWeb?: string): Promise<void>; 67 + } 68 + import { AtpAgent } from '@atproto/api'; 69 + //# sourceMappingURL=pdsmoover.d.ts.map
+1
packages/moover/types/pdsmoover.d.ts.map
··· 1 + {"version":3,"file":"pdsmoover.d.ts","sourceRoot":"","sources":["../lib/pdsmoover.js"],"names":[],"mappings":"AAUA;;;GAGG;AACH;IAEQ,uBAAuB;IACvB,UADW,QAAQ,CACC;IACpB,uBAAuB;IACvB,UADW,QAAQ,CACC;IACpB,uBAAuB;IACvB,cADW,CAAC,MAAM,CAAC,CACG;IAEtB,sBAAsB;IACtB,kBADW,OAAO,CACU;IAC5B,sBAAsB;IACtB,aADW,OAAO,CACK;IACvB,sBAAsB;IACtB,cADW,OAAO,CACM;IACxB,sBAAsB;IACtB,qBADW,OAAO,CACa;IAC/B,sBAAsB;IACtB,cADW,OAAO,CACM;IACxB,sBAAsB;IACtB,kBADW,OAAO,CACU;IAGhC;;;;;;;;;;;;;;OAcG;IACH,mBATW,MAAM,YACN,MAAM,aACN,MAAM,YACN,MAAM,aACN,MAAM,cACN,MAAM,GAAC,IAAI,wBACX,WAAS,IAAI,kBACb,MAAM,GAAC,IAAI,iBAuLrB;IAED;;;;;OAKG;IACH,wBAJW,MAAM,gCACsB,MAAM,EAAE,GAClC,OAAO,CAAC,IAAI,CAAC,CA4BzB;IAED;;;;;;;;;;;OAWG;IACH,gCAPqB,MAAM,eACJ,MAAM,wBAClB,WAAS,IAAI,kBAEb,MAAM,GAAC,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAiEzB;IAED;;;;;OAKG;IACH,uCAHW,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAkCzB;CACJ;yBAhYsB,cAAc"}
+121
packages/moover/types/plc-ops.d.ts
··· 1 + /** 2 + * JSDoc type-only import to avoid runtime import errors in the browser. 3 + */ 4 + export type defs = typeof defs; 5 + /** 6 + * JSDoc type-only import to avoid runtime import errors in the browser. 7 + */ 8 + export type normalizeOp = any; 9 + /** 10 + * JSDoc type-only import to avoid runtime import errors in the browser. 11 + */ 12 + export type Operation = import("@atcute/did-plc").Operation; 13 + /** 14 + * JSDoc type-only import to avoid runtime import errors in the browser. 15 + */ 16 + export type CompatibleOperation = import("@atcute/did-plc").CompatibleOperation; 17 + /** 18 + * JSDoc type-only import to avoid runtime import errors in the browser. 19 + */ 20 + export type IndexedEntryLog = import("@atcute/did-plc").IndexedEntryLog; 21 + /** 22 + * JSDoc type-only import to avoid runtime import errors in the browser. 23 + */ 24 + export type IndexedEntry = import("@atcute/did-plc").IndexedEntry; 25 + /** 26 + * Class to help with various PLC operations 27 + */ 28 + export class PlcOps { 29 + /** 30 + * 31 + * @param plcDirectoryUrl {string} - The url of the plc directory, defaults to https://plc.directory 32 + */ 33 + constructor(plcDirectoryUrl?: string); 34 + /** 35 + * The url of the plc directory 36 + * @type {string} 37 + */ 38 + plcDirectoryUrl: string; 39 + /** 40 + * Gets the current rotation keys for a user via their last PlC operation 41 + * @param did 42 + * @returns {Promise<string[]>} 43 + */ 44 + getCurrentRotationKeysForUser(did: any): Promise<string[]>; 45 + /** 46 + * Gets the last PlC operation for a user from the plc directory 47 + * @param did 48 + * @returns {Promise<{lastOperation: Operation, base: any}>} 49 + */ 50 + getLastPlcOpFromPlc(did: any): Promise<{ 51 + lastOperation: Operation; 52 + base: any; 53 + }>; 54 + /** 55 + * 56 + * @param logs {IndexedEntryLog} 57 + * @returns {{lastOperation: Operation, base: IndexedEntry}} 58 + */ 59 + getLastPlcOp(logs: IndexedEntryLog): { 60 + lastOperation: Operation; 61 + base: IndexedEntry; 62 + }; 63 + /** 64 + * Gets the plc audit logs for a user from the plc directory 65 + * @param did 66 + * @returns {Promise<IndexedEntryLog>} 67 + */ 68 + getPlcAuditLogs(did: any): Promise<IndexedEntryLog>; 69 + /** 70 + * Creates a new secp256k1 key that can be used for either rotation or verification key 71 + * @returns {Promise<{privateKey: string, publicKey: `did:key:${string}`}>} 72 + */ 73 + createANewSecp256k1(): Promise<{ 74 + privateKey: string; 75 + publicKey: `did:key:${string}`; 76 + }>; 77 + /** 78 + * Signs a new operation with the provided signing key, and information and submits it to the plc directory 79 + * @param did {string} - The user's did 80 + * @param signingRotationKey { P256PrivateKey|Secp256k1PrivateKey} - The keypair to sign the op with 81 + * @param alsoKnownAs {string[]} 82 + * @param rotationKeys {string[]} 83 + * @param pds {string} 84 + * @param verificationKey {string} - The public verification key 85 + * @param prev {string} - The previous valid operation's cid. 86 + * @returns {Promise<void>} 87 + */ 88 + signAndPublishNewOp(did: string, signingRotationKey: P256PrivateKey | Secp256k1PrivateKey, alsoKnownAs: string[], rotationKeys: string[], pds: string, verificationKey: string, prev: string): Promise<void>; 89 + /** 90 + * Takes a multi or hex based private key and returns a keypair 91 + * @param privateKeyString {string} 92 + * @param type {string} - secp256k1 or p256, needed if the private key is hex based, can be assumed if it's a multikey 93 + * @returns {Promise<{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}>} 94 + */ 95 + getKeyPair(privateKeyString: string, type?: string): Promise<{ 96 + type: string; 97 + didPublicKey: `did:key:${string}`; 98 + keypair: P256PrivateKey | Secp256k1PrivateKey; 99 + }>; 100 + /** 101 + * Submits a new operation to the plc directory 102 + * @param did {string} - The user's did 103 + * @param operation 104 + * @returns {Promise<void>} 105 + */ 106 + pushPlcOperation(did: string, operation: any): Promise<void>; 107 + /** 108 + * Creates a new service auth token for a user. This is what is used to create a new account on a PDS for your did 109 + * 110 + * @param iss The user's did 111 + * @param aud The did:web, if it's a PDS it's usually from /xrpc/com.atproto.server.describeServer 112 + * @param keypair The keypair to sign with only supporting ES256K atm 113 + * @param lxm The lxm which is usually com.atproto.server.createAccount for creating a new account 114 + * @returns {Promise<string>} 115 + */ 116 + createANewServiceAuthToken(iss: any, aud: any, keypair: any, lxm: any): Promise<string>; 117 + } 118 + import { defs } from '@atcute/did-plc'; 119 + import { P256PrivateKey } from '@atcute/crypto'; 120 + import { Secp256k1PrivateKey } from '@atcute/crypto'; 121 + //# sourceMappingURL=plc-ops.d.ts.map
+1
packages/moover/types/plc-ops.d.ts.map
··· 1 + {"version":3,"file":"plc-ops.d.ts","sourceRoot":"","sources":["../lib/plc-ops.js"],"names":[],"mappings":";;;;;;;;;;;wBAIa,OAAO,iBAAiB,EAAE,SAAS;;;;kCACnC,OAAO,iBAAiB,EAAE,mBAAmB;;;;8BAC7C,OAAO,iBAAiB,EAAE,eAAe;;;;2BACzC,OAAO,iBAAiB,EAAE,YAAY;AAgBnD;;GAEG;AACH;IACI;;;OAGG;IACH,8BAF2B,MAAM,EAQhC;IALG;;;OAGG;IACH,iBAFU,MAAM,CAEsB;IAG1C;;;;OAIG;IACH,yCAFa,OAAO,CAAC,MAAM,EAAE,CAAC,CAM7B;IAED;;;;OAIG;IACH,+BAFa,OAAO,CAAC;QAAC,aAAa,EAAE,SAAS,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAC,CAAC,CAK1D;IAED;;;;OAIG;IACH,mBAHgB,eAAe,GAClB;QAAC,aAAa,EAAE,SAAS,CAAC;QAAC,IAAI,EAAE,YAAY,CAAA;KAAC,CAK1D;IAGD;;;;OAIG;IACH,2BAFa,OAAO,CAAC,eAAe,CAAC,CAUpC;IAED;;;OAGG;IACH,uBAFa,OAAO,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,WAAW,MAAM,EAAE,CAAA;KAAC,CAAC,CAUzE;IAGD;;;;;;;;;;OAUG;IACH,yBATe,MAAM,sBACU,cAAc,GAAC,mBAAmB,eAC1C,MAAM,EAAE,gBACP,MAAM,EAAE,OACjB,MAAM,mBACM,MAAM,QACjB,MAAM,GACT,OAAO,CAAC,IAAI,CAAC,CAuCzB;IAED;;;;;OAKG;IACH,6BAJ4B,MAAM,SAClB,MAAM,GACT,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,WAAW,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,cAAc,GAAC,mBAAmB,CAAA;KAAC,CAAC,CAkDnH;IAED;;;;;OAKG;IACH,sBAJe,MAAM,mBAER,OAAO,CAAC,IAAI,CAAC,CAwBzB;IAGD;;;;;;;;OAQG;IACH,wEAFa,OAAO,CAAC,MAAM,CAAC,CAwC3B;CAGJ;qBAtR+B,iBAAiB;+BACsD,gBAAgB;oCAAhB,gBAAgB"}
+79
packages/moover/types/restore.d.ts
··· 1 + export type Operation = import("@atcute/did-plc").Operation; 2 + export class Restore { 3 + /** 4 + * 5 + * @param pdsMooverInstance {string} - The url of the pds moover instance to restore from. Defaults to https://pdsmover.com 6 + */ 7 + constructor(pdsMooverInstance?: string); 8 + /** 9 + * If you want to use a different plc directory create your own instance of the plc ops class and pass it in here 10 + * @type {PlcOps} */ 11 + plcOps: PlcOps; 12 + /** 13 + * This is the base url for the pds moover instance used to restore the files from a backup. 14 + * @type {string} 15 + */ 16 + pdsMooverInstance: string; 17 + /** 18 + * To keep it simple, only uses secp256k for the temp verification key that is used to create the new account on the new PDS 19 + * and is temporarily assigned to the user's account on PLC 20 + * @type {null|Secp256k1PrivateKeyExportable} 21 + */ 22 + tempVerificationKeypair: null | Secp256k1PrivateKeyExportable; 23 + /** @type {AtpAgent} */ 24 + atpAgent: AtpAgent; 25 + /** 26 + * The keypair that is used to sign the plc operation 27 + * @type {null|{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}} 28 + */ 29 + recoveryRotationKeyPair: null | { 30 + type: string; 31 + didPublicKey: `did:key:${string}`; 32 + keypair: P256PrivateKey | Secp256k1PrivateKey; 33 + }; 34 + /** 35 + * If this is true we are just restoring the repo and blobs. Ideally for rerunning a restore process after account recovery 36 + * @type {boolean} 37 + */ 38 + RestoreFromBackup: boolean; 39 + /** 40 + * If set to true then it will do the account recovery. Writes a temp key to the did doc, 41 + * create a new account on the new pds, and then submit a new plc op for the pds to have control (finishes the migration, can always restore the backup later) 42 + * @type {boolean} 43 + */ 44 + AccountRecovery: boolean; 45 + /** 46 + * Recovers an account with the users rotation key and restores the repo from a PDS MOOver backup 47 + * This method can fail, and the account was still recovered, it's best to check the PLC logs to see where an account stands before reruns 48 + * @param rotationKey {string} - The users private rotation key, can be a multi key or hex key 49 + * @param rotationKeyType {string} - The type of the key, secp256k1 or p256. Required if the key is in hex format, defaults to secp256k1 50 + * @param currentHandleOrDid {string} - The users current handle or did, if they don't have a DNS record it will have to be their did for success 51 + * @param newPDS {string} - The new PDS url, like https://coolnewpds.com 52 + * @param newHandle {string} - Can be the users DNS handle if it is already setup with their did, if not it's bob.mypds.com 53 + * @param newPassword {string} - The new password for the new account 54 + * @param newEmail {string} - The new email for the new account 55 + * @param inviteCode {string|null} - The invite code for the new PDS if it requires one 56 + * @param cidToRestoreTo {string|null} - The cid of the plc op to restore to, used mostly to revert a fraudulent plc op. Want to give it the last valid operations cid 57 + * @param onStatus {function|null} - A function that takes a string used to update the UI. Like (status) => console.log(status) 58 + * @returns {Promise<void>} If there is a failure during restoring the back up (after the status Success! Restoring your repo...) then your account is most likely 59 + * recovered and future runs need to have the RestoreFromBackup flag set to true and AccountRecovery set to false. 60 + */ 61 + recover(rotationKey: string, rotationKeyType: string, currentHandleOrDid: string, newPDS: string, newHandle: string, newPassword: string, newEmail: string, inviteCode: string | null, cidToRestoreTo?: string | null, onStatus?: Function | null): Promise<void>; 62 + /** 63 + * This method signs the plc operation over to the new PDS and activates the account 64 + * Assumes you have already created a new account during the recovery process and logged in 65 + * Uses the recommended did doc from the PDS as a base and adds the users rotation key to the rotation keys array 66 + * 67 + * @param usersDid 68 + * @param additionalRotationKeysToAdd 69 + * @param prevCid 70 + * @returns {Promise<void>} 71 + */ 72 + signRestorePlcOperation(usersDid: any, additionalRotationKeysToAdd: any[], prevCid: any): Promise<void>; 73 + } 74 + import { PlcOps } from './plc-ops.js'; 75 + import { Secp256k1PrivateKeyExportable } from '@atcute/crypto'; 76 + import { AtpAgent } from '@atproto/api'; 77 + import { P256PrivateKey } from '@atcute/crypto'; 78 + import { Secp256k1PrivateKey } from '@atcute/crypto'; 79 + //# sourceMappingURL=restore.d.ts.map
+1
packages/moover/types/restore.d.ts.map
··· 1 + {"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../lib/restore.js"],"names":[],"mappings":"wBACa,OAAO,iBAAiB,EAAE,SAAS;AAWhD;IAEI;;;OAGG;IACH,gCAF6B,MAAM,EA0ClC;IAvCG;;wBAEoB;IACpB,QADU,MAAM,CACU;IAE1B;;;OAGG;IACH,mBAFU,MAAM,CAE0B;IAE1C;;;;OAIG;IACH,yBAFU,IAAI,GAAC,6BAA6B,CAET;IAEnC,uBAAuB;IACvB,UADW,QAAQ,CACC;IAEpB;;;OAGG;IACH,yBAFU,IAAI,GAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,WAAW,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,cAAc,GAAC,mBAAmB,CAAA;KAAC,CAE1E;IAEnC;;;OAGG;IACH,mBAFU,OAAO,CAEY;IAE7B;;;;OAIG;IACH,iBAFU,OAAO,CAEU;IAG/B;;;;;;;;;;;;;;;OAeG;IACH,qBAbuB,MAAM,mBACF,MAAM,sBACH,MAAM,UAClB,MAAM,aACH,MAAM,eACJ,MAAM,YACT,MAAM,cACJ,MAAM,GAAC,IAAI,mBACP,MAAM,GAAC,IAAI,aACjB,WAAS,IAAI,GACpB,OAAO,CAAC,IAAI,CAAC,CA4MzB;IAGD;;;;;;;;;OASG;IACH,0FAFa,OAAO,CAAC,IAAI,CAAC,CAuCzB;CACJ;uBAnUoB,cAAc;8CAGS,gBAAgB;yBADrC,cAAc;+BAJa,gBAAgB;oCAAhB,gBAAgB"}
+28
packages/moover/vite.config.js
··· 1 + import {dirname, resolve} from 'node:path' 2 + import {fileURLToPath} from 'node:url' 3 + import {defineConfig} from 'vite' 4 + 5 + const __dirname = dirname(fileURLToPath(import.meta.url)) 6 + 7 + export default defineConfig({ 8 + build: { 9 + lib: { 10 + entry: resolve(__dirname, 'lib/main.js'), 11 + name: '@pds-moover/moover', 12 + // the proper extensions will be added 13 + fileName: 'pds-moover', 14 + }, 15 + rollupOptions: { 16 + // // make sure to externalize deps that shouldn't be bundled 17 + // // into your library 18 + // external: ['vue'], 19 + // output: { 20 + // // Provide global variables to use in the UMD build 21 + // // for externalized deps 22 + // globals: { 23 + // vue: 'Vue', 24 + // }, 25 + // }, 26 + }, 27 + }, 28 + })
+1 -1
shared/src/db/models.rs
··· 30 30 pub created_at: DateTime<Utc>, 31 31 pub account_did: String, 32 32 pub size: i64, 33 - pub blob_type: BlobType, 33 + pub r#type: BlobType, 34 34 pub cid_or_rev: String, 35 35 } 36 36
+1 -2
shared/src/jobs/account_backup.rs
··· 68 68 .await 69 69 .map_err(|e| Error::Failed(Arc::new(Box::new(e))))?; 70 70 71 - let missing_cids = filter_missing_blob_cids(&pool, &resp.cids) 71 + let missing_cids = filter_missing_blob_cids(&pool, &resp.cids, &job.did) 72 72 .await 73 73 .map_err(|e| Error::Failed(Arc::new(Box::new(AnyhowErrorWrapper(e)))))?; 74 74 75 75 // Process missing CIDs in batches of 5 76 76 let mut processed = 0; 77 77 78 - //TODO need to fix where if it fails still tries the other 4 chunks 79 78 for chunk in missing_cids.chunks(5) { 80 79 processed += chunk.len(); 81 80 let last_chunk = processed >= missing_cids.len();
+66 -25
shared/src/jobs/mod.rs
··· 4 4 pub mod scheduled_back_up_start; 5 5 pub mod start_all_backup; 6 6 pub mod upload_blob; 7 + pub mod verify_backups; 7 8 8 9 use crate::db::models; 9 10 use crate::db::models::BlobModel; ··· 64 65 } 65 66 66 67 /// Given a list of CIDs, returns those that are NOT already present in the blobs table 67 - /// with blob type = 'blob'. The returned order matches the input order and duplicates in the 68 - /// input are preserved if they are not present in the DB. 68 + /// with blob type = 'blob' and matches the user's did in the case of duplicate blobs for each user 69 69 pub async fn filter_missing_blob_cids( 70 70 pool: &Pool<Postgres>, 71 - cids: &[String], 71 + cids: &Vec<String>, 72 + users_did: &String, 72 73 ) -> anyhow::Result<Vec<String>> { 73 74 if cids.is_empty() { 74 75 return Ok(Vec::new()); ··· 76 77 77 78 // Fetch the subset of provided CIDs that already exist as type 'blob' 78 79 let existing: Vec<String> = sqlx::query_scalar( 79 - r#"SELECT cid_or_rev FROM blobs WHERE type = $1 AND cid_or_rev = ANY($2)"#, 80 + r#"SELECT cid_or_rev FROM blobs WHERE type = $1 AND cid_or_rev = ANY($2) AND account_did = $3"#, 80 81 ) 81 82 .bind(crate::db::models::BlobType::Blob) 82 - .bind(cids) 83 + .bind(&cids) 84 + .bind(users_did) 83 85 .fetch_all(pool) 84 86 .await?; 85 87 ··· 100 102 size: i64, 101 103 blob_type: models::BlobType, 102 104 ) -> anyhow::Result<models::BlobModel> { 103 - // For repo blobs, perform an upsert on the unique `cid` to avoid duplicate inserts 104 - // and to refresh metadata if the same cid is seen again. 105 - Ok(sqlx::query_as::<_, BlobModel>( 106 - r#" 107 - INSERT INTO blobs (account_did, size, type, cid_or_rev) 108 - VALUES ($1, $2, $3, $4) 109 - ON CONFLICT (cid_or_rev) DO UPDATE 110 - SET account_did = EXCLUDED.account_did, 111 - size = EXCLUDED.size, 112 - type = EXCLUDED.type, 113 - cid_or_rev = EXCLUDED.cid_or_rev 114 - RETURNING id, created_at, account_did, size, type AS blob_type, cid_or_rev 115 - "#, 116 - ) 117 - .bind(did) 118 - .bind(size) 119 - .bind(blob_type) 120 - .bind(cid_or_rev) 121 - .fetch_one(pool) 122 - .await?) 105 + match blob_type { 106 + //On repo we need to upsert on did 107 + models::BlobType::Repo => { 108 + // First try to update an existing 'repo' blob row for this DID. 109 + if let Some(updated) = sqlx::query_as::<_, BlobModel>( 110 + r#" 111 + UPDATE blobs 112 + SET size = $2, 113 + type = $3, 114 + cid_or_rev = $4 115 + WHERE account_did = $1 AND type = $3 116 + RETURNING id, created_at, account_did, size, type, cid_or_rev 117 + "#, 118 + ) 119 + .bind(&did) 120 + .bind(size) 121 + .bind(&blob_type) 122 + .bind(&cid_or_rev) 123 + .fetch_optional(pool) 124 + .await? 125 + { 126 + Ok(updated) 127 + } else { 128 + // If no row was updated, insert a new one for this DID and repo type. 129 + Ok(sqlx::query_as::<_, BlobModel>( 130 + r#" 131 + INSERT INTO blobs (account_did, size, type, cid_or_rev) 132 + VALUES ($1, $2, $3, $4) 133 + RETURNING id, created_at, account_did, size, type, cid_or_rev 134 + "#, 135 + ) 136 + .bind(did) 137 + .bind(size) 138 + .bind(blob_type) 139 + .bind(cid_or_rev) 140 + .fetch_one(pool) 141 + .await?) 142 + } 143 + } 144 + //on blob we upsert on cid (shouldnt happen ideally) 145 + models::BlobType::Blob | _ => Ok(sqlx::query_as::<_, BlobModel>( 146 + r#" 147 + INSERT INTO blobs (account_did, size, type, cid_or_rev) 148 + VALUES ($1, $2, $3, $4) 149 + ON CONFLICT (cid_or_rev) DO UPDATE 150 + SET account_did = EXCLUDED.account_did, 151 + size = EXCLUDED.size, 152 + type = EXCLUDED.type, 153 + cid_or_rev = EXCLUDED.cid_or_rev 154 + RETURNING id, created_at, account_did, size, type, cid_or_rev 155 + "#, 156 + ) 157 + .bind(did) 158 + .bind(size) 159 + .bind(blob_type) 160 + .bind(cid_or_rev) 161 + .fetch_one(pool) 162 + .await?), 163 + } 123 164 } 124 165 125 166 /// Look up the user's account by DID and return their repo_rev, if present.
+9 -11
shared/src/jobs/scheduled_back_up_start.rs
··· 11 11 pub struct ScheduledBackUpStartJobContext; 12 12 13 13 /// This scheduled job finds: 14 - /// - accounts that have not been backed up in the last 24 hours and have pds_sign_up = false, 14 + /// - accounts that have not been backed up in the last 6 hours and have pds_sign_up = false, 15 15 /// and enqueues AccountBackup jobs for them; 16 - /// - pds_hosts that are active and have not started a backup in the last 24 hours (tracked via 16 + /// - pds_hosts that are active and have not started a backup in the last 6 hours (tracked via 17 17 /// pds_hosts.last_backup_start), and enqueues PdsBackup jobs for each. 18 18 pub async fn scheduled_back_up_start_job( 19 19 _job: ScheduledBackUpStartJobContext, ··· 21 21 ) -> Result<(), Error> { 22 22 log::info!("Starting a backup for the whole instance"); 23 23 // Record the start of a whole-network backup run 24 - sqlx::query( 25 - r#"INSERT INTO network_backup_runs DEFAULT VALUES"#, 26 - ) 27 - .execute(&*pool) 28 - .await 29 - .map_err(|e| Error::Failed(Arc::new(Box::new(e))))?; 24 + sqlx::query(r#"INSERT INTO network_backup_runs DEFAULT VALUES"#) 25 + .execute(&*pool) 26 + .await 27 + .map_err(|e| Error::Failed(Arc::new(Box::new(e))))?; 30 28 // 1) Query accounts needing backup 31 - // Condition: pds_sign_up = false AND (last_backup is NULL OR older than 24h) 29 + // Condition: pds_sign_up = false AND (last_backup is NULL OR older than 6h) 32 30 // We include did and pds_host to build AccountBackupJobContext 33 31 let accounts: Vec<(String, String)> = sqlx::query_as( 34 32 r#" 35 33 SELECT did, pds_host 36 34 FROM accounts 37 35 WHERE pds_sign_up = FALSE 38 - AND (last_backup IS NULL OR last_backup < NOW() - INTERVAL '24 HOURS') 36 + AND (last_backup IS NULL OR last_backup < NOW() - INTERVAL '6 HOURS') 39 37 "#, 40 38 ) 41 39 .fetch_all(&*pool) ··· 58 56 SELECT pds_host 59 57 FROM pds_hosts 60 58 WHERE active = TRUE 61 - AND (last_backup_start IS NULL OR last_backup_start < NOW() - INTERVAL '24 HOURS') 59 + AND (last_backup_start IS NULL OR last_backup_start < NOW() - INTERVAL '6 HOURS') 62 60 "#, 63 61 ) 64 62 .fetch_all(&*pool)
+76
shared/src/jobs/verify_backups.rs
··· 1 + use crate::db::models::{BlobModel, BlobType}; 2 + use crate::storage::{blob_backup_path, repo_backup_path}; 3 + use anyhow::Result; 4 + use s3::Bucket; 5 + use sqlx::{Pool, Postgres}; 6 + 7 + /// Verifies that all blobs in the database exist in S3. 8 + /// Returns a Vec of missing blob information (did, cid_or_rev, blob_type). 9 + pub async fn verify_backups(pool: &Pool<Postgres>, s3_bucket: &Bucket) -> Result<Vec<MissingBlob>> { 10 + // Get all blobs from the database 11 + let blobs = sqlx::query_as::<_, BlobModel>("SELECT * FROM blobs ORDER BY created_at") 12 + .fetch_all(pool) 13 + .await?; 14 + 15 + let total_blobs = blobs.len(); 16 + log::info!("Checking {} blobs in S3...", total_blobs); 17 + 18 + let mut missing_blobs = Vec::new(); 19 + let mut checked = 0; 20 + 21 + for blob in blobs { 22 + checked += 1; 23 + if checked % 100 == 0 { 24 + log::info!("Checked {}/{} blobs...", checked, total_blobs); 25 + } 26 + 27 + let s3_path = match blob.r#type { 28 + BlobType::Repo => repo_backup_path(blob.account_did.clone()), 29 + BlobType::Blob => blob_backup_path(blob.account_did.clone(), blob.cid_or_rev.clone()), 30 + BlobType::Prefs => { 31 + // Handle prefs if needed - for now skip 32 + log::debug!("Skipping prefs blob: {:?}", blob); 33 + continue; 34 + } 35 + }; 36 + 37 + // Check if the object exists in S3 38 + match s3_bucket.head_object(&s3_path).await { 39 + Ok(_) => { 40 + // Object exists, all good 41 + log::debug!("✓ Found: {}", s3_path); 42 + } 43 + Err(e) => { 44 + // Check if it's a 404 error (not found) 45 + if e.to_string().contains("404") { 46 + log::warn!("✗ Missing: {}", s3_path); 47 + missing_blobs.push(MissingBlob { 48 + did: blob.account_did.clone(), 49 + cid_or_rev: blob.cid_or_rev.clone(), 50 + blob_type: blob.r#type.clone(), 51 + s3_path, 52 + }); 53 + } else { 54 + // Some other error - log it but don't count as missing 55 + log::error!("Error checking {}: {}", s3_path, e); 56 + } 57 + } 58 + } 59 + } 60 + 61 + log::info!( 62 + "Verification complete. Checked {} blobs, found {} missing.", 63 + checked, 64 + missing_blobs.len() 65 + ); 66 + 67 + Ok(missing_blobs) 68 + } 69 + 70 + #[derive(Debug, Clone)] 71 + pub struct MissingBlob { 72 + pub did: String, 73 + pub cid_or_rev: String, 74 + pub blob_type: BlobType, 75 + pub s3_path: String, 76 + }
+23
web-ui/.gitignore
··· 1 + node_modules 2 + 3 + # Output 4 + .output 5 + .vercel 6 + .netlify 7 + .wrangler 8 + /.svelte-kit 9 + /build 10 + 11 + # OS 12 + .DS_Store 13 + Thumbs.db 14 + 15 + # Env 16 + .env.local 17 + .env.* 18 + !.env.example 19 + !.env.test 20 + 21 + # Vite 22 + vite.config.js.timestamp-* 23 + vite.config.ts.timestamp-*
+1
web-ui/.npmrc
··· 1 + engine-strict=true
+38
web-ui/README.md
··· 1 + # sv 2 + 3 + Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). 4 + 5 + ## Creating a project 6 + 7 + If you're seeing this, you've probably already done this step. Congrats! 8 + 9 + ```sh 10 + # create a new project in the current directory 11 + npx sv create 12 + 13 + # create a new project in my-app 14 + npx sv create my-app 15 + ``` 16 + 17 + ## Developing 18 + 19 + Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 20 + 21 + ```sh 22 + npm run dev 23 + 24 + # or start the server and open the app in a new browser tab 25 + npm run dev -- --open 26 + ``` 27 + 28 + ## Building 29 + 30 + To create a production version of your app: 31 + 32 + ```sh 33 + npm run build 34 + ``` 35 + 36 + You can preview the production build with `npm run preview`. 37 + 38 + > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
+588
web-ui/bun.lock
··· 1 + { 2 + "lockfileVersion": 1, 3 + "workspaces": { 4 + "": { 5 + "name": "web-ui", 6 + "dependencies": { 7 + "@atcute/client": "^4.0.5", 8 + "@atcute/lexicons": "^1.2.2", 9 + "@pds-moover/lexicons": "^1.0.1", 10 + "@pds-moover/moover": "^1.0.0", 11 + }, 12 + "devDependencies": { 13 + "@eslint/compat": "^1.4.0", 14 + "@eslint/js": "^9.36.0", 15 + "@sveltejs/adapter-auto": "^6.1.0", 16 + "@sveltejs/adapter-node": "^5.4.0", 17 + "@sveltejs/kit": "^2.43.2", 18 + "@sveltejs/vite-plugin-svelte": "^6.2.0", 19 + "@types/node": "^22", 20 + "eslint": "^9.36.0", 21 + "eslint-plugin-svelte": "^3.12.4", 22 + "globals": "^16.4.0", 23 + "svelte": "^5.39.5", 24 + "svelte-check": "^4.3.2", 25 + "typescript": "^5.9.2", 26 + "typescript-eslint": "^8.44.1", 27 + "vite": "^7.1.7", 28 + }, 29 + }, 30 + }, 31 + "packages": { 32 + "@atcute/cbor": ["@atcute/cbor@2.2.7", "", { "dependencies": { "@atcute/cid": "^2.2.5", "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.5" } }, "sha512-/mwAF0gnokOphceZqFq3uzMGdd8sbw5y6bxF8CRutRkCCUcpjjpJc5fkLwhxyGgOveF3mZuHE6p7t/+IAqb7Aw=="], 33 + 34 + "@atcute/cid": ["@atcute/cid@2.2.6", "", { "dependencies": { "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.5" } }, "sha512-bTAHHbJ24p+E//V4KCS4xdmd39o211jJswvqQOevj7vk+5IYcgDLx1ryZWZ1sEPOo9x875li/kj5gpKL14RDwQ=="], 35 + 36 + "@atcute/client": ["@atcute/client@4.0.5", "", { "dependencies": { "@atcute/identity": "^1.1.1", "@atcute/lexicons": "^1.2.2" } }, "sha512-R8Qen8goGmEkynYGg2m6XFlVmz0GTDvQ+9w+4QqOob+XMk8/WDpF4aImev7WKEde/rV2gjcqW7zM8E6W9NShDA=="], 37 + 38 + "@atcute/crypto": ["@atcute/crypto@2.2.5", "", { "dependencies": { "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.5", "@noble/secp256k1": "^2.3.0" } }, "sha512-9CbQ9cJ68XewsbLrgdmWQS2uDD9D0hizWFJ3OOZ16TCuARREmzKEpFgHlMxPswR3bDxjwfiXzmYUlHaTqsnxRQ=="], 39 + 40 + "@atcute/did-plc": ["@atcute/did-plc@0.1.7", "", { "dependencies": { "@atcute/cbor": "^2.2.6", "@atcute/cid": "^2.2.4", "@atcute/crypto": "^2.2.5", "@atcute/identity": "^1.1.1", "@atcute/lexicons": "^1.2.2", "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.5", "@badrap/valita": "^0.4.6" } }, "sha512-a7yOQNqViae3rB5/xa3U0EPJbFD9l8zOHXx6XASZ5F8+Vy2uTgXK3omurpNZ5UxRpy1ni1AMhSohXr61cqWbkg=="], 41 + 42 + "@atcute/identity": ["@atcute/identity@1.1.1", "", { "dependencies": { "@atcute/lexicons": "^1.2.2", "@badrap/valita": "^0.4.6" } }, "sha512-zax42n693VEhnC+5tndvO2KLDTMkHOz8UExwmklvJv7R9VujfEwiSWhcv6Jgwb3ellaG8wjiQ1lMOIjLLvwh0Q=="], 43 + 44 + "@atcute/identity-resolver": ["@atcute/identity-resolver@1.1.4", "", { "dependencies": { "@atcute/lexicons": "^1.2.2", "@atcute/util-fetch": "^1.0.3", "@badrap/valita": "^0.4.6" }, "peerDependencies": { "@atcute/identity": "^1.0.0" } }, "sha512-/SVh8vf2cXFJenmBnGeYF2aY3WGQm3cJeew5NWTlkqoy3LvJ5wkvKq9PWu4Tv653VF40rPOp6LOdVr9Fa+q5rA=="], 45 + 46 + "@atcute/lexicons": ["@atcute/lexicons@1.2.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "esm-env": "^1.2.2" } }, "sha512-bgEhJq5Z70/0TbK5sx+tAkrR8FsCODNiL2gUEvS5PuJfPxmFmRYNWaMGehxSPaXWpU2+Oa9ckceHiYbrItDTkA=="], 47 + 48 + "@atcute/multibase": ["@atcute/multibase@1.1.6", "", { "dependencies": { "@atcute/uint8array": "^1.0.5" } }, "sha512-HBxuCgYLKPPxETV0Rot4VP9e24vKl8JdzGCZOVsDaOXJgbRZoRIF67Lp0H/OgnJeH/Xpva8Z5ReoTNJE5dn3kg=="], 49 + 50 + "@atcute/uint8array": ["@atcute/uint8array@1.0.5", "", {}, "sha512-XLWWxoR2HNl2qU+FCr0rp1APwJXci7HnzbOQLxK55OaMNBXZ19+xNC5ii4QCsThsDxa4JS/JTzuiQLziITWf2Q=="], 51 + 52 + "@atcute/util-fetch": ["@atcute/util-fetch@1.0.3", "", { "dependencies": { "@badrap/valita": "^0.4.6" } }, "sha512-f8zzTb/xlKIwv2OQ31DhShPUNCmIIleX6p7qIXwWwEUjX6x8skUtpdISSjnImq01LXpltGV5y8yhV4/Mlb7CRQ=="], 53 + 54 + "@atproto/api": ["@atproto/api@0.16.11", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/lexicon": "^0.5.1", "@atproto/syntax": "^0.4.1", "@atproto/xrpc": "^0.7.5", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", "zod": "^3.23.8" } }, "sha512-1dhfQNHiclb102RW+Ea8Nft5olfqU0Ev/vlQaSX6mWNo1aP5zT+sPODJ8+BTUOYk3vcuvL7QMkqA/rLYy2PMyw=="], 55 + 56 + "@atproto/common-web": ["@atproto/common-web@0.4.3", "", { "dependencies": { "graphemer": "^1.4.0", "multiformats": "^9.9.0", "uint8arrays": "3.0.0", "zod": "^3.23.8" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 57 + 58 + "@atproto/lexicon": ["@atproto/lexicon@0.5.1", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/syntax": "^0.4.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A=="], 59 + 60 + "@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw=="], 61 + 62 + "@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA=="], 63 + 64 + "@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="], 65 + 66 + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="], 67 + 68 + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="], 69 + 70 + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="], 71 + 72 + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="], 73 + 74 + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="], 75 + 76 + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="], 77 + 78 + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="], 79 + 80 + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="], 81 + 82 + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="], 83 + 84 + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="], 85 + 86 + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="], 87 + 88 + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="], 89 + 90 + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="], 91 + 92 + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="], 93 + 94 + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="], 95 + 96 + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="], 97 + 98 + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="], 99 + 100 + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="], 101 + 102 + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="], 103 + 104 + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="], 105 + 106 + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="], 107 + 108 + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="], 109 + 110 + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="], 111 + 112 + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="], 113 + 114 + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="], 115 + 116 + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="], 117 + 118 + "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], 119 + 120 + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], 121 + 122 + "@eslint/compat": ["@eslint/compat@1.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0" }, "peerDependencies": { "eslint": "^8.40 || 9" }, "optionalPeers": ["eslint"] }, "sha512-DEzm5dKeDBPm3r08Ixli/0cmxr8LkRdwxMRUIJBlSCpAwSrvFEJpVBzV+66JhDxiaqKxnRzCXhtiMiczF7Hglg=="], 123 + 124 + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], 125 + 126 + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.1", "", { "dependencies": { "@eslint/core": "^0.16.0" } }, "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw=="], 127 + 128 + "@eslint/core": ["@eslint/core@0.16.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q=="], 129 + 130 + "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], 131 + 132 + "@eslint/js": ["@eslint/js@9.38.0", "", {}, "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A=="], 133 + 134 + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], 135 + 136 + "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0", "levn": "^0.4.1" } }, "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A=="], 137 + 138 + "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], 139 + 140 + "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="], 141 + 142 + "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="], 143 + 144 + "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], 145 + 146 + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], 147 + 148 + "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], 149 + 150 + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], 151 + 152 + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], 153 + 154 + "@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=="], 155 + 156 + "@noble/secp256k1": ["@noble/secp256k1@2.3.0", "", {}, "sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw=="], 157 + 158 + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], 159 + 160 + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], 161 + 162 + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], 163 + 164 + "@pds-moover/lexicons": ["@pds-moover/lexicons@1.0.1", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "@atproto/xrpc": "^0.7.5" } }, "sha512-fv5b/DtHM7FEo/JklyF9gdK0ainlb6mWjWrBe6cmSAeg9G/4O2jBlQUOqfOAICY9gOcrCpkOrk9PHgGw//JQ2A=="], 165 + 166 + "@pds-moover/moover": ["@pds-moover/moover@1.0.0", "", { "dependencies": { "@atcute/cbor": "^2.2.7", "@atcute/client": "^4.0.4", "@atcute/crypto": "^2.2.5", "@atcute/did-plc": "^0.1.7", "@atcute/identity-resolver": "^1.1.3", "@atcute/lexicons": "^1.2.2", "@atcute/multibase": "^1.1.6", "@atproto/api": "^0.16.7", "@pds-moover/lexicons": "^1.0.0", "alpinejs": "^3.15.0", "vite-plugin-full-reload": "^1.2.0", "vite-rs-plugin": "1.0.1" } }, "sha512-RTwxM7K8M3PbFladdrnMv2N7qNz5ikWR/trw3wipijDfZl7VcpvPlFliEhIO1JqHnr2PTRc9ORXQsFGgCjSmRg=="], 167 + 168 + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], 169 + 170 + "@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.9", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA=="], 171 + 172 + "@rollup/plugin-json": ["@rollup/plugin-json@6.1.0", "", { "dependencies": { "@rollup/pluginutils": "^5.1.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA=="], 173 + 174 + "@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.3", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg=="], 175 + 176 + "@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=="], 177 + 178 + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="], 179 + 180 + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="], 181 + 182 + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="], 183 + 184 + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="], 185 + 186 + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="], 187 + 188 + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="], 189 + 190 + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="], 191 + 192 + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="], 193 + 194 + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="], 195 + 196 + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="], 197 + 198 + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="], 199 + 200 + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="], 201 + 202 + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="], 203 + 204 + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="], 205 + 206 + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="], 207 + 208 + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="], 209 + 210 + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="], 211 + 212 + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="], 213 + 214 + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="], 215 + 216 + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="], 217 + 218 + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="], 219 + 220 + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="], 221 + 222 + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], 223 + 224 + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.6", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ=="], 225 + 226 + "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@6.1.1", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-cBNt4jgH4KuaNO5gRSB2CZKkGtz+OCZ8lPjRQGjhvVUD4akotnj2weUia6imLl2v07K3IgsQRyM36909miSwoQ=="], 227 + 228 + "@sveltejs/adapter-node": ["@sveltejs/adapter-node@5.4.0", "", { "dependencies": { "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.0", "rollup": "^4.9.5" }, "peerDependencies": { "@sveltejs/kit": "^2.4.0" } }, "sha512-NMsrwGVPEn+J73zH83Uhss/hYYZN6zT3u31R3IHAn3MiKC3h8fjmIAhLfTSOeNHr5wPYfjjMg8E+1gyFgyrEcQ=="], 229 + 230 + "@sveltejs/kit": ["@sveltejs/kit@2.48.0", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-GAAbkWrbRJvysL7+HOWs5v/+TmnRcEQPeED2sUcDFTHpPvRYADEtScL6x8hWuKp0DKauJVaVJLTjQVy9e7cMiw=="], 231 + 232 + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@6.2.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "debug": "^4.4.1", "deepmerge": "^4.3.1", "magic-string": "^0.30.17", "vitefu": "^1.1.1" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ=="], 233 + 234 + "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@5.0.1", "", { "dependencies": { "debug": "^4.4.1" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", "svelte": "^5.0.0", "vite": "^6.3.0 || ^7.0.0" } }, "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA=="], 235 + 236 + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], 237 + 238 + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], 239 + 240 + "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], 241 + 242 + "@types/node": ["@types/node@22.18.12", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog=="], 243 + 244 + "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], 245 + 246 + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.46.2", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/type-utils": "8.46.2", "@typescript-eslint/utils": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.46.2", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w=="], 247 + 248 + "@typescript-eslint/parser": ["@typescript-eslint/parser@8.46.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g=="], 249 + 250 + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.46.2", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.46.2", "@typescript-eslint/types": "^8.46.2", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg=="], 251 + 252 + "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2" } }, "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA=="], 253 + 254 + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.46.2", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag=="], 255 + 256 + "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/utils": "8.46.2", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA=="], 257 + 258 + "@typescript-eslint/types": ["@typescript-eslint/types@8.46.2", "", {}, "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ=="], 259 + 260 + "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.46.2", "", { "dependencies": { "@typescript-eslint/project-service": "8.46.2", "@typescript-eslint/tsconfig-utils": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ=="], 261 + 262 + "@typescript-eslint/utils": ["@typescript-eslint/utils@8.46.2", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg=="], 263 + 264 + "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.46.2", "", { "dependencies": { "@typescript-eslint/types": "8.46.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w=="], 265 + 266 + "@vue/reactivity": ["@vue/reactivity@3.1.5", "", { "dependencies": { "@vue/shared": "3.1.5" } }, "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg=="], 267 + 268 + "@vue/shared": ["@vue/shared@3.1.5", "", {}, "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="], 269 + 270 + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], 271 + 272 + "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], 273 + 274 + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], 275 + 276 + "alpinejs": ["alpinejs@3.15.1", "", { "dependencies": { "@vue/reactivity": "~3.1.1" } }, "sha512-HLO1TtiE92VajFHtLLPK8BWaK1YepV/uj31UrfoGnQ00lyFOJZ+oVY3F0DghPAwvg8sLU79pmjGQSytERa2gEg=="], 277 + 278 + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 279 + 280 + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], 281 + 282 + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], 283 + 284 + "await-lock": ["await-lock@2.2.2", "", {}, "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw=="], 285 + 286 + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], 287 + 288 + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], 289 + 290 + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], 291 + 292 + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], 293 + 294 + "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="], 295 + 296 + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], 297 + 298 + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], 299 + 300 + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 301 + 302 + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 303 + 304 + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 305 + 306 + "commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="], 307 + 308 + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], 309 + 310 + "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], 311 + 312 + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], 313 + 314 + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], 315 + 316 + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], 317 + 318 + "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], 319 + 320 + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], 321 + 322 + "devalue": ["devalue@5.4.2", "", {}, "sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw=="], 323 + 324 + "esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="], 325 + 326 + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], 327 + 328 + "eslint": ["eslint@9.38.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.1", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.38.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw=="], 329 + 330 + "eslint-plugin-svelte": ["eslint-plugin-svelte@3.12.5", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.6.1", "@jridgewell/sourcemap-codec": "^1.5.0", "esutils": "^2.0.3", "globals": "^16.0.0", "known-css-properties": "^0.37.0", "postcss": "^8.4.49", "postcss-load-config": "^3.1.4", "postcss-safe-parser": "^7.0.0", "semver": "^7.6.3", "svelte-eslint-parser": "^1.4.0" }, "peerDependencies": { "eslint": "^8.57.1 || ^9.0.0", "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-4KRG84eAHQfYd9OjZ1K7sCHy0nox+9KwT+s5WCCku3jTim5RV4tVENob274nCwIaApXsYPKAUAZFBxKZ3Wyfjw=="], 331 + 332 + "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="], 333 + 334 + "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], 335 + 336 + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 337 + 338 + "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="], 339 + 340 + "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="], 341 + 342 + "esrap": ["esrap@2.1.1", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-ebTT9B6lOtZGMgJ3o5r12wBacHctG7oEWazIda8UlPfA3HD/Wrv8FdXoVo73vzdpwCxNyXjPauyN2bbJzMkB9A=="], 343 + 344 + "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="], 345 + 346 + "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], 347 + 348 + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], 349 + 350 + "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], 351 + 352 + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], 353 + 354 + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], 355 + 356 + "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], 357 + 358 + "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], 359 + 360 + "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], 361 + 362 + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 363 + 364 + "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], 365 + 366 + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], 367 + 368 + "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="], 369 + 370 + "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="], 371 + 372 + "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="], 373 + 374 + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 375 + 376 + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], 377 + 378 + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], 379 + 380 + "globals": ["globals@16.4.0", "", {}, "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw=="], 381 + 382 + "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="], 383 + 384 + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], 385 + 386 + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 387 + 388 + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], 389 + 390 + "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], 391 + 392 + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], 393 + 394 + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], 395 + 396 + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 397 + 398 + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 399 + 400 + "is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="], 401 + 402 + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], 403 + 404 + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], 405 + 406 + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], 407 + 408 + "iso-datestring-validator": ["iso-datestring-validator@2.2.2", "", {}, "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA=="], 409 + 410 + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], 411 + 412 + "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="], 413 + 414 + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], 415 + 416 + "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], 417 + 418 + "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], 419 + 420 + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], 421 + 422 + "known-css-properties": ["known-css-properties@0.37.0", "", {}, "sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ=="], 423 + 424 + "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], 425 + 426 + "lilconfig": ["lilconfig@2.1.0", "", {}, "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="], 427 + 428 + "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], 429 + 430 + "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="], 431 + 432 + "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], 433 + 434 + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 435 + 436 + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], 437 + 438 + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], 439 + 440 + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], 441 + 442 + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], 443 + 444 + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], 445 + 446 + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 447 + 448 + "multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 449 + 450 + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], 451 + 452 + "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], 453 + 454 + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], 455 + 456 + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], 457 + 458 + "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], 459 + 460 + "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="], 461 + 462 + "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], 463 + 464 + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], 465 + 466 + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], 467 + 468 + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 469 + 470 + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 471 + 472 + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], 473 + 474 + "postcss-load-config": ["postcss-load-config@3.1.4", "", { "dependencies": { "lilconfig": "^2.0.5", "yaml": "^1.10.2" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg=="], 475 + 476 + "postcss-safe-parser": ["postcss-safe-parser@7.0.1", "", { "peerDependencies": { "postcss": "^8.4.31" } }, "sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A=="], 477 + 478 + "postcss-scss": ["postcss-scss@4.0.9", "", { "peerDependencies": { "postcss": "^8.4.29" } }, "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A=="], 479 + 480 + "postcss-selector-parser": ["postcss-selector-parser@7.1.0", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA=="], 481 + 482 + "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], 483 + 484 + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], 485 + 486 + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], 487 + 488 + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], 489 + 490 + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], 491 + 492 + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], 493 + 494 + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], 495 + 496 + "rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="], 497 + 498 + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], 499 + 500 + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], 501 + 502 + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], 503 + 504 + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], 505 + 506 + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], 507 + 508 + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], 509 + 510 + "sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="], 511 + 512 + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 513 + 514 + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], 515 + 516 + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], 517 + 518 + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], 519 + 520 + "svelte": ["svelte@5.42.2", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-iSry5jsBHispVczyt9UrBX/1qu3HQ/UyKPAIjqlvlu3o/eUvc+kpyMyRS2O4HLLx4MvLurLGIUOyyP11pyD59g=="], 521 + 522 + "svelte-check": ["svelte-check@4.3.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg=="], 523 + 524 + "svelte-eslint-parser": ["svelte-eslint-parser@1.4.0", "", { "dependencies": { "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.0.0", "espree": "^10.0.0", "postcss": "^8.4.49", "postcss-scss": "^4.0.9", "postcss-selector-parser": "^7.0.0" }, "peerDependencies": { "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["svelte"] }, "sha512-fjPzOfipR5S7gQ/JvI9r2H8y9gMGXO3JtmrylHLLyahEMquXI0lrebcjT+9/hNgDej0H7abTyox5HpHmW1PSWA=="], 525 + 526 + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 527 + 528 + "tlds": ["tlds@1.261.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA=="], 529 + 530 + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 531 + 532 + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], 533 + 534 + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], 535 + 536 + "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], 537 + 538 + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 539 + 540 + "typescript-eslint": ["typescript-eslint@8.46.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.46.2", "@typescript-eslint/parser": "8.46.2", "@typescript-eslint/typescript-estree": "8.46.2", "@typescript-eslint/utils": "8.46.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg=="], 541 + 542 + "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="], 543 + 544 + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 545 + 546 + "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], 547 + 548 + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], 549 + 550 + "vite": ["vite@7.1.12", "", { "dependencies": { "esbuild": "^0.25.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-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug=="], 551 + 552 + "vite-plugin-full-reload": ["vite-plugin-full-reload@1.2.0", "", { "dependencies": { "picocolors": "^1.0.0", "picomatch": "^2.3.1" } }, "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA=="], 553 + 554 + "vite-rs-plugin": ["vite-rs-plugin@1.0.1", "", { "dependencies": { "vite-plugin-full-reload": "^1.2.0" }, "peerDependencies": { "vite": "^5.0.0" }, "bin": { "cleanup": "bin/cleanForBuild.js" } }, "sha512-YhgflKQIRzuS5x66J3yICoVLH25D2fNU+jThK8tpYl/jGrXeIKT4w5VH1lkLPRC0SjK2ZCm9S6K9Z2ZFVDHjPQ=="], 555 + 556 + "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], 557 + 558 + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], 559 + 560 + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], 561 + 562 + "yaml": ["yaml@1.10.2", "", {}, "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="], 563 + 564 + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], 565 + 566 + "zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="], 567 + 568 + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], 569 + 570 + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], 571 + 572 + "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], 573 + 574 + "@rollup/plugin-commonjs/is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="], 575 + 576 + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], 577 + 578 + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], 579 + 580 + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], 581 + 582 + "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 583 + 584 + "vite-plugin-full-reload/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 585 + 586 + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], 587 + } 588 + }
+43
web-ui/eslint.config.js
··· 1 + import {fileURLToPath} from 'node:url'; 2 + import {includeIgnoreFile} from '@eslint/compat'; 3 + import js from '@eslint/js'; 4 + import svelte from 'eslint-plugin-svelte'; 5 + import {defineConfig} from 'eslint/config'; 6 + import globals from 'globals'; 7 + import ts from 'typescript-eslint'; 8 + import svelteConfig from './svelte.config.js'; 9 + 10 + const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); 11 + 12 + export default defineConfig( 13 + includeIgnoreFile(gitignorePath), 14 + js.configs.recommended, 15 + ...ts.configs.recommended, 16 + ...svelte.configs.recommended, 17 + { 18 + languageOptions: { 19 + globals: {...globals.browser, ...globals.node} 20 + }, 21 + rules: { // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. 22 + // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors 23 + 'no-undef': 'off', 24 + 'quotes': ['error', 'single'], 25 + 26 + } 27 + }, 28 + { 29 + files: [ 30 + '**/*.svelte', 31 + '**/*.svelte.ts', 32 + '**/*.svelte.js' 33 + ], 34 + languageOptions: { 35 + parserOptions: { 36 + projectService: true, 37 + extraFileExtensions: ['.svelte'], 38 + parser: ts.parser, 39 + svelteConfig 40 + } 41 + } 42 + } 43 + );
+2713
web-ui/package-lock.json
··· 1 + { 2 + "name": "web-ui", 3 + "version": "0.0.1", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "web-ui", 9 + "version": "0.0.1", 10 + "dependencies": { 11 + "@atcute/client": "^4.0.5", 12 + "@atcute/lexicons": "^1.2.2", 13 + "@pds-moover/lexicons": "^1.0.1", 14 + "@pds-moover/moover": "^1.0.4" 15 + }, 16 + "devDependencies": { 17 + "@eslint/compat": "^1.4.0", 18 + "@eslint/js": "^9.36.0", 19 + "@sveltejs/adapter-auto": "^6.1.0", 20 + "@sveltejs/adapter-node": "^5.4.0", 21 + "@sveltejs/kit": "^2.43.2", 22 + "@sveltejs/vite-plugin-svelte": "^6.2.0", 23 + "@types/node": "^22", 24 + "eslint": "^9.36.0", 25 + "eslint-plugin-svelte": "^3.12.4", 26 + "globals": "^16.4.0", 27 + "svelte": "^5.39.5", 28 + "svelte-check": "^4.3.2", 29 + "typescript": "^5.9.2", 30 + "typescript-eslint": "^8.44.1", 31 + "vite": "^7.1.7" 32 + }, 33 + "optionalDependencies": { 34 + "@rollup/rollup-linux-x64-musl": "^4.52.5" 35 + } 36 + }, 37 + "../lexicon_types": { 38 + "name": "@pds-moover/lexicon_types", 39 + "version": "0.0.0", 40 + "extraneous": true, 41 + "dependencies": { 42 + "@atproto/lexicon": "^0.5.1", 43 + "@atproto/xrpc": "^0.7.5", 44 + "cid": "multiformats/cid" 45 + }, 46 + "devDependencies": { 47 + "@atcute/lex-cli": "^2.2.2", 48 + "@types/node": "^24.7.2", 49 + "typescript": "~5.9.3", 50 + "vite": "^7.1.7", 51 + "vite-plugin-dts": "^4.5.4" 52 + } 53 + }, 54 + "../packages/lexicon_types": { 55 + "name": "@pds-moover/lexicon_types", 56 + "version": "0.0.0", 57 + "extraneous": true, 58 + "dependencies": { 59 + "@atproto/lexicon": "^0.5.1", 60 + "@atproto/xrpc": "^0.7.5", 61 + "cid": "multiformats/cid" 62 + }, 63 + "devDependencies": { 64 + "@atcute/lex-cli": "^2.2.2" 65 + } 66 + }, 67 + "../packages/moover": { 68 + "name": "@pds-moover/moover", 69 + "version": "1.0.4", 70 + "license": "MIT", 71 + "dependencies": { 72 + "@atcute/cbor": "^2.2.7", 73 + "@atcute/client": "^4.0.4", 74 + "@atcute/crypto": "^2.2.5", 75 + "@atcute/did-plc": "^0.1.7", 76 + "@atcute/identity-resolver": "^1.1.3", 77 + "@atcute/lexicons": "^1.2.2", 78 + "@atcute/multibase": "^1.1.6", 79 + "@atproto/api": "^0.16.7", 80 + "@pds-moover/lexicons": "^1.0.0", 81 + "alpinejs": "^3.15.0", 82 + "vite-plugin-full-reload": "^1.2.0", 83 + "vite-rs-plugin": "1.0.1" 84 + }, 85 + "devDependencies": { 86 + "eslint": "^9.34.0", 87 + "eslint-plugin-import": "^2.32.0", 88 + "vite": "^7.1.7" 89 + } 90 + }, 91 + "../packages/moover/types": { 92 + "extraneous": true 93 + }, 94 + "node_modules/@atcute/client": { 95 + "version": "4.0.5", 96 + "resolved": "https://registry.npmjs.org/@atcute/client/-/client-4.0.5.tgz", 97 + "integrity": "sha512-R8Qen8goGmEkynYGg2m6XFlVmz0GTDvQ+9w+4QqOob+XMk8/WDpF4aImev7WKEde/rV2gjcqW7zM8E6W9NShDA==", 98 + "license": "0BSD", 99 + "dependencies": { 100 + "@atcute/identity": "^1.1.1", 101 + "@atcute/lexicons": "^1.2.2" 102 + } 103 + }, 104 + "node_modules/@atcute/identity": { 105 + "version": "1.1.1", 106 + "resolved": "https://registry.npmjs.org/@atcute/identity/-/identity-1.1.1.tgz", 107 + "integrity": "sha512-zax42n693VEhnC+5tndvO2KLDTMkHOz8UExwmklvJv7R9VujfEwiSWhcv6Jgwb3ellaG8wjiQ1lMOIjLLvwh0Q==", 108 + "license": "0BSD", 109 + "dependencies": { 110 + "@atcute/lexicons": "^1.2.2", 111 + "@badrap/valita": "^0.4.6" 112 + } 113 + }, 114 + "node_modules/@atcute/lexicons": { 115 + "version": "1.2.2", 116 + "resolved": "https://registry.npmjs.org/@atcute/lexicons/-/lexicons-1.2.2.tgz", 117 + "integrity": "sha512-bgEhJq5Z70/0TbK5sx+tAkrR8FsCODNiL2gUEvS5PuJfPxmFmRYNWaMGehxSPaXWpU2+Oa9ckceHiYbrItDTkA==", 118 + "license": "0BSD", 119 + "dependencies": { 120 + "@standard-schema/spec": "^1.0.0", 121 + "esm-env": "^1.2.2" 122 + } 123 + }, 124 + "node_modules/@atproto/common-web": { 125 + "version": "0.4.3", 126 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz", 127 + "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", 128 + "license": "MIT", 129 + "dependencies": { 130 + "graphemer": "^1.4.0", 131 + "multiformats": "^9.9.0", 132 + "uint8arrays": "3.0.0", 133 + "zod": "^3.23.8" 134 + } 135 + }, 136 + "node_modules/@atproto/lexicon": { 137 + "version": "0.5.1", 138 + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", 139 + "integrity": "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==", 140 + "license": "MIT", 141 + "dependencies": { 142 + "@atproto/common-web": "^0.4.3", 143 + "@atproto/syntax": "^0.4.1", 144 + "iso-datestring-validator": "^2.2.2", 145 + "multiformats": "^9.9.0", 146 + "zod": "^3.23.8" 147 + } 148 + }, 149 + "node_modules/@atproto/syntax": { 150 + "version": "0.4.1", 151 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 152 + "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 153 + "license": "MIT" 154 + }, 155 + "node_modules/@atproto/xrpc": { 156 + "version": "0.7.5", 157 + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz", 158 + "integrity": "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==", 159 + "license": "MIT", 160 + "dependencies": { 161 + "@atproto/lexicon": "^0.5.1", 162 + "zod": "^3.23.8" 163 + } 164 + }, 165 + "node_modules/@badrap/valita": { 166 + "version": "0.4.6", 167 + "resolved": "https://registry.npmjs.org/@badrap/valita/-/valita-0.4.6.tgz", 168 + "integrity": "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==", 169 + "license": "MIT", 170 + "engines": { 171 + "node": ">= 18" 172 + } 173 + }, 174 + "node_modules/@esbuild/darwin-arm64": { 175 + "version": "0.25.11", 176 + "cpu": [ 177 + "arm64" 178 + ], 179 + "dev": true, 180 + "license": "MIT", 181 + "optional": true, 182 + "os": [ 183 + "darwin" 184 + ], 185 + "engines": { 186 + "node": ">=18" 187 + } 188 + }, 189 + "node_modules/@eslint-community/eslint-utils": { 190 + "version": "4.9.0", 191 + "dev": true, 192 + "license": "MIT", 193 + "dependencies": { 194 + "eslint-visitor-keys": "^3.4.3" 195 + }, 196 + "engines": { 197 + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 198 + }, 199 + "funding": { 200 + "url": "https://opencollective.com/eslint" 201 + }, 202 + "peerDependencies": { 203 + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 204 + } 205 + }, 206 + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { 207 + "version": "3.4.3", 208 + "dev": true, 209 + "license": "Apache-2.0", 210 + "engines": { 211 + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 212 + }, 213 + "funding": { 214 + "url": "https://opencollective.com/eslint" 215 + } 216 + }, 217 + "node_modules/@eslint-community/regexpp": { 218 + "version": "4.12.2", 219 + "dev": true, 220 + "license": "MIT", 221 + "engines": { 222 + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 223 + } 224 + }, 225 + "node_modules/@eslint/compat": { 226 + "version": "1.4.0", 227 + "dev": true, 228 + "license": "Apache-2.0", 229 + "dependencies": { 230 + "@eslint/core": "^0.16.0" 231 + }, 232 + "engines": { 233 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 234 + }, 235 + "peerDependencies": { 236 + "eslint": "^8.40 || 9" 237 + }, 238 + "peerDependenciesMeta": { 239 + "eslint": { 240 + "optional": true 241 + } 242 + } 243 + }, 244 + "node_modules/@eslint/config-array": { 245 + "version": "0.21.1", 246 + "dev": true, 247 + "license": "Apache-2.0", 248 + "dependencies": { 249 + "@eslint/object-schema": "^2.1.7", 250 + "debug": "^4.3.1", 251 + "minimatch": "^3.1.2" 252 + }, 253 + "engines": { 254 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 255 + } 256 + }, 257 + "node_modules/@eslint/config-helpers": { 258 + "version": "0.4.1", 259 + "dev": true, 260 + "license": "Apache-2.0", 261 + "dependencies": { 262 + "@eslint/core": "^0.16.0" 263 + }, 264 + "engines": { 265 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 266 + } 267 + }, 268 + "node_modules/@eslint/core": { 269 + "version": "0.16.0", 270 + "dev": true, 271 + "license": "Apache-2.0", 272 + "dependencies": { 273 + "@types/json-schema": "^7.0.15" 274 + }, 275 + "engines": { 276 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 277 + } 278 + }, 279 + "node_modules/@eslint/eslintrc": { 280 + "version": "3.3.1", 281 + "dev": true, 282 + "license": "MIT", 283 + "dependencies": { 284 + "ajv": "^6.12.4", 285 + "debug": "^4.3.2", 286 + "espree": "^10.0.1", 287 + "globals": "^14.0.0", 288 + "ignore": "^5.2.0", 289 + "import-fresh": "^3.2.1", 290 + "js-yaml": "^4.1.0", 291 + "minimatch": "^3.1.2", 292 + "strip-json-comments": "^3.1.1" 293 + }, 294 + "engines": { 295 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 296 + }, 297 + "funding": { 298 + "url": "https://opencollective.com/eslint" 299 + } 300 + }, 301 + "node_modules/@eslint/eslintrc/node_modules/globals": { 302 + "version": "14.0.0", 303 + "dev": true, 304 + "license": "MIT", 305 + "engines": { 306 + "node": ">=18" 307 + }, 308 + "funding": { 309 + "url": "https://github.com/sponsors/sindresorhus" 310 + } 311 + }, 312 + "node_modules/@eslint/js": { 313 + "version": "9.38.0", 314 + "dev": true, 315 + "license": "MIT", 316 + "engines": { 317 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 318 + }, 319 + "funding": { 320 + "url": "https://eslint.org/donate" 321 + } 322 + }, 323 + "node_modules/@eslint/object-schema": { 324 + "version": "2.1.7", 325 + "dev": true, 326 + "license": "Apache-2.0", 327 + "engines": { 328 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 329 + } 330 + }, 331 + "node_modules/@eslint/plugin-kit": { 332 + "version": "0.4.0", 333 + "dev": true, 334 + "license": "Apache-2.0", 335 + "dependencies": { 336 + "@eslint/core": "^0.16.0", 337 + "levn": "^0.4.1" 338 + }, 339 + "engines": { 340 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 341 + } 342 + }, 343 + "node_modules/@humanfs/core": { 344 + "version": "0.19.1", 345 + "dev": true, 346 + "license": "Apache-2.0", 347 + "engines": { 348 + "node": ">=18.18.0" 349 + } 350 + }, 351 + "node_modules/@humanfs/node": { 352 + "version": "0.16.7", 353 + "dev": true, 354 + "license": "Apache-2.0", 355 + "dependencies": { 356 + "@humanfs/core": "^0.19.1", 357 + "@humanwhocodes/retry": "^0.4.0" 358 + }, 359 + "engines": { 360 + "node": ">=18.18.0" 361 + } 362 + }, 363 + "node_modules/@humanwhocodes/module-importer": { 364 + "version": "1.0.1", 365 + "dev": true, 366 + "license": "Apache-2.0", 367 + "engines": { 368 + "node": ">=12.22" 369 + }, 370 + "funding": { 371 + "type": "github", 372 + "url": "https://github.com/sponsors/nzakas" 373 + } 374 + }, 375 + "node_modules/@humanwhocodes/retry": { 376 + "version": "0.4.3", 377 + "dev": true, 378 + "license": "Apache-2.0", 379 + "engines": { 380 + "node": ">=18.18" 381 + }, 382 + "funding": { 383 + "type": "github", 384 + "url": "https://github.com/sponsors/nzakas" 385 + } 386 + }, 387 + "node_modules/@jridgewell/gen-mapping": { 388 + "version": "0.3.13", 389 + "dev": true, 390 + "license": "MIT", 391 + "dependencies": { 392 + "@jridgewell/sourcemap-codec": "^1.5.0", 393 + "@jridgewell/trace-mapping": "^0.3.24" 394 + } 395 + }, 396 + "node_modules/@jridgewell/remapping": { 397 + "version": "2.3.5", 398 + "dev": true, 399 + "license": "MIT", 400 + "dependencies": { 401 + "@jridgewell/gen-mapping": "^0.3.5", 402 + "@jridgewell/trace-mapping": "^0.3.24" 403 + } 404 + }, 405 + "node_modules/@jridgewell/resolve-uri": { 406 + "version": "3.1.2", 407 + "dev": true, 408 + "license": "MIT", 409 + "engines": { 410 + "node": ">=6.0.0" 411 + } 412 + }, 413 + "node_modules/@jridgewell/sourcemap-codec": { 414 + "version": "1.5.5", 415 + "dev": true, 416 + "license": "MIT" 417 + }, 418 + "node_modules/@jridgewell/trace-mapping": { 419 + "version": "0.3.31", 420 + "dev": true, 421 + "license": "MIT", 422 + "dependencies": { 423 + "@jridgewell/resolve-uri": "^3.1.0", 424 + "@jridgewell/sourcemap-codec": "^1.4.14" 425 + } 426 + }, 427 + "node_modules/@nodelib/fs.scandir": { 428 + "version": "2.1.5", 429 + "dev": true, 430 + "license": "MIT", 431 + "dependencies": { 432 + "@nodelib/fs.stat": "2.0.5", 433 + "run-parallel": "^1.1.9" 434 + }, 435 + "engines": { 436 + "node": ">= 8" 437 + } 438 + }, 439 + "node_modules/@nodelib/fs.stat": { 440 + "version": "2.0.5", 441 + "dev": true, 442 + "license": "MIT", 443 + "engines": { 444 + "node": ">= 8" 445 + } 446 + }, 447 + "node_modules/@nodelib/fs.walk": { 448 + "version": "1.2.8", 449 + "dev": true, 450 + "license": "MIT", 451 + "dependencies": { 452 + "@nodelib/fs.scandir": "2.1.5", 453 + "fastq": "^1.6.0" 454 + }, 455 + "engines": { 456 + "node": ">= 8" 457 + } 458 + }, 459 + "node_modules/@pds-moover/lexicons": { 460 + "version": "1.0.1", 461 + "resolved": "https://registry.npmjs.org/@pds-moover/lexicons/-/lexicons-1.0.1.tgz", 462 + "integrity": "sha512-fv5b/DtHM7FEo/JklyF9gdK0ainlb6mWjWrBe6cmSAeg9G/4O2jBlQUOqfOAICY9gOcrCpkOrk9PHgGw//JQ2A==", 463 + "license": "MIT", 464 + "dependencies": { 465 + "@atproto/lexicon": "^0.5.1", 466 + "@atproto/xrpc": "^0.7.5" 467 + } 468 + }, 469 + "node_modules/@pds-moover/moover": { 470 + "resolved": "../packages/moover", 471 + "link": true 472 + }, 473 + "node_modules/@polka/url": { 474 + "version": "1.0.0-next.29", 475 + "dev": true, 476 + "license": "MIT" 477 + }, 478 + "node_modules/@rollup/plugin-commonjs": { 479 + "version": "28.0.9", 480 + "dev": true, 481 + "license": "MIT", 482 + "dependencies": { 483 + "@rollup/pluginutils": "^5.0.1", 484 + "commondir": "^1.0.1", 485 + "estree-walker": "^2.0.2", 486 + "fdir": "^6.2.0", 487 + "is-reference": "1.2.1", 488 + "magic-string": "^0.30.3", 489 + "picomatch": "^4.0.2" 490 + }, 491 + "engines": { 492 + "node": ">=16.0.0 || 14 >= 14.17" 493 + }, 494 + "peerDependencies": { 495 + "rollup": "^2.68.0||^3.0.0||^4.0.0" 496 + }, 497 + "peerDependenciesMeta": { 498 + "rollup": { 499 + "optional": true 500 + } 501 + } 502 + }, 503 + "node_modules/@rollup/plugin-commonjs/node_modules/is-reference": { 504 + "version": "1.2.1", 505 + "dev": true, 506 + "license": "MIT", 507 + "dependencies": { 508 + "@types/estree": "*" 509 + } 510 + }, 511 + "node_modules/@rollup/plugin-json": { 512 + "version": "6.1.0", 513 + "dev": true, 514 + "license": "MIT", 515 + "dependencies": { 516 + "@rollup/pluginutils": "^5.1.0" 517 + }, 518 + "engines": { 519 + "node": ">=14.0.0" 520 + }, 521 + "peerDependencies": { 522 + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" 523 + }, 524 + "peerDependenciesMeta": { 525 + "rollup": { 526 + "optional": true 527 + } 528 + } 529 + }, 530 + "node_modules/@rollup/plugin-node-resolve": { 531 + "version": "16.0.3", 532 + "dev": true, 533 + "license": "MIT", 534 + "dependencies": { 535 + "@rollup/pluginutils": "^5.0.1", 536 + "@types/resolve": "1.20.2", 537 + "deepmerge": "^4.2.2", 538 + "is-module": "^1.0.0", 539 + "resolve": "^1.22.1" 540 + }, 541 + "engines": { 542 + "node": ">=14.0.0" 543 + }, 544 + "peerDependencies": { 545 + "rollup": "^2.78.0||^3.0.0||^4.0.0" 546 + }, 547 + "peerDependenciesMeta": { 548 + "rollup": { 549 + "optional": true 550 + } 551 + } 552 + }, 553 + "node_modules/@rollup/pluginutils": { 554 + "version": "5.3.0", 555 + "dev": true, 556 + "license": "MIT", 557 + "dependencies": { 558 + "@types/estree": "^1.0.0", 559 + "estree-walker": "^2.0.2", 560 + "picomatch": "^4.0.2" 561 + }, 562 + "engines": { 563 + "node": ">=14.0.0" 564 + }, 565 + "peerDependencies": { 566 + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" 567 + }, 568 + "peerDependenciesMeta": { 569 + "rollup": { 570 + "optional": true 571 + } 572 + } 573 + }, 574 + "node_modules/@rollup/rollup-darwin-arm64": { 575 + "version": "4.52.5", 576 + "cpu": [ 577 + "arm64" 578 + ], 579 + "dev": true, 580 + "license": "MIT", 581 + "optional": true, 582 + "os": [ 583 + "darwin" 584 + ] 585 + }, 586 + "node_modules/@rollup/rollup-linux-x64-musl": { 587 + "version": "4.52.5", 588 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", 589 + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", 590 + "cpu": [ 591 + "x64" 592 + ], 593 + "license": "MIT", 594 + "optional": true, 595 + "os": [ 596 + "linux" 597 + ] 598 + }, 599 + "node_modules/@standard-schema/spec": { 600 + "version": "1.0.0", 601 + "license": "MIT" 602 + }, 603 + "node_modules/@sveltejs/acorn-typescript": { 604 + "version": "1.0.6", 605 + "dev": true, 606 + "license": "MIT", 607 + "peerDependencies": { 608 + "acorn": "^8.9.0" 609 + } 610 + }, 611 + "node_modules/@sveltejs/adapter-auto": { 612 + "version": "6.1.1", 613 + "dev": true, 614 + "license": "MIT", 615 + "peerDependencies": { 616 + "@sveltejs/kit": "^2.0.0" 617 + } 618 + }, 619 + "node_modules/@sveltejs/adapter-node": { 620 + "version": "5.4.0", 621 + "dev": true, 622 + "license": "MIT", 623 + "dependencies": { 624 + "@rollup/plugin-commonjs": "^28.0.1", 625 + "@rollup/plugin-json": "^6.1.0", 626 + "@rollup/plugin-node-resolve": "^16.0.0", 627 + "rollup": "^4.9.5" 628 + }, 629 + "peerDependencies": { 630 + "@sveltejs/kit": "^2.4.0" 631 + } 632 + }, 633 + "node_modules/@sveltejs/kit": { 634 + "version": "2.48.0", 635 + "dev": true, 636 + "license": "MIT", 637 + "dependencies": { 638 + "@standard-schema/spec": "^1.0.0", 639 + "@sveltejs/acorn-typescript": "^1.0.5", 640 + "@types/cookie": "^0.6.0", 641 + "acorn": "^8.14.1", 642 + "cookie": "^0.6.0", 643 + "devalue": "^5.3.2", 644 + "esm-env": "^1.2.2", 645 + "kleur": "^4.1.5", 646 + "magic-string": "^0.30.5", 647 + "mrmime": "^2.0.0", 648 + "sade": "^1.8.1", 649 + "set-cookie-parser": "^2.6.0", 650 + "sirv": "^3.0.0" 651 + }, 652 + "bin": { 653 + "svelte-kit": "svelte-kit.js" 654 + }, 655 + "engines": { 656 + "node": ">=18.13" 657 + }, 658 + "peerDependencies": { 659 + "@opentelemetry/api": "^1.0.0", 660 + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", 661 + "svelte": "^4.0.0 || ^5.0.0-next.0", 662 + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" 663 + }, 664 + "peerDependenciesMeta": { 665 + "@opentelemetry/api": { 666 + "optional": true 667 + } 668 + } 669 + }, 670 + "node_modules/@sveltejs/vite-plugin-svelte": { 671 + "version": "6.2.1", 672 + "dev": true, 673 + "license": "MIT", 674 + "dependencies": { 675 + "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", 676 + "debug": "^4.4.1", 677 + "deepmerge": "^4.3.1", 678 + "magic-string": "^0.30.17", 679 + "vitefu": "^1.1.1" 680 + }, 681 + "engines": { 682 + "node": "^20.19 || ^22.12 || >=24" 683 + }, 684 + "peerDependencies": { 685 + "svelte": "^5.0.0", 686 + "vite": "^6.3.0 || ^7.0.0" 687 + } 688 + }, 689 + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { 690 + "version": "5.0.1", 691 + "dev": true, 692 + "license": "MIT", 693 + "dependencies": { 694 + "debug": "^4.4.1" 695 + }, 696 + "engines": { 697 + "node": "^20.19 || ^22.12 || >=24" 698 + }, 699 + "peerDependencies": { 700 + "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", 701 + "svelte": "^5.0.0", 702 + "vite": "^6.3.0 || ^7.0.0" 703 + } 704 + }, 705 + "node_modules/@types/cookie": { 706 + "version": "0.6.0", 707 + "dev": true, 708 + "license": "MIT" 709 + }, 710 + "node_modules/@types/estree": { 711 + "version": "1.0.8", 712 + "dev": true, 713 + "license": "MIT" 714 + }, 715 + "node_modules/@types/json-schema": { 716 + "version": "7.0.15", 717 + "dev": true, 718 + "license": "MIT" 719 + }, 720 + "node_modules/@types/node": { 721 + "version": "22.18.12", 722 + "dev": true, 723 + "license": "MIT", 724 + "dependencies": { 725 + "undici-types": "~6.21.0" 726 + } 727 + }, 728 + "node_modules/@types/resolve": { 729 + "version": "1.20.2", 730 + "dev": true, 731 + "license": "MIT" 732 + }, 733 + "node_modules/@typescript-eslint/eslint-plugin": { 734 + "version": "8.46.2", 735 + "dev": true, 736 + "license": "MIT", 737 + "dependencies": { 738 + "@eslint-community/regexpp": "^4.10.0", 739 + "@typescript-eslint/scope-manager": "8.46.2", 740 + "@typescript-eslint/type-utils": "8.46.2", 741 + "@typescript-eslint/utils": "8.46.2", 742 + "@typescript-eslint/visitor-keys": "8.46.2", 743 + "graphemer": "^1.4.0", 744 + "ignore": "^7.0.0", 745 + "natural-compare": "^1.4.0", 746 + "ts-api-utils": "^2.1.0" 747 + }, 748 + "engines": { 749 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 750 + }, 751 + "funding": { 752 + "type": "opencollective", 753 + "url": "https://opencollective.com/typescript-eslint" 754 + }, 755 + "peerDependencies": { 756 + "@typescript-eslint/parser": "^8.46.2", 757 + "eslint": "^8.57.0 || ^9.0.0", 758 + "typescript": ">=4.8.4 <6.0.0" 759 + } 760 + }, 761 + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { 762 + "version": "7.0.5", 763 + "dev": true, 764 + "license": "MIT", 765 + "engines": { 766 + "node": ">= 4" 767 + } 768 + }, 769 + "node_modules/@typescript-eslint/parser": { 770 + "version": "8.46.2", 771 + "dev": true, 772 + "license": "MIT", 773 + "dependencies": { 774 + "@typescript-eslint/scope-manager": "8.46.2", 775 + "@typescript-eslint/types": "8.46.2", 776 + "@typescript-eslint/typescript-estree": "8.46.2", 777 + "@typescript-eslint/visitor-keys": "8.46.2", 778 + "debug": "^4.3.4" 779 + }, 780 + "engines": { 781 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 782 + }, 783 + "funding": { 784 + "type": "opencollective", 785 + "url": "https://opencollective.com/typescript-eslint" 786 + }, 787 + "peerDependencies": { 788 + "eslint": "^8.57.0 || ^9.0.0", 789 + "typescript": ">=4.8.4 <6.0.0" 790 + } 791 + }, 792 + "node_modules/@typescript-eslint/project-service": { 793 + "version": "8.46.2", 794 + "dev": true, 795 + "license": "MIT", 796 + "dependencies": { 797 + "@typescript-eslint/tsconfig-utils": "^8.46.2", 798 + "@typescript-eslint/types": "^8.46.2", 799 + "debug": "^4.3.4" 800 + }, 801 + "engines": { 802 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 803 + }, 804 + "funding": { 805 + "type": "opencollective", 806 + "url": "https://opencollective.com/typescript-eslint" 807 + }, 808 + "peerDependencies": { 809 + "typescript": ">=4.8.4 <6.0.0" 810 + } 811 + }, 812 + "node_modules/@typescript-eslint/scope-manager": { 813 + "version": "8.46.2", 814 + "dev": true, 815 + "license": "MIT", 816 + "dependencies": { 817 + "@typescript-eslint/types": "8.46.2", 818 + "@typescript-eslint/visitor-keys": "8.46.2" 819 + }, 820 + "engines": { 821 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 822 + }, 823 + "funding": { 824 + "type": "opencollective", 825 + "url": "https://opencollective.com/typescript-eslint" 826 + } 827 + }, 828 + "node_modules/@typescript-eslint/tsconfig-utils": { 829 + "version": "8.46.2", 830 + "dev": true, 831 + "license": "MIT", 832 + "engines": { 833 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 834 + }, 835 + "funding": { 836 + "type": "opencollective", 837 + "url": "https://opencollective.com/typescript-eslint" 838 + }, 839 + "peerDependencies": { 840 + "typescript": ">=4.8.4 <6.0.0" 841 + } 842 + }, 843 + "node_modules/@typescript-eslint/type-utils": { 844 + "version": "8.46.2", 845 + "dev": true, 846 + "license": "MIT", 847 + "dependencies": { 848 + "@typescript-eslint/types": "8.46.2", 849 + "@typescript-eslint/typescript-estree": "8.46.2", 850 + "@typescript-eslint/utils": "8.46.2", 851 + "debug": "^4.3.4", 852 + "ts-api-utils": "^2.1.0" 853 + }, 854 + "engines": { 855 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 856 + }, 857 + "funding": { 858 + "type": "opencollective", 859 + "url": "https://opencollective.com/typescript-eslint" 860 + }, 861 + "peerDependencies": { 862 + "eslint": "^8.57.0 || ^9.0.0", 863 + "typescript": ">=4.8.4 <6.0.0" 864 + } 865 + }, 866 + "node_modules/@typescript-eslint/types": { 867 + "version": "8.46.2", 868 + "dev": true, 869 + "license": "MIT", 870 + "engines": { 871 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 872 + }, 873 + "funding": { 874 + "type": "opencollective", 875 + "url": "https://opencollective.com/typescript-eslint" 876 + } 877 + }, 878 + "node_modules/@typescript-eslint/typescript-estree": { 879 + "version": "8.46.2", 880 + "dev": true, 881 + "license": "MIT", 882 + "dependencies": { 883 + "@typescript-eslint/project-service": "8.46.2", 884 + "@typescript-eslint/tsconfig-utils": "8.46.2", 885 + "@typescript-eslint/types": "8.46.2", 886 + "@typescript-eslint/visitor-keys": "8.46.2", 887 + "debug": "^4.3.4", 888 + "fast-glob": "^3.3.2", 889 + "is-glob": "^4.0.3", 890 + "minimatch": "^9.0.4", 891 + "semver": "^7.6.0", 892 + "ts-api-utils": "^2.1.0" 893 + }, 894 + "engines": { 895 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 896 + }, 897 + "funding": { 898 + "type": "opencollective", 899 + "url": "https://opencollective.com/typescript-eslint" 900 + }, 901 + "peerDependencies": { 902 + "typescript": ">=4.8.4 <6.0.0" 903 + } 904 + }, 905 + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { 906 + "version": "9.0.5", 907 + "dev": true, 908 + "license": "ISC", 909 + "dependencies": { 910 + "brace-expansion": "^2.0.1" 911 + }, 912 + "engines": { 913 + "node": ">=16 || 14 >=14.17" 914 + }, 915 + "funding": { 916 + "url": "https://github.com/sponsors/isaacs" 917 + } 918 + }, 919 + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules/brace-expansion": { 920 + "version": "2.0.2", 921 + "dev": true, 922 + "license": "MIT", 923 + "dependencies": { 924 + "balanced-match": "^1.0.0" 925 + } 926 + }, 927 + "node_modules/@typescript-eslint/utils": { 928 + "version": "8.46.2", 929 + "dev": true, 930 + "license": "MIT", 931 + "dependencies": { 932 + "@eslint-community/eslint-utils": "^4.7.0", 933 + "@typescript-eslint/scope-manager": "8.46.2", 934 + "@typescript-eslint/types": "8.46.2", 935 + "@typescript-eslint/typescript-estree": "8.46.2" 936 + }, 937 + "engines": { 938 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 939 + }, 940 + "funding": { 941 + "type": "opencollective", 942 + "url": "https://opencollective.com/typescript-eslint" 943 + }, 944 + "peerDependencies": { 945 + "eslint": "^8.57.0 || ^9.0.0", 946 + "typescript": ">=4.8.4 <6.0.0" 947 + } 948 + }, 949 + "node_modules/@typescript-eslint/visitor-keys": { 950 + "version": "8.46.2", 951 + "dev": true, 952 + "license": "MIT", 953 + "dependencies": { 954 + "@typescript-eslint/types": "8.46.2", 955 + "eslint-visitor-keys": "^4.2.1" 956 + }, 957 + "engines": { 958 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 959 + }, 960 + "funding": { 961 + "type": "opencollective", 962 + "url": "https://opencollective.com/typescript-eslint" 963 + } 964 + }, 965 + "node_modules/acorn": { 966 + "version": "8.15.0", 967 + "dev": true, 968 + "license": "MIT", 969 + "bin": { 970 + "acorn": "bin/acorn" 971 + }, 972 + "engines": { 973 + "node": ">=0.4.0" 974 + } 975 + }, 976 + "node_modules/acorn-jsx": { 977 + "version": "5.3.2", 978 + "dev": true, 979 + "license": "MIT", 980 + "peerDependencies": { 981 + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 982 + } 983 + }, 984 + "node_modules/ajv": { 985 + "version": "6.12.6", 986 + "dev": true, 987 + "license": "MIT", 988 + "dependencies": { 989 + "fast-deep-equal": "^3.1.1", 990 + "fast-json-stable-stringify": "^2.0.0", 991 + "json-schema-traverse": "^0.4.1", 992 + "uri-js": "^4.2.2" 993 + }, 994 + "funding": { 995 + "type": "github", 996 + "url": "https://github.com/sponsors/epoberezkin" 997 + } 998 + }, 999 + "node_modules/ansi-styles": { 1000 + "version": "4.3.0", 1001 + "dev": true, 1002 + "license": "MIT", 1003 + "dependencies": { 1004 + "color-convert": "^2.0.1" 1005 + }, 1006 + "engines": { 1007 + "node": ">=8" 1008 + }, 1009 + "funding": { 1010 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1011 + } 1012 + }, 1013 + "node_modules/argparse": { 1014 + "version": "2.0.1", 1015 + "dev": true, 1016 + "license": "Python-2.0" 1017 + }, 1018 + "node_modules/aria-query": { 1019 + "version": "5.3.2", 1020 + "dev": true, 1021 + "license": "Apache-2.0", 1022 + "engines": { 1023 + "node": ">= 0.4" 1024 + } 1025 + }, 1026 + "node_modules/axobject-query": { 1027 + "version": "4.1.0", 1028 + "dev": true, 1029 + "license": "Apache-2.0", 1030 + "engines": { 1031 + "node": ">= 0.4" 1032 + } 1033 + }, 1034 + "node_modules/balanced-match": { 1035 + "version": "1.0.2", 1036 + "dev": true, 1037 + "license": "MIT" 1038 + }, 1039 + "node_modules/brace-expansion": { 1040 + "version": "1.1.12", 1041 + "dev": true, 1042 + "license": "MIT", 1043 + "dependencies": { 1044 + "balanced-match": "^1.0.0", 1045 + "concat-map": "0.0.1" 1046 + } 1047 + }, 1048 + "node_modules/braces": { 1049 + "version": "3.0.3", 1050 + "dev": true, 1051 + "license": "MIT", 1052 + "dependencies": { 1053 + "fill-range": "^7.1.1" 1054 + }, 1055 + "engines": { 1056 + "node": ">=8" 1057 + } 1058 + }, 1059 + "node_modules/callsites": { 1060 + "version": "3.1.0", 1061 + "dev": true, 1062 + "license": "MIT", 1063 + "engines": { 1064 + "node": ">=6" 1065 + } 1066 + }, 1067 + "node_modules/chalk": { 1068 + "version": "4.1.2", 1069 + "dev": true, 1070 + "license": "MIT", 1071 + "dependencies": { 1072 + "ansi-styles": "^4.1.0", 1073 + "supports-color": "^7.1.0" 1074 + }, 1075 + "engines": { 1076 + "node": ">=10" 1077 + }, 1078 + "funding": { 1079 + "url": "https://github.com/chalk/chalk?sponsor=1" 1080 + } 1081 + }, 1082 + "node_modules/chokidar": { 1083 + "version": "4.0.3", 1084 + "dev": true, 1085 + "license": "MIT", 1086 + "dependencies": { 1087 + "readdirp": "^4.0.1" 1088 + }, 1089 + "engines": { 1090 + "node": ">= 14.16.0" 1091 + }, 1092 + "funding": { 1093 + "url": "https://paulmillr.com/funding/" 1094 + } 1095 + }, 1096 + "node_modules/clsx": { 1097 + "version": "2.1.1", 1098 + "dev": true, 1099 + "license": "MIT", 1100 + "engines": { 1101 + "node": ">=6" 1102 + } 1103 + }, 1104 + "node_modules/color-convert": { 1105 + "version": "2.0.1", 1106 + "dev": true, 1107 + "license": "MIT", 1108 + "dependencies": { 1109 + "color-name": "~1.1.4" 1110 + }, 1111 + "engines": { 1112 + "node": ">=7.0.0" 1113 + } 1114 + }, 1115 + "node_modules/color-name": { 1116 + "version": "1.1.4", 1117 + "dev": true, 1118 + "license": "MIT" 1119 + }, 1120 + "node_modules/commondir": { 1121 + "version": "1.0.1", 1122 + "dev": true, 1123 + "license": "MIT" 1124 + }, 1125 + "node_modules/concat-map": { 1126 + "version": "0.0.1", 1127 + "dev": true, 1128 + "license": "MIT" 1129 + }, 1130 + "node_modules/cookie": { 1131 + "version": "0.6.0", 1132 + "dev": true, 1133 + "license": "MIT", 1134 + "engines": { 1135 + "node": ">= 0.6" 1136 + } 1137 + }, 1138 + "node_modules/cross-spawn": { 1139 + "version": "7.0.6", 1140 + "dev": true, 1141 + "license": "MIT", 1142 + "dependencies": { 1143 + "path-key": "^3.1.0", 1144 + "shebang-command": "^2.0.0", 1145 + "which": "^2.0.1" 1146 + }, 1147 + "engines": { 1148 + "node": ">= 8" 1149 + } 1150 + }, 1151 + "node_modules/cssesc": { 1152 + "version": "3.0.0", 1153 + "dev": true, 1154 + "license": "MIT", 1155 + "bin": { 1156 + "cssesc": "bin/cssesc" 1157 + }, 1158 + "engines": { 1159 + "node": ">=4" 1160 + } 1161 + }, 1162 + "node_modules/debug": { 1163 + "version": "4.4.3", 1164 + "dev": true, 1165 + "license": "MIT", 1166 + "dependencies": { 1167 + "ms": "^2.1.3" 1168 + }, 1169 + "engines": { 1170 + "node": ">=6.0" 1171 + }, 1172 + "peerDependenciesMeta": { 1173 + "supports-color": { 1174 + "optional": true 1175 + } 1176 + } 1177 + }, 1178 + "node_modules/deep-is": { 1179 + "version": "0.1.4", 1180 + "dev": true, 1181 + "license": "MIT" 1182 + }, 1183 + "node_modules/deepmerge": { 1184 + "version": "4.3.1", 1185 + "dev": true, 1186 + "license": "MIT", 1187 + "engines": { 1188 + "node": ">=0.10.0" 1189 + } 1190 + }, 1191 + "node_modules/devalue": { 1192 + "version": "5.4.2", 1193 + "dev": true, 1194 + "license": "MIT" 1195 + }, 1196 + "node_modules/esbuild": { 1197 + "version": "0.25.11", 1198 + "dev": true, 1199 + "hasInstallScript": true, 1200 + "license": "MIT", 1201 + "bin": { 1202 + "esbuild": "bin/esbuild" 1203 + }, 1204 + "engines": { 1205 + "node": ">=18" 1206 + }, 1207 + "optionalDependencies": { 1208 + "@esbuild/aix-ppc64": "0.25.11", 1209 + "@esbuild/android-arm": "0.25.11", 1210 + "@esbuild/android-arm64": "0.25.11", 1211 + "@esbuild/android-x64": "0.25.11", 1212 + "@esbuild/darwin-arm64": "0.25.11", 1213 + "@esbuild/darwin-x64": "0.25.11", 1214 + "@esbuild/freebsd-arm64": "0.25.11", 1215 + "@esbuild/freebsd-x64": "0.25.11", 1216 + "@esbuild/linux-arm": "0.25.11", 1217 + "@esbuild/linux-arm64": "0.25.11", 1218 + "@esbuild/linux-ia32": "0.25.11", 1219 + "@esbuild/linux-loong64": "0.25.11", 1220 + "@esbuild/linux-mips64el": "0.25.11", 1221 + "@esbuild/linux-ppc64": "0.25.11", 1222 + "@esbuild/linux-riscv64": "0.25.11", 1223 + "@esbuild/linux-s390x": "0.25.11", 1224 + "@esbuild/linux-x64": "0.25.11", 1225 + "@esbuild/netbsd-arm64": "0.25.11", 1226 + "@esbuild/netbsd-x64": "0.25.11", 1227 + "@esbuild/openbsd-arm64": "0.25.11", 1228 + "@esbuild/openbsd-x64": "0.25.11", 1229 + "@esbuild/openharmony-arm64": "0.25.11", 1230 + "@esbuild/sunos-x64": "0.25.11", 1231 + "@esbuild/win32-arm64": "0.25.11", 1232 + "@esbuild/win32-ia32": "0.25.11", 1233 + "@esbuild/win32-x64": "0.25.11" 1234 + } 1235 + }, 1236 + "node_modules/escape-string-regexp": { 1237 + "version": "4.0.0", 1238 + "dev": true, 1239 + "license": "MIT", 1240 + "engines": { 1241 + "node": ">=10" 1242 + }, 1243 + "funding": { 1244 + "url": "https://github.com/sponsors/sindresorhus" 1245 + } 1246 + }, 1247 + "node_modules/eslint": { 1248 + "version": "9.38.0", 1249 + "dev": true, 1250 + "license": "MIT", 1251 + "dependencies": { 1252 + "@eslint-community/eslint-utils": "^4.8.0", 1253 + "@eslint-community/regexpp": "^4.12.1", 1254 + "@eslint/config-array": "^0.21.1", 1255 + "@eslint/config-helpers": "^0.4.1", 1256 + "@eslint/core": "^0.16.0", 1257 + "@eslint/eslintrc": "^3.3.1", 1258 + "@eslint/js": "9.38.0", 1259 + "@eslint/plugin-kit": "^0.4.0", 1260 + "@humanfs/node": "^0.16.6", 1261 + "@humanwhocodes/module-importer": "^1.0.1", 1262 + "@humanwhocodes/retry": "^0.4.2", 1263 + "@types/estree": "^1.0.6", 1264 + "ajv": "^6.12.4", 1265 + "chalk": "^4.0.0", 1266 + "cross-spawn": "^7.0.6", 1267 + "debug": "^4.3.2", 1268 + "escape-string-regexp": "^4.0.0", 1269 + "eslint-scope": "^8.4.0", 1270 + "eslint-visitor-keys": "^4.2.1", 1271 + "espree": "^10.4.0", 1272 + "esquery": "^1.5.0", 1273 + "esutils": "^2.0.2", 1274 + "fast-deep-equal": "^3.1.3", 1275 + "file-entry-cache": "^8.0.0", 1276 + "find-up": "^5.0.0", 1277 + "glob-parent": "^6.0.2", 1278 + "ignore": "^5.2.0", 1279 + "imurmurhash": "^0.1.4", 1280 + "is-glob": "^4.0.0", 1281 + "json-stable-stringify-without-jsonify": "^1.0.1", 1282 + "lodash.merge": "^4.6.2", 1283 + "minimatch": "^3.1.2", 1284 + "natural-compare": "^1.4.0", 1285 + "optionator": "^0.9.3" 1286 + }, 1287 + "bin": { 1288 + "eslint": "bin/eslint.js" 1289 + }, 1290 + "engines": { 1291 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1292 + }, 1293 + "funding": { 1294 + "url": "https://eslint.org/donate" 1295 + }, 1296 + "peerDependencies": { 1297 + "jiti": "*" 1298 + }, 1299 + "peerDependenciesMeta": { 1300 + "jiti": { 1301 + "optional": true 1302 + } 1303 + } 1304 + }, 1305 + "node_modules/eslint-plugin-svelte": { 1306 + "version": "3.12.5", 1307 + "dev": true, 1308 + "license": "MIT", 1309 + "dependencies": { 1310 + "@eslint-community/eslint-utils": "^4.6.1", 1311 + "@jridgewell/sourcemap-codec": "^1.5.0", 1312 + "esutils": "^2.0.3", 1313 + "globals": "^16.0.0", 1314 + "known-css-properties": "^0.37.0", 1315 + "postcss": "^8.4.49", 1316 + "postcss-load-config": "^3.1.4", 1317 + "postcss-safe-parser": "^7.0.0", 1318 + "semver": "^7.6.3", 1319 + "svelte-eslint-parser": "^1.4.0" 1320 + }, 1321 + "engines": { 1322 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1323 + }, 1324 + "funding": { 1325 + "url": "https://github.com/sponsors/ota-meshi" 1326 + }, 1327 + "peerDependencies": { 1328 + "eslint": "^8.57.1 || ^9.0.0", 1329 + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" 1330 + }, 1331 + "peerDependenciesMeta": { 1332 + "svelte": { 1333 + "optional": true 1334 + } 1335 + } 1336 + }, 1337 + "node_modules/eslint-scope": { 1338 + "version": "8.4.0", 1339 + "dev": true, 1340 + "license": "BSD-2-Clause", 1341 + "dependencies": { 1342 + "esrecurse": "^4.3.0", 1343 + "estraverse": "^5.2.0" 1344 + }, 1345 + "engines": { 1346 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1347 + }, 1348 + "funding": { 1349 + "url": "https://opencollective.com/eslint" 1350 + } 1351 + }, 1352 + "node_modules/eslint-visitor-keys": { 1353 + "version": "4.2.1", 1354 + "dev": true, 1355 + "license": "Apache-2.0", 1356 + "engines": { 1357 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1358 + }, 1359 + "funding": { 1360 + "url": "https://opencollective.com/eslint" 1361 + } 1362 + }, 1363 + "node_modules/esm-env": { 1364 + "version": "1.2.2", 1365 + "license": "MIT" 1366 + }, 1367 + "node_modules/espree": { 1368 + "version": "10.4.0", 1369 + "dev": true, 1370 + "license": "BSD-2-Clause", 1371 + "dependencies": { 1372 + "acorn": "^8.15.0", 1373 + "acorn-jsx": "^5.3.2", 1374 + "eslint-visitor-keys": "^4.2.1" 1375 + }, 1376 + "engines": { 1377 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1378 + }, 1379 + "funding": { 1380 + "url": "https://opencollective.com/eslint" 1381 + } 1382 + }, 1383 + "node_modules/esquery": { 1384 + "version": "1.6.0", 1385 + "dev": true, 1386 + "license": "BSD-3-Clause", 1387 + "dependencies": { 1388 + "estraverse": "^5.1.0" 1389 + }, 1390 + "engines": { 1391 + "node": ">=0.10" 1392 + } 1393 + }, 1394 + "node_modules/esrap": { 1395 + "version": "2.1.1", 1396 + "dev": true, 1397 + "license": "MIT", 1398 + "dependencies": { 1399 + "@jridgewell/sourcemap-codec": "^1.4.15" 1400 + } 1401 + }, 1402 + "node_modules/esrecurse": { 1403 + "version": "4.3.0", 1404 + "dev": true, 1405 + "license": "BSD-2-Clause", 1406 + "dependencies": { 1407 + "estraverse": "^5.2.0" 1408 + }, 1409 + "engines": { 1410 + "node": ">=4.0" 1411 + } 1412 + }, 1413 + "node_modules/estraverse": { 1414 + "version": "5.3.0", 1415 + "dev": true, 1416 + "license": "BSD-2-Clause", 1417 + "engines": { 1418 + "node": ">=4.0" 1419 + } 1420 + }, 1421 + "node_modules/estree-walker": { 1422 + "version": "2.0.2", 1423 + "dev": true, 1424 + "license": "MIT" 1425 + }, 1426 + "node_modules/esutils": { 1427 + "version": "2.0.3", 1428 + "dev": true, 1429 + "license": "BSD-2-Clause", 1430 + "engines": { 1431 + "node": ">=0.10.0" 1432 + } 1433 + }, 1434 + "node_modules/fast-deep-equal": { 1435 + "version": "3.1.3", 1436 + "dev": true, 1437 + "license": "MIT" 1438 + }, 1439 + "node_modules/fast-glob": { 1440 + "version": "3.3.3", 1441 + "dev": true, 1442 + "license": "MIT", 1443 + "dependencies": { 1444 + "@nodelib/fs.stat": "^2.0.2", 1445 + "@nodelib/fs.walk": "^1.2.3", 1446 + "glob-parent": "^5.1.2", 1447 + "merge2": "^1.3.0", 1448 + "micromatch": "^4.0.8" 1449 + }, 1450 + "engines": { 1451 + "node": ">=8.6.0" 1452 + } 1453 + }, 1454 + "node_modules/fast-glob/node_modules/glob-parent": { 1455 + "version": "5.1.2", 1456 + "dev": true, 1457 + "license": "ISC", 1458 + "dependencies": { 1459 + "is-glob": "^4.0.1" 1460 + }, 1461 + "engines": { 1462 + "node": ">= 6" 1463 + } 1464 + }, 1465 + "node_modules/fast-json-stable-stringify": { 1466 + "version": "2.1.0", 1467 + "dev": true, 1468 + "license": "MIT" 1469 + }, 1470 + "node_modules/fast-levenshtein": { 1471 + "version": "2.0.6", 1472 + "dev": true, 1473 + "license": "MIT" 1474 + }, 1475 + "node_modules/fastq": { 1476 + "version": "1.19.1", 1477 + "dev": true, 1478 + "license": "ISC", 1479 + "dependencies": { 1480 + "reusify": "^1.0.4" 1481 + } 1482 + }, 1483 + "node_modules/fdir": { 1484 + "version": "6.5.0", 1485 + "dev": true, 1486 + "license": "MIT", 1487 + "engines": { 1488 + "node": ">=12.0.0" 1489 + }, 1490 + "peerDependencies": { 1491 + "picomatch": "^3 || ^4" 1492 + }, 1493 + "peerDependenciesMeta": { 1494 + "picomatch": { 1495 + "optional": true 1496 + } 1497 + } 1498 + }, 1499 + "node_modules/file-entry-cache": { 1500 + "version": "8.0.0", 1501 + "dev": true, 1502 + "license": "MIT", 1503 + "dependencies": { 1504 + "flat-cache": "^4.0.0" 1505 + }, 1506 + "engines": { 1507 + "node": ">=16.0.0" 1508 + } 1509 + }, 1510 + "node_modules/fill-range": { 1511 + "version": "7.1.1", 1512 + "dev": true, 1513 + "license": "MIT", 1514 + "dependencies": { 1515 + "to-regex-range": "^5.0.1" 1516 + }, 1517 + "engines": { 1518 + "node": ">=8" 1519 + } 1520 + }, 1521 + "node_modules/find-up": { 1522 + "version": "5.0.0", 1523 + "dev": true, 1524 + "license": "MIT", 1525 + "dependencies": { 1526 + "locate-path": "^6.0.0", 1527 + "path-exists": "^4.0.0" 1528 + }, 1529 + "engines": { 1530 + "node": ">=10" 1531 + }, 1532 + "funding": { 1533 + "url": "https://github.com/sponsors/sindresorhus" 1534 + } 1535 + }, 1536 + "node_modules/flat-cache": { 1537 + "version": "4.0.1", 1538 + "dev": true, 1539 + "license": "MIT", 1540 + "dependencies": { 1541 + "flatted": "^3.2.9", 1542 + "keyv": "^4.5.4" 1543 + }, 1544 + "engines": { 1545 + "node": ">=16" 1546 + } 1547 + }, 1548 + "node_modules/flatted": { 1549 + "version": "3.3.3", 1550 + "dev": true, 1551 + "license": "ISC" 1552 + }, 1553 + "node_modules/fsevents": { 1554 + "version": "2.3.3", 1555 + "dev": true, 1556 + "license": "MIT", 1557 + "optional": true, 1558 + "os": [ 1559 + "darwin" 1560 + ], 1561 + "engines": { 1562 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1563 + } 1564 + }, 1565 + "node_modules/function-bind": { 1566 + "version": "1.1.2", 1567 + "dev": true, 1568 + "license": "MIT", 1569 + "funding": { 1570 + "url": "https://github.com/sponsors/ljharb" 1571 + } 1572 + }, 1573 + "node_modules/glob-parent": { 1574 + "version": "6.0.2", 1575 + "dev": true, 1576 + "license": "ISC", 1577 + "dependencies": { 1578 + "is-glob": "^4.0.3" 1579 + }, 1580 + "engines": { 1581 + "node": ">=10.13.0" 1582 + } 1583 + }, 1584 + "node_modules/globals": { 1585 + "version": "16.4.0", 1586 + "dev": true, 1587 + "license": "MIT", 1588 + "engines": { 1589 + "node": ">=18" 1590 + }, 1591 + "funding": { 1592 + "url": "https://github.com/sponsors/sindresorhus" 1593 + } 1594 + }, 1595 + "node_modules/graphemer": { 1596 + "version": "1.4.0", 1597 + "license": "MIT" 1598 + }, 1599 + "node_modules/has-flag": { 1600 + "version": "4.0.0", 1601 + "dev": true, 1602 + "license": "MIT", 1603 + "engines": { 1604 + "node": ">=8" 1605 + } 1606 + }, 1607 + "node_modules/hasown": { 1608 + "version": "2.0.2", 1609 + "dev": true, 1610 + "license": "MIT", 1611 + "dependencies": { 1612 + "function-bind": "^1.1.2" 1613 + }, 1614 + "engines": { 1615 + "node": ">= 0.4" 1616 + } 1617 + }, 1618 + "node_modules/ignore": { 1619 + "version": "5.3.2", 1620 + "dev": true, 1621 + "license": "MIT", 1622 + "engines": { 1623 + "node": ">= 4" 1624 + } 1625 + }, 1626 + "node_modules/import-fresh": { 1627 + "version": "3.3.1", 1628 + "dev": true, 1629 + "license": "MIT", 1630 + "dependencies": { 1631 + "parent-module": "^1.0.0", 1632 + "resolve-from": "^4.0.0" 1633 + }, 1634 + "engines": { 1635 + "node": ">=6" 1636 + }, 1637 + "funding": { 1638 + "url": "https://github.com/sponsors/sindresorhus" 1639 + } 1640 + }, 1641 + "node_modules/imurmurhash": { 1642 + "version": "0.1.4", 1643 + "dev": true, 1644 + "license": "MIT", 1645 + "engines": { 1646 + "node": ">=0.8.19" 1647 + } 1648 + }, 1649 + "node_modules/is-core-module": { 1650 + "version": "2.16.1", 1651 + "dev": true, 1652 + "license": "MIT", 1653 + "dependencies": { 1654 + "hasown": "^2.0.2" 1655 + }, 1656 + "engines": { 1657 + "node": ">= 0.4" 1658 + }, 1659 + "funding": { 1660 + "url": "https://github.com/sponsors/ljharb" 1661 + } 1662 + }, 1663 + "node_modules/is-extglob": { 1664 + "version": "2.1.1", 1665 + "dev": true, 1666 + "license": "MIT", 1667 + "engines": { 1668 + "node": ">=0.10.0" 1669 + } 1670 + }, 1671 + "node_modules/is-glob": { 1672 + "version": "4.0.3", 1673 + "dev": true, 1674 + "license": "MIT", 1675 + "dependencies": { 1676 + "is-extglob": "^2.1.1" 1677 + }, 1678 + "engines": { 1679 + "node": ">=0.10.0" 1680 + } 1681 + }, 1682 + "node_modules/is-module": { 1683 + "version": "1.0.0", 1684 + "dev": true, 1685 + "license": "MIT" 1686 + }, 1687 + "node_modules/is-number": { 1688 + "version": "7.0.0", 1689 + "dev": true, 1690 + "license": "MIT", 1691 + "engines": { 1692 + "node": ">=0.12.0" 1693 + } 1694 + }, 1695 + "node_modules/is-reference": { 1696 + "version": "3.0.3", 1697 + "dev": true, 1698 + "license": "MIT", 1699 + "dependencies": { 1700 + "@types/estree": "^1.0.6" 1701 + } 1702 + }, 1703 + "node_modules/isexe": { 1704 + "version": "2.0.0", 1705 + "dev": true, 1706 + "license": "ISC" 1707 + }, 1708 + "node_modules/iso-datestring-validator": { 1709 + "version": "2.2.2", 1710 + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 1711 + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 1712 + "license": "MIT" 1713 + }, 1714 + "node_modules/js-yaml": { 1715 + "version": "4.1.0", 1716 + "dev": true, 1717 + "license": "MIT", 1718 + "dependencies": { 1719 + "argparse": "^2.0.1" 1720 + }, 1721 + "bin": { 1722 + "js-yaml": "bin/js-yaml.js" 1723 + } 1724 + }, 1725 + "node_modules/json-buffer": { 1726 + "version": "3.0.1", 1727 + "dev": true, 1728 + "license": "MIT" 1729 + }, 1730 + "node_modules/json-schema-traverse": { 1731 + "version": "0.4.1", 1732 + "dev": true, 1733 + "license": "MIT" 1734 + }, 1735 + "node_modules/json-stable-stringify-without-jsonify": { 1736 + "version": "1.0.1", 1737 + "dev": true, 1738 + "license": "MIT" 1739 + }, 1740 + "node_modules/keyv": { 1741 + "version": "4.5.4", 1742 + "dev": true, 1743 + "license": "MIT", 1744 + "dependencies": { 1745 + "json-buffer": "3.0.1" 1746 + } 1747 + }, 1748 + "node_modules/kleur": { 1749 + "version": "4.1.5", 1750 + "dev": true, 1751 + "license": "MIT", 1752 + "engines": { 1753 + "node": ">=6" 1754 + } 1755 + }, 1756 + "node_modules/known-css-properties": { 1757 + "version": "0.37.0", 1758 + "dev": true, 1759 + "license": "MIT" 1760 + }, 1761 + "node_modules/levn": { 1762 + "version": "0.4.1", 1763 + "dev": true, 1764 + "license": "MIT", 1765 + "dependencies": { 1766 + "prelude-ls": "^1.2.1", 1767 + "type-check": "~0.4.0" 1768 + }, 1769 + "engines": { 1770 + "node": ">= 0.8.0" 1771 + } 1772 + }, 1773 + "node_modules/lilconfig": { 1774 + "version": "2.1.0", 1775 + "dev": true, 1776 + "license": "MIT", 1777 + "engines": { 1778 + "node": ">=10" 1779 + } 1780 + }, 1781 + "node_modules/locate-character": { 1782 + "version": "3.0.0", 1783 + "dev": true, 1784 + "license": "MIT" 1785 + }, 1786 + "node_modules/locate-path": { 1787 + "version": "6.0.0", 1788 + "dev": true, 1789 + "license": "MIT", 1790 + "dependencies": { 1791 + "p-locate": "^5.0.0" 1792 + }, 1793 + "engines": { 1794 + "node": ">=10" 1795 + }, 1796 + "funding": { 1797 + "url": "https://github.com/sponsors/sindresorhus" 1798 + } 1799 + }, 1800 + "node_modules/lodash.merge": { 1801 + "version": "4.6.2", 1802 + "dev": true, 1803 + "license": "MIT" 1804 + }, 1805 + "node_modules/magic-string": { 1806 + "version": "0.30.21", 1807 + "dev": true, 1808 + "license": "MIT", 1809 + "dependencies": { 1810 + "@jridgewell/sourcemap-codec": "^1.5.5" 1811 + } 1812 + }, 1813 + "node_modules/merge2": { 1814 + "version": "1.4.1", 1815 + "dev": true, 1816 + "license": "MIT", 1817 + "engines": { 1818 + "node": ">= 8" 1819 + } 1820 + }, 1821 + "node_modules/micromatch": { 1822 + "version": "4.0.8", 1823 + "dev": true, 1824 + "license": "MIT", 1825 + "dependencies": { 1826 + "braces": "^3.0.3", 1827 + "picomatch": "^2.3.1" 1828 + }, 1829 + "engines": { 1830 + "node": ">=8.6" 1831 + } 1832 + }, 1833 + "node_modules/micromatch/node_modules/picomatch": { 1834 + "version": "2.3.1", 1835 + "dev": true, 1836 + "license": "MIT", 1837 + "engines": { 1838 + "node": ">=8.6" 1839 + }, 1840 + "funding": { 1841 + "url": "https://github.com/sponsors/jonschlinkert" 1842 + } 1843 + }, 1844 + "node_modules/minimatch": { 1845 + "version": "3.1.2", 1846 + "dev": true, 1847 + "license": "ISC", 1848 + "dependencies": { 1849 + "brace-expansion": "^1.1.7" 1850 + }, 1851 + "engines": { 1852 + "node": "*" 1853 + } 1854 + }, 1855 + "node_modules/mri": { 1856 + "version": "1.2.0", 1857 + "dev": true, 1858 + "license": "MIT", 1859 + "engines": { 1860 + "node": ">=4" 1861 + } 1862 + }, 1863 + "node_modules/mrmime": { 1864 + "version": "2.0.1", 1865 + "dev": true, 1866 + "license": "MIT", 1867 + "engines": { 1868 + "node": ">=10" 1869 + } 1870 + }, 1871 + "node_modules/ms": { 1872 + "version": "2.1.3", 1873 + "dev": true, 1874 + "license": "MIT" 1875 + }, 1876 + "node_modules/multiformats": { 1877 + "version": "9.9.0", 1878 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 1879 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 1880 + "license": "(Apache-2.0 AND MIT)" 1881 + }, 1882 + "node_modules/nanoid": { 1883 + "version": "3.3.11", 1884 + "dev": true, 1885 + "funding": [ 1886 + { 1887 + "type": "github", 1888 + "url": "https://github.com/sponsors/ai" 1889 + } 1890 + ], 1891 + "license": "MIT", 1892 + "bin": { 1893 + "nanoid": "bin/nanoid.cjs" 1894 + }, 1895 + "engines": { 1896 + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1897 + } 1898 + }, 1899 + "node_modules/natural-compare": { 1900 + "version": "1.4.0", 1901 + "dev": true, 1902 + "license": "MIT" 1903 + }, 1904 + "node_modules/optionator": { 1905 + "version": "0.9.4", 1906 + "dev": true, 1907 + "license": "MIT", 1908 + "dependencies": { 1909 + "deep-is": "^0.1.3", 1910 + "fast-levenshtein": "^2.0.6", 1911 + "levn": "^0.4.1", 1912 + "prelude-ls": "^1.2.1", 1913 + "type-check": "^0.4.0", 1914 + "word-wrap": "^1.2.5" 1915 + }, 1916 + "engines": { 1917 + "node": ">= 0.8.0" 1918 + } 1919 + }, 1920 + "node_modules/p-limit": { 1921 + "version": "3.1.0", 1922 + "dev": true, 1923 + "license": "MIT", 1924 + "dependencies": { 1925 + "yocto-queue": "^0.1.0" 1926 + }, 1927 + "engines": { 1928 + "node": ">=10" 1929 + }, 1930 + "funding": { 1931 + "url": "https://github.com/sponsors/sindresorhus" 1932 + } 1933 + }, 1934 + "node_modules/p-locate": { 1935 + "version": "5.0.0", 1936 + "dev": true, 1937 + "license": "MIT", 1938 + "dependencies": { 1939 + "p-limit": "^3.0.2" 1940 + }, 1941 + "engines": { 1942 + "node": ">=10" 1943 + }, 1944 + "funding": { 1945 + "url": "https://github.com/sponsors/sindresorhus" 1946 + } 1947 + }, 1948 + "node_modules/parent-module": { 1949 + "version": "1.0.1", 1950 + "dev": true, 1951 + "license": "MIT", 1952 + "dependencies": { 1953 + "callsites": "^3.0.0" 1954 + }, 1955 + "engines": { 1956 + "node": ">=6" 1957 + } 1958 + }, 1959 + "node_modules/path-exists": { 1960 + "version": "4.0.0", 1961 + "dev": true, 1962 + "license": "MIT", 1963 + "engines": { 1964 + "node": ">=8" 1965 + } 1966 + }, 1967 + "node_modules/path-key": { 1968 + "version": "3.1.1", 1969 + "dev": true, 1970 + "license": "MIT", 1971 + "engines": { 1972 + "node": ">=8" 1973 + } 1974 + }, 1975 + "node_modules/path-parse": { 1976 + "version": "1.0.7", 1977 + "dev": true, 1978 + "license": "MIT" 1979 + }, 1980 + "node_modules/picocolors": { 1981 + "version": "1.1.1", 1982 + "dev": true, 1983 + "license": "ISC" 1984 + }, 1985 + "node_modules/picomatch": { 1986 + "version": "4.0.3", 1987 + "dev": true, 1988 + "license": "MIT", 1989 + "engines": { 1990 + "node": ">=12" 1991 + }, 1992 + "funding": { 1993 + "url": "https://github.com/sponsors/jonschlinkert" 1994 + } 1995 + }, 1996 + "node_modules/postcss": { 1997 + "version": "8.5.6", 1998 + "dev": true, 1999 + "funding": [ 2000 + { 2001 + "type": "opencollective", 2002 + "url": "https://opencollective.com/postcss/" 2003 + }, 2004 + { 2005 + "type": "tidelift", 2006 + "url": "https://tidelift.com/funding/github/npm/postcss" 2007 + }, 2008 + { 2009 + "type": "github", 2010 + "url": "https://github.com/sponsors/ai" 2011 + } 2012 + ], 2013 + "license": "MIT", 2014 + "dependencies": { 2015 + "nanoid": "^3.3.11", 2016 + "picocolors": "^1.1.1", 2017 + "source-map-js": "^1.2.1" 2018 + }, 2019 + "engines": { 2020 + "node": "^10 || ^12 || >=14" 2021 + } 2022 + }, 2023 + "node_modules/postcss-load-config": { 2024 + "version": "3.1.4", 2025 + "dev": true, 2026 + "license": "MIT", 2027 + "dependencies": { 2028 + "lilconfig": "^2.0.5", 2029 + "yaml": "^1.10.2" 2030 + }, 2031 + "engines": { 2032 + "node": ">= 10" 2033 + }, 2034 + "funding": { 2035 + "type": "opencollective", 2036 + "url": "https://opencollective.com/postcss/" 2037 + }, 2038 + "peerDependencies": { 2039 + "postcss": ">=8.0.9", 2040 + "ts-node": ">=9.0.0" 2041 + }, 2042 + "peerDependenciesMeta": { 2043 + "postcss": { 2044 + "optional": true 2045 + }, 2046 + "ts-node": { 2047 + "optional": true 2048 + } 2049 + } 2050 + }, 2051 + "node_modules/postcss-load-config/node_modules/yaml": { 2052 + "version": "1.10.2", 2053 + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", 2054 + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", 2055 + "dev": true, 2056 + "license": "ISC", 2057 + "engines": { 2058 + "node": ">= 6" 2059 + } 2060 + }, 2061 + "node_modules/postcss-safe-parser": { 2062 + "version": "7.0.1", 2063 + "dev": true, 2064 + "funding": [ 2065 + { 2066 + "type": "opencollective", 2067 + "url": "https://opencollective.com/postcss/" 2068 + }, 2069 + { 2070 + "type": "tidelift", 2071 + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" 2072 + }, 2073 + { 2074 + "type": "github", 2075 + "url": "https://github.com/sponsors/ai" 2076 + } 2077 + ], 2078 + "license": "MIT", 2079 + "engines": { 2080 + "node": ">=18.0" 2081 + }, 2082 + "peerDependencies": { 2083 + "postcss": "^8.4.31" 2084 + } 2085 + }, 2086 + "node_modules/postcss-scss": { 2087 + "version": "4.0.9", 2088 + "dev": true, 2089 + "funding": [ 2090 + { 2091 + "type": "opencollective", 2092 + "url": "https://opencollective.com/postcss/" 2093 + }, 2094 + { 2095 + "type": "tidelift", 2096 + "url": "https://tidelift.com/funding/github/npm/postcss-scss" 2097 + }, 2098 + { 2099 + "type": "github", 2100 + "url": "https://github.com/sponsors/ai" 2101 + } 2102 + ], 2103 + "license": "MIT", 2104 + "engines": { 2105 + "node": ">=12.0" 2106 + }, 2107 + "peerDependencies": { 2108 + "postcss": "^8.4.29" 2109 + } 2110 + }, 2111 + "node_modules/postcss-selector-parser": { 2112 + "version": "7.1.0", 2113 + "dev": true, 2114 + "license": "MIT", 2115 + "dependencies": { 2116 + "cssesc": "^3.0.0", 2117 + "util-deprecate": "^1.0.2" 2118 + }, 2119 + "engines": { 2120 + "node": ">=4" 2121 + } 2122 + }, 2123 + "node_modules/prelude-ls": { 2124 + "version": "1.2.1", 2125 + "dev": true, 2126 + "license": "MIT", 2127 + "engines": { 2128 + "node": ">= 0.8.0" 2129 + } 2130 + }, 2131 + "node_modules/punycode": { 2132 + "version": "2.3.1", 2133 + "dev": true, 2134 + "license": "MIT", 2135 + "engines": { 2136 + "node": ">=6" 2137 + } 2138 + }, 2139 + "node_modules/queue-microtask": { 2140 + "version": "1.2.3", 2141 + "dev": true, 2142 + "funding": [ 2143 + { 2144 + "type": "github", 2145 + "url": "https://github.com/sponsors/feross" 2146 + }, 2147 + { 2148 + "type": "patreon", 2149 + "url": "https://www.patreon.com/feross" 2150 + }, 2151 + { 2152 + "type": "consulting", 2153 + "url": "https://feross.org/support" 2154 + } 2155 + ], 2156 + "license": "MIT" 2157 + }, 2158 + "node_modules/readdirp": { 2159 + "version": "4.1.2", 2160 + "dev": true, 2161 + "license": "MIT", 2162 + "engines": { 2163 + "node": ">= 14.18.0" 2164 + }, 2165 + "funding": { 2166 + "type": "individual", 2167 + "url": "https://paulmillr.com/funding/" 2168 + } 2169 + }, 2170 + "node_modules/resolve": { 2171 + "version": "1.22.11", 2172 + "dev": true, 2173 + "license": "MIT", 2174 + "dependencies": { 2175 + "is-core-module": "^2.16.1", 2176 + "path-parse": "^1.0.7", 2177 + "supports-preserve-symlinks-flag": "^1.0.0" 2178 + }, 2179 + "bin": { 2180 + "resolve": "bin/resolve" 2181 + }, 2182 + "engines": { 2183 + "node": ">= 0.4" 2184 + }, 2185 + "funding": { 2186 + "url": "https://github.com/sponsors/ljharb" 2187 + } 2188 + }, 2189 + "node_modules/resolve-from": { 2190 + "version": "4.0.0", 2191 + "dev": true, 2192 + "license": "MIT", 2193 + "engines": { 2194 + "node": ">=4" 2195 + } 2196 + }, 2197 + "node_modules/reusify": { 2198 + "version": "1.1.0", 2199 + "dev": true, 2200 + "license": "MIT", 2201 + "engines": { 2202 + "iojs": ">=1.0.0", 2203 + "node": ">=0.10.0" 2204 + } 2205 + }, 2206 + "node_modules/rollup": { 2207 + "version": "4.52.5", 2208 + "dev": true, 2209 + "license": "MIT", 2210 + "dependencies": { 2211 + "@types/estree": "1.0.8" 2212 + }, 2213 + "bin": { 2214 + "rollup": "dist/bin/rollup" 2215 + }, 2216 + "engines": { 2217 + "node": ">=18.0.0", 2218 + "npm": ">=8.0.0" 2219 + }, 2220 + "optionalDependencies": { 2221 + "@rollup/rollup-android-arm-eabi": "4.52.5", 2222 + "@rollup/rollup-android-arm64": "4.52.5", 2223 + "@rollup/rollup-darwin-arm64": "4.52.5", 2224 + "@rollup/rollup-darwin-x64": "4.52.5", 2225 + "@rollup/rollup-freebsd-arm64": "4.52.5", 2226 + "@rollup/rollup-freebsd-x64": "4.52.5", 2227 + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", 2228 + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", 2229 + "@rollup/rollup-linux-arm64-gnu": "4.52.5", 2230 + "@rollup/rollup-linux-arm64-musl": "4.52.5", 2231 + "@rollup/rollup-linux-loong64-gnu": "4.52.5", 2232 + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", 2233 + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", 2234 + "@rollup/rollup-linux-riscv64-musl": "4.52.5", 2235 + "@rollup/rollup-linux-s390x-gnu": "4.52.5", 2236 + "@rollup/rollup-linux-x64-gnu": "4.52.5", 2237 + "@rollup/rollup-linux-x64-musl": "4.52.5", 2238 + "@rollup/rollup-openharmony-arm64": "4.52.5", 2239 + "@rollup/rollup-win32-arm64-msvc": "4.52.5", 2240 + "@rollup/rollup-win32-ia32-msvc": "4.52.5", 2241 + "@rollup/rollup-win32-x64-gnu": "4.52.5", 2242 + "@rollup/rollup-win32-x64-msvc": "4.52.5", 2243 + "fsevents": "~2.3.2" 2244 + } 2245 + }, 2246 + "node_modules/run-parallel": { 2247 + "version": "1.2.0", 2248 + "dev": true, 2249 + "funding": [ 2250 + { 2251 + "type": "github", 2252 + "url": "https://github.com/sponsors/feross" 2253 + }, 2254 + { 2255 + "type": "patreon", 2256 + "url": "https://www.patreon.com/feross" 2257 + }, 2258 + { 2259 + "type": "consulting", 2260 + "url": "https://feross.org/support" 2261 + } 2262 + ], 2263 + "license": "MIT", 2264 + "dependencies": { 2265 + "queue-microtask": "^1.2.2" 2266 + } 2267 + }, 2268 + "node_modules/sade": { 2269 + "version": "1.8.1", 2270 + "dev": true, 2271 + "license": "MIT", 2272 + "dependencies": { 2273 + "mri": "^1.1.0" 2274 + }, 2275 + "engines": { 2276 + "node": ">=6" 2277 + } 2278 + }, 2279 + "node_modules/semver": { 2280 + "version": "7.7.3", 2281 + "dev": true, 2282 + "license": "ISC", 2283 + "bin": { 2284 + "semver": "bin/semver.js" 2285 + }, 2286 + "engines": { 2287 + "node": ">=10" 2288 + } 2289 + }, 2290 + "node_modules/set-cookie-parser": { 2291 + "version": "2.7.1", 2292 + "dev": true, 2293 + "license": "MIT" 2294 + }, 2295 + "node_modules/shebang-command": { 2296 + "version": "2.0.0", 2297 + "dev": true, 2298 + "license": "MIT", 2299 + "dependencies": { 2300 + "shebang-regex": "^3.0.0" 2301 + }, 2302 + "engines": { 2303 + "node": ">=8" 2304 + } 2305 + }, 2306 + "node_modules/shebang-regex": { 2307 + "version": "3.0.0", 2308 + "dev": true, 2309 + "license": "MIT", 2310 + "engines": { 2311 + "node": ">=8" 2312 + } 2313 + }, 2314 + "node_modules/sirv": { 2315 + "version": "3.0.2", 2316 + "dev": true, 2317 + "license": "MIT", 2318 + "dependencies": { 2319 + "@polka/url": "^1.0.0-next.24", 2320 + "mrmime": "^2.0.0", 2321 + "totalist": "^3.0.0" 2322 + }, 2323 + "engines": { 2324 + "node": ">=18" 2325 + } 2326 + }, 2327 + "node_modules/source-map-js": { 2328 + "version": "1.2.1", 2329 + "dev": true, 2330 + "license": "BSD-3-Clause", 2331 + "engines": { 2332 + "node": ">=0.10.0" 2333 + } 2334 + }, 2335 + "node_modules/strip-json-comments": { 2336 + "version": "3.1.1", 2337 + "dev": true, 2338 + "license": "MIT", 2339 + "engines": { 2340 + "node": ">=8" 2341 + }, 2342 + "funding": { 2343 + "url": "https://github.com/sponsors/sindresorhus" 2344 + } 2345 + }, 2346 + "node_modules/supports-color": { 2347 + "version": "7.2.0", 2348 + "dev": true, 2349 + "license": "MIT", 2350 + "dependencies": { 2351 + "has-flag": "^4.0.0" 2352 + }, 2353 + "engines": { 2354 + "node": ">=8" 2355 + } 2356 + }, 2357 + "node_modules/supports-preserve-symlinks-flag": { 2358 + "version": "1.0.0", 2359 + "dev": true, 2360 + "license": "MIT", 2361 + "engines": { 2362 + "node": ">= 0.4" 2363 + }, 2364 + "funding": { 2365 + "url": "https://github.com/sponsors/ljharb" 2366 + } 2367 + }, 2368 + "node_modules/svelte": { 2369 + "version": "5.42.2", 2370 + "dev": true, 2371 + "license": "MIT", 2372 + "dependencies": { 2373 + "@jridgewell/remapping": "^2.3.4", 2374 + "@jridgewell/sourcemap-codec": "^1.5.0", 2375 + "@sveltejs/acorn-typescript": "^1.0.5", 2376 + "@types/estree": "^1.0.5", 2377 + "acorn": "^8.12.1", 2378 + "aria-query": "^5.3.1", 2379 + "axobject-query": "^4.1.0", 2380 + "clsx": "^2.1.1", 2381 + "esm-env": "^1.2.1", 2382 + "esrap": "^2.1.0", 2383 + "is-reference": "^3.0.3", 2384 + "locate-character": "^3.0.0", 2385 + "magic-string": "^0.30.11", 2386 + "zimmerframe": "^1.1.2" 2387 + }, 2388 + "engines": { 2389 + "node": ">=18" 2390 + } 2391 + }, 2392 + "node_modules/svelte-check": { 2393 + "version": "4.3.3", 2394 + "dev": true, 2395 + "license": "MIT", 2396 + "dependencies": { 2397 + "@jridgewell/trace-mapping": "^0.3.25", 2398 + "chokidar": "^4.0.1", 2399 + "fdir": "^6.2.0", 2400 + "picocolors": "^1.0.0", 2401 + "sade": "^1.7.4" 2402 + }, 2403 + "bin": { 2404 + "svelte-check": "bin/svelte-check" 2405 + }, 2406 + "engines": { 2407 + "node": ">= 18.0.0" 2408 + }, 2409 + "peerDependencies": { 2410 + "svelte": "^4.0.0 || ^5.0.0-next.0", 2411 + "typescript": ">=5.0.0" 2412 + } 2413 + }, 2414 + "node_modules/svelte-eslint-parser": { 2415 + "version": "1.4.0", 2416 + "dev": true, 2417 + "license": "MIT", 2418 + "dependencies": { 2419 + "eslint-scope": "^8.2.0", 2420 + "eslint-visitor-keys": "^4.0.0", 2421 + "espree": "^10.0.0", 2422 + "postcss": "^8.4.49", 2423 + "postcss-scss": "^4.0.9", 2424 + "postcss-selector-parser": "^7.0.0" 2425 + }, 2426 + "engines": { 2427 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0", 2428 + "pnpm": "10.18.3" 2429 + }, 2430 + "funding": { 2431 + "url": "https://github.com/sponsors/ota-meshi" 2432 + }, 2433 + "peerDependencies": { 2434 + "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0" 2435 + }, 2436 + "peerDependenciesMeta": { 2437 + "svelte": { 2438 + "optional": true 2439 + } 2440 + } 2441 + }, 2442 + "node_modules/tinyglobby": { 2443 + "version": "0.2.15", 2444 + "dev": true, 2445 + "license": "MIT", 2446 + "dependencies": { 2447 + "fdir": "^6.5.0", 2448 + "picomatch": "^4.0.3" 2449 + }, 2450 + "engines": { 2451 + "node": ">=12.0.0" 2452 + }, 2453 + "funding": { 2454 + "url": "https://github.com/sponsors/SuperchupuDev" 2455 + } 2456 + }, 2457 + "node_modules/to-regex-range": { 2458 + "version": "5.0.1", 2459 + "dev": true, 2460 + "license": "MIT", 2461 + "dependencies": { 2462 + "is-number": "^7.0.0" 2463 + }, 2464 + "engines": { 2465 + "node": ">=8.0" 2466 + } 2467 + }, 2468 + "node_modules/totalist": { 2469 + "version": "3.0.1", 2470 + "dev": true, 2471 + "license": "MIT", 2472 + "engines": { 2473 + "node": ">=6" 2474 + } 2475 + }, 2476 + "node_modules/ts-api-utils": { 2477 + "version": "2.1.0", 2478 + "dev": true, 2479 + "license": "MIT", 2480 + "engines": { 2481 + "node": ">=18.12" 2482 + }, 2483 + "peerDependencies": { 2484 + "typescript": ">=4.8.4" 2485 + } 2486 + }, 2487 + "node_modules/type-check": { 2488 + "version": "0.4.0", 2489 + "dev": true, 2490 + "license": "MIT", 2491 + "dependencies": { 2492 + "prelude-ls": "^1.2.1" 2493 + }, 2494 + "engines": { 2495 + "node": ">= 0.8.0" 2496 + } 2497 + }, 2498 + "node_modules/typescript": { 2499 + "version": "5.9.3", 2500 + "dev": true, 2501 + "license": "Apache-2.0", 2502 + "bin": { 2503 + "tsc": "bin/tsc", 2504 + "tsserver": "bin/tsserver" 2505 + }, 2506 + "engines": { 2507 + "node": ">=14.17" 2508 + } 2509 + }, 2510 + "node_modules/typescript-eslint": { 2511 + "version": "8.46.2", 2512 + "dev": true, 2513 + "license": "MIT", 2514 + "dependencies": { 2515 + "@typescript-eslint/eslint-plugin": "8.46.2", 2516 + "@typescript-eslint/parser": "8.46.2", 2517 + "@typescript-eslint/typescript-estree": "8.46.2", 2518 + "@typescript-eslint/utils": "8.46.2" 2519 + }, 2520 + "engines": { 2521 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2522 + }, 2523 + "funding": { 2524 + "type": "opencollective", 2525 + "url": "https://opencollective.com/typescript-eslint" 2526 + }, 2527 + "peerDependencies": { 2528 + "eslint": "^8.57.0 || ^9.0.0", 2529 + "typescript": ">=4.8.4 <6.0.0" 2530 + } 2531 + }, 2532 + "node_modules/uint8arrays": { 2533 + "version": "3.0.0", 2534 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 2535 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 2536 + "license": "MIT", 2537 + "dependencies": { 2538 + "multiformats": "^9.4.2" 2539 + } 2540 + }, 2541 + "node_modules/undici-types": { 2542 + "version": "6.21.0", 2543 + "dev": true, 2544 + "license": "MIT" 2545 + }, 2546 + "node_modules/uri-js": { 2547 + "version": "4.4.1", 2548 + "dev": true, 2549 + "license": "BSD-2-Clause", 2550 + "dependencies": { 2551 + "punycode": "^2.1.0" 2552 + } 2553 + }, 2554 + "node_modules/util-deprecate": { 2555 + "version": "1.0.2", 2556 + "dev": true, 2557 + "license": "MIT" 2558 + }, 2559 + "node_modules/vite": { 2560 + "version": "7.1.12", 2561 + "dev": true, 2562 + "license": "MIT", 2563 + "dependencies": { 2564 + "esbuild": "^0.25.0", 2565 + "fdir": "^6.5.0", 2566 + "picomatch": "^4.0.3", 2567 + "postcss": "^8.5.6", 2568 + "rollup": "^4.43.0", 2569 + "tinyglobby": "^0.2.15" 2570 + }, 2571 + "bin": { 2572 + "vite": "bin/vite.js" 2573 + }, 2574 + "engines": { 2575 + "node": "^20.19.0 || >=22.12.0" 2576 + }, 2577 + "funding": { 2578 + "url": "https://github.com/vitejs/vite?sponsor=1" 2579 + }, 2580 + "optionalDependencies": { 2581 + "fsevents": "~2.3.3" 2582 + }, 2583 + "peerDependencies": { 2584 + "@types/node": "^20.19.0 || >=22.12.0", 2585 + "jiti": ">=1.21.0", 2586 + "less": "^4.0.0", 2587 + "lightningcss": "^1.21.0", 2588 + "sass": "^1.70.0", 2589 + "sass-embedded": "^1.70.0", 2590 + "stylus": ">=0.54.8", 2591 + "sugarss": "^5.0.0", 2592 + "terser": "^5.16.0", 2593 + "tsx": "^4.8.1", 2594 + "yaml": "^2.4.2" 2595 + }, 2596 + "peerDependenciesMeta": { 2597 + "@types/node": { 2598 + "optional": true 2599 + }, 2600 + "jiti": { 2601 + "optional": true 2602 + }, 2603 + "less": { 2604 + "optional": true 2605 + }, 2606 + "lightningcss": { 2607 + "optional": true 2608 + }, 2609 + "sass": { 2610 + "optional": true 2611 + }, 2612 + "sass-embedded": { 2613 + "optional": true 2614 + }, 2615 + "stylus": { 2616 + "optional": true 2617 + }, 2618 + "sugarss": { 2619 + "optional": true 2620 + }, 2621 + "terser": { 2622 + "optional": true 2623 + }, 2624 + "tsx": { 2625 + "optional": true 2626 + }, 2627 + "yaml": { 2628 + "optional": true 2629 + } 2630 + } 2631 + }, 2632 + "node_modules/vitefu": { 2633 + "version": "1.1.1", 2634 + "dev": true, 2635 + "license": "MIT", 2636 + "workspaces": [ 2637 + "tests/deps/*", 2638 + "tests/projects/*", 2639 + "tests/projects/workspace/packages/*" 2640 + ], 2641 + "peerDependencies": { 2642 + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" 2643 + }, 2644 + "peerDependenciesMeta": { 2645 + "vite": { 2646 + "optional": true 2647 + } 2648 + } 2649 + }, 2650 + "node_modules/which": { 2651 + "version": "2.0.2", 2652 + "dev": true, 2653 + "license": "ISC", 2654 + "dependencies": { 2655 + "isexe": "^2.0.0" 2656 + }, 2657 + "bin": { 2658 + "node-which": "bin/node-which" 2659 + }, 2660 + "engines": { 2661 + "node": ">= 8" 2662 + } 2663 + }, 2664 + "node_modules/word-wrap": { 2665 + "version": "1.2.5", 2666 + "dev": true, 2667 + "license": "MIT", 2668 + "engines": { 2669 + "node": ">=0.10.0" 2670 + } 2671 + }, 2672 + "node_modules/yaml": { 2673 + "version": "2.8.1", 2674 + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", 2675 + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", 2676 + "dev": true, 2677 + "license": "ISC", 2678 + "optional": true, 2679 + "peer": true, 2680 + "bin": { 2681 + "yaml": "bin.mjs" 2682 + }, 2683 + "engines": { 2684 + "node": ">= 14.6" 2685 + } 2686 + }, 2687 + "node_modules/yocto-queue": { 2688 + "version": "0.1.0", 2689 + "dev": true, 2690 + "license": "MIT", 2691 + "engines": { 2692 + "node": ">=10" 2693 + }, 2694 + "funding": { 2695 + "url": "https://github.com/sponsors/sindresorhus" 2696 + } 2697 + }, 2698 + "node_modules/zimmerframe": { 2699 + "version": "1.1.4", 2700 + "dev": true, 2701 + "license": "MIT" 2702 + }, 2703 + "node_modules/zod": { 2704 + "version": "3.25.76", 2705 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 2706 + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 2707 + "license": "MIT", 2708 + "funding": { 2709 + "url": "https://github.com/sponsors/colinhacks" 2710 + } 2711 + } 2712 + } 2713 + }
+41
web-ui/package.json
··· 1 + { 2 + "name": "web-ui", 3 + "private": true, 4 + "version": "0.0.1", 5 + "type": "module", 6 + "scripts": { 7 + "dev": "vite dev", 8 + "build": "vite build", 9 + "preview": "vite preview", 10 + "prepare": "svelte-kit sync || echo ''", 11 + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 12 + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 13 + "lint": "eslint ." 14 + }, 15 + "dependencies": { 16 + "@atcute/client": "^4.0.5", 17 + "@atcute/lexicons": "^1.2.2", 18 + "@pds-moover/lexicons": "^1.0.1", 19 + "@pds-moover/moover": "^1.0.4" 20 + }, 21 + "devDependencies": { 22 + "@eslint/compat": "^1.4.0", 23 + "@eslint/js": "^9.36.0", 24 + "@sveltejs/adapter-auto": "^6.1.0", 25 + "@sveltejs/adapter-node": "^5.4.0", 26 + "@sveltejs/kit": "^2.43.2", 27 + "@sveltejs/vite-plugin-svelte": "^6.2.0", 28 + "@types/node": "^22", 29 + "eslint": "^9.36.0", 30 + "eslint-plugin-svelte": "^3.12.4", 31 + "globals": "^16.4.0", 32 + "svelte": "^5.39.5", 33 + "svelte-check": "^4.3.2", 34 + "typescript": "^5.9.2", 35 + "typescript-eslint": "^8.44.1", 36 + "vite": "^7.1.7" 37 + }, 38 + "optionalDependencies": { 39 + "@rollup/rollup-linux-x64-musl": "^4.52.5" 40 + } 41 + }
+15
web-ui/src/app.d.ts
··· 1 + import {} from '@pds-moover/lexicons' 2 + 3 + // See https://svelte.dev/docs/kit/types#app.d.ts 4 + // for information about these interfaces 5 + declare global { 6 + namespace App { 7 + // interface Error {} 8 + // interface Locals {} 9 + // interface PageData {} 10 + // interface PageState {} 11 + // interface Platform {} 12 + } 13 + } 14 + 15 + export {};
+11
web-ui/src/app.html
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="utf-8" /> 5 + <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 + %sveltekit.head% 7 + </head> 8 + <body data-sveltekit-preload-data="hover"> 9 + <div style="display: contents">%sveltekit.body%</div> 10 + </body> 11 + </html>
+1
web-ui/src/lib/assets/favicon.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
+27
web-ui/src/lib/components/LoadingSpinner.svelte
··· 1 + <script lang="ts"> 2 + let { size = 16, className = '' }: { size?: number; className?: string } = $props(); 3 + </script> 4 + 5 + <svg 6 + class="spinner {className}" 7 + viewBox="0 0 24 24" 8 + style="display: inline-block; width: {size}px; height: {size}px; margin-right: 8px;" 9 + > 10 + <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="3" fill="none" opacity="0.25" /> 11 + <path 12 + d="M12 2 A10 10 0 0 1 22 12" 13 + stroke="currentColor" 14 + stroke-width="3" 15 + fill="none" 16 + stroke-linecap="round" 17 + > 18 + <animateTransform 19 + attributeName="transform" 20 + type="rotate" 21 + from="0 12 12" 22 + to="360 12 12" 23 + dur="1s" 24 + repeatCount="indefinite" 25 + /> 26 + </path> 27 + </svg>
+31
web-ui/src/lib/components/MooHeader.svelte
··· 1 + <script lang="ts"> 2 + import cowPicture from '$lib/assets/moo.webp' 3 + 4 + let {title, customImg = null} = $props(); 5 + 6 + </script> 7 + 8 + 9 + <div> 10 + <h1>{ title }</h1> 11 + <div class="cow-image"> 12 + {#if customImg === null } 13 + <img src={cowPicture} alt="Cartoon milk cow" 14 + style="max-width: 100%; max-height: 100%; object-fit: contain;"> 15 + {:else} 16 + {@render customImg()} 17 + {/if} 18 + </div> 19 + <div class="made-by-blur">Made by <a href="https://bsky.app/profile/baileytownsend.dev">@baileytownsend.dev</a> 20 + </div> 21 + <div class="support-buttons"> 22 + <span class="kofi-slot"> 23 + <a href='https://ko-fi.com/T6T61FYPX' target='_blank'><img height='36' style='border:0px;height:36px;' 24 + src='https://storage.ko-fi.com/cdn/kofi1.png?v=6' 25 + border='0' 26 + alt='Buy Me a Coffee at ko-fi.com'/></a> 27 + </span> 28 + <iframe src="https://github.com/sponsors/fatfingers23/button" title="Sponsor fatfingers23" height="32" 29 + width="114" style="border: 0; border-radius: 6px;"></iframe> 30 + </div> 31 + </div>
+52
web-ui/src/lib/components/NavBar.svelte
··· 1 + <script lang="ts"> 2 + import {page} from '$app/state'; 3 + import {resolve} from '$app/paths'; 4 + 5 + let open = $state(false); 6 + 7 + let links = [ 8 + {text: 'Info', path: '/info'}, 9 + {text: 'Moover', path: '/moover'}, 10 + {text: 'Missing Blobs', path: '/missing-blobs'}, 11 + {text: 'Turn Off', path: '/turn-off'}, 12 + {text: 'Backups', path: '/backups'}, 13 + {text: 'Restore', path: '/restore'}, 14 + {text: 'Legal', path: '/terms'} 15 + ]; 16 + </script> 17 + 18 + <header class="navbar" role="banner"> 19 + 20 + <div class="navbar-inner"> 21 + <a class="brand" href={resolve('/')}>PDS MOOver</a> 22 + <button class="navbar-toggle" onclick={() => open = !open} aria-controls="primary-navigation" 23 + aria-label="Toggle navigation"> 24 + 25 + <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 26 + viewBox="0 0 24.9512 17.1997"> 27 + <g> 28 + <rect height="17.1997" opacity="0" width="24.9512" x="0" y="0"/> 29 + <path d="M8.1543 16.9678L23.5962 16.9678C24.1455 16.9678 24.5972 16.5405 24.5972 15.9912C24.5972 15.4297 24.1455 15.0024 23.5962 15.0024L8.1543 15.0024C7.59277 15.0024 7.16553 15.4297 7.16553 15.9912C7.16553 16.5405 7.59277 16.9678 8.1543 16.9678Z" 30 + fill="white" fill-opacity="0.85"/> 31 + <path d="M1.2085 17.1997L3.2959 17.1997C3.96729 17.1997 4.5166 16.6504 4.5166 15.9912C4.5166 15.3076 3.96729 14.7705 3.2959 14.7705L1.2085 14.7705C0.537109 14.7705 0 15.3076 0 15.9912C0 16.6504 0.537109 17.1997 1.2085 17.1997Z" 32 + fill="white" fill-opacity="0.85"/> 33 + <path d="M8.1543 9.59473L23.5962 9.59473C24.1455 9.59473 24.5972 9.15527 24.5972 8.60596C24.5972 8.05664 24.1455 7.62939 23.5962 7.62939L8.1543 7.62939C7.59277 7.62939 7.16553 8.05664 7.16553 8.60596C7.16553 9.15527 7.59277 9.59473 8.1543 9.59473Z" 34 + fill="white" fill-opacity="0.85"/> 35 + <path d="M1.2085 9.82666L3.2959 9.82666C3.96729 9.82666 4.5166 9.27734 4.5166 8.60596C4.5166 7.93457 3.96729 7.39746 3.2959 7.39746L1.2085 7.39746C0.537109 7.39746 0 7.93457 0 8.60596C0 9.27734 0.537109 9.82666 1.2085 9.82666Z" 36 + fill="white" fill-opacity="0.85"/> 37 + <path d="M8.1543 2.20947L23.5962 2.20947C24.1455 2.20947 24.5972 1.78223 24.5972 1.23291C24.5972 0.671387 24.1455 0.244141 23.5962 0.244141L8.1543 0.244141C7.59277 0.244141 7.16553 0.671387 7.16553 1.23291C7.16553 1.78223 7.59277 2.20947 8.1543 2.20947Z" 38 + fill="white" fill-opacity="0.85"/> 39 + <path d="M1.2085 2.44141L3.2959 2.44141C3.96729 2.44141 4.5166 1.89209 4.5166 1.23291C4.5166 0.549316 3.96729 0.012207 3.2959 0.012207L1.2085 0.012207C0.537109 0.012207 0 0.549316 0 1.23291C0 1.89209 0.537109 2.44141 1.2085 2.44141Z" 40 + fill="white" fill-opacity="0.85"/> 41 + </g> 42 + </svg> 43 + 44 + </button> 45 + <nav id="primary-navigation" class="nav-links {open ? 'open' : ''}" aria-label="Primary" 46 + > 47 + {#each links as link (link.path)} 48 + <a href={resolve(link.path)} class={page.url.pathname === link.path ? 'active' : '' } onclick={() => open = false}>{link.text}</a> 49 + {/each} 50 + </nav> 51 + </div> 52 + </header>
+4
web-ui/src/lib/components/OgImage.svelte
··· 1 + <script> 2 + import cowPicture from '$lib/assets/halloween_moover.webp' 3 + </script> 4 + <meta property="og:image" content="{cowPicture}">
+74
web-ui/src/lib/components/RotationKeyDisplay.svelte
··· 1 + <script lang="ts"> 2 + import {handleAndPDSResolver} from '@pds-moover/moover' 3 + import type {RotationKeyType} from '$lib/types'; 4 + 5 + 6 + let {handle, rotationKey}: { 7 + handle: string, 8 + rotationKey: RotationKeyType 9 + } = $props(); 10 + 11 + 12 + const copyToClipboard = async (text: string) => { 13 + try { 14 + await navigator.clipboard.writeText(text); 15 + alert('Copied to clipboard'); 16 + } catch (e) { 17 + console.error(e); 18 + alert('Failed to copy to clipboard'); 19 + } 20 + } 21 + 22 + const downloadNewRotationKey = async (rotationKey: RotationKeyType, handle: string) => { 23 + if (!rotationKey) return; 24 + //try and find the did to add to the file as well 25 + let didText = ''; 26 + try { 27 + let {usersDid} = await handleAndPDSResolver(handle); 28 + didText = `DID: ${usersDid}\n`; 29 + } catch (e) { 30 + //sliently log. Rather the user have their rotation key than not. a did can always be found other ways if needed 31 + console.error(e); 32 + } 33 + 34 + const content = `You can use these to recover your account if it's ever necessary via https://pdsmoover.com/restore. The restore process will ask for the Private key\n\nKEEP IN A SECURE LOCATION\n\n${didText}PublicKey: ${rotationKey.publicKey}\nPrivateKey: ${rotationKey.privateKey}\n`; 35 + const blob = new Blob([content], {type: 'text/plain'}); 36 + const url = URL.createObjectURL(blob); 37 + const a = document.createElement('a'); 38 + a.href = url; 39 + 40 + 41 + a.download = `${handle}-rotation-key.txt`; 42 + document.body.appendChild(a); 43 + a.click(); 44 + document.body.removeChild(a); 45 + URL.revokeObjectURL(url); 46 + } 47 + </script> 48 + 49 + 50 + <div class="section" style="margin-top: 16px; border: 2px solid #f39c12; padding: 16px;"> 51 + <h3 style="color: #d35400;">Important: Save Your New Rotation Key Now</h3> 52 + <p style="color: #c0392b; font-weight: bold;"> 53 + Warning: This is the only time we will show you your private rotation key. Save it in a secure place. 54 + If you lose it, you may not be able to recover your account in the event of a PDS failure or hijack. 55 + </p> 56 + <div class="form-group"> 57 + <span>New Rotation Key (Private - keep secret)</span> 58 + <div style="display:flex; gap:8px; align-items:center;"> 59 + {#if rotationKey} 60 + <code 61 + style="overflow-wrap:anywhere;">{rotationKey.privateKey}</code> 62 + {/if} 63 + 64 + <button type="button" 65 + onclick={async () => await copyToClipboard(rotationKey.privateKey)}>Copy 66 + </button> 67 + </div> 68 + </div> 69 + <div class="form-group"> 70 + <button type="button" onclick={async () => await downloadNewRotationKey(rotationKey, handle)}>Download 71 + Key File 72 + </button> 73 + </div> 74 + </div>
+1
web-ui/src/lib/index.ts
··· 1 + // place files you want to import through the `$lib` alias in this folder.
+4
web-ui/src/lib/types.ts
··· 1 + type RotationKeyType = 2 + { privateKey: string, publicKey: `did:key:${string}` } 3 + 4 + export type {RotationKeyType}
+23
web-ui/src/lib/utils/displayUtils.ts
··· 1 + function formatBytes(bytes: number | null) { 2 + if (bytes == null) return '—'; 3 + const units = ['B', 'KB', 'MB', 'GB', 'TB']; 4 + let i = 0; 5 + let v = Number(bytes); 6 + while (v >= 1024 && i < units.length - 1) { 7 + v /= 1024; 8 + i++; 9 + } 10 + return v.toFixed(1) + ' ' + units[i]; 11 + } 12 + 13 + function formatDate(value: string | undefined) { 14 + if (!value) return '—'; 15 + try { 16 + const d = new Date(value); 17 + return d.toLocaleString(); 18 + } catch (_) { 19 + return String(value); 20 + } 21 + } 22 + 23 + export {formatBytes, formatDate};
+20
web-ui/src/routes/+layout.svelte
··· 1 + <script lang="ts"> 2 + import favicon from '$lib/assets/moo.webp'; 3 + import '$lib/assets/style.css' 4 + import NavBar from '$lib/components/NavBar.svelte'; 5 + 6 + let {children} = $props(); 7 + 8 + 9 + </script> 10 + 11 + <svelte:head> 12 + <link rel="icon" href={favicon}/> 13 + </svelte:head> 14 + 15 + 16 + <NavBar/> 17 + 18 + <main class="page-content"> 19 + {@render children?.()} 20 + </main>
+79
web-ui/src/routes/+page.svelte
··· 1 + <script lang="ts"> 2 + import MooHeader from '$lib/components/MooHeader.svelte'; 3 + import type {PageProps} from '../../.svelte-kit/types/src/routes/$types'; 4 + import {formatDate, formatBytes} from '$lib/utils/displayUtils'; 5 + import {resolve} from '$app/paths'; 6 + import OgImage from '$lib/components/OgImage.svelte'; 7 + 8 + let {data}: PageProps = $props(); 9 + let repoSize = $derived(formatBytes(data.estimatedReposSizeOnDisk)); 10 + let blobSize = $derived(formatBytes(data.estimatedBlobsSizeOnDisk)); 11 + let lastBackedUp = $derived(formatDate(data.lastBackupAt)) 12 + let lastStatusUpdate = $derived(formatDate(data.statusLastUpdated)) 13 + 14 + </script> 15 + 16 + <svelte:head> 17 + <title>PDS MOOver</title> 18 + <meta property="og:description" content="PDS MOOver – ATProto tools for PDS migrations and backups"/> 19 + <OgImage/> 20 + </svelte:head> 21 + 22 + <div class="container"> 23 + 24 + <MooHeader title="PDS MOOver"/> 25 + <section class="section" style="text-align:left"> 26 + <p> 27 + PDS MOOver is a set of AT Protocol tools to help you 28 + <a href={resolve('/moover')}>migrate to a new PDS</a>, 29 + <a href="/missing-blobs">find your missing blobs</a>, 30 + sign up for free automated <a href="/backups">backups</a>, and <a href="/restore">restore your account</a> 31 + in the event you need to. 32 + </p> 33 + <ul> 34 + <li><a href={resolve('/moover')}>Moover</a> – helps you migrate to a new PDS.</li> 35 + <li><a href="/missing-blobs">Missing Blobs</a> – find any missing blobs (pictures/videos) from a previous 36 + migration. 37 + </li> 38 + <li><a href="/backups">Backups</a> – sign up for free automated backups stored on PDS MOOver servers and 39 + view your account's backup status. 40 + </li> 41 + <li><a href="/restore">Restore</a> – restore from your backups if needed.</li> 42 + <li><a href="/turn-off">Turn Off</a> – helper to make sure your old account is deactivated.</li> 43 + <li><a href={resolve('/info')}>Info</a> – FAQs and a few other bits of information about our tools.</li> 44 + <li><a href="https://tangled.org/@baileytownsend.dev/pds-moover">Check our source code on tangled</a></li> 45 + </ul> 46 + </section> 47 + 48 + <section class="section" aria-labelledby="stats-heading"> 49 + <h2 id="stats-heading">Server stats</h2> 50 + <span>Total stats for all accounts backed up on pdsmoover.com</span> 51 + <div style="padding-top: 5%" class="stats-grid"> 52 + <div class="stat-card"> 53 + <div class="stat-label">Total repositories</div> 54 + <div class="stat-value">{data.totalRepos.toLocaleString()}</div> 55 + </div> 56 + <div class="stat-card"> 57 + <div class="stat-label">Total blobs</div> 58 + <div class="stat-value">{data.totalBlobs.toLocaleString()}</div> 59 + </div> 60 + <div class="stat-card"> 61 + <div class="stat-label">Estimated total repo size</div> 62 + <div class="stat-value">{repoSize}</div> 63 + </div> 64 + <div class="stat-card"> 65 + <div class="stat-label">Estimated total blob size</div> 66 + <div class="stat-value">{blobSize}</div> 67 + </div> 68 + <div class="stat-card"> 69 + <div class="stat-label">Last backup ran at</div> 70 + <div class="stat-value stat-value--small">{lastBackedUp}</div> 71 + </div> 72 + <div class="stat-card"> 73 + <div class="stat-label">Server Status last updated</div> 74 + <div class="stat-value stat-value--small">{lastStatusUpdate}</div> 75 + </div> 76 + </div> 77 + <!-- <div class="warning-message" x-show="error" x-text="error"></div>--> 78 + </section> 79 + </div>
+15
web-ui/src/routes/+page.ts
··· 1 + import {Client, ok, simpleFetchHandler} from '@atcute/client'; 2 + import {ComPdsmooverBackupDescribeServer} from '@pds-moover/lexicons'; 3 + import type {PageLoad} from './$types'; 4 + import {PUBLIC_XRPC_BASE} from '$env/static/public'; 5 + import type {InferXRPCBodyOutput} from '@atcute/lexicons'; 6 + 7 + export const load: PageLoad = async () => { 8 + const handler = simpleFetchHandler({service: `https://${PUBLIC_XRPC_BASE}`}); 9 + const rpc = new Client({handler}); 10 + return await ok( 11 + //@ts-expect-error: says it's not assignable to never 12 + rpc.get('com.pdsmoover.backup.describeServer'), 13 + ) as InferXRPCBodyOutput<ComPdsmooverBackupDescribeServer.mainSchema['output']> 14 + 15 + };
+187
web-ui/src/routes/backups/+page.svelte
··· 1 + <script lang="ts"> 2 + import {BackupService, PlcOps} from '@pds-moover/moover'; 3 + import MooHeader from '$lib/components/MooHeader.svelte'; 4 + import SignUpForm from './SignUpForm.svelte'; 5 + import RepoStatus from './RepoStatus.svelte'; 6 + import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; 7 + import type {InferXRPCBodyOutput} from '@atcute/lexicons'; 8 + import {ComPdsmooverBackupGetRepoStatus} from '@pds-moover/lexicons'; 9 + import {PUBLIC_XRPC_BASE} from '$env/static/public'; 10 + 11 + 12 + // Service instances 13 + let backupService = $state(new BackupService(`did:web:${PUBLIC_XRPC_BASE}`)); 14 + let plcOps = $state(new PlcOps()); 15 + 16 + // State variables 17 + let handle = $state(''); 18 + let password = $state(''); 19 + let twoFactorCode = $state(''); 20 + let showTwoFactorCodeInput = $state(false); 21 + let errorMessage = $state<string | null>(null); 22 + let showStatusMessage = $state(false); 23 + let showLoginScreen = $state(true); 24 + let showRepoNotFoundScreen = $state(false); 25 + let repoStatus = $state<null | InferXRPCBodyOutput< 26 + ComPdsmooverBackupGetRepoStatus.mainSchema['output'] 27 + >>(null); 28 + let statusMessageText = $state(''); 29 + let isLoggingIn = $state(false); 30 + 31 + function updateStatusHandler(status: string) { 32 + console.log('Status update:', status); 33 + statusMessageText = status; 34 + } 35 + 36 + async function handleLoginSubmit() { 37 + errorMessage = null; 38 + showStatusMessage = false; 39 + isLoggingIn = true; 40 + 41 + try { 42 + if (showTwoFactorCodeInput && !twoFactorCode) { 43 + errorMessage = 'Please enter the 2FA that was sent to your email.'; 44 + isLoggingIn = false; 45 + return; 46 + } 47 + statusMessageText = 'Logging in...'; 48 + showStatusMessage = true; 49 + const result = await backupService.loginAndStatus( 50 + handle, 51 + password, 52 + updateStatusHandler, 53 + twoFactorCode 54 + ); 55 + if (result === null) { 56 + showRepoNotFoundScreen = true; 57 + showLoginScreen = false; 58 + showStatusMessage = false; 59 + } else { 60 + repoStatus = result; 61 + showLoginScreen = false; 62 + showStatusMessage = false; 63 + } 64 + } catch (e) { 65 + console.error(e); 66 + showStatusMessage = false; 67 + //@ts-expect-error: Error is handled 68 + if (e.error === 'AuthFactorTokenRequired') { 69 + showTwoFactorCodeInput = true; 70 + errorMessage = 'Two-factor code required. Check your email and enter the code.'; 71 + } else { 72 + //@ts-expect-error: Error is handled 73 + errorMessage = e.message || 'An unexpected error occurred.'; 74 + } 75 + } finally { 76 + isLoggingIn = false; 77 + } 78 + } 79 + 80 + function handleSignUpComplete( 81 + status: InferXRPCBodyOutput<ComPdsmooverBackupGetRepoStatus.mainSchema['output']> 82 + ) { 83 + repoStatus = status; 84 + showRepoNotFoundScreen = false; 85 + showLoginScreen = false; 86 + } 87 + </script> 88 + 89 + <svelte:head> 90 + <title>PDS MOOver - Backups</title> 91 + <meta property="og:description" content="PDS MOOver backups"/> 92 + <meta property="og:image" content="/halloween_moover.webp"/> 93 + </svelte:head> 94 + 95 + <div class="container"> 96 + <MooHeader title="Backups"/> 97 + 98 + <!-- Login Screen --> 99 + {#if showLoginScreen} 100 + <div class="section" style="text-align: left;"> 101 + <p> 102 + PDS MOOver can provide worry-free backups of your AT Protocol account. This is a free 103 + service for individual accounts and stores the backups on PDS MOOver's servers. Just like 104 + your <a 105 + target="_blank" 106 + rel="noopener noreferrer" 107 + href="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy" 108 + >AT Proto data</a 109 + >, this is also public. On login, you will be asked if you'd like to add a rotation key to 110 + your account. A rotation key is a recovery key that allows you to restore your account if 111 + your PDS ever goes down. If you're already signed up for backups, then you can log in here 112 + to manage them. 113 + </p> 114 + </div> 115 + 116 + <form 117 + id="backup-signup-form" 118 + onsubmit={(e) => { 119 + e.preventDefault(); 120 + handleLoginSubmit(); 121 + }} 122 + > 123 + <div class="section"> 124 + <h2>Sign in to your account</h2> 125 + <div class="form-group"> 126 + <label for="handle">Handle</label> 127 + <input 128 + type="text" 129 + id="handle" 130 + name="handle" 131 + placeholder="alice.bsky.social" 132 + bind:value={handle} 133 + required 134 + /> 135 + </div> 136 + 137 + <div class="form-group"> 138 + <label for="password">Real Password</label> 139 + <input type="password" id="password" name="password" bind:value={password} required/> 140 + <p> 141 + If you are signing up and adding a rotation key you have to use your account's real 142 + password. If you are just managing your backups or have your own rotation key you can use 143 + an app password 144 + </p> 145 + </div> 146 + 147 + {#if showTwoFactorCodeInput} 148 + <div class="form-group"> 149 + <label for="two-factor-code">Two-factor code (email)</label> 150 + <input 151 + type="text" 152 + id="two-factor-code" 153 + name="two-factor-code" 154 + bind:value={twoFactorCode} 155 + /> 156 + <div class="error-message">Enter the 2FA code from your email.</div> 157 + </div> 158 + {/if} 159 + </div> 160 + 161 + {#if errorMessage} 162 + <div class="error-message">{errorMessage}</div> 163 + {/if} 164 + {#if showStatusMessage} 165 + <div class="status-message">{statusMessageText}</div> 166 + {/if} 167 + <div> 168 + <button type="submit" disabled={isLoggingIn}> 169 + {#if isLoggingIn} 170 + <LoadingSpinner/> 171 + {/if} 172 + Login for backups 173 + </button> 174 + </div> 175 + </form> 176 + {/if} 177 + 178 + <!-- Sign Up Screen --> 179 + {#if showRepoNotFoundScreen} 180 + <SignUpForm {backupService} {plcOps} {handle} onComplete={handleSignUpComplete}/> 181 + {/if} 182 + 183 + <!-- Repo Status View --> 184 + {#if repoStatus && !showLoginScreen && !showRepoNotFoundScreen} 185 + <RepoStatus {backupService} bind:repoStatus/> 186 + {/if} 187 + </div>
+181
web-ui/src/routes/backups/RepoStatus.svelte
··· 1 + <script lang="ts"> 2 + import type {BackupService} from '@pds-moover/moover'; 3 + import type {InferXRPCBodyOutput} from '@atcute/lexicons'; 4 + import {ComPdsmooverBackupGetRepoStatus} from '@pds-moover/lexicons'; 5 + import {formatDate, formatBytes} from '$lib/utils/displayUtils'; 6 + import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; 7 + 8 + 9 + let { 10 + backupService, 11 + repoStatus = $bindable() 12 + }: { 13 + backupService: BackupService; 14 + repoStatus: InferXRPCBodyOutput<ComPdsmooverBackupGetRepoStatus.mainSchema['output']> | null; 15 + } = $props(); 16 + 17 + // State variables 18 + let errorMessage = $state<string | null>(null); 19 + let showStatusMessage = $state(false); 20 + let statusMessageText = $state(''); 21 + let isRefreshing = $state(false); 22 + let isRunningBackup = $state(false); 23 + let isDeleting = $state(false); 24 + 25 + function updateStatusHandler(status: string) { 26 + console.log('Status update:', status); 27 + statusMessageText = status; 28 + } 29 + 30 + async function handleRunBackupNow() { 31 + errorMessage = null; 32 + isRunningBackup = true; 33 + showStatusMessage = true; 34 + try { 35 + await backupService.runBackupNow(updateStatusHandler); 36 + repoStatus = await backupService.getUsersRepoStatus(updateStatusHandler); 37 + updateStatusHandler('Backup request sent. Status refreshed.'); 38 + showStatusMessage = false; 39 + } catch (e) { 40 + console.error(e); 41 + //@ts-expect-error: Error is handled 42 + errorMessage = e.message || 'Failed to request backup.'; 43 + } finally { 44 + isRunningBackup = false; 45 + } 46 + } 47 + 48 + async function handleRemoveRepo() { 49 + if ( 50 + confirm( 51 + 'Deleting your backups removes all your data and unregisters you for automatic backups. Your rotation key created here will stay valid, but you will not be able to restore your data from PDS MOOver.' 52 + ) 53 + ) { 54 + isDeleting = true; 55 + try { 56 + await backupService.removeRepo(updateStatusHandler); 57 + repoStatus = await backupService.getUsersRepoStatus(updateStatusHandler); 58 + } catch (e) { 59 + console.error(e); 60 + //@ts-expect-error: Error is handled 61 + errorMessage = e.message || 'Failed to delete backup repository'; 62 + } finally { 63 + isDeleting = false; 64 + } 65 + } 66 + } 67 + 68 + async function refreshStatus() { 69 + isRefreshing = true; 70 + try { 71 + repoStatus = await backupService.getUsersRepoStatus(updateStatusHandler); 72 + } catch (e) { 73 + console.error(e); 74 + //@ts-expect-error: Error is handled 75 + errorMessage = e.message || 'Failed to refresh status'; 76 + } finally { 77 + showStatusMessage = false; 78 + isRefreshing = false; 79 + } 80 + } 81 + </script> 82 + 83 + <div class="section"> 84 + <div class="section-header"> 85 + <h2 style="margin: 0;">Backup repository status</h2> 86 + <button 87 + type="button" 88 + class="icon-button" 89 + title="Refresh status" 90 + aria-label="Refresh status" 91 + onclick={refreshStatus} 92 + disabled={isRefreshing} 93 + style={isRefreshing ? 'opacity: 0.6;' : ''} 94 + > 95 + <svg class="icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false" 96 + style={isRefreshing ? 'animation: spin 1s linear infinite;' : ''}> 97 + <path 98 + d="M17.65 6.35A7.95 7.95 0 0012 4a8 8 0 108 8h-2a6 6 0 11-6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z" 99 + ></path> 100 + </svg> 101 + </button> 102 + </div> 103 + <div class="stats-grid"> 104 + <div class="stat-card"> 105 + <div class="stat-label">DID</div> 106 + <div class="stat-value stat-value--small">{repoStatus?.did || '—'}</div> 107 + </div> 108 + <div class="stat-card"> 109 + <div class="stat-label">Created</div> 110 + <div class="stat-value stat-value--small">{formatDate(repoStatus?.createdAt)}</div> 111 + </div> 112 + {#if repoStatus?.lastBackup != null} 113 + <div class="stat-card"> 114 + <div class="stat-label">Last backup</div> 115 + <div class="stat-value stat-value--small">{formatDate(repoStatus.lastBackup)}</div> 116 + </div> 117 + {/if} 118 + {#if repoStatus?.rev != null} 119 + <div class="stat-card"> 120 + <div class="stat-label">Current rev</div> 121 + <div class="stat-value stat-value--small">{repoStatus.rev}</div> 122 + </div> 123 + {/if} 124 + {#if repoStatus?.blobCount != null} 125 + <div class="stat-card"> 126 + <div class="stat-label">Total blobs</div> 127 + <div class="stat-value">{repoStatus.blobCount.toLocaleString()}</div> 128 + </div> 129 + {/if} 130 + {#if repoStatus?.estimatedBackupSize != null} 131 + <div class="stat-card"> 132 + <div class="stat-label">Estimated size</div> 133 + <div class="stat-value">{formatBytes(repoStatus.estimatedBackupSize)}</div> 134 + </div> 135 + {/if} 136 + {#if repoStatus?.missingBlobCount != null} 137 + <div class="stat-card"> 138 + <div class="stat-label">Missing blobs</div> 139 + <div class="stat-value">{repoStatus.missingBlobCount.toLocaleString()}</div> 140 + </div> 141 + {/if} 142 + {#if repoStatus?.source != null} 143 + <div class="stat-card"> 144 + <div class="stat-label">Source</div> 145 + <div class="stat-value stat-value--small">{repoStatus.source}</div> 146 + </div> 147 + {/if} 148 + </div> 149 + <div class="actions" style="padding-top: 5%"> 150 + <button type="button" onclick={handleRunBackupNow} disabled={isRunningBackup}> 151 + {#if isRunningBackup} 152 + <LoadingSpinner/> 153 + {/if} 154 + Manually run backup now 155 + </button> 156 + <button type="button" style="background-color: #c62828; color: #fff;" onclick={handleRemoveRepo} 157 + disabled={isDeleting}> 158 + {#if isDeleting} 159 + <LoadingSpinner/> 160 + {/if} 161 + Delete Backups 162 + </button> 163 + </div> 164 + {#if errorMessage} 165 + <div class="error-message">{errorMessage}</div> 166 + {/if} 167 + {#if showStatusMessage} 168 + <div class="status-message">{statusMessageText}</div> 169 + {/if} 170 + </div> 171 + 172 + <style> 173 + @keyframes spin { 174 + from { 175 + transform: rotate(0deg); 176 + } 177 + to { 178 + transform: rotate(360deg); 179 + } 180 + } 181 + </style>
+225
web-ui/src/routes/backups/SignUpForm.svelte
··· 1 + <script lang="ts"> 2 + import type {BackupService, PlcOps} from '@pds-moover/moover'; 3 + import type {RotationKeyType} from '$lib/types'; 4 + import type {InferXRPCBodyOutput} from '@atcute/lexicons'; 5 + import {ComPdsmooverBackupGetRepoStatus} from '@pds-moover/lexicons'; 6 + import RotationKeyDisplay from '$lib/components/RotationKeyDisplay.svelte'; 7 + import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; 8 + 9 + let { 10 + backupService, 11 + plcOps, 12 + handle, 13 + onComplete 14 + }: { 15 + backupService: BackupService; 16 + plcOps: PlcOps; 17 + handle: string; 18 + onComplete: ( 19 + repoStatus: InferXRPCBodyOutput<ComPdsmooverBackupGetRepoStatus.mainSchema['output']> 20 + ) => void; 21 + } = $props(); 22 + 23 + // State variables 24 + let errorMessage = $state<string | null>(null); 25 + let showStatusMessage = $state(false); 26 + let statusMessageText = $state(''); 27 + let addRecoveryKey = $state(true); 28 + let plcTokenInput = $state<string | null>(null); 29 + let newlyCreatedRotationKey = $state<RotationKeyType | null>(null); 30 + let showRotationKeyScreen = $state(false); 31 + let isRequestingToken = $state(false); 32 + let isSigningUp = $state(false); 33 + let isProceeding = $state(false); 34 + 35 + function updateStatusHandler(status: string) { 36 + console.log('Status update:', status); 37 + statusMessageText = status; 38 + } 39 + 40 + async function requestPlcToken() { 41 + errorMessage = null; 42 + isRequestingToken = true; 43 + try { 44 + showStatusMessage = true; 45 + updateStatusHandler('Requesting PLC token…'); 46 + await backupService.requestAPlcToken(); 47 + updateStatusHandler('PLC token emailed. Check your inbox and paste the token below.'); 48 + } catch (e) { 49 + //@ts-expect-error: Error is handled 50 + errorMessage = e?.message || 'Failed to request PLC token'; 51 + } finally { 52 + showStatusMessage = false; 53 + isRequestingToken = false; 54 + } 55 + } 56 + 57 + async function handleSignUpSubmit() { 58 + errorMessage = null; 59 + isSigningUp = true; 60 + showStatusMessage = true; 61 + try { 62 + if (addRecoveryKey) { 63 + if (!plcTokenInput) { 64 + errorMessage = 65 + 'Please paste your PLC token. Use the "Email me a PLC token" button to request one.'; 66 + showStatusMessage = false; 67 + isSigningUp = false; 68 + return; 69 + } 70 + updateStatusHandler('Creating new rotation key…'); 71 + const created = await plcOps.createANewSecp256k1(); 72 + newlyCreatedRotationKey = created; 73 + updateStatusHandler('Adding new rotation key to your DID…'); 74 + await backupService.addANewRotationKey(plcTokenInput, created.publicKey); 75 + updateStatusHandler('Rotation key added. Please save the private key now.'); 76 + showRotationKeyScreen = true; 77 + showStatusMessage = false; 78 + isSigningUp = false; 79 + //Return and then on next button click we sign the user up that way if it fails it won't ruin them from 80 + //Getting their rotation key 81 + return; 82 + } 83 + await backupService.signUp(updateStatusHandler); 84 + updateStatusHandler('Signed up for backups successfully.'); 85 + 86 + const repoStatus = await backupService.getUsersRepoStatus(updateStatusHandler); 87 + showStatusMessage = false; 88 + isSigningUp = false; 89 + onComplete(repoStatus); 90 + } catch (e) { 91 + console.error(e); 92 + //@ts-expect-error: Error is handled 93 + errorMessage = e.message || 'Failed to sign up for backups.'; 94 + showStatusMessage = false; 95 + isSigningUp = false; 96 + } 97 + } 98 + 99 + async function proceedToRepoStatus() { 100 + isProceeding = true; 101 + try { 102 + await backupService.signUp(updateStatusHandler); 103 + updateStatusHandler('Signed up for backups successfully.'); 104 + 105 + showStatusMessage = true; 106 + updateStatusHandler('Fetching repository status…'); 107 + const repoStatus = await backupService.getUsersRepoStatus(updateStatusHandler); 108 + showRotationKeyScreen = false; 109 + showStatusMessage = false; 110 + isProceeding = false; 111 + onComplete(repoStatus); 112 + } catch (e) { 113 + console.error(e); 114 + //@ts-expect-error: Error is handled 115 + errorMessage = e?.message || 'Failed to load repository status'; 116 + showStatusMessage = false; 117 + isProceeding = false; 118 + } 119 + } 120 + </script> 121 + 122 + {#if showRotationKeyScreen && newlyCreatedRotationKey} 123 + <div class="section"> 124 + <RotationKeyDisplay {handle} rotationKey={newlyCreatedRotationKey}/> 125 + <div class="form-group"> 126 + <button type="button" onclick={proceedToRepoStatus} disabled={isProceeding}> 127 + {#if isProceeding} 128 + <LoadingSpinner/> 129 + {/if} 130 + Next 131 + </button> 132 + </div> 133 + </div> 134 + {:else} 135 + <div class="section"> 136 + <h2>No backup repository found</h2> 137 + <p style="text-align: left;"> 138 + Sign up now to backup your AT Protocol account. PDS MOOver automatically backups your posts, 139 + likes, media, and all account data every 6 hours. This is stored on our servers in something 140 + called an <a 141 + target="_blank" 142 + rel="noopener noreferrer" 143 + href="https://en.wikipedia.org/wiki/Object_storage">object store.</a 144 + > 145 + Just like your 146 + <a 147 + target="_blank" 148 + rel="noopener noreferrer" 149 + href="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy" 150 + >AT Proto data</a 151 + >, this data is public. 152 + </p> 153 + <div class="info"> 154 + <h3>Critical: Save your rotation key</h3> 155 + <p style="text-align: left;"> 156 + A rotation key is your account recovery key. If you've migrated to a selfhosted or 157 + thirdparty PDS, this key is the <span class="bold">ONLY</span> way to recover your account if 158 + your PDS goes down. Without it, a failed or rogue PDS means permanent account loss. Generate 159 + one and store it safely. 160 + </p> 161 + </div> 162 + <div class="form-group"> 163 + <label style="display: inline-flex; align-items: center; gap: 0.5rem; white-space: nowrap;"> 164 + <input type="checkbox" bind:checked={addRecoveryKey} style="margin: 0;"/> 165 + <span>Create a rotation key (recommended)</span> 166 + </label> 167 + </div> 168 + {#if addRecoveryKey} 169 + <div class="form-group"> 170 + <p> 171 + To add a new rotation key, you must authorize a PLC operation. Click the button to email 172 + yourself a PLC token, then paste it below. 173 + <span class="bold" 174 + >NOTE: Adding a new key will remove all current rotation keys except for the one created 175 + and the one from your PDS</span 176 + > 177 + </p> 178 + 179 + <div class="actions" style="margin-bottom: 0.5rem;"> 180 + <button type="button" onclick={requestPlcToken} disabled={isRequestingToken}> 181 + {#if isRequestingToken} 182 + <LoadingSpinner/> 183 + {/if} 184 + Email me a PLC token 185 + </button> 186 + </div> 187 + <div class="form-group"> 188 + <label for="plc-token">PLC token</label> 189 + <input 190 + type="text" 191 + id="plc-token" 192 + name="plc-token" 193 + bind:value={plcTokenInput} 194 + placeholder="Paste PLC token from email" 195 + autocomplete="one-time-code" 196 + /> 197 + </div> 198 + </div> 199 + {:else} 200 + <div class="form-group"> 201 + <div class="status-message" style="text-align: left;"> 202 + <p> 203 + <span class="bold">Note:</span> a recovery key is 204 + <span class="bold">required</span> to restore your account. If you're not sure what this means, 205 + then it is important to check the above box and save the rotation key file given. 206 + </p> 207 + </div> 208 + </div> 209 + {/if} 210 + {#if errorMessage} 211 + <div class="error-message">{errorMessage}</div> 212 + {/if} 213 + {#if showStatusMessage} 214 + <div class="status-message">{statusMessageText}</div> 215 + {/if} 216 + <div> 217 + <button type="button" onclick={handleSignUpSubmit} disabled={isSigningUp}> 218 + {#if isSigningUp} 219 + <LoadingSpinner/> 220 + {/if} 221 + Sign up for backups 222 + </button> 223 + </div> 224 + </div> 225 + {/if}
+202
web-ui/src/routes/info/+page.svelte
··· 1 + <script lang="ts"> 2 + import OgImage from '$lib/components/OgImage.svelte'; 3 + import MooHeader from '$lib/components/MooHeader.svelte'; 4 + import SignThePapersImg from '$lib/assets/sign_the_papers.png' 5 + </script> 6 + 7 + <svelte:head> 8 + <title>PDS MOOver - Info</title> 9 + <meta property="og:description" content="ATProto account migration tool"/> 10 + <OgImage/> 11 + </svelte:head> 12 + 13 + <div class="container"> 14 + <MooHeader title="PDS MOOver Info"/> 15 + 16 + 17 + <div class="section" id="top"> 18 + <p> This page is to help you decide if you want to use PDS MOOver to move your ATProto(Bluesky) account to a new 19 + PDS along with some other information about all the new tools. 20 + One way or the other. TLDR (You should still read the whole thing), at least read and follow the <a 21 + href="#precautions">precautions section</a>.</p> 22 + 23 + 24 + <section class="section" style="text-align:left"> 25 + <h2>Info</h2> 26 + <p>PDS MOOver is a set of tools to help you migrate to a new PDS. The creator 27 + or host of this tool will not be able to help you recover your account if something goes wrong. So be 28 + advised you and your PDS admin may be on your own besides helpful answers and understand the risk you 29 + take in doing an account movement.</p> 30 + </section> 31 + 32 + 33 + <nav aria-label="Table of contents" class="section" style="text-align:left"> 34 + <h3>Table of contents</h3> 35 + <ol> 36 + <li><a href="#precautions">Precautions</a></li> 37 + <li><a href="#backups">Backups</a></li> 38 + <li><a href="#restore">Restore</a></li> 39 + <li><a href="#blacksky">I'm here for Blacksky, is there a video guide?</a></li> 40 + <li><a href="#cant-login">I can't log in?/Says my account is deactivated?</a></li> 41 + <li><a href="#invalid-handle">My account says Invalid Handle?</a></li> 42 + <li><a href="#help">!!!!!HELP!!!!!</a></li> 43 + <li><a href="#why">Why doesn't PDS MOOver have xyz?</a></li> 44 + <li><a href="#done">Alright account migrated, now what?</a></li> 45 + <li><a href="#slow">Why is it so SLOW?</a></li> 46 + <li><a href="#open-source">Can I check out the code anywhere?</a></li> 47 + </ol> 48 + </nav> 49 + 50 + <section id="precautions" class="section" style="text-align:left"> 51 + <h2>Precautions</h2> 52 + <p> Migrations can be a potentially dangerous operation. It is recommended to follow these few steps to 53 + protect your account and identity.</p> 54 + <ul> 55 + <li>During migration make sure to do not leave the page</li> 56 + <li>It is recommended to use a desktop computer for this process due to the amount of time it can 57 + take. 58 + </li> 59 + <li>Your account is not actually fully moved over to the new PDS till you receive a code in your email 60 + and enter it on PDS MOOver, this is the final step. 61 + </li> 62 + <li>Your data will not be deleted from Bluesky(or your previous PDS) during migration. If you find you 63 + are missing any pictures or videos after the move you can use the <a href="/missing">Missing 64 + tool</a> to recover those from your previous PDS. 65 + </li> 66 + </ul> 67 + 68 + <p>At the end of your migration and before you move you will be asked if you'd like to sign up for PDS 69 + MOOver's backup service and to add a rotation key. Both of these are recommended and secure your account 70 + if your PDS ever goes down, allowing for account recovery. </p> 71 + <img src="{SignThePapersImg}" alt="Sign the papers" 72 + style="max-width: 100%; max-height: 100%; object-fit: contain;"> 73 + 74 + </section> 75 + 76 + <section id="backups" class="section" style="text-align:left"> 77 + <h2>Backups</h2> 78 + <p>PDS MOOver now supports backups. These are automated backups of your account saving your repo 79 + (posts,likes,etc), and your blobs(picture/videos) from your AT Proto account to a cloud base object 80 + store (S3). This is a free service for individual accounts and stores the backups on PDS MOOver's 81 + servers. These backups will happen every 6 hours from the time you sign up. We are expecting to lower 82 + this as we see how the service does. On login, you will be asked if you'd like to add a rotation key to 83 + your account. It is highly recommended to do this if you do not already have one. This is the only way 84 + you can recover your account in the event of a PDS failure or rogue account takeover</p> 85 + 86 + <p>Just like your <a target="_blank" rel="noopener noreferrer" 87 + href="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">AT 88 + Proto data</a>, your backedup data is also public. 89 + You can access your data much the same way you do on your PDS by calling these endpoints. Since these 90 + behave much the same as the PDS they are public. 91 + </p> 92 + <ul> 93 + <li><code>/xrpc/com.atproto.sync.getRepo?did="your did"</code> to get a copy of your repo's CAR export 94 + </li> 95 + <li><code>/xrpc/com.atproto.sync.getBlob?did="your did"&cid="cid of the blob"</code></li> 96 + </ul> 97 + 98 + </section> 99 + 100 + <section id="restore" class="section" style="text-align:left"> 101 + <h2>Restore</h2> 102 + <p>Backups without a restore option aren't very good backups. So to pair well with our new backup service 103 + PDS MOOver now also offers recovery of your account and restoring from a backup. To use this you must 104 + have your Private Rotation Key. You can add one to your account either after migration or during backups 105 + sign up. This recovery process follows much the same process as out line in <a 106 + href="https://www.da.vidbuchanan.co.uk/blog/adversarial-pds-migration.html">Adversarial ATProto 107 + PDS Migration</a> blog post by <a href="https://bsky.app/profile/retr0.id">@retr0.id</a>. </p> 108 + </section> 109 + 110 + <section id="blacksky" class="section" style="text-align:left"> 111 + <h2>I'm here for Blacksky, Is there a video guide?</h2> 112 + <p> 113 + <a href="https://blacksky.community/profile/did:plc:g7j6qok5us4hjqlwjxwrrkjm">@sharpiepls.com</a> has 114 + made an amazing video guide on 115 + how to use PDS MOOver to move your bluesky account to blacksky.app. 116 + <video width="100%" controls> 117 + 118 + <source src="https://blacksky.app/xrpc/com.atproto.sync.getBlob?did=did:plc:g7j6qok5us4hjqlwjxwrrkjm&cid=bafkreielhdoa2lcyiat5oumkbkwy26hlj36rwwwfi5fbnvd5haxq3t4joa"/> 119 + </video> 120 + </p> 121 + </section> 122 + 123 + <section id="cant-login" class="section" style="text-align:left"> 124 + <h2>I can't log in?/Says my account is deactivated?</h2> 125 + <p>When you move to a non Bluesky PDS you have to do an extra step on login.</p> 126 + <ol> 127 + <li>On the Sign in screen for <a href="https://bsky.app">bsky.app</a> or on the app click the top input 128 + titled "Hosting provider" and has a globe icon and says Bluesky Social" 129 + </li> 130 + <li>Click the tab labeled custom</li> 131 + <li>In the input for server address you put the same URL you used for the new PDS URL with the https:// 132 + like so <code>https://example.com</code></li> 133 + <li>Click done and enter your new handle(or email) and password</li> 134 + </ol> 135 + </section> 136 + 137 + <section id="invalid-handle" class="section" style="text-align:left"> 138 + <h2>My account says Invalid Handle?</h2> 139 + <p>It's a bit of a bug sometimes. I'm not sure what causes it, but usually mentioning your handle in a post 140 + or reply fixes it. 141 + Like <code>@fullhandle.newpds.com</code>, may or may not highlight it blue and autofill it but make sure 142 + you have the 143 + full handle and the @ like that. Can also check your handle with the <a 144 + href="https://bsky-debug.app/handle">Bluesky Debug Page</a>. If you see green, and it says one 145 + of 146 + them pass you should be fine and just may take a while to update.</p> 147 + </section> 148 + 149 + <section id="help" class="section" style="text-align:left"> 150 + <h2>!!!!!HELP!!!!!</h2> 151 + <p>If you're having issues with PDS MOOver first of all, I'm very sorry. I have tested this to the best of 152 + my 153 + ability, but PDS migrations do come with risks. I would recommend getting with the owner of the PDS and 154 + seeing where the account stands with tools like <a href="https://pdsls.dev">pdsls</a>.</p> 155 + 156 + <p> The tool is designed to be able to be re ran IF you set the Advance Options flags.For example, lets say 157 + if it created the account, repo is there but some blobs are missing. You can uncheck everything but 158 + "Migrate Missing Blobs", "Migrate Prefs", and "Migrate PLC record" and it will pick up after the account 159 + repo migration. It is odd in the fact that all the fields are required. That's just to cut down on logic 160 + to hopefully cut down on bugs. If you don't ever see the "Please enter your PLC Token" and enter the 161 + token sent to your email, you can just 162 + forget about it and call it a day if it's too much. Your old account is still active and working.</p> 163 + </section> 164 + 165 + 166 + <section id="why" class="section" style="text-align:left"> 167 + <h2>Why doesn't PDS MOOver have xyz for migrations?</h2> 168 + <p>PDS MOOver was designed to pretty much be the goat account migration with a UI. Like in this <a 169 + href="https://whtwnd.com/bnewbold.net/3l5ii332pf32u"> post</a>. Keeping it simple and hard fails if 170 + anything 171 + goes wrong 172 + to 173 + hopefully cover most use cases.</p> 174 + </section> 175 + 176 + <section id="done" class="section" style="text-align:left"> 177 + <h2>Alright account migrated, now what?</h2> 178 + <p>Welcome to your new PDS! You can login to your new PDS on Bluesky's login screen by selecting "Hosting 179 + provider" and entering your PDS url. I also recommend making sure you are signed up for our <a 180 + href="/backups">backups</a> and have a recovery key that you control in case your PDS disappears 181 + overnight you can regain your account. </p> 182 + </section> 183 + 184 + <section id="slow" class="section" style="text-align:left"> 185 + <h2>Why is it so SLOW?</h2> 186 + <p>Everything happens client side, and the blob uploads take a while. Nothing runs in parallel. Blob uploads 187 + happen one at a time; once one is done, the next goes. This is done just to keep it as simple as 188 + possible and to hopefully limit the chance of failures on uploads. My personal account takes about 189 + 20-30ish mins to move with 1,700ish blobs at 800mb on a 1gig internet connection.</p> 190 + </section> 191 + 192 + <section id="open-source" class="section" style="text-align:left"> 193 + <h2>Can I check out the code anywhere?</h2> 194 + <p>Yep! PDS MOOver is 100% open source and can find the code on <a 195 + href="https://tangled.sh/@baileytownsend.dev/pds-moover">tangled.sh</a>. Also, if you're a 196 + developer, 197 + and you want to fork the code for a new UI. PDS MOOver's logic is all in one js file. Just take it and 198 + its dependencies and have at it.</p> 199 + </section> 200 + </div> 201 + 202 + </div>
+230
web-ui/src/routes/missing-blobs/+page.svelte
··· 1 + <script lang="ts"> 2 + import MooHeader from '$lib/components/MooHeader.svelte'; 3 + import missingPicture from '$lib/assets/missing.webp' 4 + import {MissingBlobs} from '@pds-moover/moover'; 5 + 6 + let missingBlobs = $state(new MissingBlobs()); 7 + 8 + // Form state 9 + let showCurrentLogin = $state(true); 10 + let showOldLogin = $state(false); 11 + let showAdvance = $state(false); 12 + let disableLoginButton = $state(false); 13 + let showBlobMoveProgress = $state(false); 14 + let oldPdsUrl = $state<string | null>(null); 15 + let showTryAgain = $state(false); 16 + let errorMessage: string | null = $state(null); 17 + let showStatusMessage = $state(false); 18 + let statusMessage = $state(''); 19 + 20 + let loginForm = $state({ 21 + handle: '', 22 + password: '', 23 + twoFactorCode: '', 24 + showTwoFactorCodeInput: false, 25 + }); 26 + 27 + 28 + function resetStatusAndErrors() { 29 + showStatusMessage = false; 30 + statusMessage = ''; 31 + errorMessage = ''; 32 + disableLoginButton = true; 33 + } 34 + 35 + async function handleCurrentLogin(event: SubmitEvent) { 36 + event.preventDefault(); 37 + resetStatusAndErrors(); 38 + try { 39 + const { 40 + accountStatus, 41 + missingBlobsCount 42 + } = await missingBlobs.currentAgentLogin(loginForm.handle, loginForm.password, loginForm.twoFactorCode); 43 + console.log(missingBlobsCount); 44 + const noMissingBlobs = missingBlobsCount === 0; 45 + if (noMissingBlobs) { 46 + statusMessage = `You are good to go! You are not missing any blobs. Your account has ${accountStatus.importedBlobs} imported blobs and expects to have at least ${accountStatus.expectedBlobs} blobs. No action is required.` 47 + } else { 48 + showCurrentLogin = false; 49 + statusMessage = 'You are currently missing some blobs. Login with your old password to import the missing blobs. We will automatically find your old handle.'; 50 + showOldLogin = true; 51 + //Reset the form 52 + loginForm.showTwoFactorCodeInput = false; 53 + loginForm.twoFactorCode = ''; 54 + loginForm.password = ''; 55 + loginForm.handle = ''; 56 + } 57 + showStatusMessage = true; 58 + 59 + } catch (err) { 60 + //@ts-expect-error: Should always have an error message 61 + if (err.error === 'AuthFactorTokenRequired') { 62 + loginForm.showTwoFactorCodeInput = true; 63 + } 64 + //@ts-expect-error: Should always have an error message 65 + errorMessage = err.message; 66 + } 67 + disableLoginButton = false; 68 + } 69 + 70 + async function handleOldLogin(event: SubmitEvent) { 71 + event.preventDefault(); 72 + resetStatusAndErrors(); 73 + try { 74 + await missingBlobs.oldAgentLogin(loginForm.password, loginForm.twoFactorCode, oldPdsUrl); 75 + showOldLogin = false; 76 + showBlobMoveProgress = true; 77 + showStatusMessage = true; 78 + statusMessage = ''; 79 + await migrateMissingBlobs(); 80 + } catch (err) { 81 + //@ts-expect-error: Should always have an error message 82 + if (err.error === 'AuthFactorTokenRequired') { 83 + loginForm.showTwoFactorCodeInput = true; 84 + } 85 + //@ts-expect-error: Should always have an error message 86 + errorMessage = err.message; 87 + } 88 + disableLoginButton = false; 89 + } 90 + 91 + function updateStatusHandler(status: string) { 92 + console.log('Status update:', status); 93 + statusMessage = status; 94 + } 95 + 96 + async function migrateMissingBlobs() { 97 + try { 98 + resetStatusAndErrors(); 99 + showStatusMessage = true; 100 + showTryAgain = false; 101 + const { 102 + accountStatus, 103 + missingBlobsCount 104 + } = await missingBlobs.migrateMissingBlobs(updateStatusHandler); 105 + const noMissingBlobs = missingBlobsCount === 0; 106 + if (noMissingBlobs) { 107 + statusMessage = `You are good to go! You have all ${accountStatus.importedBlobs} of the expected ${accountStatus.expectedBlobs} blobs. You're done!!` 108 + } else { 109 + statusMessage = `Expected blobs: ${accountStatus.expectedBlobs} Imported blobs: ${accountStatus.importedBlobs}`; 110 + showTryAgain = true; 111 + } 112 + } catch (err) { 113 + //@ts-expect-error: Should always have an error message 114 + errorMessage = err.message; 115 + showTryAgain = true; 116 + } 117 + disableLoginButton = false; 118 + } 119 + 120 + function toggleAdvanceMenu() { 121 + showAdvance = !showAdvance; 122 + } 123 + </script> 124 + 125 + <svelte:head> 126 + <title>PDS MOOver - Missing Blobs</title> 127 + <meta property="og:description" content="Import missing blobs from your old PDS to your new PDS"/> 128 + <meta property="og:image" content="{missingPicture}"> 129 + </svelte:head> 130 + 131 + {#snippet custom_img()} 132 + <img src='{missingPicture}' alt='Cartoon milk cow on a missing poster' 133 + style='max-width: 100%; max-height: 100%; object-fit: contain;'> 134 + {/snippet} 135 + 136 + <div class="container"> 137 + <MooHeader title="Missing Blobs Importer" customImg={custom_img}/> 138 + 139 + <a href="https://blacksky.community/profile/did:plc:g7j6qok5us4hjqlwjxwrrkjm/post/3lyylumcpok2c">How to video 140 + guide</a> 141 + 142 + <!-- Login Form --> 143 + {#if showCurrentLogin || showOldLogin} 144 + <form id="moover-form" onsubmit={showCurrentLogin ? handleCurrentLogin : handleOldLogin}> 145 + <div class="section"> 146 + <h2>{showCurrentLogin ? 'Login for your current PDS' : 'Password for your OLD PDS'}</h2> 147 + 148 + {#if showOldLogin} 149 + <p>We only need your password for your old account. We can find your old handle from your current 150 + login.</p> 151 + {/if} 152 + 153 + {#if showCurrentLogin} 154 + <div class="form-group"> 155 + <label for="handle">Current Handle:</label> 156 + <input type="text" id="handle" name="handle" placeholder="alice.bsky.social" 157 + bind:value={loginForm.handle} 158 + required> 159 + </div> 160 + {/if} 161 + 162 + <div class="form-group"> 163 + <label for="password">{showCurrentLogin ? 'Current' : 'OLD'} Password:</label> 164 + <input type="password" id="password" name="password" 165 + bind:value={loginForm.password} 166 + required> 167 + </div> 168 + 169 + {#if (showCurrentLogin && loginForm.showTwoFactorCodeInput) || (showOldLogin && loginForm.showTwoFactorCodeInput)} 170 + <div class="form-group"> 171 + <label for="two-factor-code">2FA from the email sent</label> 172 + <input type="text" id="two-factor-code" name="two-factor-code" 173 + bind:value={loginForm.twoFactorCode}> 174 + <div class="error-message">Enter your 2fa code here</div> 175 + </div> 176 + {/if} 177 + 178 + {#if showOldLogin} 179 + {#if showAdvance} 180 + <div class="form-group show-advance"> 181 + <label for="old_pds">This is optional. If you do not know your old PDS url please leave it 182 + blank. We will find it for you.</label> 183 + <input type="url" id="old_pds" name="old-pds" 184 + placeholder="(Optional) Your old PDS URL" bind:value={oldPdsUrl}> 185 + </div> 186 + {/if} 187 + <div class="form-group"> 188 + <button type="button" onclick={toggleAdvanceMenu} id="advance" name="advance">Advance Options 189 + </button> 190 + </div> 191 + {/if} 192 + 193 + {#if errorMessage} 194 + <div class="error-message">{errorMessage}</div> 195 + {/if} 196 + {#if showStatusMessage} 197 + <div class="status-message">{statusMessage}</div> 198 + {/if} 199 + 200 + <div> 201 + <button disabled={disableLoginButton} type="submit"> 202 + {showCurrentLogin ? 'Login' : 'Login and start the import of missing blobs'} 203 + </button> 204 + </div> 205 + </div> 206 + </form> 207 + {/if} 208 + 209 + <!-- Progress while uploading blobs--> 210 + {#if showBlobMoveProgress} 211 + <div> 212 + {#if showStatusMessage} 213 + <div id="warning">*This will take a while. Please do not close this tab. And watch 214 + the status message below for updates 215 + </div> 216 + <div id="missing-status-message" class="status-message">{statusMessage}</div> 217 + {/if} 218 + {#if errorMessage} 219 + <div class="error-message">{errorMessage}</div> 220 + {/if} 221 + {#if showTryAgain} 222 + <p style="color: yellow">We were unable to import all of your previous blobs, please try again. If it is 223 + still not completing give it a few hours and come back and try again. It may be rate limited. Re 224 + running this tool does not harm your account.</p> 225 + <br> 226 + <button onclick={migrateMissingBlobs}>Try again</button> 227 + {/if} 228 + </div> 229 + {/if} 230 + </div>
+267
web-ui/src/routes/moover/+page.svelte
··· 1 + <script lang="ts"> 2 + import MooHeader from '$lib/components/MooHeader.svelte'; 3 + import OgImage from '$lib/components/OgImage.svelte'; 4 + import {resolve} from '$app/paths'; 5 + import {Migrator} from '@pds-moover/moover'; 6 + import SignThePapers from './SignThePapers.svelte'; 7 + 8 + let formData = $state({ 9 + handle: '', 10 + password: '', 11 + newPds: '', 12 + newEmail: '', 13 + newHandle: '', 14 + inviteCode: null, 15 + twoFactorCode: null, 16 + confirmation: false, 17 + // Advanced options 18 + createNewAccount: true, 19 + migrateRepo: true, 20 + migrateBlobs: true, 21 + migrateMissingBlobs: true, 22 + migratePrefs: true, 23 + migratePlcRecord: true, 24 + }); 25 + 26 + let migrator = $state(new Migrator()); 27 + 28 + //UI state 29 + let showTwoFactorCodeInput = $state(false); 30 + let showAdvance = $state(false); 31 + let showStatusMessage = $state(false); 32 + let askForPlcToken = $state(false); 33 + let disableSubmit = $state(false); 34 + 35 + let errorMessage: null | string = $state(null); 36 + let statusMessage: null | string = $state(null); 37 + 38 + const updateStatusHandler = (status: string) => { 39 + statusMessage = status; 40 + } 41 + 42 + async function submitMoove(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }) { 43 + event.preventDefault(); 44 + disableSubmit = true; 45 + errorMessage = null; 46 + showStatusMessage = false; 47 + 48 + if (!formData.confirmation) { 49 + errorMessage = 'Please confirm that you understand the risks of doing an account migration'; 50 + disableSubmit = false; 51 + return; 52 + } 53 + 54 + try { 55 + 56 + if (showTwoFactorCodeInput) { 57 + if (showTwoFactorCodeInput === null) { 58 + errorMessage = 'Please enter the 2FA that was sent to your email.' 59 + disableSubmit = false; 60 + return; 61 + } 62 + } 63 + 64 + // Advance options from $state 65 + migrator.createNewAccount = formData.createNewAccount; 66 + migrator.migrateRepo = formData.migrateRepo; 67 + migrator.migrateBlobs = formData.migrateBlobs; 68 + migrator.migrateMissingBlobs = formData.migrateMissingBlobs; 69 + migrator.migratePrefs = formData.migratePrefs; 70 + migrator.migratePlcRecord = formData.migratePlcRecord; 71 + 72 + console.log(migrator); 73 + 74 + updateStatusHandler('Starting migration...'); 75 + showStatusMessage = true; 76 + await migrator.migrate( 77 + formData.handle, 78 + formData.password, 79 + formData.newPds, 80 + formData.newEmail, 81 + formData.newHandle, 82 + formData.inviteCode, 83 + updateStatusHandler, 84 + formData.twoFactorCode, 85 + ); 86 + if (migrator.migratePlcRecord) { 87 + //I don't think disable submit is needed, but you never know. 88 + disableSubmit = false; 89 + askForPlcToken = true; 90 + } else { 91 + updateStatusHandler('Migration of your repo is complete! But the PLC operation was not done so your old account is still the valid one.'); 92 + } 93 + } catch (error) { 94 + disableSubmit = false; 95 + console.error(error); 96 + //@ts-expect-error: JS being js. doesn't like not having the type' 97 + if (error.error === 'AuthFactorTokenRequired') { 98 + showTwoFactorCodeInput = true; 99 + } 100 + //@ts-expect-error: JS being js. doesn't like not having the type' 101 + errorMessage = error.message; 102 + } 103 + } 104 + </script> 105 + 106 + <svelte:head> 107 + <title>PDS MOOver</title> 108 + <meta property="og:description" content="ATProto account migration tool"/> 109 + <OgImage/> 110 + </svelte:head> 111 + 112 + <div class="container"> 113 + <MooHeader title="PDS MOOver"/> 114 + {#if !askForPlcToken} 115 + <a href={resolve('/info')}>Idk if I trust a cow to move my atproto account to a new PDS</a> 116 + <br/> 117 + <a href="https://blacksky.community/profile/did:plc:g7j6qok5us4hjqlwjxwrrkjm/post/3lw3hcuojck2u">Video guide for 118 + joining blacksky.app</a> 119 + 120 + <form id="moover-form" onsubmit={submitMoove}> 121 + <!-- First section: Login credentials --> 122 + <div class="section"> 123 + <h2>Login for your current PDS</h2> 124 + <div class="form-group"> 125 + <label for="handle">Old Handle:</label> 126 + <input type="text" id="handle" name="handle" placeholder="alice.bsky.social" required 127 + bind:value={formData.handle}> 128 + </div> 129 + 130 + <div class="form-group"> 131 + <label for="password">Old Password (Will also be your new password):</label> 132 + <input type="password" id="password" name="password" required bind:value={formData.password}> 133 + </div> 134 + {#if showTwoFactorCodeInput} 135 + <div class="form-group"> 136 + <label for="two-factor-code">2FA from the email sent</label> 137 + <input type="text" id="two-factor-code" name="twoFactorCode" 138 + bind:value={formData.twoFactorCode}> 139 + <div class="error-message">Enter your 2fa code here</div> 140 + 141 + </div> 142 + {/if} 143 + </div> 144 + 145 + <!-- Second section: New account details --> 146 + <div class="section"> 147 + <h2>Setup for the new PDS</h2> 148 + <div class="form-group"> 149 + <label for="new-pds">New PDS (URL):</label> 150 + <input type="url" id="new-pds" name="newPds" placeholder="https://coolnewpds.com" 151 + required bind:value={formData.newPds}> 152 + </div> 153 + 154 + <div class="form-group"> 155 + <label for="new-email">New Email:</label> 156 + <input type="email" id="new-email" name="newEmail" placeholder="CanBeSameEmailAsTheOldPds@email.com" 157 + required bind:value={formData.newEmail}> 158 + </div> 159 + 160 + <div class="form-group"> 161 + <label for="new-handle">New Handle:</label> 162 + <input type="text" id="new-handle" name="newHandle" 163 + placeholder="username.newpds.com or mycooldomain.com" required 164 + bind:value={formData.newHandle}> 165 + </div> 166 + 167 + <div class="form-group"> 168 + <label for="invite-code">Invite Code:</label> 169 + <input type="text" id="invite-code" name="inviteCode" 170 + placeholder="Invite code from your new PDS (Leave blank if you don't have one)" 171 + bind:value={formData.inviteCode}> 172 + </div> 173 + </div> 174 + 175 + <div class="form-group"> 176 + <button type="button" onclick={() => showAdvance = !showAdvance} id="advance" name="advance">Advance 177 + Options 178 + </button> 179 + </div> 180 + {#if showAdvance} 181 + <div class="section" style="padding-bottom: 10px; text-align: left"> 182 + <h3>Pick and choose which actions to run</h3> 183 + <p>Useful if a migration failed and you want to have a bit more manual control</p> 184 + <div class="form-control"> 185 + <label class="moove-checkbox-label"> 186 + <input type="checkbox" id="createNewAccount" name="createNewAccount" 187 + bind:checked={formData.createNewAccount}> 188 + Create a New Account on the New PDS 189 + </label> 190 + </div> 191 + <div class="form-control"> 192 + <label class="moove-checkbox-label"> 193 + <input bind:checked={formData.migrateRepo} type="checkbox" id="migrateRepo" 194 + name="migrateRepo"> 195 + Migrate Repo 196 + </label> 197 + </div> 198 + <div class="form-control"> 199 + <label class="moove-checkbox-label"> 200 + <input bind:checked={formData.migrateBlobs} type="checkbox" id="migrateBlobs" 201 + name="migrateBlobs"> 202 + Migrate Blobs 203 + </label> 204 + </div> 205 + <div class="form-control"> 206 + <label class="moove-checkbox-label"> 207 + <input bind:checked={formData.migrateMissingBlobs} type="checkbox" id="migrateMissingBlobs" 208 + name="migrateMissingBlobs"> 209 + Migrate Missing Blobs 210 + </label> 211 + </div> 212 + <div class="form-control"> 213 + <label class="moove-checkbox-label"> 214 + <input bind:checked={formData.migratePrefs} type="checkbox" id="migratePrefs" 215 + name="migratePrefs"> 216 + Migrate Prefs 217 + </label> 218 + </div> 219 + <div class="form-control"> 220 + <label class="moove-checkbox-label"> 221 + <input bind:checked={formData.migratePlcRecord} type="checkbox" id="migratePlcRecord" 222 + name="migratePlcRecord"> 223 + Migrate PLC Record 224 + </label> 225 + </div> 226 + 227 + </div> 228 + {/if} 229 + 230 + <p style="text-align: left">There are some risks that come with doing an account migration. 231 + (Can view them 232 + <a href="https://github.com/bluesky-social/pds/blob/main/ACCOUNT_MIGRATION.md#%EF%B8%8F-warning-%EF%B8%8F-%EF%B8%8F">here</a>) 233 + and that the creator or host of this migration tool is not liable and will not be able to help you in 234 + the 235 + event something goes wrong. I also have read over the <a href={resolve('/info')}>extended information 236 + from 237 + PDS MOOver 238 + about account 239 + migrations.</a> 240 + </p> 241 + <div class="form-group"> 242 + <label for="confirmation" class="moove-checkbox-label"> 243 + <input bind:checked={formData.confirmation} type="checkbox" id="confirmation" name="confirmation" 244 + required> 245 + <span>I understand</span> 246 + </label> 247 + </div> 248 + {#if errorMessage !== null} 249 + <div class="error-message">{errorMessage}</div> 250 + {/if} 251 + 252 + {#if showStatusMessage} 253 + <div id="warning">*Please make sure to stay on this page during the MOOve for the 254 + best result 255 + </div> 256 + <div id="status-message" class="status-message">{statusMessage}</div> 257 + {/if} 258 + 259 + <div> 260 + <button disabled={disableSubmit} type="submit">MOOve</button> 261 + </div> 262 + </form> 263 + 264 + {:else} 265 + <SignThePapers migrator={migrator} newHandle={formData.newHandle}/> 266 + {/if} 267 + </div>
+178
web-ui/src/routes/moover/SignThePapers.svelte
··· 1 + <script lang="ts"> 2 + import {Migrator, PlcOps} from '@pds-moover/moover' 3 + import {resolve} from '$app/paths' 4 + import RotationKeyDisplay from '$lib/components/RotationKeyDisplay.svelte'; 5 + import type {RotationKeyType} from '$lib/types'; 6 + import {PUBLIC_XRPC_BASE} from '$env/static/public'; 7 + 8 + let {migrator, newHandle}: { migrator: Migrator, newHandle: string } = $props(); 9 + 10 + //UI State 11 + let errorMessage: null | string = $state(null); 12 + let done = $state(false); 13 + let plcStatus: null | string = $state(null); 14 + let showAdvancedPlcOptions = $state(false); 15 + let backupSignupMessage: null | string = $state(null); 16 + let backupSignupError: null | string = $state(null); 17 + 18 + //Input State 19 + let createANewRotationKey = $state(false); 20 + let signupForBackups = $state(false); 21 + let plcToken = $state(''); 22 + let rotationKeys = $state(['', '', '', '']); 23 + let newlyCreatedRotationKey: RotationKeyType | null = $state(null); 24 + 25 + 26 + async function signPlcOperation(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }) { 27 + event.preventDefault(); 28 + try { 29 + plcStatus = 'Signing PLC operation...'; 30 + backupSignupMessage = null; 31 + backupSignupError = null; 32 + // Build an additional rotation keys list (user-provided and/or newly created) 33 + const additionalRotationKeysToAdd: string[] = []; 34 + // Generate a new rotation key if requested 35 + if (createANewRotationKey) { 36 + 37 + let plcOps = new PlcOps(); 38 + const created = await plcOps.createANewSecp256k1(); 39 + newlyCreatedRotationKey = created; // { publicKey, privateKey } 40 + // Reserve the first slot for the newly created key (will appear above the PDS rotation key) 41 + additionalRotationKeysToAdd.push(created.publicKey); 42 + } 43 + // Append any manually entered rotation keys (non-empty) 44 + //TODO idk about this i need to look at it again 45 + for (let i = 0; i < rotationKeys.length; i++) { 46 + const k = (rotationKeys[i] || '').trim(); 47 + if (k) { 48 + additionalRotationKeysToAdd.push(k); 49 + } 50 + } 51 + 52 + //TODO nervous about this state 53 + await migrator.signPlcOperation(plcToken, additionalRotationKeysToAdd); 54 + plcStatus = 'PLC operation signed successfully! Your account has been MOOved to the new PDS.'; 55 + done = true; 56 + 57 + if (signupForBackups) { 58 + try { 59 + backupSignupMessage = 'Signing you up for automated backups...'; 60 + //TODO nervous about this state 61 + await migrator.signUpForBackupsFromMigration(`did:web:${PUBLIC_XRPC_BASE}`); 62 + backupSignupMessage = 'Signed up for automated backups successfully.'; 63 + } catch (e) { 64 + console.error(e); 65 + //@ts-expect-error: There is a e.message, or at least a check for it 66 + backupSignupError = e?.message || 'Failed to sign you up for automated backups.'; 67 + } 68 + } 69 + } catch (error) { 70 + //@ts-expect-error: There is a message 71 + errorMessage = error.message; 72 + console.error(error); 73 + } 74 + } 75 + 76 + </script> 77 + 78 + 79 + <div class="section"> 80 + <form onsubmit="{signPlcOperation}"> 81 + {#if !done} 82 + <div> 83 + <h2>Please enter your PLC Token you received in an email</h2> 84 + <div class="form-group"> 85 + <label for="plc-token">PLC Token:</label> 86 + <input type="text" id="plc-token" name="plc-token" bind:value={plcToken} required> 87 + </div> 88 + <p style="text-align: left">You can now select to add a new Rotation Key during migration and sign up 89 + for PDS MOOver's free backup service. With a Rotation Key and backups if your new PDS ever goes down 90 + you can recover your account and it's data.</p> 91 + <div class="form-group"> 92 + <label for="rotation-key" class="moove-checkbox-label"> 93 + <input bind:checked={createANewRotationKey} type="checkbox" id="rotation-key" 94 + name="rotation-key"> 95 + <span>Create and add a new Rotation Key</span> 96 + </label> 97 + </div> 98 + <div class="form-group"> 99 + <label for="backups-signup" class="moove-checkbox-label"> 100 + <input bind:checked={signupForBackups} type="checkbox" id="backups-signup" 101 + name="backups-signup"> 102 + <span>Signup for automated account backups</span> 103 + </label> 104 + </div> 105 + <div class="form-group"> 106 + <button type="button" id="plc-advance" 107 + onclick={() => showAdvancedPlcOptions = !showAdvancedPlcOptions}>Add 108 + Additional Rotation Keys 109 + </button> 110 + </div> 111 + {#if showAdvancedPlcOptions} 112 + <div class="section" style="padding-bottom: 10px;"> 113 + <div style="text-align: left;"> 114 + You can pick up to 4 rotation keys to your PLC document. These will appear above your new 115 + PDS 116 + rotation key so you can recover your account in the event of an adversarial take over from a 117 + rogue PDS 118 + </div> 119 + <div class="form-group" style="margin-top: 10px;"> 120 + <label for="rotation-key-1">Rotation key 1</label> 121 + <input type="text" id="rotation-key-1" name="rotation-key-1" 122 + bind:value={rotationKeys[0]} 123 + disabled={createANewRotationKey} 124 + placeholder={createANewRotationKey ? 'reserved for the newly created rotation key' : ''}> 125 + </div> 126 + <div class="form-group"> 127 + <label for="rotation-key-2">Rotation key 2</label> 128 + <input type="text" id="rotation-key-2" name="rotation-key-2" bind:value={rotationKeys[1]}> 129 + </div> 130 + <div class="form-group"> 131 + <label for="rotation-key-3">Rotation key 3</label> 132 + <input type="text" id="rotation-key-3" name="rotation-key-3" bind:value={rotationKeys[2]}> 133 + </div> 134 + <div class="form-group"> 135 + <label for="rotation-key-4">Rotation key 4</label> 136 + <input type="text" id="rotation-key-4" name="rotation-key-4" bind:value={rotationKeys[3]}> 137 + </div> 138 + </div> 139 + {/if} 140 + </div> 141 + {/if} 142 + {#if errorMessage} 143 + <div class="error-message">{errorMessage}</div> 144 + {/if} 145 + 146 + {#if done && createANewRotationKey && newlyCreatedRotationKey} 147 + <div> 148 + <RotationKeyDisplay handle={newHandle} rotationKey={newlyCreatedRotationKey}/> 149 + </div> 150 + {/if} 151 + 152 + {#if !done && plcStatus} 153 + <div class="status-message">{plcStatus}</div> 154 + {/if} 155 + 156 + {#if signupForBackups && backupSignupMessage} 157 + <div> 158 + <div class="status-message">{backupSignupMessage}</div> 159 + {#if backupSignupError} 160 + <div class="error-message">{backupSignupError}</div> 161 + {/if} 162 + 163 + </div> 164 + {/if} 165 + 166 + {#if done} 167 + <div class="status-message">Congratulations! You have MOOved to a new PDS! Remember to use 168 + your new PDS URL under "Hosting provider" when logging in on Bluesky. Can find more detail information 169 + <a href={resolve('/info#cant-login')}>here.</a></div> 170 + {:else } 171 + <div> 172 + <button type="submit">Sign the papers</button> 173 + </div> 174 + {/if} 175 + 176 + 177 + </form> 178 + </div>
+296
web-ui/src/routes/restore/+page.svelte
··· 1 + <script lang="ts"> 2 + import {Restore} from '@pds-moover/moover'; 3 + import MooHeader from '$lib/components/MooHeader.svelte'; 4 + import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; 5 + import {PUBLIC_XRPC_BASE} from '$env/static/public'; 6 + import OgImage from '$lib/components/OgImage.svelte'; 7 + 8 + //Regexs to catch rotation key type 9 + const HEX_REGEX = /^[0-9a-f]+$/i; 10 + 11 + // Service instances 12 + let restoreService = $state(new Restore(`https://${PUBLIC_XRPC_BASE}`)); 13 + 14 + // Form inputs 15 + let currentHandle = $state(''); 16 + let rotationKey = $state(''); 17 + let newPds = $state(''); 18 + let newHandle = $state(''); 19 + let newPassword = $state(''); 20 + let newEmail = $state(''); 21 + let inviteCode = $state(''); 22 + let cid = $state(''); 23 + 24 + // Rotation key type detection and selection 25 + let isHexKey = $state(false); 26 + let rotationKeyType = $state<'secp256k1' | 'p256'>('secp256k1'); 27 + 28 + // Advanced options 29 + let restoreFilesFromBackup = $state(true); 30 + let recoverAccount = $state(true); 31 + 32 + // UI state 33 + let errorMessage = $state<string | null>(null); 34 + let showStatusMessage = $state(false); 35 + let showAdvanced = $state(false); 36 + let done = $state(false); 37 + let statusMessageText = $state(''); 38 + let isSubmitting = $state(false); 39 + 40 + function setStatus(message: string) { 41 + console.log('Status update:', message); 42 + statusMessageText = message; 43 + showStatusMessage = true; 44 + } 45 + 46 + function handleRotationKeyChange() { 47 + const trimmedKey = rotationKey.trim(); 48 + if (trimmedKey && HEX_REGEX.test(trimmedKey)) { 49 + isHexKey = true; 50 + } else { 51 + isHexKey = false; 52 + } 53 + } 54 + 55 + async function handleSubmit() { 56 + errorMessage = null; 57 + showStatusMessage = false; 58 + isSubmitting = true; 59 + 60 + try { 61 + restoreService.RestoreFromBackup = restoreFilesFromBackup; 62 + restoreService.AccountRecovery = recoverAccount; 63 + 64 + await restoreService.recover( 65 + rotationKey, 66 + rotationKeyType, 67 + currentHandle, 68 + newPds, 69 + newHandle, 70 + newPassword, 71 + newEmail, 72 + inviteCode || null, 73 + cid || null, 74 + setStatus 75 + ); 76 + done = true; 77 + } catch (e) { 78 + console.error(e); 79 + // @ts-expect-error: Error is handled 80 + errorMessage = e.message || 'An error occurred'; 81 + } finally { 82 + isSubmitting = false; 83 + } 84 + } 85 + </script> 86 + 87 + <svelte:head> 88 + <title>PDS MOOver - Restore</title> 89 + <meta property="og:description" content="Recovery and restore your ATProto account"/> 90 + <OgImage/> 91 + </svelte:head> 92 + 93 + <div class="container"> 94 + <MooHeader title="Restore"/> 95 + 96 + <div class="section"> 97 + <p style="text-align: left"> 98 + Use this page to restore your AT Protocol account on a new PDS using your private rotation 99 + key. It's intended to be used in the worst case scenario, and not for normal account 100 + migrations. We do not need your password since your rotation key is what is used to recover 101 + your account. Once your account has been moved to a new PDS we will also attempt to restore 102 + your repo and blobs(pictures/videos) from our backups. 103 + </p> 104 + </div> 105 + 106 + {#if !done} 107 + <form 108 + id="restore-form" 109 + onsubmit={(e) => { 110 + e.preventDefault(); 111 + handleSubmit(); 112 + }} 113 + > 114 + <div class="form-group"> 115 + <label for="current-handle" 116 + >Your current handle or did (found in the recovery rotation-key.txt)</label 117 + > 118 + <input 119 + id="current-handle" 120 + bind:value={currentHandle} 121 + placeholder="did:plc:rnpkyqnmsw4ipey6eotbdnnf" 122 + required 123 + /> 124 + </div> 125 + 126 + {#if recoverAccount} 127 + <div class="form-group"> 128 + <label for="rotationKey">Private Rotation Key</label> 129 + <input 130 + id="rotationKey" 131 + bind:value={rotationKey} 132 + oninput={handleRotationKeyChange} 133 + placeholder="a secp256k1 or p256 in either hex or multikey format" 134 + required={recoverAccount} 135 + /> 136 + </div> 137 + 138 + {#if isHexKey} 139 + <div class="form-group"> 140 + <fieldset style="border: none; padding: 0; margin: 0;"> 141 + <legend style="margin-bottom: 0.5rem;">Rotation Key Type</legend> 142 + <div style="display: flex; gap: 1rem;"> 143 + <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;"> 144 + <input 145 + type="radio" 146 + name="rotationKeyType" 147 + value="secp256k1" 148 + bind:group={rotationKeyType} 149 + /> 150 + secp256k1 151 + </label> 152 + <label style="display: flex; align-items: center; gap: 0.5rem; cursor: pointer;"> 153 + <input 154 + type="radio" 155 + name="rotationKeyType" 156 + value="p256" 157 + bind:group={rotationKeyType} 158 + /> 159 + p256 160 + </label> 161 + </div> 162 + <small class="help-text">Select the type of your hex-encoded rotation key</small> 163 + </fieldset> 164 + </div> 165 + {/if} 166 + {/if} 167 + 168 + <div class="form-group"> 169 + <label for="newPds">New PDS</label> 170 + <input 171 + id="newPds" 172 + type="text" 173 + bind:value={newPds} 174 + placeholder="https://newpds.com" 175 + required 176 + /> 177 + </div> 178 + 179 + <div class="form-group"> 180 + <label for="newHandle">New Handle</label> 181 + <input 182 + id="newHandle" 183 + type="text" 184 + bind:value={newHandle} 185 + placeholder="you.newpds.com or thisisme.com" 186 + required 187 + /> 188 + </div> 189 + 190 + <div class="form-group"> 191 + <label for="newPassword">New Password</label> 192 + <input id="newPassword" type="password" bind:value={newPassword} required/> 193 + </div> 194 + 195 + <div class="form-group"> 196 + <label for="newEmail">New Email</label> 197 + <input 198 + id="newEmail" 199 + type="email" 200 + bind:value={newEmail} 201 + placeholder="you@example.com" 202 + required 203 + /> 204 + </div> 205 + 206 + <div class="form-group"> 207 + <label for="inviteCode">Invite code (optional)</label> 208 + <input id="inviteCode" type="text" bind:value={inviteCode} placeholder="Optional"/> 209 + </div> 210 + 211 + <div class="form-group"> 212 + <button type="button" onclick={() => (showAdvanced = !showAdvanced)}> 213 + {showAdvanced ? 'Hide advanced options' : 'Show advanced options'} 214 + </button> 215 + </div> 216 + 217 + {#if showAdvanced} 218 + <div class="advanced-options"> 219 + <div class="form-group"> 220 + <label class="moove-checkbox-label"> 221 + <input 222 + id="recoverAccount" 223 + type="checkbox" 224 + bind:checked={recoverAccount} 225 + /> 226 + Recover the did doc & account. 227 + </label> 228 + <br/> 229 + <small class="help-text">This option temporary assigns a signing key to your did doc, uses it to 230 + create a new account on the new PDS with your did, and migrates the account to the new 231 + PDS.</small> 232 + </div> 233 + 234 + <div class="form-group"> 235 + <label class="moove-checkbox-label"> 236 + <input 237 + id="restoreFilesFromBackup" 238 + type="checkbox" 239 + bind:checked={restoreFilesFromBackup} 240 + /> 241 + Restores files from backup. 242 + </label> 243 + </div> 244 + 245 + {#if recoverAccount} 246 + <div class="form-group"> 247 + <label for="cid">Cid (optional)</label> 248 + <input 249 + id="cid" 250 + type="text" 251 + bind:value={cid} 252 + placeholder="Specific PLC audit log CID to restore to (advanced)" 253 + /> 254 + <small class="help-text" 255 + >Leave empty for normal restore. IF someone besides you changed your did doc and 256 + stole your atproto identity or a mistake was made you have up to 72hrs to reverse that 257 + change with a valid 258 + rotation key from the last valid operation. This input allows you to restore to the 259 + last good PLC op via it's 260 + CID.</small 261 + > 262 + </div> 263 + {/if} 264 + </div> 265 + {/if} 266 + 267 + {#if errorMessage} 268 + <div class="error-message">{errorMessage}</div> 269 + {/if} 270 + {#if showStatusMessage} 271 + <div class="status-message">{statusMessageText}</div> 272 + {/if} 273 + 274 + <div class="form-actions"> 275 + <button type="submit" disabled={isSubmitting}> 276 + {#if isSubmitting} 277 + <LoadingSpinner/> 278 + {/if} 279 + Recover and restore your account 280 + </button> 281 + </div> 282 + </form> 283 + {/if} 284 + 285 + <!-- Done --> 286 + {#if done} 287 + <div class="section"> 288 + <div class="status-message"> 289 + Congratulations! You have successfully recovered your account! Remember to use your new 290 + PDS URL under "Hosting provider" when logging in on Bluesky. Can find more detail 291 + information 292 + <a href="/info#cant-login">here.</a> 293 + </div> 294 + </div> 295 + {/if} 296 + </div>
+59
web-ui/src/routes/rotation-key/+page.svelte
··· 1 + <script lang="ts"> 2 + import MooHeader from '$lib/components/MooHeader.svelte'; 3 + import {PlcOps} from '@pds-moover/moover'; 4 + import type {RotationKeyType} from '$lib/types'; 5 + import RotationKeyDisplay from '$lib/components/RotationKeyDisplay.svelte'; 6 + 7 + let handle: string = $state(''); 8 + let errorMessage: null | string = $state(null); 9 + let status: null | string = $state(null); 10 + let newlyCreatedRotationPrivateKey: null | RotationKeyType = $state(null); 11 + 12 + 13 + const generateNewKey = async () => { 14 + errorMessage = null; 15 + status = 'Generating a new rotation key…'; 16 + try { 17 + const plcOps = new PlcOps(); 18 + newlyCreatedRotationPrivateKey = await plcOps.createANewSecp256k1(); 19 + status = 'New rotation key generated.'; 20 + } catch (e) { 21 + console.error(e); 22 + // @ts-expect-error: already checked for null 23 + errorMessage = e?.message || 'Failed to generate a new rotation key'; 24 + status = null; 25 + } 26 + }; 27 + 28 + </script> 29 + 30 + 31 + <div class="container"> 32 + 33 + <MooHeader title="Rotation Key"/> 34 + <div class="section"> 35 + <p style="text-align: left;">This page is intended for development and is not listed. This will NOT save a key 36 + to your account.</p> 37 + <div class="form-group"> 38 + <label for="handle">Handle</label> 39 + <input bind:value={handle} type="text" id="handle" name="handle" placeholder="Enter your handle"/> 40 + </div> 41 + <div class="form-group"> 42 + <button type="button" onclick={generateNewKey}>Generate New Rotation Key</button> 43 + </div> 44 + </div> 45 + 46 + {#if status} 47 + <div class="status-message">{status}</div> 48 + {/if} 49 + 50 + {#if errorMessage} 51 + <div class="error-message">{errorMessage}</div> 52 + {/if} 53 + 54 + 55 + {#if newlyCreatedRotationPrivateKey} 56 + <RotationKeyDisplay handle={handle} rotationKey={newlyCreatedRotationPrivateKey}/> 57 + {/if} 58 + 59 + </div>
+105
web-ui/src/routes/terms/+page.svelte
··· 1 + <script lang="ts"> 2 + import OgImage from '$lib/components/OgImage.svelte'; 3 + </script> 4 + 5 + <svelte:head> 6 + <title>PDS MOOver - Legal</title> 7 + <meta name="description" content="Terms of Service and Privacy Policy for PDS MOOver"/> 8 + <meta property="og:description" content="Terms of Service and Privacy Policy for PDS MOOver"/> 9 + <OgImage/> 10 + </svelte:head> 11 + 12 + <div class="container" style="text-align:left"> 13 + <section class="section"> 14 + 15 + <h1>Privacy Policy</h1> 16 + <p>Last updated: 2025-10-20</p> 17 + 18 + <h2>Overview</h2> 19 + <p>PDS MOOver performs migrations in your browser. Where possible, operations are client side to minimize 20 + collection of personal data by the server. Backups happen on our servers, and your data is stored in a cloud 21 + base object store we have access to</p> 22 + 23 + <h2>Information We May Receive</h2> 24 + <ul> 25 + <li>If you use optional features (e.g., backups), we take a backup of your AT Proto repo when it has a 26 + change, any blobs you have posted to your account are stored in a <a target="_blank" 27 + rel="noopener noreferrer" 28 + href="https://en.wikipedia.org/wiki/Object_storage">object 29 + store</a> that we control. Just like your <a target="_blank" rel="noopener noreferrer" 30 + href="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">AT 31 + Proto data</a> this is public and accessible to anyone. But this does not mean anyone is able to 32 + take this data and impersonate you without what is known as a Rotation Key. 33 + </li> 34 + </ul> 35 + 36 + <h2>What We Do Not Do</h2> 37 + <ul> 38 + <li>If you use PDS MOOver just for migrations, missing blobs, or turn off feature. We do not collect or 39 + store any information. This happens all client side. 40 + </li> 41 + <li>We do not collect any personal information beyond your current PDS host and your DID if you sign up for 42 + backups. 43 + </li> 44 + <li>Your passwords are only ever sent to your PDS. Your passwords are never collected, and all authenticated 45 + requests happen proxied via your PDS to our services for backups 46 + </li> 47 + </ul> 48 + 49 + <h2>Retention</h2> 50 + <p>Server-side logs may be retained for a limited time for security, debugging, and rate-limiting. This is short 51 + term logging and cleared on instance reboots and kept no longer than 60 days. Backup-related data, if 52 + enabled by you, is stored indefinitely for the life of this service unless you opt out via <a 53 + href="/backups">backups</a> and click delete to remove all of your backups and from any future ones. 54 + </p> 55 + 56 + <h2>Your Choices</h2> 57 + <p>You may choose not to use the service. If you proceed, ensure you have backups and understand the migration 58 + risks. If you ever want to be removed from our services you can also login via <a 59 + href="/backups">backups</a> and click delete to remove all of your backups and from any future ones. 60 + </p> 61 + 62 + <h2>Changes</h2> 63 + <p>We may update this Privacy Policy from time to time. Continued use after changes means you accept the updated 64 + policy.</p> 65 + 66 + <h2>Service Provider and Location</h2> 67 + <p>All backup data is currently being stored on <a href="https://upcloud.com/">UpCloud's</a> <a 68 + href="https://upcloud.com/products/object-storage/">object store</a> in a data center located in the US. 69 + UpCloud is also our service provider for the VPS running the services. We may change service providers in 70 + the future, if we do this terms of service will be updated to reflect that.</p> 71 + 72 + </section> 73 + 74 + <section class="section"> 75 + <h1>Terms of Service</h1> 76 + <p>Last updated: 2025-10-20</p> 77 + 78 + <p>PDS MOOver is provided on an as-is and as-available basis, without warranties of any kind. By using this site 79 + and its tools, you agree that you understand the risks of migrating and or restoring ATProto/Bluesky 80 + accounts and that neither the creator nor the host of this tool is responsible for any loss, corruption, or 81 + unavailability of data, accounts, or services.</p> 82 + 83 + <h2>Acceptable Use</h2> 84 + <p>You agree not to misuse the service, disrupt other users, or attempt to gain unauthorized access to systems 85 + or data. You are responsible for complying with applicable laws and policies of your hosting 86 + provider(s).</p> 87 + 88 + <h2>Limitation of Liability</h2> 89 + <p>To the fullest extent permitted by law, the creator and host are not liable for any indirect, incidental, 90 + special, consequential, or punitive damages, or any loss of data, profits, or revenues, whether incurred 91 + directly or indirectly, resulting from your use of the service.</p> 92 + 93 + <h2>Changes to the Service</h2> 94 + <p>Features may change, be limited, or be discontinued at any time without notice.</p> 95 + 96 + <h2>Backup User Content</h2> 97 + <p>In extreme circumstances we reserve the right to ban, block, and or remove your content placed on our servers 98 + if it is found to be illegal or harmful. We will notify you via Bluesky that you are being removed along 99 + with giving you an export of your backed-up data.</p> 100 + 101 + <h2>Contact</h2> 102 + <p>If you have questions about these terms, please reach out via the project repository or to <a 103 + href="https://bsky.app/profile/baileytownsend.dev">@baileytownsend.dev</a> directly.</p> 104 + </section> 105 + </div>
+101
web-ui/src/routes/turn-off/+page.svelte
··· 1 + <script lang="ts"> 2 + import MooHeader from '$lib/components/MooHeader.svelte'; 3 + import {Migrator} from '@pds-moover/moover'; 4 + import OgImage from '$lib/components/OgImage.svelte'; 5 + 6 + let migrator = $state(new Migrator()); 7 + 8 + // Form state 9 + let oldHandle = $state(''); 10 + let oldPassword = $state(''); 11 + let twoFactorCode = $state(''); 12 + let showTwoFactorCodeInput = $state(false); 13 + let errorMessage: string | null = $state(null); 14 + let showStatusMessage = $state(false); 15 + let statusMessage = $state(''); 16 + 17 + function updateStatusHandler(status: string) { 18 + console.log('Status update:', status); 19 + statusMessage = status; 20 + } 21 + 22 + async function handleSubmit(event: SubmitEvent) { 23 + event.preventDefault(); 24 + errorMessage = null; 25 + showStatusMessage = false; 26 + 27 + 28 + try { 29 + if (showTwoFactorCodeInput) { 30 + if (twoFactorCode === null || twoFactorCode === '') { 31 + errorMessage = 'Please enter the 2FA that was sent to your email.'; 32 + return; 33 + } 34 + } 35 + 36 + showStatusMessage = true; 37 + await migrator.deactivateOldAccount( 38 + oldHandle, 39 + oldPassword, 40 + updateStatusHandler, 41 + twoFactorCode 42 + ); 43 + } catch (err) { 44 + //@ts-expect-error: error should be fine 45 + if (err.error === 'AuthFactorTokenRequired') { 46 + showTwoFactorCodeInput = true; 47 + } 48 + //@ts-expect-error: error should be fine 49 + errorMessage = err.message; 50 + } 51 + } 52 + </script> 53 + 54 + <svelte:head> 55 + <title>PDS MOOver - Turn OFF</title> 56 + <meta property="og:description" content="Deactivate your old account"/> 57 + <OgImage/> 58 + </svelte:head> 59 + 60 + <div class="container"> 61 + <MooHeader title="Turn OFF"/> 62 + 63 + <p>Use this page to make sure your old account is deactivated</p> 64 + 65 + <form id="moover-form" onsubmit={handleSubmit}> 66 + <!-- Login credentials --> 67 + <div class="section"> 68 + <h2>Login for your old PDS</h2> 69 + <div class="form-group"> 70 + <label for="handle">Old Handle:</label> 71 + <input type="text" id="handle" name="handle" placeholder="alice.bsky.social" 72 + bind:value={oldHandle} 73 + required> 74 + </div> 75 + 76 + <div class="form-group"> 77 + <label for="password">Old Password:</label> 78 + <input type="password" id="password" name="password" bind:value={oldPassword} required> 79 + </div> 80 + 81 + {#if showTwoFactorCodeInput} 82 + <div class="form-group"> 83 + <label for="two-factor-code">2FA from the email sent</label> 84 + <input type="text" id="two-factor-code" name="two-factor-code" bind:value={twoFactorCode}> 85 + <div class="error-message">Enter your 2fa code here</div> 86 + </div> 87 + {/if} 88 + </div> 89 + 90 + {#if errorMessage} 91 + <div class="error-message">{errorMessage}</div> 92 + {/if} 93 + {#if showStatusMessage} 94 + <div id="status-message" class="status-message">{statusMessage}</div> 95 + {/if} 96 + 97 + <div> 98 + <button type="submit">Turn it off</button> 99 + </div> 100 + </form> 101 + </div>
+6
web-ui/src/routes/turnoff.html/+server.ts
··· 1 + import { redirect } from '@sveltejs/kit'; 2 + import type { RequestHandler } from './$types'; 3 + 4 + export const GET: RequestHandler = () => { 5 + throw redirect(301, '/turn-off'); 6 + };
+3
web-ui/static/robots.txt
··· 1 + # allow crawling everything by default 2 + User-agent: * 3 + Disallow:
+16
web-ui/svelte.config.js
··· 1 + import adapter from '@sveltejs/adapter-node'; 2 + import {vitePreprocess} from '@sveltejs/vite-plugin-svelte'; 3 + 4 + /** @type {import('@sveltejs/kit').Config} */ 5 + const config = { 6 + // Consult https://svelte.dev/docs/kit/integrations 7 + // for more information about preprocessors 8 + preprocess: vitePreprocess(), 9 + 10 + kit: { 11 + // https://svelte.dev/docs/kit/adapter-node 12 + adapter: adapter() 13 + } 14 + }; 15 + 16 + export default config;
+22
web-ui/tsconfig.json
··· 1 + { 2 + "extends": "./.svelte-kit/tsconfig.json", 3 + "compilerOptions": { 4 + "allowJs": true, 5 + "checkJs": true, 6 + "esModuleInterop": true, 7 + "forceConsistentCasingInFileNames": true, 8 + "resolveJsonModule": true, 9 + "skipLibCheck": true, 10 + "sourceMap": true, 11 + "strict": true, 12 + "moduleResolution": "bundler", 13 + "types": [ 14 + "@pds-moover/lexicon_types" 15 + ] 16 + } 17 + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 18 + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 19 + // 20 + // To make changes to top-level options such as include and exclude, we recommend extending 21 + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript 22 + }
+6
web-ui/vite.config.ts
··· 1 + import { sveltekit } from '@sveltejs/kit/vite'; 2 + import { defineConfig } from 'vite'; 3 + 4 + export default defineConfig({ 5 + plugins: [sveltekit()] 6 + });
+1 -3
web/Cargo.toml
··· 11 11 tracing = "0.1" 12 12 tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 13 dotenvy.workspace = true 14 - askama = "0.14" 15 14 serde = { version = "1", features = ["derive"] } 16 15 serde_json = "1" 17 16 anyhow.workspace = true 18 17 log.workspace = true 19 18 shared = { path = "../shared" } 20 - vite-rust = "0.2.4" 21 - tower-http = { version = "0.6.1", features = ["fs", "trace"] } 19 + tower-http = { version = "0.6.1", features = ["fs", "trace", "cors"] } 22 20 tower = { version = "0.5.1", features = ["util"] } 23 21 jacquard-axum.workspace = true 24 22 jacquard-common.workspace = true
web/public/PDSMOOver.excalidraw.png web-ui/src/lib/assets/PDSMOOver.excalidraw.png
web/public/halloween_moover.webp web-ui/src/lib/assets/halloween_moover.webp
web/public/missing.webp web-ui/src/lib/assets/missing.webp
web/public/moo.webp web-ui/src/lib/assets/moo.webp
web/public/sign_the_papers.png web-ui/src/lib/assets/sign_the_papers.png
+1 -1
web/public/style.css web-ui/src/lib/assets/style.css
··· 15 15 16 16 a { 17 17 font-weight: 500; 18 - color: #646cff; 18 + color: #a2a7ff; 19 19 text-decoration: inherit; 20 20 text-decoration: underline; 21 21 }
-1
web/src/handlers/mod.rs
··· 1 - pub mod pages; 2 1 pub mod well_known; 3 2 pub mod xrpc;
-172
web/src/handlers/pages.rs
··· 1 - use crate::AppState; 2 - use crate::templates::{ 3 - BackupsPage, HtmlTemplate, IndexPage, InfoPage, LayoutContext, MissingBlobsPage, MooverPage, 4 - RestorePage, RotationKeyPage, TermsPrivacyPage, TurnOffPage, error_response, 5 - }; 6 - use axum::Router; 7 - use axum::extract::State; 8 - use axum::http::StatusCode; 9 - use axum::response::{IntoResponse, Response}; 10 - use axum::routing::get; 11 - 12 - pub async fn index_handler(State(state): State<AppState>) -> Result<impl IntoResponse, Response> { 13 - let layout = LayoutContext::new("PDS MOOver".to_string(), state.vite, "/").map_err(|err| { 14 - tracing::error!(?err, "Error rendering out the index page"); 15 - error_response( 16 - StatusCode::INTERNAL_SERVER_ERROR, 17 - "Error rendering out the index page", 18 - ) 19 - })?; 20 - 21 - Ok(HtmlTemplate(IndexPage { 22 - layout_context: layout, 23 - })) 24 - } 25 - 26 - pub async fn moover_handler(State(state): State<AppState>) -> Result<impl IntoResponse, Response> { 27 - let layout = 28 - LayoutContext::new("PDS MOOver".to_string(), state.vite, "/moover").map_err(|err| { 29 - tracing::error!(?err, "Error rendering out the index page"); 30 - error_response( 31 - StatusCode::INTERNAL_SERVER_ERROR, 32 - "Error rendering out the index page", 33 - ) 34 - })?; 35 - 36 - Ok(HtmlTemplate(MooverPage { 37 - layout_context: layout, 38 - })) 39 - } 40 - 41 - pub async fn info_handler(State(state): State<AppState>) -> Result<impl IntoResponse, Response> { 42 - let layout = LayoutContext::new("PDS MOOver - Info".to_string(), state.vite, "/info").map_err( 43 - |err| { 44 - tracing::error!(?err, "Error rendering out the index page"); 45 - error_response( 46 - StatusCode::INTERNAL_SERVER_ERROR, 47 - "Error rendering out the index page", 48 - ) 49 - }, 50 - )?; 51 - 52 - Ok(HtmlTemplate(InfoPage { 53 - layout_context: layout, 54 - })) 55 - } 56 - 57 - pub async fn turn_off_handler( 58 - State(state): State<AppState>, 59 - ) -> Result<impl IntoResponse, Response> { 60 - let layout = LayoutContext::new("PDS MOOver - Turn OFF".to_string(), state.vite, "/turn-off") 61 - .map_err(|err| { 62 - tracing::error!(?err, "Error rendering out the index page"); 63 - error_response( 64 - StatusCode::INTERNAL_SERVER_ERROR, 65 - "Error rendering out the index page", 66 - ) 67 - })?; 68 - 69 - Ok(HtmlTemplate(TurnOffPage { 70 - layout_context: layout, 71 - })) 72 - } 73 - 74 - pub async fn missing_blobs_handler( 75 - State(state): State<AppState>, 76 - ) -> Result<impl IntoResponse, Response> { 77 - let layout = LayoutContext::new( 78 - "Import Missing Blobs".to_string(), 79 - state.vite, 80 - "/missing-blobs", 81 - ) 82 - .map_err(|err| { 83 - tracing::error!(?err, "Error rendering out the index page"); 84 - error_response( 85 - StatusCode::INTERNAL_SERVER_ERROR, 86 - "Error rendering out the index page", 87 - ) 88 - })?; 89 - 90 - Ok(HtmlTemplate(MissingBlobsPage { 91 - layout_context: layout, 92 - })) 93 - } 94 - 95 - pub async fn backups_signup_handler( 96 - State(state): State<AppState>, 97 - ) -> Result<impl IntoResponse, Response> { 98 - let layout = LayoutContext::new("Backups - Signup".to_string(), state.vite, "/backups") 99 - .map_err(|err| { 100 - tracing::error!(?err, "Error rendering out the index page"); 101 - error_response( 102 - StatusCode::INTERNAL_SERVER_ERROR, 103 - "Error rendering out the index page", 104 - ) 105 - })?; 106 - 107 - Ok(HtmlTemplate(BackupsPage { 108 - layout_context: layout, 109 - })) 110 - } 111 - 112 - pub async fn rotation_key_handler( 113 - State(state): State<AppState>, 114 - ) -> Result<impl IntoResponse, Response> { 115 - let layout = LayoutContext::new("Rotation Key".to_string(), state.vite, "/rotation-key") 116 - .map_err(|err| { 117 - tracing::error!(?err, "Error rendering out the rotation key page"); 118 - error_response( 119 - StatusCode::INTERNAL_SERVER_ERROR, 120 - "Error rendering out the rotation key page", 121 - ) 122 - })?; 123 - 124 - Ok(HtmlTemplate(RotationKeyPage { 125 - layout_context: layout, 126 - })) 127 - } 128 - 129 - pub async fn restore_handler(State(state): State<AppState>) -> Result<impl IntoResponse, Response> { 130 - let layout = 131 - LayoutContext::new("Restore".to_string(), state.vite, "/restore").map_err(|err| { 132 - tracing::error!(?err, "Error rendering out the restore page"); 133 - error_response( 134 - StatusCode::INTERNAL_SERVER_ERROR, 135 - "Error rendering out the restore page", 136 - ) 137 - })?; 138 - 139 - Ok(HtmlTemplate(RestorePage { 140 - layout_context: layout, 141 - })) 142 - } 143 - 144 - pub async fn terms_privacy_handler( 145 - State(state): State<AppState>, 146 - ) -> Result<impl IntoResponse, Response> { 147 - let layout = 148 - LayoutContext::new("Terms & Privacy".to_string(), state.vite, "/legal").map_err(|err| { 149 - tracing::error!(?err, "Error rendering out the legal page"); 150 - error_response( 151 - StatusCode::INTERNAL_SERVER_ERROR, 152 - "Error rendering out the legal page", 153 - ) 154 - })?; 155 - 156 - Ok(HtmlTemplate(TermsPrivacyPage { 157 - layout_context: layout, 158 - })) 159 - } 160 - 161 - pub fn pages_handlers() -> Router<AppState> { 162 - Router::new() 163 - .route("/", get(index_handler)) 164 - .route("/moover", get(moover_handler)) 165 - .route("/info", get(info_handler)) 166 - .route("/turn-off", get(turn_off_handler)) 167 - .route("/missing-blobs", get(missing_blobs_handler)) 168 - .route("/backups", get(backups_signup_handler)) 169 - .route("/rotation-key", get(rotation_key_handler)) 170 - .route("/restore", get(restore_handler)) 171 - .route("/terms", get(terms_privacy_handler)) 172 - }
+35 -48
web/src/main.rs
··· 1 1 mod handlers; 2 - mod templates; 3 2 4 - use crate::handlers::pages::pages_handlers; 5 3 use crate::handlers::well_known::{ 6 4 ServiceDID, ServiceDocument, ServiceKey, build_service_document, handle_wellknown_did_web, 7 5 }; ··· 9 7 use crate::handlers::xrpc::com_pdsmoover_admin_handlers::admin_routes; 10 8 use crate::handlers::xrpc::com_pdsmoover_backup_handlers::backup_routes; 11 9 use atproto_identity::key::to_public; 12 - use axum::handler::HandlerWithoutStateExt; 13 - use axum::http::StatusCode; 14 - use axum::response::Redirect; 10 + use axum::http::header; 15 11 use axum::{Router, routing::get}; 16 12 use chrono::{DateTime, Utc}; 17 13 use dotenvy::dotenv; ··· 28 24 use tower_governor::GovernorLayer; 29 25 use tower_governor::governor::GovernorConfigBuilder; 30 26 use tower_governor::key_extractor::SmartIpKeyExtractor; 31 - use tower_http::services::ServeDir; 27 + use tower_http::cors::{Any, CorsLayer}; 32 28 use tracing::info; 33 29 use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt}; 34 - use vite_rust::utils::resolve_path; 35 - use vite_rust::{Vite, ViteConfig}; 36 30 37 31 #[derive(Clone)] 38 32 struct AppState { 39 33 db: Db, 40 - vite: Arc<Vite>, 41 34 public_resolver: Arc<PublicResolver>, 42 35 service_document: ServiceDocument, 43 36 did_web: ServiceDID, ··· 69 62 let db = Db::connect(&database_url).await?; 70 63 db.apply_migrations().await?; 71 64 72 - //Vite setup 73 - let vite_dist = std::env::var("VITE_DIST").unwrap_or_else(|_| "../ui-code/dist".to_string()); 74 - let manifest_path: String = 75 - resolve_path(file!(), format!("{vite_dist}/.vite/manifest.json").as_str()); 76 - let mut vite_config: ViteConfig<'_> = ViteConfig::default().set_manifest_path(&manifest_path); 77 - vite_config.entrypoints = Some(vec![ 78 - "src/main.js", 79 - "src/pdsmoover.js", 80 - "src/missingBlobs.js", 81 - ]); 82 - let vite = vite_rust::Vite::new(vite_config).await?; 83 - 84 - let fallback_404_service = handle_404.into_service(); 85 - let dist_dir = ServeDir::new(format!("{vite_dist}/assets")) 86 - .not_found_service(fallback_404_service.clone()); 87 - let public_dir = 88 - ServeDir::new(env::var("PUBLIC_ASSETS")?).not_found_service(fallback_404_service); 89 - 90 65 //did:web/atproto service setup 91 66 let external_domain = env::var("EXTERNAL_DOMAIN")?; 92 67 let service_key_string = env::var("SERVICE_KEY")?; ··· 125 100 126 101 let state = AppState { 127 102 db, 128 - vite: Arc::new(vite), 129 103 service_document, 130 104 public_resolver: Arc::new(resolver), 131 105 did_web: service_did, ··· 136 110 137 111 // Build Axum router 138 112 let mut app = Router::new() 113 + .route("/", get(root_handler)) 139 114 //XRPC Routes 140 115 .merge(backup_routes(state.clone())) 141 116 .merge(atproto_routes(state.clone())) 142 117 //Other routes 143 - .merge(pages_handlers()) 144 - .route("/.well-known/did.json", get(handle_wellknown_did_web)) 145 - // Example legacy redirects 146 - .route( 147 - "/turnoff.html", 148 - get(async move || Redirect::permanent("/turn-off")), 149 - ) 150 - .route( 151 - "/info.html", 152 - get(async move || Redirect::permanent("/turn-off")), 153 - ); 118 + .route("/.well-known/did.json", get(handle_wellknown_did_web)); 154 119 155 120 // Conditionally add admin endpoints only if ADMIN_PASSWORD is set 156 121 if state.admin_token.is_some() { ··· 177 142 app = app.merge(admin_routes(state.clone()).layer(governor_layer)); 178 143 } 179 144 180 - //catch all routes 181 - app = app 182 - .nest_service("/assets", dist_dir) 183 - .fallback_service(public_dir); 145 + //CORS 146 + let cors = CorsLayer::new() 147 + .allow_methods(Any) 148 + .allow_origin(Any) 149 + .allow_headers(Any); 184 150 185 151 // Finalize with state 186 - let app = app 187 - // .route("/demo", post(create_demo)) 188 - .with_state(state); 152 + let app = app.layer(cors).with_state(state); 189 153 190 154 // Read PORT from env or default to 3000 191 155 let port: u16 = std::env::var("PORT") ··· 202 166 Ok(()) 203 167 } 204 168 205 - async fn handle_404() -> (StatusCode, &'static str) { 206 - (StatusCode::NOT_FOUND, "Not found") 169 + async fn root_handler() -> impl axum::response::IntoResponse { 170 + let body = r" 171 + .---. .---. .--. 172 + : .; :: . :: .--' 173 + : _.': :: :`. `. 174 + : : : :; : _`, : 175 + :_; :___.'`.__.' 176 + 177 + 178 + .-..-. .--. .--. 179 + : `' :: ,. :: ,. : 180 + : .. :: :: :: :: :.-..-. .--. .--. 181 + : :; :: :; :: :; :: `; :' '_.': ..' 182 + :_;:_;`.__.'`.__.'`.__.'`.__.':_; 183 + 184 + "; 185 + 186 + let intro = "\n\nThis is a PDS MOOver xrpc service\n\nCode: https://tangled.sh/@baileytownsend.dev/pds-moover\n"; 187 + 188 + let banner = format!(" {body}\n{intro}"); 189 + 190 + ( 191 + [(header::CONTENT_TYPE, "text/plain; charset=utf-8")], 192 + banner, 193 + ) 207 194 }
-127
web/src/templates.rs
··· 1 - use askama::Template; 2 - use axum::http::StatusCode; 3 - use axum::response::{Html, IntoResponse, Response}; 4 - use serde::{Deserialize, Serialize}; 5 - use std::sync::Arc; 6 - use vite_rust::Vite; 7 - 8 - #[derive(Clone, Debug, Serialize, Deserialize)] 9 - pub struct LayoutContext { 10 - pub title: String, 11 - pub script_tags: String, 12 - pub vite_hmr_url: Option<String>, 13 - pub current_path: String, 14 - } 15 - 16 - impl LayoutContext { 17 - pub fn new(title: String, vite: Arc<Vite>, current_path: &str) -> anyhow::Result<Self> { 18 - let vite_script_tags = vite.get_resolved_vite_scripts()?; 19 - let hmr = if vite.get_hmr_script() == "" { 20 - None 21 - } else { 22 - Some(vite.get_hmr_script()) 23 - }; 24 - Ok(Self { 25 - title, 26 - script_tags: vite_script_tags, 27 - vite_hmr_url: hmr, 28 - current_path: current_path.to_string(), 29 - }) 30 - } 31 - } 32 - 33 - pub fn error_response(status: StatusCode, message: &str) -> Response { 34 - IntoResponse::into_response(( 35 - status, 36 - HtmlTemplate(ErrorTemplate { 37 - title: "PDS MOOver - Error", 38 - message, 39 - }), 40 - )) 41 - } 42 - pub struct HtmlTemplate<T>(pub T); 43 - 44 - /// Allows us to convert Askama HTML templates into valid HTML 45 - /// for axum to serve in the response. 46 - impl<T> IntoResponse for HtmlTemplate<T> 47 - where 48 - T: Template, 49 - { 50 - fn into_response(self) -> Response { 51 - // Attempt to render the template with askama 52 - match self.0.render() { 53 - // If we're able to successfully parse and aggregate the template, serve it 54 - Ok(html) => Html(html).into_response(), 55 - // If we're not, return an error or some bit of fallback HTML 56 - Err(err) => { 57 - log::error!("Failed to render template: {}", err); 58 - IntoResponse::into_response(( 59 - StatusCode::INTERNAL_SERVER_ERROR, 60 - "Failed to render the HTML Template", 61 - )) 62 - } 63 - } 64 - } 65 - } 66 - 67 - #[derive(Template)] 68 - #[template(path = "error.askama.html")] 69 - pub struct ErrorTemplate<'a> { 70 - pub title: &'a str, 71 - pub message: &'a str, 72 - } 73 - 74 - #[derive(Template, Debug)] 75 - #[template(path = "index.askama.html")] 76 - pub struct IndexPage { 77 - pub(crate) layout_context: LayoutContext, 78 - } 79 - 80 - #[derive(Template, Debug)] 81 - #[template(path = "moover.askama.html")] 82 - pub struct MooverPage { 83 - pub(crate) layout_context: LayoutContext, 84 - } 85 - 86 - #[derive(Template, Debug)] 87 - #[template(path = "info.askama.html")] 88 - pub struct InfoPage { 89 - pub(crate) layout_context: LayoutContext, 90 - } 91 - 92 - #[derive(Template, Debug)] 93 - #[template(path = "turn-off.askama.html")] 94 - pub struct TurnOffPage { 95 - pub(crate) layout_context: LayoutContext, 96 - } 97 - 98 - #[derive(Template, Debug)] 99 - #[template(path = "missing-blobs.askama.html")] 100 - pub struct MissingBlobsPage { 101 - pub(crate) layout_context: LayoutContext, 102 - } 103 - 104 - #[derive(Template, Debug)] 105 - #[template(path = "backups.askama.html")] 106 - pub struct BackupsPage { 107 - pub(crate) layout_context: LayoutContext, 108 - } 109 - 110 - 111 - #[derive(Template, Debug)] 112 - #[template(path = "rotation-key.askama.html")] 113 - pub struct RotationKeyPage { 114 - pub(crate) layout_context: LayoutContext, 115 - } 116 - 117 - #[derive(Template, Debug)] 118 - #[template(path = "restore.askama.html")] 119 - pub struct RestorePage { 120 - pub(crate) layout_context: LayoutContext, 121 - } 122 - 123 - #[derive(Template, Debug)] 124 - #[template(path = "terms.askama.html")] 125 - pub struct TermsPrivacyPage { 126 - pub(crate) layout_context: LayoutContext, 127 - }
+64 -30
web/templates/backups.askama.html
··· 17 17 showTwoFactorCodeInput: false, 18 18 error: null, 19 19 showStatusMessage: false, 20 - showLoginScreen: true, 20 + //The landing page to pick to login with password or oauth 21 + showLandingButtons: true, 22 + //Password login 23 + showLoginScreen: false, 21 24 showRepoNotFoundScreen: false, 22 25 addRecoveryKey: true, 23 26 // Rotation key flow state ··· 47 50 this.showStatusMessage = false; 48 51 } 49 52 }, 53 + handleShowLogin() { 54 + this.showLandingButtons = false; 55 + this.showLoginScreen = true; 56 + }, 57 + async handleOAuthSignup() { 58 + // TODO: Replace this URL with your actual OAuth signup endpoint for backups 59 + // If you have an environment-specific route, consider injecting it or making it configurable 60 + window.location.href = '/oauth/backups'; 61 + }, 50 62 async handleLoginSubmit() { 63 + this.error = null; 64 + this.showStatusMessage = false; 65 + this.showLandingButtons = false; 51 66 this.error = null; 52 67 this.showStatusMessage = false; 53 68 ··· 203 218 {% call cow::cow_header("Backups") %} 204 219 205 220 206 - <form id="backup-signup-form" @submit.prevent="await handleLoginSubmit()" x-show="showLoginScreen"> 221 + <!-- Landing choice: two buttons --> 222 + <div x-show="showLoginScreen"> 207 223 <!-- Informational section before sign-in --> 208 224 <div class="section" style="text-align: left;"> 209 - <p> 210 - PDS MOOver can provide worry free backups of your AT Protocol account. This is a free service for individual accounts and stores the backups on PDS MOOver's servers. Just like your <a target="_blank" rel="noopener noreferrer" href="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">AT Proto data</a>, this is also public. On login, you will be asked if you'd like to add a rotation key to your account. 211 - A rotation key is a recovery key that allows you to restore your account if your PDS ever goes down. If you're already signed up for backups, then you can log in here to manage them. 212 - </p> 225 + <p> 226 + PDS MOOver can provide worry-free backups of your AT Protocol account. 227 + This is a free service for individual accounts 228 + and stores the backups on PDS MOOver's servers. 229 + Just like your <a target="_blank" rel="noopener noreferrer" href="https://blueskyweb.zendesk.com/hc/en-us/articles/15835264007693-Data-Privacy">AT Proto data</a>, 230 + this is also public. 231 + <span x-show="showLoginScreen">On login, 232 + you will be asked if you'd like to add a rotation key to your account. 233 + A rotation key is a recovery key 234 + that allows you to restore your account if your PDS ever goes down. 235 + If you're already signed up for backups, then you can log in here to manage them.</span> 236 + 237 + </p> 213 238 </div> 214 - <!-- Sign-in section --> 215 - <div class="section"> 216 - <h2>Sign in to your account</h2> 217 - <div class="form-group"> 218 - <label for="handle">Handle</label> 219 - <input type="text" id="handle" name="handle" placeholder="alice.bsky.social" x-model="handle" required> 220 - </div> 221 239 222 - <div class="form-group"> 223 - <label for="password">Real Password</label> 224 - <input type="password" id="password" name="password" x-model="password" required> 225 - <p> If you are signing up and adding a rotation key you have to use your account's real password. If you are just managing your backups or have your own rotation key you can use an app password</p> 240 + <div x-show="showLandingButtons" class="actions" style="display: flex; gap: 1rem; flex-wrap: wrap;"> 241 + <button type="button" @click="handleShowLogin()">Sign in to add a recovery key</button> 242 + <button type="button" @click="await handleOAuthSignup()">Sign in for backups with OAuth</button> 243 + </div> 244 + 245 + <!-- Sign in with password section --> 246 + <form id="backup-signup-form" @submit.prevent="await handleLoginSubmit()"> 247 + 248 + <div class="section"> 249 + <h2>Sign in to your account</h2> 250 + <div class="form-group"> 251 + <label for="handle">Handle</label> 252 + <input type="text" id="handle" name="handle" placeholder="alice.bsky.social" x-model="handle" required> 253 + </div> 254 + 255 + <div class="form-group"> 256 + <label for="password">Real Password</label> 257 + <input type="password" id="password" name="password" x-model="password" required> 258 + <p> If you are signing up and adding a rotation key you have to use your account's real password. If you are just managing your backups or have your own rotation key you can use an app password</p> 259 + 260 + </div> 226 261 262 + <div x-show="showTwoFactorCodeInput" class="form-group"> 263 + <label for="two-factor-code">Two-factor code (email)</label> 264 + <input type="text" id="two-factor-code" name="two-factor-code" x-model="twoFactorCode"> 265 + <div class="error-message">Enter the 2FA code from your email.</div> 266 + </div> 227 267 </div> 228 268 229 - <div x-show="showTwoFactorCodeInput" class="form-group"> 230 - <label for="two-factor-code">Two-factor code (email)</label> 231 - <input type="text" id="two-factor-code" name="two-factor-code" x-model="twoFactorCode"> 232 - <div class="error-message">Enter the 2FA code from your email.</div> 269 + <div x-show="error" x-text="error" class="error-message"></div> 270 + <div x-show="showStatusMessage" id="status-message" class="status-message"></div> 271 + <div> 272 + <button type="submit">Login for backups</button> 233 273 </div> 234 - </div> 235 - 236 - <div x-show="error" x-text="error" class="error-message"></div> 237 - <div x-show="showStatusMessage" id="status-message" class="status-message"></div> 238 - <div> 239 - <button type="submit">Login for backups</button> 240 - </div> 241 - </form> 274 + </form> 275 + </div> 242 276 243 277 <!-- Repo not found prompt --> 244 278 <div class="section" x-show="showRepoNotFoundScreen"> ··· 298 332 </template> 299 333 300 334 <!-- Repo status view for signed-up users --> 301 - <div class="section" x-show="!showLoginScreen && !showRepoNotFoundScreen && !showRotationKeyScreen"> 335 + <div class="section" x-show="!showLandingButtons && !showLoginScreen && !showRepoNotFoundScreen && !showRotationKeyScreen"> 302 336 <div class="section-header"> 303 337 <h2 style="margin: 0;">Backup repository status</h2> 304 338 <button type="button" class="icon-button" title="Refresh status" aria-label="Refresh status" @click="refreshStatus">
+1 -20
web/templates/index.askama.html
··· 28 28 } 29 29 }, 30 30 //TODO bad but do not want to figure out onload with current setup 31 - fmtBytes(bytes) { 32 - if (bytes == null) return '—'; 33 - const units = ['B', 'KB', 'MB', 'GB', 'TB']; 34 - let i = 0; 35 - let v = Number(bytes); 36 - while (v >= 1024 && i < units.length - 1) { 37 - v /= 1024; 38 - i++; 39 - } 40 - return v.toFixed(1) + ' ' + units[i]; 41 - }, 42 - fmtDate(value) { 43 - if (!value) return '—'; 44 - try { 45 - const d = new Date(value); 46 - return d.toLocaleString(); 47 - } catch (_) { 48 - return String(value); 49 - } 50 - } 31 + 51 32 52 33 })); 53 34 });
+1
web/templates/moover.askama.html
··· 278 278 <button type="submit">MOOve</button> 279 279 </div> 280 280 </form> 281 + 281 282 <div x-show="askForPlcToken" class="section"> 282 283 <form @submit.prevent="await signPlcOperation()"> 283 284 <div x-show="!done">
web/ui-code/.gitignore packages/moover/.gitignore
+92 -60
web/ui-code/bun.lock packages/moover/bun.lock
··· 2 2 "lockfileVersion": 1, 3 3 "workspaces": { 4 4 "": { 5 - "name": "ui-code", 5 + "name": "moover", 6 6 "dependencies": { 7 + "@atcute/cbor": "^2.2.7", 8 + "@atcute/client": "^4.0.4", 9 + "@atcute/crypto": "^2.2.5", 10 + "@atcute/did-plc": "^0.1.7", 7 11 "@atcute/identity-resolver": "^1.1.3", 12 + "@atcute/lexicons": "^1.2.2", 13 + "@atcute/multibase": "^1.1.6", 8 14 "@atproto/api": "^0.16.7", 15 + "@pds-moover/lexicons": "^1.0.0", 16 + "alpinejs": "^3.15.0", 9 17 "vite-plugin-full-reload": "^1.2.0", 10 18 "vite-rs-plugin": "1.0.1", 11 19 }, ··· 17 25 }, 18 26 }, 19 27 "packages": { 28 + "@atcute/cbor": ["@atcute/cbor@2.2.7", "", { "dependencies": { "@atcute/cid": "^2.2.5", "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.5" } }, "sha512-/mwAF0gnokOphceZqFq3uzMGdd8sbw5y6bxF8CRutRkCCUcpjjpJc5fkLwhxyGgOveF3mZuHE6p7t/+IAqb7Aw=="], 29 + 30 + "@atcute/cid": ["@atcute/cid@2.2.6", "", { "dependencies": { "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.5" } }, "sha512-bTAHHbJ24p+E//V4KCS4xdmd39o211jJswvqQOevj7vk+5IYcgDLx1ryZWZ1sEPOo9x875li/kj5gpKL14RDwQ=="], 31 + 32 + "@atcute/client": ["@atcute/client@4.0.5", "", { "dependencies": { "@atcute/identity": "^1.1.1", "@atcute/lexicons": "^1.2.2" } }, "sha512-R8Qen8goGmEkynYGg2m6XFlVmz0GTDvQ+9w+4QqOob+XMk8/WDpF4aImev7WKEde/rV2gjcqW7zM8E6W9NShDA=="], 33 + 34 + "@atcute/crypto": ["@atcute/crypto@2.2.5", "", { "dependencies": { "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.5", "@noble/secp256k1": "^2.3.0" } }, "sha512-9CbQ9cJ68XewsbLrgdmWQS2uDD9D0hizWFJ3OOZ16TCuARREmzKEpFgHlMxPswR3bDxjwfiXzmYUlHaTqsnxRQ=="], 35 + 36 + "@atcute/did-plc": ["@atcute/did-plc@0.1.7", "", { "dependencies": { "@atcute/cbor": "^2.2.6", "@atcute/cid": "^2.2.4", "@atcute/crypto": "^2.2.5", "@atcute/identity": "^1.1.1", "@atcute/lexicons": "^1.2.2", "@atcute/multibase": "^1.1.6", "@atcute/uint8array": "^1.0.5", "@badrap/valita": "^0.4.6" } }, "sha512-a7yOQNqViae3rB5/xa3U0EPJbFD9l8zOHXx6XASZ5F8+Vy2uTgXK3omurpNZ5UxRpy1ni1AMhSohXr61cqWbkg=="], 37 + 20 38 "@atcute/identity": ["@atcute/identity@1.1.1", "", { "dependencies": { "@atcute/lexicons": "^1.2.2", "@badrap/valita": "^0.4.6" } }, "sha512-zax42n693VEhnC+5tndvO2KLDTMkHOz8UExwmklvJv7R9VujfEwiSWhcv6Jgwb3ellaG8wjiQ1lMOIjLLvwh0Q=="], 21 39 22 40 "@atcute/identity-resolver": ["@atcute/identity-resolver@1.1.4", "", { "dependencies": { "@atcute/lexicons": "^1.2.2", "@atcute/util-fetch": "^1.0.3", "@badrap/valita": "^0.4.6" }, "peerDependencies": { "@atcute/identity": "^1.0.0" } }, "sha512-/SVh8vf2cXFJenmBnGeYF2aY3WGQm3cJeew5NWTlkqoy3LvJ5wkvKq9PWu4Tv653VF40rPOp6LOdVr9Fa+q5rA=="], 23 41 24 42 "@atcute/lexicons": ["@atcute/lexicons@1.2.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "esm-env": "^1.2.2" } }, "sha512-bgEhJq5Z70/0TbK5sx+tAkrR8FsCODNiL2gUEvS5PuJfPxmFmRYNWaMGehxSPaXWpU2+Oa9ckceHiYbrItDTkA=="], 43 + 44 + "@atcute/multibase": ["@atcute/multibase@1.1.6", "", { "dependencies": { "@atcute/uint8array": "^1.0.5" } }, "sha512-HBxuCgYLKPPxETV0Rot4VP9e24vKl8JdzGCZOVsDaOXJgbRZoRIF67Lp0H/OgnJeH/Xpva8Z5ReoTNJE5dn3kg=="], 45 + 46 + "@atcute/uint8array": ["@atcute/uint8array@1.0.5", "", {}, "sha512-XLWWxoR2HNl2qU+FCr0rp1APwJXci7HnzbOQLxK55OaMNBXZ19+xNC5ii4QCsThsDxa4JS/JTzuiQLziITWf2Q=="], 25 47 26 48 "@atcute/util-fetch": ["@atcute/util-fetch@1.0.3", "", { "dependencies": { "@badrap/valita": "^0.4.6" } }, "sha512-f8zzTb/xlKIwv2OQ31DhShPUNCmIIleX6p7qIXwWwEUjX6x8skUtpdISSjnImq01LXpltGV5y8yhV4/Mlb7CRQ=="], 27 49 ··· 37 59 38 60 "@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="], 39 61 40 - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="], 62 + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.11", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg=="], 41 63 42 - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.10", "", { "os": "android", "cpu": "arm" }, "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w=="], 64 + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.11", "", { "os": "android", "cpu": "arm" }, "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg=="], 43 65 44 - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.10", "", { "os": "android", "cpu": "arm64" }, "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg=="], 66 + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.11", "", { "os": "android", "cpu": "arm64" }, "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ=="], 45 67 46 - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.10", "", { "os": "android", "cpu": "x64" }, "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg=="], 68 + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.11", "", { "os": "android", "cpu": "x64" }, "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g=="], 47 69 48 - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA=="], 70 + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w=="], 49 71 50 - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg=="], 72 + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ=="], 51 73 52 - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.10", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg=="], 74 + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.11", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA=="], 53 75 54 - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA=="], 76 + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.11", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw=="], 55 77 56 - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.10", "", { "os": "linux", "cpu": "arm" }, "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg=="], 78 + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.11", "", { "os": "linux", "cpu": "arm" }, "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw=="], 57 79 58 - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ=="], 80 + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA=="], 59 81 60 - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.10", "", { "os": "linux", "cpu": "ia32" }, "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ=="], 82 + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.11", "", { "os": "linux", "cpu": "ia32" }, "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw=="], 61 83 62 - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg=="], 84 + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw=="], 63 85 64 - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA=="], 86 + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ=="], 65 87 66 - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.10", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA=="], 88 + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.11", "", { "os": "linux", "cpu": "ppc64" }, "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw=="], 67 89 68 - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.10", "", { "os": "linux", "cpu": "none" }, "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA=="], 90 + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.11", "", { "os": "linux", "cpu": "none" }, "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww=="], 69 91 70 - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.10", "", { "os": "linux", "cpu": "s390x" }, "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew=="], 92 + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.11", "", { "os": "linux", "cpu": "s390x" }, "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw=="], 71 93 72 - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA=="], 94 + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.11", "", { "os": "linux", "cpu": "x64" }, "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ=="], 73 95 74 - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A=="], 96 + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg=="], 75 97 76 - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.10", "", { "os": "none", "cpu": "x64" }, "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig=="], 98 + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.11", "", { "os": "none", "cpu": "x64" }, "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A=="], 77 99 78 - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.10", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw=="], 100 + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.11", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg=="], 79 101 80 - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.10", "", { "os": "openbsd", "cpu": "x64" }, "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw=="], 102 + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.11", "", { "os": "openbsd", "cpu": "x64" }, "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw=="], 81 103 82 - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.10", "", { "os": "none", "cpu": "arm64" }, "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag=="], 104 + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.11", "", { "os": "none", "cpu": "arm64" }, "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ=="], 83 105 84 - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.10", "", { "os": "sunos", "cpu": "x64" }, "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ=="], 106 + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.11", "", { "os": "sunos", "cpu": "x64" }, "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA=="], 85 107 86 - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw=="], 108 + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q=="], 87 109 88 - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.10", "", { "os": "win32", "cpu": "ia32" }, "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw=="], 110 + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.11", "", { "os": "win32", "cpu": "ia32" }, "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA=="], 89 111 90 - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="], 112 + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.11", "", { "os": "win32", "cpu": "x64" }, "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA=="], 91 113 92 114 "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="], 93 115 94 - "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="], 116 + "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="], 95 117 96 - "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="], 118 + "@eslint/config-array": ["@eslint/config-array@0.21.1", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA=="], 97 119 98 - "@eslint/config-helpers": ["@eslint/config-helpers@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0" } }, "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog=="], 120 + "@eslint/config-helpers": ["@eslint/config-helpers@0.4.1", "", { "dependencies": { "@eslint/core": "^0.16.0" } }, "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw=="], 99 121 100 122 "@eslint/core": ["@eslint/core@0.16.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q=="], 101 123 102 124 "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="], 103 125 104 - "@eslint/js": ["@eslint/js@9.37.0", "", {}, "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg=="], 126 + "@eslint/js": ["@eslint/js@9.38.0", "", {}, "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A=="], 105 127 106 - "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="], 128 + "@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="], 107 129 108 130 "@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.0", "", { "dependencies": { "@eslint/core": "^0.16.0", "levn": "^0.4.1" } }, "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A=="], 109 131 ··· 115 137 116 138 "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], 117 139 118 - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.4", "", { "os": "android", "cpu": "arm" }, "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA=="], 140 + "@noble/secp256k1": ["@noble/secp256k1@2.3.0", "", {}, "sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw=="], 119 141 120 - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.4", "", { "os": "android", "cpu": "arm64" }, "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w=="], 142 + "@pds-moover/lexicons": ["@pds-moover/lexicons@1.0.0", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "@atproto/xrpc": "^0.7.5" } }, "sha512-1/q4ZUf0PuyDTLuyuGunsBvQBo1+V8W80/QAw7VHqO6Uvu/+VjcriM2C7O4F7Q+KFX3QPFEiK0vRSyaSdgg5Zw=="], 121 143 122 - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg=="], 144 + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="], 123 145 124 - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw=="], 146 + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="], 125 147 126 - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ=="], 148 + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="], 127 149 128 - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw=="], 150 + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="], 129 151 130 - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ=="], 152 + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="], 131 153 132 - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.4", "", { "os": "linux", "cpu": "arm" }, "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q=="], 154 + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="], 133 155 134 - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg=="], 156 + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="], 135 157 136 - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g=="], 158 + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="], 137 159 138 - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ=="], 160 + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="], 139 161 140 - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g=="], 162 + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="], 141 163 142 - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg=="], 164 + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="], 143 165 144 - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.4", "", { "os": "linux", "cpu": "none" }, "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA=="], 166 + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="], 145 167 146 - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA=="], 168 + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="], 147 169 148 - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg=="], 170 + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="], 149 171 150 - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.4", "", { "os": "linux", "cpu": "x64" }, "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw=="], 172 + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="], 151 173 152 - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.4", "", { "os": "none", "cpu": "arm64" }, "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA=="], 174 + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="], 153 175 154 - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ=="], 176 + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="], 155 177 156 - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw=="], 178 + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="], 157 179 158 - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ=="], 180 + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="], 159 181 160 - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.4", "", { "os": "win32", "cpu": "x64" }, "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w=="], 182 + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="], 183 + 184 + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="], 185 + 186 + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="], 161 187 162 188 "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="], 163 189 ··· 169 195 170 196 "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="], 171 197 198 + "@vue/reactivity": ["@vue/reactivity@3.1.5", "", { "dependencies": { "@vue/shared": "3.1.5" } }, "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg=="], 199 + 200 + "@vue/shared": ["@vue/shared@3.1.5", "", {}, "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="], 201 + 172 202 "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], 173 203 174 204 "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], 175 205 176 206 "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], 207 + 208 + "alpinejs": ["alpinejs@3.15.1", "", { "dependencies": { "@vue/reactivity": "~3.1.1" } }, "sha512-HLO1TtiE92VajFHtLLPK8BWaK1YepV/uj31UrfoGnQ00lyFOJZ+oVY3F0DghPAwvg8sLU79pmjGQSytERa2gEg=="], 177 209 178 210 "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 179 211 ··· 251 283 252 284 "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=="], 253 285 254 - "esbuild": ["esbuild@0.25.10", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.10", "@esbuild/android-arm": "0.25.10", "@esbuild/android-arm64": "0.25.10", "@esbuild/android-x64": "0.25.10", "@esbuild/darwin-arm64": "0.25.10", "@esbuild/darwin-x64": "0.25.10", "@esbuild/freebsd-arm64": "0.25.10", "@esbuild/freebsd-x64": "0.25.10", "@esbuild/linux-arm": "0.25.10", "@esbuild/linux-arm64": "0.25.10", "@esbuild/linux-ia32": "0.25.10", "@esbuild/linux-loong64": "0.25.10", "@esbuild/linux-mips64el": "0.25.10", "@esbuild/linux-ppc64": "0.25.10", "@esbuild/linux-riscv64": "0.25.10", "@esbuild/linux-s390x": "0.25.10", "@esbuild/linux-x64": "0.25.10", "@esbuild/netbsd-arm64": "0.25.10", "@esbuild/netbsd-x64": "0.25.10", "@esbuild/openbsd-arm64": "0.25.10", "@esbuild/openbsd-x64": "0.25.10", "@esbuild/openharmony-arm64": "0.25.10", "@esbuild/sunos-x64": "0.25.10", "@esbuild/win32-arm64": "0.25.10", "@esbuild/win32-ia32": "0.25.10", "@esbuild/win32-x64": "0.25.10" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ=="], 286 + "esbuild": ["esbuild@0.25.11", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.11", "@esbuild/android-arm": "0.25.11", "@esbuild/android-arm64": "0.25.11", "@esbuild/android-x64": "0.25.11", "@esbuild/darwin-arm64": "0.25.11", "@esbuild/darwin-x64": "0.25.11", "@esbuild/freebsd-arm64": "0.25.11", "@esbuild/freebsd-x64": "0.25.11", "@esbuild/linux-arm": "0.25.11", "@esbuild/linux-arm64": "0.25.11", "@esbuild/linux-ia32": "0.25.11", "@esbuild/linux-loong64": "0.25.11", "@esbuild/linux-mips64el": "0.25.11", "@esbuild/linux-ppc64": "0.25.11", "@esbuild/linux-riscv64": "0.25.11", "@esbuild/linux-s390x": "0.25.11", "@esbuild/linux-x64": "0.25.11", "@esbuild/netbsd-arm64": "0.25.11", "@esbuild/netbsd-x64": "0.25.11", "@esbuild/openbsd-arm64": "0.25.11", "@esbuild/openbsd-x64": "0.25.11", "@esbuild/openharmony-arm64": "0.25.11", "@esbuild/sunos-x64": "0.25.11", "@esbuild/win32-arm64": "0.25.11", "@esbuild/win32-ia32": "0.25.11", "@esbuild/win32-x64": "0.25.11" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q=="], 255 287 256 288 "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], 257 289 258 - "eslint": ["eslint@9.37.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.4.0", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.37.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig=="], 290 + "eslint": ["eslint@9.38.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.1", "@eslint/config-helpers": "^0.4.1", "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.38.0", "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw=="], 259 291 260 292 "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="], 261 293 ··· 475 507 476 508 "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=="], 477 509 478 - "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], 510 + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], 479 511 480 512 "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], 481 513 482 - "rollup": ["rollup@4.52.4", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.4", "@rollup/rollup-android-arm64": "4.52.4", "@rollup/rollup-darwin-arm64": "4.52.4", "@rollup/rollup-darwin-x64": "4.52.4", "@rollup/rollup-freebsd-arm64": "4.52.4", "@rollup/rollup-freebsd-x64": "4.52.4", "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", "@rollup/rollup-linux-arm-musleabihf": "4.52.4", "@rollup/rollup-linux-arm64-gnu": "4.52.4", "@rollup/rollup-linux-arm64-musl": "4.52.4", "@rollup/rollup-linux-loong64-gnu": "4.52.4", "@rollup/rollup-linux-ppc64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-gnu": "4.52.4", "@rollup/rollup-linux-riscv64-musl": "4.52.4", "@rollup/rollup-linux-s390x-gnu": "4.52.4", "@rollup/rollup-linux-x64-gnu": "4.52.4", "@rollup/rollup-linux-x64-musl": "4.52.4", "@rollup/rollup-openharmony-arm64": "4.52.4", "@rollup/rollup-win32-arm64-msvc": "4.52.4", "@rollup/rollup-win32-ia32-msvc": "4.52.4", "@rollup/rollup-win32-x64-gnu": "4.52.4", "@rollup/rollup-win32-x64-msvc": "4.52.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ=="], 514 + "rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="], 483 515 484 516 "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=="], 485 517 ··· 527 559 528 560 "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 529 561 530 - "tlds": ["tlds@1.260.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-78+28EWBhCEE7qlyaHA9OR3IPvbCLiDh3Ckla593TksfFc9vfTsgvH7eS+dr3o9qr31gwGbogcI16yN91PoRjQ=="], 562 + "tlds": ["tlds@1.261.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA=="], 531 563 532 564 "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="], 533 565 ··· 547 579 548 580 "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], 549 581 550 - "vite": ["vite@7.1.9", "", { "dependencies": { "esbuild": "^0.25.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-4nVGliEpxmhCL8DslSAUdxlB6+SMrhB0a1v5ijlh1xB1nEPuy1mxaHxysVucLHuWryAxLWg6a5ei+U4TLn/rFg=="], 582 + "vite": ["vite@7.1.12", "", { "dependencies": { "esbuild": "^0.25.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-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug=="], 551 583 552 584 "vite-plugin-full-reload": ["vite-plugin-full-reload@1.2.0", "", { "dependencies": { "picocolors": "^1.0.0", "picomatch": "^2.3.1" } }, "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA=="], 553 585
-26
web/ui-code/eslint.config.js
··· 1 - export default [ 2 - { 3 - ignores: ['node_modules/**', 'dist/**'] 4 - }, 5 - { 6 - languageOptions: { 7 - ecmaVersion: 'latest', 8 - sourceType: 'module', 9 - globals: { 10 - document: 'readonly', 11 - navigator: 'readonly', 12 - window: 'readonly', 13 - console: 'readonly' 14 - } 15 - }, 16 - linterOptions: { 17 - reportUnusedDisableDirectives: true 18 - }, 19 - rules: { 20 - 'semi': ['error', 'always'], 21 - 'quotes': ['error', 'single'], 22 - 'indent': ['error', 2], 23 - 'no-unused-vars': 'warn' 24 - } 25 - } 26 - ];
-26
web/ui-code/index.html
··· 1 - <!doctype html> 2 - <html lang="en"> 3 - <head> 4 - <meta charset="UTF-8"/> 5 - <link rel="icon" type="image/svg+xml" href="/vite.svg"/> 6 - <meta name="viewport" content="width=device-width, initial-scale=1.0"/> 7 - <title>ui-code</title> 8 - </head> 9 - <body> 10 - <div id="app"></div> 11 - <!--<script type="module" src="/src/plc-ops.js">--> 12 - 13 - 14 - <script type="module"> 15 - import {PlcOps} from './src/plc-ops.js'; 16 - 17 - const plcOps = new PlcOps(); 18 - 19 - window.runTest = async () => plcOps.test('did:plc:uc7pehijmk5jrllip4cglxdd'); 20 - </script> 21 - 22 - <button onclick="window.runTest()">Run Test</button> 23 - 24 - </body> 25 - 26 - </html>
-4287
web/ui-code/package-lock.json
··· 1 - { 2 - "name": "ui-code", 3 - "version": "0.0.0", 4 - "lockfileVersion": 3, 5 - "requires": true, 6 - "packages": { 7 - "": { 8 - "name": "ui-code", 9 - "version": "0.0.0", 10 - "dependencies": { 11 - "@atcute/cbor": "^2.2.7", 12 - "@atcute/client": "^4.0.4", 13 - "@atcute/crypto": "^2.2.5", 14 - "@atcute/did-plc": "^0.1.7", 15 - "@atcute/identity-resolver": "^1.1.3", 16 - "@atcute/multibase": "^1.1.6", 17 - "@atproto/api": "^0.16.7", 18 - "alpinejs": "^3.15.0", 19 - "vite-plugin-full-reload": "^1.2.0", 20 - "vite-rs-plugin": "1.0.1" 21 - }, 22 - "devDependencies": { 23 - "eslint": "^9.34.0", 24 - "eslint-plugin-import": "^2.32.0", 25 - "vite": "^7.1.7" 26 - } 27 - }, 28 - "node_modules/@atcute/cbor": { 29 - "version": "2.2.7", 30 - "resolved": "https://registry.npmjs.org/@atcute/cbor/-/cbor-2.2.7.tgz", 31 - "integrity": "sha512-/mwAF0gnokOphceZqFq3uzMGdd8sbw5y6bxF8CRutRkCCUcpjjpJc5fkLwhxyGgOveF3mZuHE6p7t/+IAqb7Aw==", 32 - "license": "0BSD", 33 - "dependencies": { 34 - "@atcute/cid": "^2.2.5", 35 - "@atcute/multibase": "^1.1.6", 36 - "@atcute/uint8array": "^1.0.5" 37 - } 38 - }, 39 - "node_modules/@atcute/cid": { 40 - "version": "2.2.5", 41 - "resolved": "https://registry.npmjs.org/@atcute/cid/-/cid-2.2.5.tgz", 42 - "integrity": "sha512-7SId61nMyuxSwsDI02wEZn6/gVeha2TrAN4W0UPSdSEcwQD3R2W8VU7zvR4XGfU7A/KmBnVkwx5FTfzyizKj6g==", 43 - "license": "0BSD", 44 - "dependencies": { 45 - "@atcute/multibase": "^1.1.6", 46 - "@atcute/uint8array": "^1.0.5" 47 - } 48 - }, 49 - "node_modules/@atcute/client": { 50 - "version": "4.0.5", 51 - "resolved": "https://registry.npmjs.org/@atcute/client/-/client-4.0.5.tgz", 52 - "integrity": "sha512-R8Qen8goGmEkynYGg2m6XFlVmz0GTDvQ+9w+4QqOob+XMk8/WDpF4aImev7WKEde/rV2gjcqW7zM8E6W9NShDA==", 53 - "license": "0BSD", 54 - "dependencies": { 55 - "@atcute/identity": "^1.1.1", 56 - "@atcute/lexicons": "^1.2.2" 57 - } 58 - }, 59 - "node_modules/@atcute/crypto": { 60 - "version": "2.2.5", 61 - "resolved": "https://registry.npmjs.org/@atcute/crypto/-/crypto-2.2.5.tgz", 62 - "integrity": "sha512-9CbQ9cJ68XewsbLrgdmWQS2uDD9D0hizWFJ3OOZ16TCuARREmzKEpFgHlMxPswR3bDxjwfiXzmYUlHaTqsnxRQ==", 63 - "license": "0BSD", 64 - "dependencies": { 65 - "@atcute/multibase": "^1.1.6", 66 - "@atcute/uint8array": "^1.0.5", 67 - "@noble/secp256k1": "^2.3.0" 68 - } 69 - }, 70 - "node_modules/@atcute/did-plc": { 71 - "version": "0.1.7", 72 - "resolved": "https://registry.npmjs.org/@atcute/did-plc/-/did-plc-0.1.7.tgz", 73 - "integrity": "sha512-a7yOQNqViae3rB5/xa3U0EPJbFD9l8zOHXx6XASZ5F8+Vy2uTgXK3omurpNZ5UxRpy1ni1AMhSohXr61cqWbkg==", 74 - "license": "0BSD", 75 - "dependencies": { 76 - "@atcute/cbor": "^2.2.6", 77 - "@atcute/cid": "^2.2.4", 78 - "@atcute/crypto": "^2.2.5", 79 - "@atcute/identity": "^1.1.1", 80 - "@atcute/lexicons": "^1.2.2", 81 - "@atcute/multibase": "^1.1.6", 82 - "@atcute/uint8array": "^1.0.5", 83 - "@badrap/valita": "^0.4.6" 84 - } 85 - }, 86 - "node_modules/@atcute/identity": { 87 - "version": "1.1.1", 88 - "resolved": "https://registry.npmjs.org/@atcute/identity/-/identity-1.1.1.tgz", 89 - "integrity": "sha512-zax42n693VEhnC+5tndvO2KLDTMkHOz8UExwmklvJv7R9VujfEwiSWhcv6Jgwb3ellaG8wjiQ1lMOIjLLvwh0Q==", 90 - "license": "0BSD", 91 - "dependencies": { 92 - "@atcute/lexicons": "^1.2.2", 93 - "@badrap/valita": "^0.4.6" 94 - } 95 - }, 96 - "node_modules/@atcute/identity-resolver": { 97 - "version": "1.1.4", 98 - "resolved": "https://registry.npmjs.org/@atcute/identity-resolver/-/identity-resolver-1.1.4.tgz", 99 - "integrity": "sha512-/SVh8vf2cXFJenmBnGeYF2aY3WGQm3cJeew5NWTlkqoy3LvJ5wkvKq9PWu4Tv653VF40rPOp6LOdVr9Fa+q5rA==", 100 - "license": "0BSD", 101 - "dependencies": { 102 - "@atcute/lexicons": "^1.2.2", 103 - "@atcute/util-fetch": "^1.0.3", 104 - "@badrap/valita": "^0.4.6" 105 - }, 106 - "peerDependencies": { 107 - "@atcute/identity": "^1.0.0" 108 - } 109 - }, 110 - "node_modules/@atcute/lexicons": { 111 - "version": "1.2.2", 112 - "resolved": "https://registry.npmjs.org/@atcute/lexicons/-/lexicons-1.2.2.tgz", 113 - "integrity": "sha512-bgEhJq5Z70/0TbK5sx+tAkrR8FsCODNiL2gUEvS5PuJfPxmFmRYNWaMGehxSPaXWpU2+Oa9ckceHiYbrItDTkA==", 114 - "license": "0BSD", 115 - "dependencies": { 116 - "@standard-schema/spec": "^1.0.0", 117 - "esm-env": "^1.2.2" 118 - } 119 - }, 120 - "node_modules/@atcute/multibase": { 121 - "version": "1.1.6", 122 - "resolved": "https://registry.npmjs.org/@atcute/multibase/-/multibase-1.1.6.tgz", 123 - "integrity": "sha512-HBxuCgYLKPPxETV0Rot4VP9e24vKl8JdzGCZOVsDaOXJgbRZoRIF67Lp0H/OgnJeH/Xpva8Z5ReoTNJE5dn3kg==", 124 - "license": "0BSD", 125 - "dependencies": { 126 - "@atcute/uint8array": "^1.0.5" 127 - } 128 - }, 129 - "node_modules/@atcute/uint8array": { 130 - "version": "1.0.5", 131 - "resolved": "https://registry.npmjs.org/@atcute/uint8array/-/uint8array-1.0.5.tgz", 132 - "integrity": "sha512-XLWWxoR2HNl2qU+FCr0rp1APwJXci7HnzbOQLxK55OaMNBXZ19+xNC5ii4QCsThsDxa4JS/JTzuiQLziITWf2Q==", 133 - "license": "0BSD" 134 - }, 135 - "node_modules/@atcute/util-fetch": { 136 - "version": "1.0.3", 137 - "resolved": "https://registry.npmjs.org/@atcute/util-fetch/-/util-fetch-1.0.3.tgz", 138 - "integrity": "sha512-f8zzTb/xlKIwv2OQ31DhShPUNCmIIleX6p7qIXwWwEUjX6x8skUtpdISSjnImq01LXpltGV5y8yhV4/Mlb7CRQ==", 139 - "license": "0BSD", 140 - "dependencies": { 141 - "@badrap/valita": "^0.4.6" 142 - } 143 - }, 144 - "node_modules/@atproto/api": { 145 - "version": "0.16.11", 146 - "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.16.11.tgz", 147 - "integrity": "sha512-1dhfQNHiclb102RW+Ea8Nft5olfqU0Ev/vlQaSX6mWNo1aP5zT+sPODJ8+BTUOYk3vcuvL7QMkqA/rLYy2PMyw==", 148 - "license": "MIT", 149 - "dependencies": { 150 - "@atproto/common-web": "^0.4.3", 151 - "@atproto/lexicon": "^0.5.1", 152 - "@atproto/syntax": "^0.4.1", 153 - "@atproto/xrpc": "^0.7.5", 154 - "await-lock": "^2.2.2", 155 - "multiformats": "^9.9.0", 156 - "tlds": "^1.234.0", 157 - "zod": "^3.23.8" 158 - } 159 - }, 160 - "node_modules/@atproto/common-web": { 161 - "version": "0.4.3", 162 - "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.3.tgz", 163 - "integrity": "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==", 164 - "license": "MIT", 165 - "dependencies": { 166 - "graphemer": "^1.4.0", 167 - "multiformats": "^9.9.0", 168 - "uint8arrays": "3.0.0", 169 - "zod": "^3.23.8" 170 - } 171 - }, 172 - "node_modules/@atproto/lexicon": { 173 - "version": "0.5.1", 174 - "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.5.1.tgz", 175 - "integrity": "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==", 176 - "license": "MIT", 177 - "dependencies": { 178 - "@atproto/common-web": "^0.4.3", 179 - "@atproto/syntax": "^0.4.1", 180 - "iso-datestring-validator": "^2.2.2", 181 - "multiformats": "^9.9.0", 182 - "zod": "^3.23.8" 183 - } 184 - }, 185 - "node_modules/@atproto/syntax": { 186 - "version": "0.4.1", 187 - "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.1.tgz", 188 - "integrity": "sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==", 189 - "license": "MIT" 190 - }, 191 - "node_modules/@atproto/xrpc": { 192 - "version": "0.7.5", 193 - "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.7.5.tgz", 194 - "integrity": "sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==", 195 - "license": "MIT", 196 - "dependencies": { 197 - "@atproto/lexicon": "^0.5.1", 198 - "zod": "^3.23.8" 199 - } 200 - }, 201 - "node_modules/@badrap/valita": { 202 - "version": "0.4.6", 203 - "resolved": "https://registry.npmjs.org/@badrap/valita/-/valita-0.4.6.tgz", 204 - "integrity": "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==", 205 - "license": "MIT", 206 - "engines": { 207 - "node": ">= 18" 208 - } 209 - }, 210 - "node_modules/@esbuild/aix-ppc64": { 211 - "version": "0.25.11", 212 - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", 213 - "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", 214 - "cpu": [ 215 - "ppc64" 216 - ], 217 - "license": "MIT", 218 - "optional": true, 219 - "os": [ 220 - "aix" 221 - ], 222 - "engines": { 223 - "node": ">=18" 224 - } 225 - }, 226 - "node_modules/@esbuild/android-arm": { 227 - "version": "0.25.11", 228 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", 229 - "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", 230 - "cpu": [ 231 - "arm" 232 - ], 233 - "license": "MIT", 234 - "optional": true, 235 - "os": [ 236 - "android" 237 - ], 238 - "engines": { 239 - "node": ">=18" 240 - } 241 - }, 242 - "node_modules/@esbuild/android-arm64": { 243 - "version": "0.25.11", 244 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", 245 - "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", 246 - "cpu": [ 247 - "arm64" 248 - ], 249 - "license": "MIT", 250 - "optional": true, 251 - "os": [ 252 - "android" 253 - ], 254 - "engines": { 255 - "node": ">=18" 256 - } 257 - }, 258 - "node_modules/@esbuild/android-x64": { 259 - "version": "0.25.11", 260 - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", 261 - "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", 262 - "cpu": [ 263 - "x64" 264 - ], 265 - "license": "MIT", 266 - "optional": true, 267 - "os": [ 268 - "android" 269 - ], 270 - "engines": { 271 - "node": ">=18" 272 - } 273 - }, 274 - "node_modules/@esbuild/darwin-arm64": { 275 - "version": "0.25.11", 276 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", 277 - "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", 278 - "cpu": [ 279 - "arm64" 280 - ], 281 - "license": "MIT", 282 - "optional": true, 283 - "os": [ 284 - "darwin" 285 - ], 286 - "engines": { 287 - "node": ">=18" 288 - } 289 - }, 290 - "node_modules/@esbuild/darwin-x64": { 291 - "version": "0.25.11", 292 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", 293 - "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", 294 - "cpu": [ 295 - "x64" 296 - ], 297 - "license": "MIT", 298 - "optional": true, 299 - "os": [ 300 - "darwin" 301 - ], 302 - "engines": { 303 - "node": ">=18" 304 - } 305 - }, 306 - "node_modules/@esbuild/freebsd-arm64": { 307 - "version": "0.25.11", 308 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", 309 - "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", 310 - "cpu": [ 311 - "arm64" 312 - ], 313 - "license": "MIT", 314 - "optional": true, 315 - "os": [ 316 - "freebsd" 317 - ], 318 - "engines": { 319 - "node": ">=18" 320 - } 321 - }, 322 - "node_modules/@esbuild/freebsd-x64": { 323 - "version": "0.25.11", 324 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", 325 - "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", 326 - "cpu": [ 327 - "x64" 328 - ], 329 - "license": "MIT", 330 - "optional": true, 331 - "os": [ 332 - "freebsd" 333 - ], 334 - "engines": { 335 - "node": ">=18" 336 - } 337 - }, 338 - "node_modules/@esbuild/linux-arm": { 339 - "version": "0.25.11", 340 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", 341 - "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", 342 - "cpu": [ 343 - "arm" 344 - ], 345 - "license": "MIT", 346 - "optional": true, 347 - "os": [ 348 - "linux" 349 - ], 350 - "engines": { 351 - "node": ">=18" 352 - } 353 - }, 354 - "node_modules/@esbuild/linux-arm64": { 355 - "version": "0.25.11", 356 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", 357 - "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", 358 - "cpu": [ 359 - "arm64" 360 - ], 361 - "license": "MIT", 362 - "optional": true, 363 - "os": [ 364 - "linux" 365 - ], 366 - "engines": { 367 - "node": ">=18" 368 - } 369 - }, 370 - "node_modules/@esbuild/linux-ia32": { 371 - "version": "0.25.11", 372 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", 373 - "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", 374 - "cpu": [ 375 - "ia32" 376 - ], 377 - "license": "MIT", 378 - "optional": true, 379 - "os": [ 380 - "linux" 381 - ], 382 - "engines": { 383 - "node": ">=18" 384 - } 385 - }, 386 - "node_modules/@esbuild/linux-loong64": { 387 - "version": "0.25.11", 388 - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", 389 - "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", 390 - "cpu": [ 391 - "loong64" 392 - ], 393 - "license": "MIT", 394 - "optional": true, 395 - "os": [ 396 - "linux" 397 - ], 398 - "engines": { 399 - "node": ">=18" 400 - } 401 - }, 402 - "node_modules/@esbuild/linux-mips64el": { 403 - "version": "0.25.11", 404 - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", 405 - "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", 406 - "cpu": [ 407 - "mips64el" 408 - ], 409 - "license": "MIT", 410 - "optional": true, 411 - "os": [ 412 - "linux" 413 - ], 414 - "engines": { 415 - "node": ">=18" 416 - } 417 - }, 418 - "node_modules/@esbuild/linux-ppc64": { 419 - "version": "0.25.11", 420 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", 421 - "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", 422 - "cpu": [ 423 - "ppc64" 424 - ], 425 - "license": "MIT", 426 - "optional": true, 427 - "os": [ 428 - "linux" 429 - ], 430 - "engines": { 431 - "node": ">=18" 432 - } 433 - }, 434 - "node_modules/@esbuild/linux-riscv64": { 435 - "version": "0.25.11", 436 - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", 437 - "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", 438 - "cpu": [ 439 - "riscv64" 440 - ], 441 - "license": "MIT", 442 - "optional": true, 443 - "os": [ 444 - "linux" 445 - ], 446 - "engines": { 447 - "node": ">=18" 448 - } 449 - }, 450 - "node_modules/@esbuild/linux-s390x": { 451 - "version": "0.25.11", 452 - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", 453 - "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", 454 - "cpu": [ 455 - "s390x" 456 - ], 457 - "license": "MIT", 458 - "optional": true, 459 - "os": [ 460 - "linux" 461 - ], 462 - "engines": { 463 - "node": ">=18" 464 - } 465 - }, 466 - "node_modules/@esbuild/linux-x64": { 467 - "version": "0.25.11", 468 - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", 469 - "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", 470 - "cpu": [ 471 - "x64" 472 - ], 473 - "license": "MIT", 474 - "optional": true, 475 - "os": [ 476 - "linux" 477 - ], 478 - "engines": { 479 - "node": ">=18" 480 - } 481 - }, 482 - "node_modules/@esbuild/netbsd-arm64": { 483 - "version": "0.25.11", 484 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", 485 - "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", 486 - "cpu": [ 487 - "arm64" 488 - ], 489 - "license": "MIT", 490 - "optional": true, 491 - "os": [ 492 - "netbsd" 493 - ], 494 - "engines": { 495 - "node": ">=18" 496 - } 497 - }, 498 - "node_modules/@esbuild/netbsd-x64": { 499 - "version": "0.25.11", 500 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", 501 - "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", 502 - "cpu": [ 503 - "x64" 504 - ], 505 - "license": "MIT", 506 - "optional": true, 507 - "os": [ 508 - "netbsd" 509 - ], 510 - "engines": { 511 - "node": ">=18" 512 - } 513 - }, 514 - "node_modules/@esbuild/openbsd-arm64": { 515 - "version": "0.25.11", 516 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", 517 - "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", 518 - "cpu": [ 519 - "arm64" 520 - ], 521 - "license": "MIT", 522 - "optional": true, 523 - "os": [ 524 - "openbsd" 525 - ], 526 - "engines": { 527 - "node": ">=18" 528 - } 529 - }, 530 - "node_modules/@esbuild/openbsd-x64": { 531 - "version": "0.25.11", 532 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", 533 - "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", 534 - "cpu": [ 535 - "x64" 536 - ], 537 - "license": "MIT", 538 - "optional": true, 539 - "os": [ 540 - "openbsd" 541 - ], 542 - "engines": { 543 - "node": ">=18" 544 - } 545 - }, 546 - "node_modules/@esbuild/openharmony-arm64": { 547 - "version": "0.25.11", 548 - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", 549 - "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", 550 - "cpu": [ 551 - "arm64" 552 - ], 553 - "license": "MIT", 554 - "optional": true, 555 - "os": [ 556 - "openharmony" 557 - ], 558 - "engines": { 559 - "node": ">=18" 560 - } 561 - }, 562 - "node_modules/@esbuild/sunos-x64": { 563 - "version": "0.25.11", 564 - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", 565 - "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", 566 - "cpu": [ 567 - "x64" 568 - ], 569 - "license": "MIT", 570 - "optional": true, 571 - "os": [ 572 - "sunos" 573 - ], 574 - "engines": { 575 - "node": ">=18" 576 - } 577 - }, 578 - "node_modules/@esbuild/win32-arm64": { 579 - "version": "0.25.11", 580 - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", 581 - "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", 582 - "cpu": [ 583 - "arm64" 584 - ], 585 - "license": "MIT", 586 - "optional": true, 587 - "os": [ 588 - "win32" 589 - ], 590 - "engines": { 591 - "node": ">=18" 592 - } 593 - }, 594 - "node_modules/@esbuild/win32-ia32": { 595 - "version": "0.25.11", 596 - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", 597 - "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", 598 - "cpu": [ 599 - "ia32" 600 - ], 601 - "license": "MIT", 602 - "optional": true, 603 - "os": [ 604 - "win32" 605 - ], 606 - "engines": { 607 - "node": ">=18" 608 - } 609 - }, 610 - "node_modules/@esbuild/win32-x64": { 611 - "version": "0.25.11", 612 - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", 613 - "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", 614 - "cpu": [ 615 - "x64" 616 - ], 617 - "license": "MIT", 618 - "optional": true, 619 - "os": [ 620 - "win32" 621 - ], 622 - "engines": { 623 - "node": ">=18" 624 - } 625 - }, 626 - "node_modules/@eslint-community/eslint-utils": { 627 - "version": "4.9.0", 628 - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", 629 - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", 630 - "dev": true, 631 - "license": "MIT", 632 - "dependencies": { 633 - "eslint-visitor-keys": "^3.4.3" 634 - }, 635 - "engines": { 636 - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 637 - }, 638 - "funding": { 639 - "url": "https://opencollective.com/eslint" 640 - }, 641 - "peerDependencies": { 642 - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 643 - } 644 - }, 645 - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { 646 - "version": "3.4.3", 647 - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 648 - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 649 - "dev": true, 650 - "license": "Apache-2.0", 651 - "engines": { 652 - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 653 - }, 654 - "funding": { 655 - "url": "https://opencollective.com/eslint" 656 - } 657 - }, 658 - "node_modules/@eslint-community/regexpp": { 659 - "version": "4.12.1", 660 - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", 661 - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", 662 - "dev": true, 663 - "license": "MIT", 664 - "engines": { 665 - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 666 - } 667 - }, 668 - "node_modules/@eslint/config-array": { 669 - "version": "0.21.1", 670 - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", 671 - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", 672 - "dev": true, 673 - "license": "Apache-2.0", 674 - "dependencies": { 675 - "@eslint/object-schema": "^2.1.7", 676 - "debug": "^4.3.1", 677 - "minimatch": "^3.1.2" 678 - }, 679 - "engines": { 680 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 681 - } 682 - }, 683 - "node_modules/@eslint/config-helpers": { 684 - "version": "0.4.1", 685 - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", 686 - "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", 687 - "dev": true, 688 - "license": "Apache-2.0", 689 - "dependencies": { 690 - "@eslint/core": "^0.16.0" 691 - }, 692 - "engines": { 693 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 694 - } 695 - }, 696 - "node_modules/@eslint/core": { 697 - "version": "0.16.0", 698 - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", 699 - "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", 700 - "dev": true, 701 - "license": "Apache-2.0", 702 - "dependencies": { 703 - "@types/json-schema": "^7.0.15" 704 - }, 705 - "engines": { 706 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 707 - } 708 - }, 709 - "node_modules/@eslint/eslintrc": { 710 - "version": "3.3.1", 711 - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", 712 - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", 713 - "dev": true, 714 - "license": "MIT", 715 - "dependencies": { 716 - "ajv": "^6.12.4", 717 - "debug": "^4.3.2", 718 - "espree": "^10.0.1", 719 - "globals": "^14.0.0", 720 - "ignore": "^5.2.0", 721 - "import-fresh": "^3.2.1", 722 - "js-yaml": "^4.1.0", 723 - "minimatch": "^3.1.2", 724 - "strip-json-comments": "^3.1.1" 725 - }, 726 - "engines": { 727 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 728 - }, 729 - "funding": { 730 - "url": "https://opencollective.com/eslint" 731 - } 732 - }, 733 - "node_modules/@eslint/js": { 734 - "version": "9.38.0", 735 - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", 736 - "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", 737 - "dev": true, 738 - "license": "MIT", 739 - "engines": { 740 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 741 - }, 742 - "funding": { 743 - "url": "https://eslint.org/donate" 744 - } 745 - }, 746 - "node_modules/@eslint/object-schema": { 747 - "version": "2.1.7", 748 - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", 749 - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", 750 - "dev": true, 751 - "license": "Apache-2.0", 752 - "engines": { 753 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 754 - } 755 - }, 756 - "node_modules/@eslint/plugin-kit": { 757 - "version": "0.4.0", 758 - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", 759 - "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", 760 - "dev": true, 761 - "license": "Apache-2.0", 762 - "dependencies": { 763 - "@eslint/core": "^0.16.0", 764 - "levn": "^0.4.1" 765 - }, 766 - "engines": { 767 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 768 - } 769 - }, 770 - "node_modules/@humanfs/core": { 771 - "version": "0.19.1", 772 - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", 773 - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", 774 - "dev": true, 775 - "license": "Apache-2.0", 776 - "engines": { 777 - "node": ">=18.18.0" 778 - } 779 - }, 780 - "node_modules/@humanfs/node": { 781 - "version": "0.16.7", 782 - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", 783 - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", 784 - "dev": true, 785 - "license": "Apache-2.0", 786 - "dependencies": { 787 - "@humanfs/core": "^0.19.1", 788 - "@humanwhocodes/retry": "^0.4.0" 789 - }, 790 - "engines": { 791 - "node": ">=18.18.0" 792 - } 793 - }, 794 - "node_modules/@humanwhocodes/module-importer": { 795 - "version": "1.0.1", 796 - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 797 - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 798 - "dev": true, 799 - "license": "Apache-2.0", 800 - "engines": { 801 - "node": ">=12.22" 802 - }, 803 - "funding": { 804 - "type": "github", 805 - "url": "https://github.com/sponsors/nzakas" 806 - } 807 - }, 808 - "node_modules/@humanwhocodes/retry": { 809 - "version": "0.4.3", 810 - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", 811 - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", 812 - "dev": true, 813 - "license": "Apache-2.0", 814 - "engines": { 815 - "node": ">=18.18" 816 - }, 817 - "funding": { 818 - "type": "github", 819 - "url": "https://github.com/sponsors/nzakas" 820 - } 821 - }, 822 - "node_modules/@noble/secp256k1": { 823 - "version": "2.3.0", 824 - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-2.3.0.tgz", 825 - "integrity": "sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw==", 826 - "license": "MIT", 827 - "funding": { 828 - "url": "https://paulmillr.com/funding/" 829 - } 830 - }, 831 - "node_modules/@rollup/rollup-android-arm-eabi": { 832 - "version": "4.52.5", 833 - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", 834 - "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", 835 - "cpu": [ 836 - "arm" 837 - ], 838 - "license": "MIT", 839 - "optional": true, 840 - "os": [ 841 - "android" 842 - ] 843 - }, 844 - "node_modules/@rollup/rollup-android-arm64": { 845 - "version": "4.52.5", 846 - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", 847 - "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", 848 - "cpu": [ 849 - "arm64" 850 - ], 851 - "license": "MIT", 852 - "optional": true, 853 - "os": [ 854 - "android" 855 - ] 856 - }, 857 - "node_modules/@rollup/rollup-darwin-arm64": { 858 - "version": "4.52.5", 859 - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", 860 - "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", 861 - "cpu": [ 862 - "arm64" 863 - ], 864 - "license": "MIT", 865 - "optional": true, 866 - "os": [ 867 - "darwin" 868 - ] 869 - }, 870 - "node_modules/@rollup/rollup-darwin-x64": { 871 - "version": "4.52.5", 872 - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", 873 - "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", 874 - "cpu": [ 875 - "x64" 876 - ], 877 - "license": "MIT", 878 - "optional": true, 879 - "os": [ 880 - "darwin" 881 - ] 882 - }, 883 - "node_modules/@rollup/rollup-freebsd-arm64": { 884 - "version": "4.52.5", 885 - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", 886 - "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", 887 - "cpu": [ 888 - "arm64" 889 - ], 890 - "license": "MIT", 891 - "optional": true, 892 - "os": [ 893 - "freebsd" 894 - ] 895 - }, 896 - "node_modules/@rollup/rollup-freebsd-x64": { 897 - "version": "4.52.5", 898 - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", 899 - "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", 900 - "cpu": [ 901 - "x64" 902 - ], 903 - "license": "MIT", 904 - "optional": true, 905 - "os": [ 906 - "freebsd" 907 - ] 908 - }, 909 - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 910 - "version": "4.52.5", 911 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", 912 - "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", 913 - "cpu": [ 914 - "arm" 915 - ], 916 - "license": "MIT", 917 - "optional": true, 918 - "os": [ 919 - "linux" 920 - ] 921 - }, 922 - "node_modules/@rollup/rollup-linux-arm-musleabihf": { 923 - "version": "4.52.5", 924 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", 925 - "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", 926 - "cpu": [ 927 - "arm" 928 - ], 929 - "license": "MIT", 930 - "optional": true, 931 - "os": [ 932 - "linux" 933 - ] 934 - }, 935 - "node_modules/@rollup/rollup-linux-arm64-gnu": { 936 - "version": "4.52.5", 937 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", 938 - "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", 939 - "cpu": [ 940 - "arm64" 941 - ], 942 - "license": "MIT", 943 - "optional": true, 944 - "os": [ 945 - "linux" 946 - ] 947 - }, 948 - "node_modules/@rollup/rollup-linux-arm64-musl": { 949 - "version": "4.52.5", 950 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", 951 - "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", 952 - "cpu": [ 953 - "arm64" 954 - ], 955 - "license": "MIT", 956 - "optional": true, 957 - "os": [ 958 - "linux" 959 - ] 960 - }, 961 - "node_modules/@rollup/rollup-linux-loong64-gnu": { 962 - "version": "4.52.5", 963 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", 964 - "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", 965 - "cpu": [ 966 - "loong64" 967 - ], 968 - "license": "MIT", 969 - "optional": true, 970 - "os": [ 971 - "linux" 972 - ] 973 - }, 974 - "node_modules/@rollup/rollup-linux-ppc64-gnu": { 975 - "version": "4.52.5", 976 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", 977 - "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", 978 - "cpu": [ 979 - "ppc64" 980 - ], 981 - "license": "MIT", 982 - "optional": true, 983 - "os": [ 984 - "linux" 985 - ] 986 - }, 987 - "node_modules/@rollup/rollup-linux-riscv64-gnu": { 988 - "version": "4.52.5", 989 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", 990 - "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", 991 - "cpu": [ 992 - "riscv64" 993 - ], 994 - "license": "MIT", 995 - "optional": true, 996 - "os": [ 997 - "linux" 998 - ] 999 - }, 1000 - "node_modules/@rollup/rollup-linux-riscv64-musl": { 1001 - "version": "4.52.5", 1002 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", 1003 - "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", 1004 - "cpu": [ 1005 - "riscv64" 1006 - ], 1007 - "license": "MIT", 1008 - "optional": true, 1009 - "os": [ 1010 - "linux" 1011 - ] 1012 - }, 1013 - "node_modules/@rollup/rollup-linux-s390x-gnu": { 1014 - "version": "4.52.5", 1015 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", 1016 - "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", 1017 - "cpu": [ 1018 - "s390x" 1019 - ], 1020 - "license": "MIT", 1021 - "optional": true, 1022 - "os": [ 1023 - "linux" 1024 - ] 1025 - }, 1026 - "node_modules/@rollup/rollup-linux-x64-gnu": { 1027 - "version": "4.52.5", 1028 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", 1029 - "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", 1030 - "cpu": [ 1031 - "x64" 1032 - ], 1033 - "license": "MIT", 1034 - "optional": true, 1035 - "os": [ 1036 - "linux" 1037 - ] 1038 - }, 1039 - "node_modules/@rollup/rollup-linux-x64-musl": { 1040 - "version": "4.52.5", 1041 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", 1042 - "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", 1043 - "cpu": [ 1044 - "x64" 1045 - ], 1046 - "license": "MIT", 1047 - "optional": true, 1048 - "os": [ 1049 - "linux" 1050 - ] 1051 - }, 1052 - "node_modules/@rollup/rollup-openharmony-arm64": { 1053 - "version": "4.52.5", 1054 - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", 1055 - "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", 1056 - "cpu": [ 1057 - "arm64" 1058 - ], 1059 - "license": "MIT", 1060 - "optional": true, 1061 - "os": [ 1062 - "openharmony" 1063 - ] 1064 - }, 1065 - "node_modules/@rollup/rollup-win32-arm64-msvc": { 1066 - "version": "4.52.5", 1067 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", 1068 - "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", 1069 - "cpu": [ 1070 - "arm64" 1071 - ], 1072 - "license": "MIT", 1073 - "optional": true, 1074 - "os": [ 1075 - "win32" 1076 - ] 1077 - }, 1078 - "node_modules/@rollup/rollup-win32-ia32-msvc": { 1079 - "version": "4.52.5", 1080 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", 1081 - "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", 1082 - "cpu": [ 1083 - "ia32" 1084 - ], 1085 - "license": "MIT", 1086 - "optional": true, 1087 - "os": [ 1088 - "win32" 1089 - ] 1090 - }, 1091 - "node_modules/@rollup/rollup-win32-x64-gnu": { 1092 - "version": "4.52.5", 1093 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", 1094 - "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", 1095 - "cpu": [ 1096 - "x64" 1097 - ], 1098 - "license": "MIT", 1099 - "optional": true, 1100 - "os": [ 1101 - "win32" 1102 - ] 1103 - }, 1104 - "node_modules/@rollup/rollup-win32-x64-msvc": { 1105 - "version": "4.52.5", 1106 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", 1107 - "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", 1108 - "cpu": [ 1109 - "x64" 1110 - ], 1111 - "license": "MIT", 1112 - "optional": true, 1113 - "os": [ 1114 - "win32" 1115 - ] 1116 - }, 1117 - "node_modules/@rtsao/scc": { 1118 - "version": "1.1.0", 1119 - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", 1120 - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", 1121 - "dev": true, 1122 - "license": "MIT" 1123 - }, 1124 - "node_modules/@standard-schema/spec": { 1125 - "version": "1.0.0", 1126 - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", 1127 - "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", 1128 - "license": "MIT" 1129 - }, 1130 - "node_modules/@types/estree": { 1131 - "version": "1.0.8", 1132 - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", 1133 - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", 1134 - "license": "MIT" 1135 - }, 1136 - "node_modules/@types/json-schema": { 1137 - "version": "7.0.15", 1138 - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 1139 - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 1140 - "dev": true, 1141 - "license": "MIT" 1142 - }, 1143 - "node_modules/@types/json5": { 1144 - "version": "0.0.29", 1145 - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", 1146 - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", 1147 - "dev": true, 1148 - "license": "MIT" 1149 - }, 1150 - "node_modules/@vue/reactivity": { 1151 - "version": "3.1.5", 1152 - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", 1153 - "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", 1154 - "license": "MIT", 1155 - "dependencies": { 1156 - "@vue/shared": "3.1.5" 1157 - } 1158 - }, 1159 - "node_modules/@vue/shared": { 1160 - "version": "3.1.5", 1161 - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", 1162 - "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", 1163 - "license": "MIT" 1164 - }, 1165 - "node_modules/acorn": { 1166 - "version": "8.15.0", 1167 - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", 1168 - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", 1169 - "dev": true, 1170 - "license": "MIT", 1171 - "bin": { 1172 - "acorn": "bin/acorn" 1173 - }, 1174 - "engines": { 1175 - "node": ">=0.4.0" 1176 - } 1177 - }, 1178 - "node_modules/acorn-jsx": { 1179 - "version": "5.3.2", 1180 - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 1181 - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 1182 - "dev": true, 1183 - "license": "MIT", 1184 - "peerDependencies": { 1185 - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 1186 - } 1187 - }, 1188 - "node_modules/ajv": { 1189 - "version": "6.12.6", 1190 - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 1191 - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 1192 - "dev": true, 1193 - "license": "MIT", 1194 - "dependencies": { 1195 - "fast-deep-equal": "^3.1.1", 1196 - "fast-json-stable-stringify": "^2.0.0", 1197 - "json-schema-traverse": "^0.4.1", 1198 - "uri-js": "^4.2.2" 1199 - }, 1200 - "funding": { 1201 - "type": "github", 1202 - "url": "https://github.com/sponsors/epoberezkin" 1203 - } 1204 - }, 1205 - "node_modules/alpinejs": { 1206 - "version": "3.15.0", 1207 - "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.15.0.tgz", 1208 - "integrity": "sha512-lpokA5okCF1BKh10LG8YjqhfpxyHBk4gE7boIgVHltJzYoM7O9nK3M7VlntLEJGsVmu7U/RzUWajmHREGT38Eg==", 1209 - "license": "MIT", 1210 - "dependencies": { 1211 - "@vue/reactivity": "~3.1.1" 1212 - } 1213 - }, 1214 - "node_modules/ansi-styles": { 1215 - "version": "4.3.0", 1216 - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 1217 - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 1218 - "dev": true, 1219 - "license": "MIT", 1220 - "dependencies": { 1221 - "color-convert": "^2.0.1" 1222 - }, 1223 - "engines": { 1224 - "node": ">=8" 1225 - }, 1226 - "funding": { 1227 - "url": "https://github.com/chalk/ansi-styles?sponsor=1" 1228 - } 1229 - }, 1230 - "node_modules/argparse": { 1231 - "version": "2.0.1", 1232 - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 1233 - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 1234 - "dev": true, 1235 - "license": "Python-2.0" 1236 - }, 1237 - "node_modules/array-buffer-byte-length": { 1238 - "version": "1.0.2", 1239 - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", 1240 - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", 1241 - "dev": true, 1242 - "license": "MIT", 1243 - "dependencies": { 1244 - "call-bound": "^1.0.3", 1245 - "is-array-buffer": "^3.0.5" 1246 - }, 1247 - "engines": { 1248 - "node": ">= 0.4" 1249 - }, 1250 - "funding": { 1251 - "url": "https://github.com/sponsors/ljharb" 1252 - } 1253 - }, 1254 - "node_modules/array-includes": { 1255 - "version": "3.1.9", 1256 - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", 1257 - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", 1258 - "dev": true, 1259 - "license": "MIT", 1260 - "dependencies": { 1261 - "call-bind": "^1.0.8", 1262 - "call-bound": "^1.0.4", 1263 - "define-properties": "^1.2.1", 1264 - "es-abstract": "^1.24.0", 1265 - "es-object-atoms": "^1.1.1", 1266 - "get-intrinsic": "^1.3.0", 1267 - "is-string": "^1.1.1", 1268 - "math-intrinsics": "^1.1.0" 1269 - }, 1270 - "engines": { 1271 - "node": ">= 0.4" 1272 - }, 1273 - "funding": { 1274 - "url": "https://github.com/sponsors/ljharb" 1275 - } 1276 - }, 1277 - "node_modules/array.prototype.findlastindex": { 1278 - "version": "1.2.6", 1279 - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", 1280 - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", 1281 - "dev": true, 1282 - "license": "MIT", 1283 - "dependencies": { 1284 - "call-bind": "^1.0.8", 1285 - "call-bound": "^1.0.4", 1286 - "define-properties": "^1.2.1", 1287 - "es-abstract": "^1.23.9", 1288 - "es-errors": "^1.3.0", 1289 - "es-object-atoms": "^1.1.1", 1290 - "es-shim-unscopables": "^1.1.0" 1291 - }, 1292 - "engines": { 1293 - "node": ">= 0.4" 1294 - }, 1295 - "funding": { 1296 - "url": "https://github.com/sponsors/ljharb" 1297 - } 1298 - }, 1299 - "node_modules/array.prototype.flat": { 1300 - "version": "1.3.3", 1301 - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", 1302 - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", 1303 - "dev": true, 1304 - "license": "MIT", 1305 - "dependencies": { 1306 - "call-bind": "^1.0.8", 1307 - "define-properties": "^1.2.1", 1308 - "es-abstract": "^1.23.5", 1309 - "es-shim-unscopables": "^1.0.2" 1310 - }, 1311 - "engines": { 1312 - "node": ">= 0.4" 1313 - }, 1314 - "funding": { 1315 - "url": "https://github.com/sponsors/ljharb" 1316 - } 1317 - }, 1318 - "node_modules/array.prototype.flatmap": { 1319 - "version": "1.3.3", 1320 - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", 1321 - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", 1322 - "dev": true, 1323 - "license": "MIT", 1324 - "dependencies": { 1325 - "call-bind": "^1.0.8", 1326 - "define-properties": "^1.2.1", 1327 - "es-abstract": "^1.23.5", 1328 - "es-shim-unscopables": "^1.0.2" 1329 - }, 1330 - "engines": { 1331 - "node": ">= 0.4" 1332 - }, 1333 - "funding": { 1334 - "url": "https://github.com/sponsors/ljharb" 1335 - } 1336 - }, 1337 - "node_modules/arraybuffer.prototype.slice": { 1338 - "version": "1.0.4", 1339 - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", 1340 - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", 1341 - "dev": true, 1342 - "license": "MIT", 1343 - "dependencies": { 1344 - "array-buffer-byte-length": "^1.0.1", 1345 - "call-bind": "^1.0.8", 1346 - "define-properties": "^1.2.1", 1347 - "es-abstract": "^1.23.5", 1348 - "es-errors": "^1.3.0", 1349 - "get-intrinsic": "^1.2.6", 1350 - "is-array-buffer": "^3.0.4" 1351 - }, 1352 - "engines": { 1353 - "node": ">= 0.4" 1354 - }, 1355 - "funding": { 1356 - "url": "https://github.com/sponsors/ljharb" 1357 - } 1358 - }, 1359 - "node_modules/async-function": { 1360 - "version": "1.0.0", 1361 - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", 1362 - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", 1363 - "dev": true, 1364 - "license": "MIT", 1365 - "engines": { 1366 - "node": ">= 0.4" 1367 - } 1368 - }, 1369 - "node_modules/available-typed-arrays": { 1370 - "version": "1.0.7", 1371 - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", 1372 - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", 1373 - "dev": true, 1374 - "license": "MIT", 1375 - "dependencies": { 1376 - "possible-typed-array-names": "^1.0.0" 1377 - }, 1378 - "engines": { 1379 - "node": ">= 0.4" 1380 - }, 1381 - "funding": { 1382 - "url": "https://github.com/sponsors/ljharb" 1383 - } 1384 - }, 1385 - "node_modules/await-lock": { 1386 - "version": "2.2.2", 1387 - "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", 1388 - "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==", 1389 - "license": "MIT" 1390 - }, 1391 - "node_modules/balanced-match": { 1392 - "version": "1.0.2", 1393 - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 1394 - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 1395 - "dev": true, 1396 - "license": "MIT" 1397 - }, 1398 - "node_modules/brace-expansion": { 1399 - "version": "1.1.12", 1400 - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", 1401 - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", 1402 - "dev": true, 1403 - "license": "MIT", 1404 - "dependencies": { 1405 - "balanced-match": "^1.0.0", 1406 - "concat-map": "0.0.1" 1407 - } 1408 - }, 1409 - "node_modules/call-bind": { 1410 - "version": "1.0.8", 1411 - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", 1412 - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", 1413 - "dev": true, 1414 - "license": "MIT", 1415 - "dependencies": { 1416 - "call-bind-apply-helpers": "^1.0.0", 1417 - "es-define-property": "^1.0.0", 1418 - "get-intrinsic": "^1.2.4", 1419 - "set-function-length": "^1.2.2" 1420 - }, 1421 - "engines": { 1422 - "node": ">= 0.4" 1423 - }, 1424 - "funding": { 1425 - "url": "https://github.com/sponsors/ljharb" 1426 - } 1427 - }, 1428 - "node_modules/call-bind-apply-helpers": { 1429 - "version": "1.0.2", 1430 - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 1431 - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 1432 - "dev": true, 1433 - "license": "MIT", 1434 - "dependencies": { 1435 - "es-errors": "^1.3.0", 1436 - "function-bind": "^1.1.2" 1437 - }, 1438 - "engines": { 1439 - "node": ">= 0.4" 1440 - } 1441 - }, 1442 - "node_modules/call-bound": { 1443 - "version": "1.0.4", 1444 - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", 1445 - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", 1446 - "dev": true, 1447 - "license": "MIT", 1448 - "dependencies": { 1449 - "call-bind-apply-helpers": "^1.0.2", 1450 - "get-intrinsic": "^1.3.0" 1451 - }, 1452 - "engines": { 1453 - "node": ">= 0.4" 1454 - }, 1455 - "funding": { 1456 - "url": "https://github.com/sponsors/ljharb" 1457 - } 1458 - }, 1459 - "node_modules/callsites": { 1460 - "version": "3.1.0", 1461 - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 1462 - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 1463 - "dev": true, 1464 - "license": "MIT", 1465 - "engines": { 1466 - "node": ">=6" 1467 - } 1468 - }, 1469 - "node_modules/chalk": { 1470 - "version": "4.1.2", 1471 - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 1472 - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 1473 - "dev": true, 1474 - "license": "MIT", 1475 - "dependencies": { 1476 - "ansi-styles": "^4.1.0", 1477 - "supports-color": "^7.1.0" 1478 - }, 1479 - "engines": { 1480 - "node": ">=10" 1481 - }, 1482 - "funding": { 1483 - "url": "https://github.com/chalk/chalk?sponsor=1" 1484 - } 1485 - }, 1486 - "node_modules/color-convert": { 1487 - "version": "2.0.1", 1488 - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1489 - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1490 - "dev": true, 1491 - "license": "MIT", 1492 - "dependencies": { 1493 - "color-name": "~1.1.4" 1494 - }, 1495 - "engines": { 1496 - "node": ">=7.0.0" 1497 - } 1498 - }, 1499 - "node_modules/color-name": { 1500 - "version": "1.1.4", 1501 - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1502 - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1503 - "dev": true, 1504 - "license": "MIT" 1505 - }, 1506 - "node_modules/concat-map": { 1507 - "version": "0.0.1", 1508 - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 1509 - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 1510 - "dev": true, 1511 - "license": "MIT" 1512 - }, 1513 - "node_modules/cross-spawn": { 1514 - "version": "7.0.6", 1515 - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 1516 - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 1517 - "dev": true, 1518 - "license": "MIT", 1519 - "dependencies": { 1520 - "path-key": "^3.1.0", 1521 - "shebang-command": "^2.0.0", 1522 - "which": "^2.0.1" 1523 - }, 1524 - "engines": { 1525 - "node": ">= 8" 1526 - } 1527 - }, 1528 - "node_modules/data-view-buffer": { 1529 - "version": "1.0.2", 1530 - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", 1531 - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", 1532 - "dev": true, 1533 - "license": "MIT", 1534 - "dependencies": { 1535 - "call-bound": "^1.0.3", 1536 - "es-errors": "^1.3.0", 1537 - "is-data-view": "^1.0.2" 1538 - }, 1539 - "engines": { 1540 - "node": ">= 0.4" 1541 - }, 1542 - "funding": { 1543 - "url": "https://github.com/sponsors/ljharb" 1544 - } 1545 - }, 1546 - "node_modules/data-view-byte-length": { 1547 - "version": "1.0.2", 1548 - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", 1549 - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", 1550 - "dev": true, 1551 - "license": "MIT", 1552 - "dependencies": { 1553 - "call-bound": "^1.0.3", 1554 - "es-errors": "^1.3.0", 1555 - "is-data-view": "^1.0.2" 1556 - }, 1557 - "engines": { 1558 - "node": ">= 0.4" 1559 - }, 1560 - "funding": { 1561 - "url": "https://github.com/sponsors/inspect-js" 1562 - } 1563 - }, 1564 - "node_modules/data-view-byte-offset": { 1565 - "version": "1.0.1", 1566 - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", 1567 - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", 1568 - "dev": true, 1569 - "license": "MIT", 1570 - "dependencies": { 1571 - "call-bound": "^1.0.2", 1572 - "es-errors": "^1.3.0", 1573 - "is-data-view": "^1.0.1" 1574 - }, 1575 - "engines": { 1576 - "node": ">= 0.4" 1577 - }, 1578 - "funding": { 1579 - "url": "https://github.com/sponsors/ljharb" 1580 - } 1581 - }, 1582 - "node_modules/debug": { 1583 - "version": "4.4.3", 1584 - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", 1585 - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", 1586 - "dev": true, 1587 - "license": "MIT", 1588 - "dependencies": { 1589 - "ms": "^2.1.3" 1590 - }, 1591 - "engines": { 1592 - "node": ">=6.0" 1593 - }, 1594 - "peerDependenciesMeta": { 1595 - "supports-color": { 1596 - "optional": true 1597 - } 1598 - } 1599 - }, 1600 - "node_modules/deep-is": { 1601 - "version": "0.1.4", 1602 - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 1603 - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 1604 - "dev": true, 1605 - "license": "MIT" 1606 - }, 1607 - "node_modules/define-data-property": { 1608 - "version": "1.1.4", 1609 - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 1610 - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 1611 - "dev": true, 1612 - "license": "MIT", 1613 - "dependencies": { 1614 - "es-define-property": "^1.0.0", 1615 - "es-errors": "^1.3.0", 1616 - "gopd": "^1.0.1" 1617 - }, 1618 - "engines": { 1619 - "node": ">= 0.4" 1620 - }, 1621 - "funding": { 1622 - "url": "https://github.com/sponsors/ljharb" 1623 - } 1624 - }, 1625 - "node_modules/define-properties": { 1626 - "version": "1.2.1", 1627 - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", 1628 - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", 1629 - "dev": true, 1630 - "license": "MIT", 1631 - "dependencies": { 1632 - "define-data-property": "^1.0.1", 1633 - "has-property-descriptors": "^1.0.0", 1634 - "object-keys": "^1.1.1" 1635 - }, 1636 - "engines": { 1637 - "node": ">= 0.4" 1638 - }, 1639 - "funding": { 1640 - "url": "https://github.com/sponsors/ljharb" 1641 - } 1642 - }, 1643 - "node_modules/doctrine": { 1644 - "version": "2.1.0", 1645 - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", 1646 - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", 1647 - "dev": true, 1648 - "license": "Apache-2.0", 1649 - "dependencies": { 1650 - "esutils": "^2.0.2" 1651 - }, 1652 - "engines": { 1653 - "node": ">=0.10.0" 1654 - } 1655 - }, 1656 - "node_modules/dunder-proto": { 1657 - "version": "1.0.1", 1658 - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 1659 - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 1660 - "dev": true, 1661 - "license": "MIT", 1662 - "dependencies": { 1663 - "call-bind-apply-helpers": "^1.0.1", 1664 - "es-errors": "^1.3.0", 1665 - "gopd": "^1.2.0" 1666 - }, 1667 - "engines": { 1668 - "node": ">= 0.4" 1669 - } 1670 - }, 1671 - "node_modules/es-abstract": { 1672 - "version": "1.24.0", 1673 - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", 1674 - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", 1675 - "dev": true, 1676 - "license": "MIT", 1677 - "dependencies": { 1678 - "array-buffer-byte-length": "^1.0.2", 1679 - "arraybuffer.prototype.slice": "^1.0.4", 1680 - "available-typed-arrays": "^1.0.7", 1681 - "call-bind": "^1.0.8", 1682 - "call-bound": "^1.0.4", 1683 - "data-view-buffer": "^1.0.2", 1684 - "data-view-byte-length": "^1.0.2", 1685 - "data-view-byte-offset": "^1.0.1", 1686 - "es-define-property": "^1.0.1", 1687 - "es-errors": "^1.3.0", 1688 - "es-object-atoms": "^1.1.1", 1689 - "es-set-tostringtag": "^2.1.0", 1690 - "es-to-primitive": "^1.3.0", 1691 - "function.prototype.name": "^1.1.8", 1692 - "get-intrinsic": "^1.3.0", 1693 - "get-proto": "^1.0.1", 1694 - "get-symbol-description": "^1.1.0", 1695 - "globalthis": "^1.0.4", 1696 - "gopd": "^1.2.0", 1697 - "has-property-descriptors": "^1.0.2", 1698 - "has-proto": "^1.2.0", 1699 - "has-symbols": "^1.1.0", 1700 - "hasown": "^2.0.2", 1701 - "internal-slot": "^1.1.0", 1702 - "is-array-buffer": "^3.0.5", 1703 - "is-callable": "^1.2.7", 1704 - "is-data-view": "^1.0.2", 1705 - "is-negative-zero": "^2.0.3", 1706 - "is-regex": "^1.2.1", 1707 - "is-set": "^2.0.3", 1708 - "is-shared-array-buffer": "^1.0.4", 1709 - "is-string": "^1.1.1", 1710 - "is-typed-array": "^1.1.15", 1711 - "is-weakref": "^1.1.1", 1712 - "math-intrinsics": "^1.1.0", 1713 - "object-inspect": "^1.13.4", 1714 - "object-keys": "^1.1.1", 1715 - "object.assign": "^4.1.7", 1716 - "own-keys": "^1.0.1", 1717 - "regexp.prototype.flags": "^1.5.4", 1718 - "safe-array-concat": "^1.1.3", 1719 - "safe-push-apply": "^1.0.0", 1720 - "safe-regex-test": "^1.1.0", 1721 - "set-proto": "^1.0.0", 1722 - "stop-iteration-iterator": "^1.1.0", 1723 - "string.prototype.trim": "^1.2.10", 1724 - "string.prototype.trimend": "^1.0.9", 1725 - "string.prototype.trimstart": "^1.0.8", 1726 - "typed-array-buffer": "^1.0.3", 1727 - "typed-array-byte-length": "^1.0.3", 1728 - "typed-array-byte-offset": "^1.0.4", 1729 - "typed-array-length": "^1.0.7", 1730 - "unbox-primitive": "^1.1.0", 1731 - "which-typed-array": "^1.1.19" 1732 - }, 1733 - "engines": { 1734 - "node": ">= 0.4" 1735 - }, 1736 - "funding": { 1737 - "url": "https://github.com/sponsors/ljharb" 1738 - } 1739 - }, 1740 - "node_modules/es-define-property": { 1741 - "version": "1.0.1", 1742 - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 1743 - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 1744 - "dev": true, 1745 - "license": "MIT", 1746 - "engines": { 1747 - "node": ">= 0.4" 1748 - } 1749 - }, 1750 - "node_modules/es-errors": { 1751 - "version": "1.3.0", 1752 - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 1753 - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 1754 - "dev": true, 1755 - "license": "MIT", 1756 - "engines": { 1757 - "node": ">= 0.4" 1758 - } 1759 - }, 1760 - "node_modules/es-object-atoms": { 1761 - "version": "1.1.1", 1762 - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 1763 - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 1764 - "dev": true, 1765 - "license": "MIT", 1766 - "dependencies": { 1767 - "es-errors": "^1.3.0" 1768 - }, 1769 - "engines": { 1770 - "node": ">= 0.4" 1771 - } 1772 - }, 1773 - "node_modules/es-set-tostringtag": { 1774 - "version": "2.1.0", 1775 - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", 1776 - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", 1777 - "dev": true, 1778 - "license": "MIT", 1779 - "dependencies": { 1780 - "es-errors": "^1.3.0", 1781 - "get-intrinsic": "^1.2.6", 1782 - "has-tostringtag": "^1.0.2", 1783 - "hasown": "^2.0.2" 1784 - }, 1785 - "engines": { 1786 - "node": ">= 0.4" 1787 - } 1788 - }, 1789 - "node_modules/es-shim-unscopables": { 1790 - "version": "1.1.0", 1791 - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", 1792 - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", 1793 - "dev": true, 1794 - "license": "MIT", 1795 - "dependencies": { 1796 - "hasown": "^2.0.2" 1797 - }, 1798 - "engines": { 1799 - "node": ">= 0.4" 1800 - } 1801 - }, 1802 - "node_modules/es-to-primitive": { 1803 - "version": "1.3.0", 1804 - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", 1805 - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", 1806 - "dev": true, 1807 - "license": "MIT", 1808 - "dependencies": { 1809 - "is-callable": "^1.2.7", 1810 - "is-date-object": "^1.0.5", 1811 - "is-symbol": "^1.0.4" 1812 - }, 1813 - "engines": { 1814 - "node": ">= 0.4" 1815 - }, 1816 - "funding": { 1817 - "url": "https://github.com/sponsors/ljharb" 1818 - } 1819 - }, 1820 - "node_modules/esbuild": { 1821 - "version": "0.25.11", 1822 - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", 1823 - "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", 1824 - "hasInstallScript": true, 1825 - "license": "MIT", 1826 - "bin": { 1827 - "esbuild": "bin/esbuild" 1828 - }, 1829 - "engines": { 1830 - "node": ">=18" 1831 - }, 1832 - "optionalDependencies": { 1833 - "@esbuild/aix-ppc64": "0.25.11", 1834 - "@esbuild/android-arm": "0.25.11", 1835 - "@esbuild/android-arm64": "0.25.11", 1836 - "@esbuild/android-x64": "0.25.11", 1837 - "@esbuild/darwin-arm64": "0.25.11", 1838 - "@esbuild/darwin-x64": "0.25.11", 1839 - "@esbuild/freebsd-arm64": "0.25.11", 1840 - "@esbuild/freebsd-x64": "0.25.11", 1841 - "@esbuild/linux-arm": "0.25.11", 1842 - "@esbuild/linux-arm64": "0.25.11", 1843 - "@esbuild/linux-ia32": "0.25.11", 1844 - "@esbuild/linux-loong64": "0.25.11", 1845 - "@esbuild/linux-mips64el": "0.25.11", 1846 - "@esbuild/linux-ppc64": "0.25.11", 1847 - "@esbuild/linux-riscv64": "0.25.11", 1848 - "@esbuild/linux-s390x": "0.25.11", 1849 - "@esbuild/linux-x64": "0.25.11", 1850 - "@esbuild/netbsd-arm64": "0.25.11", 1851 - "@esbuild/netbsd-x64": "0.25.11", 1852 - "@esbuild/openbsd-arm64": "0.25.11", 1853 - "@esbuild/openbsd-x64": "0.25.11", 1854 - "@esbuild/openharmony-arm64": "0.25.11", 1855 - "@esbuild/sunos-x64": "0.25.11", 1856 - "@esbuild/win32-arm64": "0.25.11", 1857 - "@esbuild/win32-ia32": "0.25.11", 1858 - "@esbuild/win32-x64": "0.25.11" 1859 - } 1860 - }, 1861 - "node_modules/escape-string-regexp": { 1862 - "version": "4.0.0", 1863 - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1864 - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1865 - "dev": true, 1866 - "license": "MIT", 1867 - "engines": { 1868 - "node": ">=10" 1869 - }, 1870 - "funding": { 1871 - "url": "https://github.com/sponsors/sindresorhus" 1872 - } 1873 - }, 1874 - "node_modules/eslint": { 1875 - "version": "9.38.0", 1876 - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", 1877 - "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", 1878 - "dev": true, 1879 - "license": "MIT", 1880 - "dependencies": { 1881 - "@eslint-community/eslint-utils": "^4.8.0", 1882 - "@eslint-community/regexpp": "^4.12.1", 1883 - "@eslint/config-array": "^0.21.1", 1884 - "@eslint/config-helpers": "^0.4.1", 1885 - "@eslint/core": "^0.16.0", 1886 - "@eslint/eslintrc": "^3.3.1", 1887 - "@eslint/js": "9.38.0", 1888 - "@eslint/plugin-kit": "^0.4.0", 1889 - "@humanfs/node": "^0.16.6", 1890 - "@humanwhocodes/module-importer": "^1.0.1", 1891 - "@humanwhocodes/retry": "^0.4.2", 1892 - "@types/estree": "^1.0.6", 1893 - "ajv": "^6.12.4", 1894 - "chalk": "^4.0.0", 1895 - "cross-spawn": "^7.0.6", 1896 - "debug": "^4.3.2", 1897 - "escape-string-regexp": "^4.0.0", 1898 - "eslint-scope": "^8.4.0", 1899 - "eslint-visitor-keys": "^4.2.1", 1900 - "espree": "^10.4.0", 1901 - "esquery": "^1.5.0", 1902 - "esutils": "^2.0.2", 1903 - "fast-deep-equal": "^3.1.3", 1904 - "file-entry-cache": "^8.0.0", 1905 - "find-up": "^5.0.0", 1906 - "glob-parent": "^6.0.2", 1907 - "ignore": "^5.2.0", 1908 - "imurmurhash": "^0.1.4", 1909 - "is-glob": "^4.0.0", 1910 - "json-stable-stringify-without-jsonify": "^1.0.1", 1911 - "lodash.merge": "^4.6.2", 1912 - "minimatch": "^3.1.2", 1913 - "natural-compare": "^1.4.0", 1914 - "optionator": "^0.9.3" 1915 - }, 1916 - "bin": { 1917 - "eslint": "bin/eslint.js" 1918 - }, 1919 - "engines": { 1920 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1921 - }, 1922 - "funding": { 1923 - "url": "https://eslint.org/donate" 1924 - }, 1925 - "peerDependencies": { 1926 - "jiti": "*" 1927 - }, 1928 - "peerDependenciesMeta": { 1929 - "jiti": { 1930 - "optional": true 1931 - } 1932 - } 1933 - }, 1934 - "node_modules/eslint-import-resolver-node": { 1935 - "version": "0.3.9", 1936 - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", 1937 - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", 1938 - "dev": true, 1939 - "license": "MIT", 1940 - "dependencies": { 1941 - "debug": "^3.2.7", 1942 - "is-core-module": "^2.13.0", 1943 - "resolve": "^1.22.4" 1944 - } 1945 - }, 1946 - "node_modules/eslint-import-resolver-node/node_modules/debug": { 1947 - "version": "3.2.7", 1948 - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 1949 - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 1950 - "dev": true, 1951 - "license": "MIT", 1952 - "dependencies": { 1953 - "ms": "^2.1.1" 1954 - } 1955 - }, 1956 - "node_modules/eslint-module-utils": { 1957 - "version": "2.12.1", 1958 - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz", 1959 - "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==", 1960 - "dev": true, 1961 - "license": "MIT", 1962 - "dependencies": { 1963 - "debug": "^3.2.7" 1964 - }, 1965 - "engines": { 1966 - "node": ">=4" 1967 - }, 1968 - "peerDependenciesMeta": { 1969 - "eslint": { 1970 - "optional": true 1971 - } 1972 - } 1973 - }, 1974 - "node_modules/eslint-module-utils/node_modules/debug": { 1975 - "version": "3.2.7", 1976 - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 1977 - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 1978 - "dev": true, 1979 - "license": "MIT", 1980 - "dependencies": { 1981 - "ms": "^2.1.1" 1982 - } 1983 - }, 1984 - "node_modules/eslint-plugin-import": { 1985 - "version": "2.32.0", 1986 - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", 1987 - "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", 1988 - "dev": true, 1989 - "license": "MIT", 1990 - "dependencies": { 1991 - "@rtsao/scc": "^1.1.0", 1992 - "array-includes": "^3.1.9", 1993 - "array.prototype.findlastindex": "^1.2.6", 1994 - "array.prototype.flat": "^1.3.3", 1995 - "array.prototype.flatmap": "^1.3.3", 1996 - "debug": "^3.2.7", 1997 - "doctrine": "^2.1.0", 1998 - "eslint-import-resolver-node": "^0.3.9", 1999 - "eslint-module-utils": "^2.12.1", 2000 - "hasown": "^2.0.2", 2001 - "is-core-module": "^2.16.1", 2002 - "is-glob": "^4.0.3", 2003 - "minimatch": "^3.1.2", 2004 - "object.fromentries": "^2.0.8", 2005 - "object.groupby": "^1.0.3", 2006 - "object.values": "^1.2.1", 2007 - "semver": "^6.3.1", 2008 - "string.prototype.trimend": "^1.0.9", 2009 - "tsconfig-paths": "^3.15.0" 2010 - }, 2011 - "engines": { 2012 - "node": ">=4" 2013 - }, 2014 - "peerDependencies": { 2015 - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" 2016 - } 2017 - }, 2018 - "node_modules/eslint-plugin-import/node_modules/debug": { 2019 - "version": "3.2.7", 2020 - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", 2021 - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", 2022 - "dev": true, 2023 - "license": "MIT", 2024 - "dependencies": { 2025 - "ms": "^2.1.1" 2026 - } 2027 - }, 2028 - "node_modules/eslint-scope": { 2029 - "version": "8.4.0", 2030 - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", 2031 - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", 2032 - "dev": true, 2033 - "license": "BSD-2-Clause", 2034 - "dependencies": { 2035 - "esrecurse": "^4.3.0", 2036 - "estraverse": "^5.2.0" 2037 - }, 2038 - "engines": { 2039 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2040 - }, 2041 - "funding": { 2042 - "url": "https://opencollective.com/eslint" 2043 - } 2044 - }, 2045 - "node_modules/eslint-visitor-keys": { 2046 - "version": "4.2.1", 2047 - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", 2048 - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", 2049 - "dev": true, 2050 - "license": "Apache-2.0", 2051 - "engines": { 2052 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2053 - }, 2054 - "funding": { 2055 - "url": "https://opencollective.com/eslint" 2056 - } 2057 - }, 2058 - "node_modules/esm-env": { 2059 - "version": "1.2.2", 2060 - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", 2061 - "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", 2062 - "license": "MIT" 2063 - }, 2064 - "node_modules/espree": { 2065 - "version": "10.4.0", 2066 - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", 2067 - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", 2068 - "dev": true, 2069 - "license": "BSD-2-Clause", 2070 - "dependencies": { 2071 - "acorn": "^8.15.0", 2072 - "acorn-jsx": "^5.3.2", 2073 - "eslint-visitor-keys": "^4.2.1" 2074 - }, 2075 - "engines": { 2076 - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 2077 - }, 2078 - "funding": { 2079 - "url": "https://opencollective.com/eslint" 2080 - } 2081 - }, 2082 - "node_modules/esquery": { 2083 - "version": "1.6.0", 2084 - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", 2085 - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", 2086 - "dev": true, 2087 - "license": "BSD-3-Clause", 2088 - "dependencies": { 2089 - "estraverse": "^5.1.0" 2090 - }, 2091 - "engines": { 2092 - "node": ">=0.10" 2093 - } 2094 - }, 2095 - "node_modules/esrecurse": { 2096 - "version": "4.3.0", 2097 - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 2098 - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 2099 - "dev": true, 2100 - "license": "BSD-2-Clause", 2101 - "dependencies": { 2102 - "estraverse": "^5.2.0" 2103 - }, 2104 - "engines": { 2105 - "node": ">=4.0" 2106 - } 2107 - }, 2108 - "node_modules/estraverse": { 2109 - "version": "5.3.0", 2110 - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 2111 - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 2112 - "dev": true, 2113 - "license": "BSD-2-Clause", 2114 - "engines": { 2115 - "node": ">=4.0" 2116 - } 2117 - }, 2118 - "node_modules/esutils": { 2119 - "version": "2.0.3", 2120 - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 2121 - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 2122 - "dev": true, 2123 - "license": "BSD-2-Clause", 2124 - "engines": { 2125 - "node": ">=0.10.0" 2126 - } 2127 - }, 2128 - "node_modules/fast-deep-equal": { 2129 - "version": "3.1.3", 2130 - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 2131 - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 2132 - "dev": true, 2133 - "license": "MIT" 2134 - }, 2135 - "node_modules/fast-json-stable-stringify": { 2136 - "version": "2.1.0", 2137 - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 2138 - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 2139 - "dev": true, 2140 - "license": "MIT" 2141 - }, 2142 - "node_modules/fast-levenshtein": { 2143 - "version": "2.0.6", 2144 - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 2145 - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 2146 - "dev": true, 2147 - "license": "MIT" 2148 - }, 2149 - "node_modules/fdir": { 2150 - "version": "6.5.0", 2151 - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", 2152 - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", 2153 - "license": "MIT", 2154 - "engines": { 2155 - "node": ">=12.0.0" 2156 - }, 2157 - "peerDependencies": { 2158 - "picomatch": "^3 || ^4" 2159 - }, 2160 - "peerDependenciesMeta": { 2161 - "picomatch": { 2162 - "optional": true 2163 - } 2164 - } 2165 - }, 2166 - "node_modules/file-entry-cache": { 2167 - "version": "8.0.0", 2168 - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", 2169 - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", 2170 - "dev": true, 2171 - "license": "MIT", 2172 - "dependencies": { 2173 - "flat-cache": "^4.0.0" 2174 - }, 2175 - "engines": { 2176 - "node": ">=16.0.0" 2177 - } 2178 - }, 2179 - "node_modules/find-up": { 2180 - "version": "5.0.0", 2181 - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 2182 - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 2183 - "dev": true, 2184 - "license": "MIT", 2185 - "dependencies": { 2186 - "locate-path": "^6.0.0", 2187 - "path-exists": "^4.0.0" 2188 - }, 2189 - "engines": { 2190 - "node": ">=10" 2191 - }, 2192 - "funding": { 2193 - "url": "https://github.com/sponsors/sindresorhus" 2194 - } 2195 - }, 2196 - "node_modules/flat-cache": { 2197 - "version": "4.0.1", 2198 - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", 2199 - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", 2200 - "dev": true, 2201 - "license": "MIT", 2202 - "dependencies": { 2203 - "flatted": "^3.2.9", 2204 - "keyv": "^4.5.4" 2205 - }, 2206 - "engines": { 2207 - "node": ">=16" 2208 - } 2209 - }, 2210 - "node_modules/flatted": { 2211 - "version": "3.3.3", 2212 - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", 2213 - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", 2214 - "dev": true, 2215 - "license": "ISC" 2216 - }, 2217 - "node_modules/for-each": { 2218 - "version": "0.3.5", 2219 - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", 2220 - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", 2221 - "dev": true, 2222 - "license": "MIT", 2223 - "dependencies": { 2224 - "is-callable": "^1.2.7" 2225 - }, 2226 - "engines": { 2227 - "node": ">= 0.4" 2228 - }, 2229 - "funding": { 2230 - "url": "https://github.com/sponsors/ljharb" 2231 - } 2232 - }, 2233 - "node_modules/fsevents": { 2234 - "version": "2.3.3", 2235 - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 2236 - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 2237 - "hasInstallScript": true, 2238 - "license": "MIT", 2239 - "optional": true, 2240 - "os": [ 2241 - "darwin" 2242 - ], 2243 - "engines": { 2244 - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 2245 - } 2246 - }, 2247 - "node_modules/function-bind": { 2248 - "version": "1.1.2", 2249 - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 2250 - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 2251 - "dev": true, 2252 - "license": "MIT", 2253 - "funding": { 2254 - "url": "https://github.com/sponsors/ljharb" 2255 - } 2256 - }, 2257 - "node_modules/function.prototype.name": { 2258 - "version": "1.1.8", 2259 - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", 2260 - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", 2261 - "dev": true, 2262 - "license": "MIT", 2263 - "dependencies": { 2264 - "call-bind": "^1.0.8", 2265 - "call-bound": "^1.0.3", 2266 - "define-properties": "^1.2.1", 2267 - "functions-have-names": "^1.2.3", 2268 - "hasown": "^2.0.2", 2269 - "is-callable": "^1.2.7" 2270 - }, 2271 - "engines": { 2272 - "node": ">= 0.4" 2273 - }, 2274 - "funding": { 2275 - "url": "https://github.com/sponsors/ljharb" 2276 - } 2277 - }, 2278 - "node_modules/functions-have-names": { 2279 - "version": "1.2.3", 2280 - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", 2281 - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", 2282 - "dev": true, 2283 - "license": "MIT", 2284 - "funding": { 2285 - "url": "https://github.com/sponsors/ljharb" 2286 - } 2287 - }, 2288 - "node_modules/generator-function": { 2289 - "version": "2.0.1", 2290 - "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", 2291 - "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", 2292 - "dev": true, 2293 - "license": "MIT", 2294 - "engines": { 2295 - "node": ">= 0.4" 2296 - } 2297 - }, 2298 - "node_modules/get-intrinsic": { 2299 - "version": "1.3.0", 2300 - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 2301 - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 2302 - "dev": true, 2303 - "license": "MIT", 2304 - "dependencies": { 2305 - "call-bind-apply-helpers": "^1.0.2", 2306 - "es-define-property": "^1.0.1", 2307 - "es-errors": "^1.3.0", 2308 - "es-object-atoms": "^1.1.1", 2309 - "function-bind": "^1.1.2", 2310 - "get-proto": "^1.0.1", 2311 - "gopd": "^1.2.0", 2312 - "has-symbols": "^1.1.0", 2313 - "hasown": "^2.0.2", 2314 - "math-intrinsics": "^1.1.0" 2315 - }, 2316 - "engines": { 2317 - "node": ">= 0.4" 2318 - }, 2319 - "funding": { 2320 - "url": "https://github.com/sponsors/ljharb" 2321 - } 2322 - }, 2323 - "node_modules/get-proto": { 2324 - "version": "1.0.1", 2325 - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 2326 - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 2327 - "dev": true, 2328 - "license": "MIT", 2329 - "dependencies": { 2330 - "dunder-proto": "^1.0.1", 2331 - "es-object-atoms": "^1.0.0" 2332 - }, 2333 - "engines": { 2334 - "node": ">= 0.4" 2335 - } 2336 - }, 2337 - "node_modules/get-symbol-description": { 2338 - "version": "1.1.0", 2339 - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", 2340 - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", 2341 - "dev": true, 2342 - "license": "MIT", 2343 - "dependencies": { 2344 - "call-bound": "^1.0.3", 2345 - "es-errors": "^1.3.0", 2346 - "get-intrinsic": "^1.2.6" 2347 - }, 2348 - "engines": { 2349 - "node": ">= 0.4" 2350 - }, 2351 - "funding": { 2352 - "url": "https://github.com/sponsors/ljharb" 2353 - } 2354 - }, 2355 - "node_modules/glob-parent": { 2356 - "version": "6.0.2", 2357 - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 2358 - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 2359 - "dev": true, 2360 - "license": "ISC", 2361 - "dependencies": { 2362 - "is-glob": "^4.0.3" 2363 - }, 2364 - "engines": { 2365 - "node": ">=10.13.0" 2366 - } 2367 - }, 2368 - "node_modules/globals": { 2369 - "version": "14.0.0", 2370 - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", 2371 - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", 2372 - "dev": true, 2373 - "license": "MIT", 2374 - "engines": { 2375 - "node": ">=18" 2376 - }, 2377 - "funding": { 2378 - "url": "https://github.com/sponsors/sindresorhus" 2379 - } 2380 - }, 2381 - "node_modules/globalthis": { 2382 - "version": "1.0.4", 2383 - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", 2384 - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", 2385 - "dev": true, 2386 - "license": "MIT", 2387 - "dependencies": { 2388 - "define-properties": "^1.2.1", 2389 - "gopd": "^1.0.1" 2390 - }, 2391 - "engines": { 2392 - "node": ">= 0.4" 2393 - }, 2394 - "funding": { 2395 - "url": "https://github.com/sponsors/ljharb" 2396 - } 2397 - }, 2398 - "node_modules/gopd": { 2399 - "version": "1.2.0", 2400 - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 2401 - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 2402 - "dev": true, 2403 - "license": "MIT", 2404 - "engines": { 2405 - "node": ">= 0.4" 2406 - }, 2407 - "funding": { 2408 - "url": "https://github.com/sponsors/ljharb" 2409 - } 2410 - }, 2411 - "node_modules/graphemer": { 2412 - "version": "1.4.0", 2413 - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 2414 - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 2415 - "license": "MIT" 2416 - }, 2417 - "node_modules/has-bigints": { 2418 - "version": "1.1.0", 2419 - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", 2420 - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", 2421 - "dev": true, 2422 - "license": "MIT", 2423 - "engines": { 2424 - "node": ">= 0.4" 2425 - }, 2426 - "funding": { 2427 - "url": "https://github.com/sponsors/ljharb" 2428 - } 2429 - }, 2430 - "node_modules/has-flag": { 2431 - "version": "4.0.0", 2432 - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 2433 - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 2434 - "dev": true, 2435 - "license": "MIT", 2436 - "engines": { 2437 - "node": ">=8" 2438 - } 2439 - }, 2440 - "node_modules/has-property-descriptors": { 2441 - "version": "1.0.2", 2442 - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 2443 - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 2444 - "dev": true, 2445 - "license": "MIT", 2446 - "dependencies": { 2447 - "es-define-property": "^1.0.0" 2448 - }, 2449 - "funding": { 2450 - "url": "https://github.com/sponsors/ljharb" 2451 - } 2452 - }, 2453 - "node_modules/has-proto": { 2454 - "version": "1.2.0", 2455 - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", 2456 - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", 2457 - "dev": true, 2458 - "license": "MIT", 2459 - "dependencies": { 2460 - "dunder-proto": "^1.0.0" 2461 - }, 2462 - "engines": { 2463 - "node": ">= 0.4" 2464 - }, 2465 - "funding": { 2466 - "url": "https://github.com/sponsors/ljharb" 2467 - } 2468 - }, 2469 - "node_modules/has-symbols": { 2470 - "version": "1.1.0", 2471 - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 2472 - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 2473 - "dev": true, 2474 - "license": "MIT", 2475 - "engines": { 2476 - "node": ">= 0.4" 2477 - }, 2478 - "funding": { 2479 - "url": "https://github.com/sponsors/ljharb" 2480 - } 2481 - }, 2482 - "node_modules/has-tostringtag": { 2483 - "version": "1.0.2", 2484 - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", 2485 - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", 2486 - "dev": true, 2487 - "license": "MIT", 2488 - "dependencies": { 2489 - "has-symbols": "^1.0.3" 2490 - }, 2491 - "engines": { 2492 - "node": ">= 0.4" 2493 - }, 2494 - "funding": { 2495 - "url": "https://github.com/sponsors/ljharb" 2496 - } 2497 - }, 2498 - "node_modules/hasown": { 2499 - "version": "2.0.2", 2500 - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 2501 - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 2502 - "dev": true, 2503 - "license": "MIT", 2504 - "dependencies": { 2505 - "function-bind": "^1.1.2" 2506 - }, 2507 - "engines": { 2508 - "node": ">= 0.4" 2509 - } 2510 - }, 2511 - "node_modules/ignore": { 2512 - "version": "5.3.2", 2513 - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", 2514 - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", 2515 - "dev": true, 2516 - "license": "MIT", 2517 - "engines": { 2518 - "node": ">= 4" 2519 - } 2520 - }, 2521 - "node_modules/import-fresh": { 2522 - "version": "3.3.1", 2523 - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", 2524 - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", 2525 - "dev": true, 2526 - "license": "MIT", 2527 - "dependencies": { 2528 - "parent-module": "^1.0.0", 2529 - "resolve-from": "^4.0.0" 2530 - }, 2531 - "engines": { 2532 - "node": ">=6" 2533 - }, 2534 - "funding": { 2535 - "url": "https://github.com/sponsors/sindresorhus" 2536 - } 2537 - }, 2538 - "node_modules/imurmurhash": { 2539 - "version": "0.1.4", 2540 - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 2541 - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 2542 - "dev": true, 2543 - "license": "MIT", 2544 - "engines": { 2545 - "node": ">=0.8.19" 2546 - } 2547 - }, 2548 - "node_modules/internal-slot": { 2549 - "version": "1.1.0", 2550 - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", 2551 - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", 2552 - "dev": true, 2553 - "license": "MIT", 2554 - "dependencies": { 2555 - "es-errors": "^1.3.0", 2556 - "hasown": "^2.0.2", 2557 - "side-channel": "^1.1.0" 2558 - }, 2559 - "engines": { 2560 - "node": ">= 0.4" 2561 - } 2562 - }, 2563 - "node_modules/is-array-buffer": { 2564 - "version": "3.0.5", 2565 - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", 2566 - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", 2567 - "dev": true, 2568 - "license": "MIT", 2569 - "dependencies": { 2570 - "call-bind": "^1.0.8", 2571 - "call-bound": "^1.0.3", 2572 - "get-intrinsic": "^1.2.6" 2573 - }, 2574 - "engines": { 2575 - "node": ">= 0.4" 2576 - }, 2577 - "funding": { 2578 - "url": "https://github.com/sponsors/ljharb" 2579 - } 2580 - }, 2581 - "node_modules/is-async-function": { 2582 - "version": "2.1.1", 2583 - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", 2584 - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", 2585 - "dev": true, 2586 - "license": "MIT", 2587 - "dependencies": { 2588 - "async-function": "^1.0.0", 2589 - "call-bound": "^1.0.3", 2590 - "get-proto": "^1.0.1", 2591 - "has-tostringtag": "^1.0.2", 2592 - "safe-regex-test": "^1.1.0" 2593 - }, 2594 - "engines": { 2595 - "node": ">= 0.4" 2596 - }, 2597 - "funding": { 2598 - "url": "https://github.com/sponsors/ljharb" 2599 - } 2600 - }, 2601 - "node_modules/is-bigint": { 2602 - "version": "1.1.0", 2603 - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", 2604 - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", 2605 - "dev": true, 2606 - "license": "MIT", 2607 - "dependencies": { 2608 - "has-bigints": "^1.0.2" 2609 - }, 2610 - "engines": { 2611 - "node": ">= 0.4" 2612 - }, 2613 - "funding": { 2614 - "url": "https://github.com/sponsors/ljharb" 2615 - } 2616 - }, 2617 - "node_modules/is-boolean-object": { 2618 - "version": "1.2.2", 2619 - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", 2620 - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", 2621 - "dev": true, 2622 - "license": "MIT", 2623 - "dependencies": { 2624 - "call-bound": "^1.0.3", 2625 - "has-tostringtag": "^1.0.2" 2626 - }, 2627 - "engines": { 2628 - "node": ">= 0.4" 2629 - }, 2630 - "funding": { 2631 - "url": "https://github.com/sponsors/ljharb" 2632 - } 2633 - }, 2634 - "node_modules/is-callable": { 2635 - "version": "1.2.7", 2636 - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", 2637 - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", 2638 - "dev": true, 2639 - "license": "MIT", 2640 - "engines": { 2641 - "node": ">= 0.4" 2642 - }, 2643 - "funding": { 2644 - "url": "https://github.com/sponsors/ljharb" 2645 - } 2646 - }, 2647 - "node_modules/is-core-module": { 2648 - "version": "2.16.1", 2649 - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", 2650 - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", 2651 - "dev": true, 2652 - "license": "MIT", 2653 - "dependencies": { 2654 - "hasown": "^2.0.2" 2655 - }, 2656 - "engines": { 2657 - "node": ">= 0.4" 2658 - }, 2659 - "funding": { 2660 - "url": "https://github.com/sponsors/ljharb" 2661 - } 2662 - }, 2663 - "node_modules/is-data-view": { 2664 - "version": "1.0.2", 2665 - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", 2666 - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", 2667 - "dev": true, 2668 - "license": "MIT", 2669 - "dependencies": { 2670 - "call-bound": "^1.0.2", 2671 - "get-intrinsic": "^1.2.6", 2672 - "is-typed-array": "^1.1.13" 2673 - }, 2674 - "engines": { 2675 - "node": ">= 0.4" 2676 - }, 2677 - "funding": { 2678 - "url": "https://github.com/sponsors/ljharb" 2679 - } 2680 - }, 2681 - "node_modules/is-date-object": { 2682 - "version": "1.1.0", 2683 - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", 2684 - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", 2685 - "dev": true, 2686 - "license": "MIT", 2687 - "dependencies": { 2688 - "call-bound": "^1.0.2", 2689 - "has-tostringtag": "^1.0.2" 2690 - }, 2691 - "engines": { 2692 - "node": ">= 0.4" 2693 - }, 2694 - "funding": { 2695 - "url": "https://github.com/sponsors/ljharb" 2696 - } 2697 - }, 2698 - "node_modules/is-extglob": { 2699 - "version": "2.1.1", 2700 - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 2701 - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 2702 - "dev": true, 2703 - "license": "MIT", 2704 - "engines": { 2705 - "node": ">=0.10.0" 2706 - } 2707 - }, 2708 - "node_modules/is-finalizationregistry": { 2709 - "version": "1.1.1", 2710 - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", 2711 - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", 2712 - "dev": true, 2713 - "license": "MIT", 2714 - "dependencies": { 2715 - "call-bound": "^1.0.3" 2716 - }, 2717 - "engines": { 2718 - "node": ">= 0.4" 2719 - }, 2720 - "funding": { 2721 - "url": "https://github.com/sponsors/ljharb" 2722 - } 2723 - }, 2724 - "node_modules/is-generator-function": { 2725 - "version": "1.1.2", 2726 - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", 2727 - "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", 2728 - "dev": true, 2729 - "license": "MIT", 2730 - "dependencies": { 2731 - "call-bound": "^1.0.4", 2732 - "generator-function": "^2.0.0", 2733 - "get-proto": "^1.0.1", 2734 - "has-tostringtag": "^1.0.2", 2735 - "safe-regex-test": "^1.1.0" 2736 - }, 2737 - "engines": { 2738 - "node": ">= 0.4" 2739 - }, 2740 - "funding": { 2741 - "url": "https://github.com/sponsors/ljharb" 2742 - } 2743 - }, 2744 - "node_modules/is-glob": { 2745 - "version": "4.0.3", 2746 - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 2747 - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 2748 - "dev": true, 2749 - "license": "MIT", 2750 - "dependencies": { 2751 - "is-extglob": "^2.1.1" 2752 - }, 2753 - "engines": { 2754 - "node": ">=0.10.0" 2755 - } 2756 - }, 2757 - "node_modules/is-map": { 2758 - "version": "2.0.3", 2759 - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", 2760 - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", 2761 - "dev": true, 2762 - "license": "MIT", 2763 - "engines": { 2764 - "node": ">= 0.4" 2765 - }, 2766 - "funding": { 2767 - "url": "https://github.com/sponsors/ljharb" 2768 - } 2769 - }, 2770 - "node_modules/is-negative-zero": { 2771 - "version": "2.0.3", 2772 - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", 2773 - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", 2774 - "dev": true, 2775 - "license": "MIT", 2776 - "engines": { 2777 - "node": ">= 0.4" 2778 - }, 2779 - "funding": { 2780 - "url": "https://github.com/sponsors/ljharb" 2781 - } 2782 - }, 2783 - "node_modules/is-number-object": { 2784 - "version": "1.1.1", 2785 - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", 2786 - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", 2787 - "dev": true, 2788 - "license": "MIT", 2789 - "dependencies": { 2790 - "call-bound": "^1.0.3", 2791 - "has-tostringtag": "^1.0.2" 2792 - }, 2793 - "engines": { 2794 - "node": ">= 0.4" 2795 - }, 2796 - "funding": { 2797 - "url": "https://github.com/sponsors/ljharb" 2798 - } 2799 - }, 2800 - "node_modules/is-regex": { 2801 - "version": "1.2.1", 2802 - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", 2803 - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", 2804 - "dev": true, 2805 - "license": "MIT", 2806 - "dependencies": { 2807 - "call-bound": "^1.0.2", 2808 - "gopd": "^1.2.0", 2809 - "has-tostringtag": "^1.0.2", 2810 - "hasown": "^2.0.2" 2811 - }, 2812 - "engines": { 2813 - "node": ">= 0.4" 2814 - }, 2815 - "funding": { 2816 - "url": "https://github.com/sponsors/ljharb" 2817 - } 2818 - }, 2819 - "node_modules/is-set": { 2820 - "version": "2.0.3", 2821 - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", 2822 - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", 2823 - "dev": true, 2824 - "license": "MIT", 2825 - "engines": { 2826 - "node": ">= 0.4" 2827 - }, 2828 - "funding": { 2829 - "url": "https://github.com/sponsors/ljharb" 2830 - } 2831 - }, 2832 - "node_modules/is-shared-array-buffer": { 2833 - "version": "1.0.4", 2834 - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", 2835 - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", 2836 - "dev": true, 2837 - "license": "MIT", 2838 - "dependencies": { 2839 - "call-bound": "^1.0.3" 2840 - }, 2841 - "engines": { 2842 - "node": ">= 0.4" 2843 - }, 2844 - "funding": { 2845 - "url": "https://github.com/sponsors/ljharb" 2846 - } 2847 - }, 2848 - "node_modules/is-string": { 2849 - "version": "1.1.1", 2850 - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", 2851 - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", 2852 - "dev": true, 2853 - "license": "MIT", 2854 - "dependencies": { 2855 - "call-bound": "^1.0.3", 2856 - "has-tostringtag": "^1.0.2" 2857 - }, 2858 - "engines": { 2859 - "node": ">= 0.4" 2860 - }, 2861 - "funding": { 2862 - "url": "https://github.com/sponsors/ljharb" 2863 - } 2864 - }, 2865 - "node_modules/is-symbol": { 2866 - "version": "1.1.1", 2867 - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", 2868 - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", 2869 - "dev": true, 2870 - "license": "MIT", 2871 - "dependencies": { 2872 - "call-bound": "^1.0.2", 2873 - "has-symbols": "^1.1.0", 2874 - "safe-regex-test": "^1.1.0" 2875 - }, 2876 - "engines": { 2877 - "node": ">= 0.4" 2878 - }, 2879 - "funding": { 2880 - "url": "https://github.com/sponsors/ljharb" 2881 - } 2882 - }, 2883 - "node_modules/is-typed-array": { 2884 - "version": "1.1.15", 2885 - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", 2886 - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", 2887 - "dev": true, 2888 - "license": "MIT", 2889 - "dependencies": { 2890 - "which-typed-array": "^1.1.16" 2891 - }, 2892 - "engines": { 2893 - "node": ">= 0.4" 2894 - }, 2895 - "funding": { 2896 - "url": "https://github.com/sponsors/ljharb" 2897 - } 2898 - }, 2899 - "node_modules/is-weakmap": { 2900 - "version": "2.0.2", 2901 - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", 2902 - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", 2903 - "dev": true, 2904 - "license": "MIT", 2905 - "engines": { 2906 - "node": ">= 0.4" 2907 - }, 2908 - "funding": { 2909 - "url": "https://github.com/sponsors/ljharb" 2910 - } 2911 - }, 2912 - "node_modules/is-weakref": { 2913 - "version": "1.1.1", 2914 - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", 2915 - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", 2916 - "dev": true, 2917 - "license": "MIT", 2918 - "dependencies": { 2919 - "call-bound": "^1.0.3" 2920 - }, 2921 - "engines": { 2922 - "node": ">= 0.4" 2923 - }, 2924 - "funding": { 2925 - "url": "https://github.com/sponsors/ljharb" 2926 - } 2927 - }, 2928 - "node_modules/is-weakset": { 2929 - "version": "2.0.4", 2930 - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", 2931 - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", 2932 - "dev": true, 2933 - "license": "MIT", 2934 - "dependencies": { 2935 - "call-bound": "^1.0.3", 2936 - "get-intrinsic": "^1.2.6" 2937 - }, 2938 - "engines": { 2939 - "node": ">= 0.4" 2940 - }, 2941 - "funding": { 2942 - "url": "https://github.com/sponsors/ljharb" 2943 - } 2944 - }, 2945 - "node_modules/isarray": { 2946 - "version": "2.0.5", 2947 - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", 2948 - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", 2949 - "dev": true, 2950 - "license": "MIT" 2951 - }, 2952 - "node_modules/isexe": { 2953 - "version": "2.0.0", 2954 - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 2955 - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 2956 - "dev": true, 2957 - "license": "ISC" 2958 - }, 2959 - "node_modules/iso-datestring-validator": { 2960 - "version": "2.2.2", 2961 - "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", 2962 - "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==", 2963 - "license": "MIT" 2964 - }, 2965 - "node_modules/js-yaml": { 2966 - "version": "4.1.0", 2967 - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 2968 - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 2969 - "dev": true, 2970 - "license": "MIT", 2971 - "dependencies": { 2972 - "argparse": "^2.0.1" 2973 - }, 2974 - "bin": { 2975 - "js-yaml": "bin/js-yaml.js" 2976 - } 2977 - }, 2978 - "node_modules/json-buffer": { 2979 - "version": "3.0.1", 2980 - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 2981 - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 2982 - "dev": true, 2983 - "license": "MIT" 2984 - }, 2985 - "node_modules/json-schema-traverse": { 2986 - "version": "0.4.1", 2987 - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 2988 - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 2989 - "dev": true, 2990 - "license": "MIT" 2991 - }, 2992 - "node_modules/json-stable-stringify-without-jsonify": { 2993 - "version": "1.0.1", 2994 - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 2995 - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 2996 - "dev": true, 2997 - "license": "MIT" 2998 - }, 2999 - "node_modules/json5": { 3000 - "version": "1.0.2", 3001 - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", 3002 - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", 3003 - "dev": true, 3004 - "license": "MIT", 3005 - "dependencies": { 3006 - "minimist": "^1.2.0" 3007 - }, 3008 - "bin": { 3009 - "json5": "lib/cli.js" 3010 - } 3011 - }, 3012 - "node_modules/keyv": { 3013 - "version": "4.5.4", 3014 - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 3015 - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 3016 - "dev": true, 3017 - "license": "MIT", 3018 - "dependencies": { 3019 - "json-buffer": "3.0.1" 3020 - } 3021 - }, 3022 - "node_modules/levn": { 3023 - "version": "0.4.1", 3024 - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 3025 - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 3026 - "dev": true, 3027 - "license": "MIT", 3028 - "dependencies": { 3029 - "prelude-ls": "^1.2.1", 3030 - "type-check": "~0.4.0" 3031 - }, 3032 - "engines": { 3033 - "node": ">= 0.8.0" 3034 - } 3035 - }, 3036 - "node_modules/locate-path": { 3037 - "version": "6.0.0", 3038 - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 3039 - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 3040 - "dev": true, 3041 - "license": "MIT", 3042 - "dependencies": { 3043 - "p-locate": "^5.0.0" 3044 - }, 3045 - "engines": { 3046 - "node": ">=10" 3047 - }, 3048 - "funding": { 3049 - "url": "https://github.com/sponsors/sindresorhus" 3050 - } 3051 - }, 3052 - "node_modules/lodash.merge": { 3053 - "version": "4.6.2", 3054 - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 3055 - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 3056 - "dev": true, 3057 - "license": "MIT" 3058 - }, 3059 - "node_modules/math-intrinsics": { 3060 - "version": "1.1.0", 3061 - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 3062 - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 3063 - "dev": true, 3064 - "license": "MIT", 3065 - "engines": { 3066 - "node": ">= 0.4" 3067 - } 3068 - }, 3069 - "node_modules/minimatch": { 3070 - "version": "3.1.2", 3071 - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 3072 - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 3073 - "dev": true, 3074 - "license": "ISC", 3075 - "dependencies": { 3076 - "brace-expansion": "^1.1.7" 3077 - }, 3078 - "engines": { 3079 - "node": "*" 3080 - } 3081 - }, 3082 - "node_modules/minimist": { 3083 - "version": "1.2.8", 3084 - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 3085 - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 3086 - "dev": true, 3087 - "license": "MIT", 3088 - "funding": { 3089 - "url": "https://github.com/sponsors/ljharb" 3090 - } 3091 - }, 3092 - "node_modules/ms": { 3093 - "version": "2.1.3", 3094 - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 3095 - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 3096 - "dev": true, 3097 - "license": "MIT" 3098 - }, 3099 - "node_modules/multiformats": { 3100 - "version": "9.9.0", 3101 - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 3102 - "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 3103 - "license": "(Apache-2.0 AND MIT)" 3104 - }, 3105 - "node_modules/nanoid": { 3106 - "version": "3.3.11", 3107 - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 3108 - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 3109 - "funding": [ 3110 - { 3111 - "type": "github", 3112 - "url": "https://github.com/sponsors/ai" 3113 - } 3114 - ], 3115 - "license": "MIT", 3116 - "bin": { 3117 - "nanoid": "bin/nanoid.cjs" 3118 - }, 3119 - "engines": { 3120 - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 3121 - } 3122 - }, 3123 - "node_modules/natural-compare": { 3124 - "version": "1.4.0", 3125 - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 3126 - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 3127 - "dev": true, 3128 - "license": "MIT" 3129 - }, 3130 - "node_modules/object-inspect": { 3131 - "version": "1.13.4", 3132 - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", 3133 - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", 3134 - "dev": true, 3135 - "license": "MIT", 3136 - "engines": { 3137 - "node": ">= 0.4" 3138 - }, 3139 - "funding": { 3140 - "url": "https://github.com/sponsors/ljharb" 3141 - } 3142 - }, 3143 - "node_modules/object-keys": { 3144 - "version": "1.1.1", 3145 - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", 3146 - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", 3147 - "dev": true, 3148 - "license": "MIT", 3149 - "engines": { 3150 - "node": ">= 0.4" 3151 - } 3152 - }, 3153 - "node_modules/object.assign": { 3154 - "version": "4.1.7", 3155 - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", 3156 - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", 3157 - "dev": true, 3158 - "license": "MIT", 3159 - "dependencies": { 3160 - "call-bind": "^1.0.8", 3161 - "call-bound": "^1.0.3", 3162 - "define-properties": "^1.2.1", 3163 - "es-object-atoms": "^1.0.0", 3164 - "has-symbols": "^1.1.0", 3165 - "object-keys": "^1.1.1" 3166 - }, 3167 - "engines": { 3168 - "node": ">= 0.4" 3169 - }, 3170 - "funding": { 3171 - "url": "https://github.com/sponsors/ljharb" 3172 - } 3173 - }, 3174 - "node_modules/object.fromentries": { 3175 - "version": "2.0.8", 3176 - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", 3177 - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", 3178 - "dev": true, 3179 - "license": "MIT", 3180 - "dependencies": { 3181 - "call-bind": "^1.0.7", 3182 - "define-properties": "^1.2.1", 3183 - "es-abstract": "^1.23.2", 3184 - "es-object-atoms": "^1.0.0" 3185 - }, 3186 - "engines": { 3187 - "node": ">= 0.4" 3188 - }, 3189 - "funding": { 3190 - "url": "https://github.com/sponsors/ljharb" 3191 - } 3192 - }, 3193 - "node_modules/object.groupby": { 3194 - "version": "1.0.3", 3195 - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", 3196 - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", 3197 - "dev": true, 3198 - "license": "MIT", 3199 - "dependencies": { 3200 - "call-bind": "^1.0.7", 3201 - "define-properties": "^1.2.1", 3202 - "es-abstract": "^1.23.2" 3203 - }, 3204 - "engines": { 3205 - "node": ">= 0.4" 3206 - } 3207 - }, 3208 - "node_modules/object.values": { 3209 - "version": "1.2.1", 3210 - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", 3211 - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", 3212 - "dev": true, 3213 - "license": "MIT", 3214 - "dependencies": { 3215 - "call-bind": "^1.0.8", 3216 - "call-bound": "^1.0.3", 3217 - "define-properties": "^1.2.1", 3218 - "es-object-atoms": "^1.0.0" 3219 - }, 3220 - "engines": { 3221 - "node": ">= 0.4" 3222 - }, 3223 - "funding": { 3224 - "url": "https://github.com/sponsors/ljharb" 3225 - } 3226 - }, 3227 - "node_modules/optionator": { 3228 - "version": "0.9.4", 3229 - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", 3230 - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", 3231 - "dev": true, 3232 - "license": "MIT", 3233 - "dependencies": { 3234 - "deep-is": "^0.1.3", 3235 - "fast-levenshtein": "^2.0.6", 3236 - "levn": "^0.4.1", 3237 - "prelude-ls": "^1.2.1", 3238 - "type-check": "^0.4.0", 3239 - "word-wrap": "^1.2.5" 3240 - }, 3241 - "engines": { 3242 - "node": ">= 0.8.0" 3243 - } 3244 - }, 3245 - "node_modules/own-keys": { 3246 - "version": "1.0.1", 3247 - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", 3248 - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", 3249 - "dev": true, 3250 - "license": "MIT", 3251 - "dependencies": { 3252 - "get-intrinsic": "^1.2.6", 3253 - "object-keys": "^1.1.1", 3254 - "safe-push-apply": "^1.0.0" 3255 - }, 3256 - "engines": { 3257 - "node": ">= 0.4" 3258 - }, 3259 - "funding": { 3260 - "url": "https://github.com/sponsors/ljharb" 3261 - } 3262 - }, 3263 - "node_modules/p-limit": { 3264 - "version": "3.1.0", 3265 - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 3266 - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 3267 - "dev": true, 3268 - "license": "MIT", 3269 - "dependencies": { 3270 - "yocto-queue": "^0.1.0" 3271 - }, 3272 - "engines": { 3273 - "node": ">=10" 3274 - }, 3275 - "funding": { 3276 - "url": "https://github.com/sponsors/sindresorhus" 3277 - } 3278 - }, 3279 - "node_modules/p-locate": { 3280 - "version": "5.0.0", 3281 - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 3282 - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 3283 - "dev": true, 3284 - "license": "MIT", 3285 - "dependencies": { 3286 - "p-limit": "^3.0.2" 3287 - }, 3288 - "engines": { 3289 - "node": ">=10" 3290 - }, 3291 - "funding": { 3292 - "url": "https://github.com/sponsors/sindresorhus" 3293 - } 3294 - }, 3295 - "node_modules/parent-module": { 3296 - "version": "1.0.1", 3297 - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 3298 - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 3299 - "dev": true, 3300 - "license": "MIT", 3301 - "dependencies": { 3302 - "callsites": "^3.0.0" 3303 - }, 3304 - "engines": { 3305 - "node": ">=6" 3306 - } 3307 - }, 3308 - "node_modules/path-exists": { 3309 - "version": "4.0.0", 3310 - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 3311 - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 3312 - "dev": true, 3313 - "license": "MIT", 3314 - "engines": { 3315 - "node": ">=8" 3316 - } 3317 - }, 3318 - "node_modules/path-key": { 3319 - "version": "3.1.1", 3320 - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 3321 - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 3322 - "dev": true, 3323 - "license": "MIT", 3324 - "engines": { 3325 - "node": ">=8" 3326 - } 3327 - }, 3328 - "node_modules/path-parse": { 3329 - "version": "1.0.7", 3330 - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 3331 - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 3332 - "dev": true, 3333 - "license": "MIT" 3334 - }, 3335 - "node_modules/picocolors": { 3336 - "version": "1.1.1", 3337 - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 3338 - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 3339 - "license": "ISC" 3340 - }, 3341 - "node_modules/picomatch": { 3342 - "version": "4.0.3", 3343 - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", 3344 - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", 3345 - "license": "MIT", 3346 - "engines": { 3347 - "node": ">=12" 3348 - }, 3349 - "funding": { 3350 - "url": "https://github.com/sponsors/jonschlinkert" 3351 - } 3352 - }, 3353 - "node_modules/possible-typed-array-names": { 3354 - "version": "1.1.0", 3355 - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", 3356 - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", 3357 - "dev": true, 3358 - "license": "MIT", 3359 - "engines": { 3360 - "node": ">= 0.4" 3361 - } 3362 - }, 3363 - "node_modules/postcss": { 3364 - "version": "8.5.6", 3365 - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", 3366 - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", 3367 - "funding": [ 3368 - { 3369 - "type": "opencollective", 3370 - "url": "https://opencollective.com/postcss/" 3371 - }, 3372 - { 3373 - "type": "tidelift", 3374 - "url": "https://tidelift.com/funding/github/npm/postcss" 3375 - }, 3376 - { 3377 - "type": "github", 3378 - "url": "https://github.com/sponsors/ai" 3379 - } 3380 - ], 3381 - "license": "MIT", 3382 - "dependencies": { 3383 - "nanoid": "^3.3.11", 3384 - "picocolors": "^1.1.1", 3385 - "source-map-js": "^1.2.1" 3386 - }, 3387 - "engines": { 3388 - "node": "^10 || ^12 || >=14" 3389 - } 3390 - }, 3391 - "node_modules/prelude-ls": { 3392 - "version": "1.2.1", 3393 - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 3394 - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 3395 - "dev": true, 3396 - "license": "MIT", 3397 - "engines": { 3398 - "node": ">= 0.8.0" 3399 - } 3400 - }, 3401 - "node_modules/punycode": { 3402 - "version": "2.3.1", 3403 - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 3404 - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 3405 - "dev": true, 3406 - "license": "MIT", 3407 - "engines": { 3408 - "node": ">=6" 3409 - } 3410 - }, 3411 - "node_modules/reflect.getprototypeof": { 3412 - "version": "1.0.10", 3413 - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", 3414 - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", 3415 - "dev": true, 3416 - "license": "MIT", 3417 - "dependencies": { 3418 - "call-bind": "^1.0.8", 3419 - "define-properties": "^1.2.1", 3420 - "es-abstract": "^1.23.9", 3421 - "es-errors": "^1.3.0", 3422 - "es-object-atoms": "^1.0.0", 3423 - "get-intrinsic": "^1.2.7", 3424 - "get-proto": "^1.0.1", 3425 - "which-builtin-type": "^1.2.1" 3426 - }, 3427 - "engines": { 3428 - "node": ">= 0.4" 3429 - }, 3430 - "funding": { 3431 - "url": "https://github.com/sponsors/ljharb" 3432 - } 3433 - }, 3434 - "node_modules/regexp.prototype.flags": { 3435 - "version": "1.5.4", 3436 - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", 3437 - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", 3438 - "dev": true, 3439 - "license": "MIT", 3440 - "dependencies": { 3441 - "call-bind": "^1.0.8", 3442 - "define-properties": "^1.2.1", 3443 - "es-errors": "^1.3.0", 3444 - "get-proto": "^1.0.1", 3445 - "gopd": "^1.2.0", 3446 - "set-function-name": "^2.0.2" 3447 - }, 3448 - "engines": { 3449 - "node": ">= 0.4" 3450 - }, 3451 - "funding": { 3452 - "url": "https://github.com/sponsors/ljharb" 3453 - } 3454 - }, 3455 - "node_modules/resolve": { 3456 - "version": "1.22.10", 3457 - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", 3458 - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", 3459 - "dev": true, 3460 - "license": "MIT", 3461 - "dependencies": { 3462 - "is-core-module": "^2.16.0", 3463 - "path-parse": "^1.0.7", 3464 - "supports-preserve-symlinks-flag": "^1.0.0" 3465 - }, 3466 - "bin": { 3467 - "resolve": "bin/resolve" 3468 - }, 3469 - "engines": { 3470 - "node": ">= 0.4" 3471 - }, 3472 - "funding": { 3473 - "url": "https://github.com/sponsors/ljharb" 3474 - } 3475 - }, 3476 - "node_modules/resolve-from": { 3477 - "version": "4.0.0", 3478 - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 3479 - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 3480 - "dev": true, 3481 - "license": "MIT", 3482 - "engines": { 3483 - "node": ">=4" 3484 - } 3485 - }, 3486 - "node_modules/rollup": { 3487 - "version": "4.52.5", 3488 - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", 3489 - "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", 3490 - "license": "MIT", 3491 - "dependencies": { 3492 - "@types/estree": "1.0.8" 3493 - }, 3494 - "bin": { 3495 - "rollup": "dist/bin/rollup" 3496 - }, 3497 - "engines": { 3498 - "node": ">=18.0.0", 3499 - "npm": ">=8.0.0" 3500 - }, 3501 - "optionalDependencies": { 3502 - "@rollup/rollup-android-arm-eabi": "4.52.5", 3503 - "@rollup/rollup-android-arm64": "4.52.5", 3504 - "@rollup/rollup-darwin-arm64": "4.52.5", 3505 - "@rollup/rollup-darwin-x64": "4.52.5", 3506 - "@rollup/rollup-freebsd-arm64": "4.52.5", 3507 - "@rollup/rollup-freebsd-x64": "4.52.5", 3508 - "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", 3509 - "@rollup/rollup-linux-arm-musleabihf": "4.52.5", 3510 - "@rollup/rollup-linux-arm64-gnu": "4.52.5", 3511 - "@rollup/rollup-linux-arm64-musl": "4.52.5", 3512 - "@rollup/rollup-linux-loong64-gnu": "4.52.5", 3513 - "@rollup/rollup-linux-ppc64-gnu": "4.52.5", 3514 - "@rollup/rollup-linux-riscv64-gnu": "4.52.5", 3515 - "@rollup/rollup-linux-riscv64-musl": "4.52.5", 3516 - "@rollup/rollup-linux-s390x-gnu": "4.52.5", 3517 - "@rollup/rollup-linux-x64-gnu": "4.52.5", 3518 - "@rollup/rollup-linux-x64-musl": "4.52.5", 3519 - "@rollup/rollup-openharmony-arm64": "4.52.5", 3520 - "@rollup/rollup-win32-arm64-msvc": "4.52.5", 3521 - "@rollup/rollup-win32-ia32-msvc": "4.52.5", 3522 - "@rollup/rollup-win32-x64-gnu": "4.52.5", 3523 - "@rollup/rollup-win32-x64-msvc": "4.52.5", 3524 - "fsevents": "~2.3.2" 3525 - } 3526 - }, 3527 - "node_modules/safe-array-concat": { 3528 - "version": "1.1.3", 3529 - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", 3530 - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", 3531 - "dev": true, 3532 - "license": "MIT", 3533 - "dependencies": { 3534 - "call-bind": "^1.0.8", 3535 - "call-bound": "^1.0.2", 3536 - "get-intrinsic": "^1.2.6", 3537 - "has-symbols": "^1.1.0", 3538 - "isarray": "^2.0.5" 3539 - }, 3540 - "engines": { 3541 - "node": ">=0.4" 3542 - }, 3543 - "funding": { 3544 - "url": "https://github.com/sponsors/ljharb" 3545 - } 3546 - }, 3547 - "node_modules/safe-push-apply": { 3548 - "version": "1.0.0", 3549 - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", 3550 - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", 3551 - "dev": true, 3552 - "license": "MIT", 3553 - "dependencies": { 3554 - "es-errors": "^1.3.0", 3555 - "isarray": "^2.0.5" 3556 - }, 3557 - "engines": { 3558 - "node": ">= 0.4" 3559 - }, 3560 - "funding": { 3561 - "url": "https://github.com/sponsors/ljharb" 3562 - } 3563 - }, 3564 - "node_modules/safe-regex-test": { 3565 - "version": "1.1.0", 3566 - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", 3567 - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", 3568 - "dev": true, 3569 - "license": "MIT", 3570 - "dependencies": { 3571 - "call-bound": "^1.0.2", 3572 - "es-errors": "^1.3.0", 3573 - "is-regex": "^1.2.1" 3574 - }, 3575 - "engines": { 3576 - "node": ">= 0.4" 3577 - }, 3578 - "funding": { 3579 - "url": "https://github.com/sponsors/ljharb" 3580 - } 3581 - }, 3582 - "node_modules/semver": { 3583 - "version": "6.3.1", 3584 - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", 3585 - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", 3586 - "dev": true, 3587 - "license": "ISC", 3588 - "bin": { 3589 - "semver": "bin/semver.js" 3590 - } 3591 - }, 3592 - "node_modules/set-function-length": { 3593 - "version": "1.2.2", 3594 - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", 3595 - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", 3596 - "dev": true, 3597 - "license": "MIT", 3598 - "dependencies": { 3599 - "define-data-property": "^1.1.4", 3600 - "es-errors": "^1.3.0", 3601 - "function-bind": "^1.1.2", 3602 - "get-intrinsic": "^1.2.4", 3603 - "gopd": "^1.0.1", 3604 - "has-property-descriptors": "^1.0.2" 3605 - }, 3606 - "engines": { 3607 - "node": ">= 0.4" 3608 - } 3609 - }, 3610 - "node_modules/set-function-name": { 3611 - "version": "2.0.2", 3612 - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", 3613 - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", 3614 - "dev": true, 3615 - "license": "MIT", 3616 - "dependencies": { 3617 - "define-data-property": "^1.1.4", 3618 - "es-errors": "^1.3.0", 3619 - "functions-have-names": "^1.2.3", 3620 - "has-property-descriptors": "^1.0.2" 3621 - }, 3622 - "engines": { 3623 - "node": ">= 0.4" 3624 - } 3625 - }, 3626 - "node_modules/set-proto": { 3627 - "version": "1.0.0", 3628 - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", 3629 - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", 3630 - "dev": true, 3631 - "license": "MIT", 3632 - "dependencies": { 3633 - "dunder-proto": "^1.0.1", 3634 - "es-errors": "^1.3.0", 3635 - "es-object-atoms": "^1.0.0" 3636 - }, 3637 - "engines": { 3638 - "node": ">= 0.4" 3639 - } 3640 - }, 3641 - "node_modules/shebang-command": { 3642 - "version": "2.0.0", 3643 - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 3644 - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 3645 - "dev": true, 3646 - "license": "MIT", 3647 - "dependencies": { 3648 - "shebang-regex": "^3.0.0" 3649 - }, 3650 - "engines": { 3651 - "node": ">=8" 3652 - } 3653 - }, 3654 - "node_modules/shebang-regex": { 3655 - "version": "3.0.0", 3656 - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 3657 - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 3658 - "dev": true, 3659 - "license": "MIT", 3660 - "engines": { 3661 - "node": ">=8" 3662 - } 3663 - }, 3664 - "node_modules/side-channel": { 3665 - "version": "1.1.0", 3666 - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", 3667 - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", 3668 - "dev": true, 3669 - "license": "MIT", 3670 - "dependencies": { 3671 - "es-errors": "^1.3.0", 3672 - "object-inspect": "^1.13.3", 3673 - "side-channel-list": "^1.0.0", 3674 - "side-channel-map": "^1.0.1", 3675 - "side-channel-weakmap": "^1.0.2" 3676 - }, 3677 - "engines": { 3678 - "node": ">= 0.4" 3679 - }, 3680 - "funding": { 3681 - "url": "https://github.com/sponsors/ljharb" 3682 - } 3683 - }, 3684 - "node_modules/side-channel-list": { 3685 - "version": "1.0.0", 3686 - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", 3687 - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", 3688 - "dev": true, 3689 - "license": "MIT", 3690 - "dependencies": { 3691 - "es-errors": "^1.3.0", 3692 - "object-inspect": "^1.13.3" 3693 - }, 3694 - "engines": { 3695 - "node": ">= 0.4" 3696 - }, 3697 - "funding": { 3698 - "url": "https://github.com/sponsors/ljharb" 3699 - } 3700 - }, 3701 - "node_modules/side-channel-map": { 3702 - "version": "1.0.1", 3703 - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", 3704 - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", 3705 - "dev": true, 3706 - "license": "MIT", 3707 - "dependencies": { 3708 - "call-bound": "^1.0.2", 3709 - "es-errors": "^1.3.0", 3710 - "get-intrinsic": "^1.2.5", 3711 - "object-inspect": "^1.13.3" 3712 - }, 3713 - "engines": { 3714 - "node": ">= 0.4" 3715 - }, 3716 - "funding": { 3717 - "url": "https://github.com/sponsors/ljharb" 3718 - } 3719 - }, 3720 - "node_modules/side-channel-weakmap": { 3721 - "version": "1.0.2", 3722 - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", 3723 - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", 3724 - "dev": true, 3725 - "license": "MIT", 3726 - "dependencies": { 3727 - "call-bound": "^1.0.2", 3728 - "es-errors": "^1.3.0", 3729 - "get-intrinsic": "^1.2.5", 3730 - "object-inspect": "^1.13.3", 3731 - "side-channel-map": "^1.0.1" 3732 - }, 3733 - "engines": { 3734 - "node": ">= 0.4" 3735 - }, 3736 - "funding": { 3737 - "url": "https://github.com/sponsors/ljharb" 3738 - } 3739 - }, 3740 - "node_modules/source-map-js": { 3741 - "version": "1.2.1", 3742 - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 3743 - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 3744 - "license": "BSD-3-Clause", 3745 - "engines": { 3746 - "node": ">=0.10.0" 3747 - } 3748 - }, 3749 - "node_modules/stop-iteration-iterator": { 3750 - "version": "1.1.0", 3751 - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", 3752 - "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", 3753 - "dev": true, 3754 - "license": "MIT", 3755 - "dependencies": { 3756 - "es-errors": "^1.3.0", 3757 - "internal-slot": "^1.1.0" 3758 - }, 3759 - "engines": { 3760 - "node": ">= 0.4" 3761 - } 3762 - }, 3763 - "node_modules/string.prototype.trim": { 3764 - "version": "1.2.10", 3765 - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", 3766 - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", 3767 - "dev": true, 3768 - "license": "MIT", 3769 - "dependencies": { 3770 - "call-bind": "^1.0.8", 3771 - "call-bound": "^1.0.2", 3772 - "define-data-property": "^1.1.4", 3773 - "define-properties": "^1.2.1", 3774 - "es-abstract": "^1.23.5", 3775 - "es-object-atoms": "^1.0.0", 3776 - "has-property-descriptors": "^1.0.2" 3777 - }, 3778 - "engines": { 3779 - "node": ">= 0.4" 3780 - }, 3781 - "funding": { 3782 - "url": "https://github.com/sponsors/ljharb" 3783 - } 3784 - }, 3785 - "node_modules/string.prototype.trimend": { 3786 - "version": "1.0.9", 3787 - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", 3788 - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", 3789 - "dev": true, 3790 - "license": "MIT", 3791 - "dependencies": { 3792 - "call-bind": "^1.0.8", 3793 - "call-bound": "^1.0.2", 3794 - "define-properties": "^1.2.1", 3795 - "es-object-atoms": "^1.0.0" 3796 - }, 3797 - "engines": { 3798 - "node": ">= 0.4" 3799 - }, 3800 - "funding": { 3801 - "url": "https://github.com/sponsors/ljharb" 3802 - } 3803 - }, 3804 - "node_modules/string.prototype.trimstart": { 3805 - "version": "1.0.8", 3806 - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", 3807 - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", 3808 - "dev": true, 3809 - "license": "MIT", 3810 - "dependencies": { 3811 - "call-bind": "^1.0.7", 3812 - "define-properties": "^1.2.1", 3813 - "es-object-atoms": "^1.0.0" 3814 - }, 3815 - "engines": { 3816 - "node": ">= 0.4" 3817 - }, 3818 - "funding": { 3819 - "url": "https://github.com/sponsors/ljharb" 3820 - } 3821 - }, 3822 - "node_modules/strip-bom": { 3823 - "version": "3.0.0", 3824 - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", 3825 - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", 3826 - "dev": true, 3827 - "license": "MIT", 3828 - "engines": { 3829 - "node": ">=4" 3830 - } 3831 - }, 3832 - "node_modules/strip-json-comments": { 3833 - "version": "3.1.1", 3834 - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 3835 - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 3836 - "dev": true, 3837 - "license": "MIT", 3838 - "engines": { 3839 - "node": ">=8" 3840 - }, 3841 - "funding": { 3842 - "url": "https://github.com/sponsors/sindresorhus" 3843 - } 3844 - }, 3845 - "node_modules/supports-color": { 3846 - "version": "7.2.0", 3847 - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 3848 - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 3849 - "dev": true, 3850 - "license": "MIT", 3851 - "dependencies": { 3852 - "has-flag": "^4.0.0" 3853 - }, 3854 - "engines": { 3855 - "node": ">=8" 3856 - } 3857 - }, 3858 - "node_modules/supports-preserve-symlinks-flag": { 3859 - "version": "1.0.0", 3860 - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 3861 - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 3862 - "dev": true, 3863 - "license": "MIT", 3864 - "engines": { 3865 - "node": ">= 0.4" 3866 - }, 3867 - "funding": { 3868 - "url": "https://github.com/sponsors/ljharb" 3869 - } 3870 - }, 3871 - "node_modules/tinyglobby": { 3872 - "version": "0.2.15", 3873 - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", 3874 - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", 3875 - "license": "MIT", 3876 - "dependencies": { 3877 - "fdir": "^6.5.0", 3878 - "picomatch": "^4.0.3" 3879 - }, 3880 - "engines": { 3881 - "node": ">=12.0.0" 3882 - }, 3883 - "funding": { 3884 - "url": "https://github.com/sponsors/SuperchupuDev" 3885 - } 3886 - }, 3887 - "node_modules/tlds": { 3888 - "version": "1.260.0", 3889 - "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.260.0.tgz", 3890 - "integrity": "sha512-78+28EWBhCEE7qlyaHA9OR3IPvbCLiDh3Ckla593TksfFc9vfTsgvH7eS+dr3o9qr31gwGbogcI16yN91PoRjQ==", 3891 - "license": "MIT", 3892 - "bin": { 3893 - "tlds": "bin.js" 3894 - } 3895 - }, 3896 - "node_modules/tsconfig-paths": { 3897 - "version": "3.15.0", 3898 - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", 3899 - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", 3900 - "dev": true, 3901 - "license": "MIT", 3902 - "dependencies": { 3903 - "@types/json5": "^0.0.29", 3904 - "json5": "^1.0.2", 3905 - "minimist": "^1.2.6", 3906 - "strip-bom": "^3.0.0" 3907 - } 3908 - }, 3909 - "node_modules/type-check": { 3910 - "version": "0.4.0", 3911 - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 3912 - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 3913 - "dev": true, 3914 - "license": "MIT", 3915 - "dependencies": { 3916 - "prelude-ls": "^1.2.1" 3917 - }, 3918 - "engines": { 3919 - "node": ">= 0.8.0" 3920 - } 3921 - }, 3922 - "node_modules/typed-array-buffer": { 3923 - "version": "1.0.3", 3924 - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", 3925 - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", 3926 - "dev": true, 3927 - "license": "MIT", 3928 - "dependencies": { 3929 - "call-bound": "^1.0.3", 3930 - "es-errors": "^1.3.0", 3931 - "is-typed-array": "^1.1.14" 3932 - }, 3933 - "engines": { 3934 - "node": ">= 0.4" 3935 - } 3936 - }, 3937 - "node_modules/typed-array-byte-length": { 3938 - "version": "1.0.3", 3939 - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", 3940 - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", 3941 - "dev": true, 3942 - "license": "MIT", 3943 - "dependencies": { 3944 - "call-bind": "^1.0.8", 3945 - "for-each": "^0.3.3", 3946 - "gopd": "^1.2.0", 3947 - "has-proto": "^1.2.0", 3948 - "is-typed-array": "^1.1.14" 3949 - }, 3950 - "engines": { 3951 - "node": ">= 0.4" 3952 - }, 3953 - "funding": { 3954 - "url": "https://github.com/sponsors/ljharb" 3955 - } 3956 - }, 3957 - "node_modules/typed-array-byte-offset": { 3958 - "version": "1.0.4", 3959 - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", 3960 - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", 3961 - "dev": true, 3962 - "license": "MIT", 3963 - "dependencies": { 3964 - "available-typed-arrays": "^1.0.7", 3965 - "call-bind": "^1.0.8", 3966 - "for-each": "^0.3.3", 3967 - "gopd": "^1.2.0", 3968 - "has-proto": "^1.2.0", 3969 - "is-typed-array": "^1.1.15", 3970 - "reflect.getprototypeof": "^1.0.9" 3971 - }, 3972 - "engines": { 3973 - "node": ">= 0.4" 3974 - }, 3975 - "funding": { 3976 - "url": "https://github.com/sponsors/ljharb" 3977 - } 3978 - }, 3979 - "node_modules/typed-array-length": { 3980 - "version": "1.0.7", 3981 - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", 3982 - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", 3983 - "dev": true, 3984 - "license": "MIT", 3985 - "dependencies": { 3986 - "call-bind": "^1.0.7", 3987 - "for-each": "^0.3.3", 3988 - "gopd": "^1.0.1", 3989 - "is-typed-array": "^1.1.13", 3990 - "possible-typed-array-names": "^1.0.0", 3991 - "reflect.getprototypeof": "^1.0.6" 3992 - }, 3993 - "engines": { 3994 - "node": ">= 0.4" 3995 - }, 3996 - "funding": { 3997 - "url": "https://github.com/sponsors/ljharb" 3998 - } 3999 - }, 4000 - "node_modules/uint8arrays": { 4001 - "version": "3.0.0", 4002 - "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 4003 - "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 4004 - "license": "MIT", 4005 - "dependencies": { 4006 - "multiformats": "^9.4.2" 4007 - } 4008 - }, 4009 - "node_modules/unbox-primitive": { 4010 - "version": "1.1.0", 4011 - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", 4012 - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", 4013 - "dev": true, 4014 - "license": "MIT", 4015 - "dependencies": { 4016 - "call-bound": "^1.0.3", 4017 - "has-bigints": "^1.0.2", 4018 - "has-symbols": "^1.1.0", 4019 - "which-boxed-primitive": "^1.1.1" 4020 - }, 4021 - "engines": { 4022 - "node": ">= 0.4" 4023 - }, 4024 - "funding": { 4025 - "url": "https://github.com/sponsors/ljharb" 4026 - } 4027 - }, 4028 - "node_modules/uri-js": { 4029 - "version": "4.4.1", 4030 - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 4031 - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 4032 - "dev": true, 4033 - "license": "BSD-2-Clause", 4034 - "dependencies": { 4035 - "punycode": "^2.1.0" 4036 - } 4037 - }, 4038 - "node_modules/vite": { 4039 - "version": "7.1.10", 4040 - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.10.tgz", 4041 - "integrity": "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA==", 4042 - "license": "MIT", 4043 - "dependencies": { 4044 - "esbuild": "^0.25.0", 4045 - "fdir": "^6.5.0", 4046 - "picomatch": "^4.0.3", 4047 - "postcss": "^8.5.6", 4048 - "rollup": "^4.43.0", 4049 - "tinyglobby": "^0.2.15" 4050 - }, 4051 - "bin": { 4052 - "vite": "bin/vite.js" 4053 - }, 4054 - "engines": { 4055 - "node": "^20.19.0 || >=22.12.0" 4056 - }, 4057 - "funding": { 4058 - "url": "https://github.com/vitejs/vite?sponsor=1" 4059 - }, 4060 - "optionalDependencies": { 4061 - "fsevents": "~2.3.3" 4062 - }, 4063 - "peerDependencies": { 4064 - "@types/node": "^20.19.0 || >=22.12.0", 4065 - "jiti": ">=1.21.0", 4066 - "less": "^4.0.0", 4067 - "lightningcss": "^1.21.0", 4068 - "sass": "^1.70.0", 4069 - "sass-embedded": "^1.70.0", 4070 - "stylus": ">=0.54.8", 4071 - "sugarss": "^5.0.0", 4072 - "terser": "^5.16.0", 4073 - "tsx": "^4.8.1", 4074 - "yaml": "^2.4.2" 4075 - }, 4076 - "peerDependenciesMeta": { 4077 - "@types/node": { 4078 - "optional": true 4079 - }, 4080 - "jiti": { 4081 - "optional": true 4082 - }, 4083 - "less": { 4084 - "optional": true 4085 - }, 4086 - "lightningcss": { 4087 - "optional": true 4088 - }, 4089 - "sass": { 4090 - "optional": true 4091 - }, 4092 - "sass-embedded": { 4093 - "optional": true 4094 - }, 4095 - "stylus": { 4096 - "optional": true 4097 - }, 4098 - "sugarss": { 4099 - "optional": true 4100 - }, 4101 - "terser": { 4102 - "optional": true 4103 - }, 4104 - "tsx": { 4105 - "optional": true 4106 - }, 4107 - "yaml": { 4108 - "optional": true 4109 - } 4110 - } 4111 - }, 4112 - "node_modules/vite-plugin-full-reload": { 4113 - "version": "1.2.0", 4114 - "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz", 4115 - "integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==", 4116 - "license": "MIT", 4117 - "dependencies": { 4118 - "picocolors": "^1.0.0", 4119 - "picomatch": "^2.3.1" 4120 - } 4121 - }, 4122 - "node_modules/vite-plugin-full-reload/node_modules/picomatch": { 4123 - "version": "2.3.1", 4124 - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 4125 - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 4126 - "license": "MIT", 4127 - "engines": { 4128 - "node": ">=8.6" 4129 - }, 4130 - "funding": { 4131 - "url": "https://github.com/sponsors/jonschlinkert" 4132 - } 4133 - }, 4134 - "node_modules/vite-rs-plugin": { 4135 - "version": "1.0.1", 4136 - "resolved": "https://registry.npmjs.org/vite-rs-plugin/-/vite-rs-plugin-1.0.1.tgz", 4137 - "integrity": "sha512-YhgflKQIRzuS5x66J3yICoVLH25D2fNU+jThK8tpYl/jGrXeIKT4w5VH1lkLPRC0SjK2ZCm9S6K9Z2ZFVDHjPQ==", 4138 - "license": "MIT", 4139 - "dependencies": { 4140 - "vite-plugin-full-reload": "^1.2.0" 4141 - }, 4142 - "bin": { 4143 - "cleanup": "bin/cleanForBuild.js" 4144 - }, 4145 - "peerDependencies": { 4146 - "vite": "^5.0.0" 4147 - } 4148 - }, 4149 - "node_modules/which": { 4150 - "version": "2.0.2", 4151 - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 4152 - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 4153 - "dev": true, 4154 - "license": "ISC", 4155 - "dependencies": { 4156 - "isexe": "^2.0.0" 4157 - }, 4158 - "bin": { 4159 - "node-which": "bin/node-which" 4160 - }, 4161 - "engines": { 4162 - "node": ">= 8" 4163 - } 4164 - }, 4165 - "node_modules/which-boxed-primitive": { 4166 - "version": "1.1.1", 4167 - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", 4168 - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", 4169 - "dev": true, 4170 - "license": "MIT", 4171 - "dependencies": { 4172 - "is-bigint": "^1.1.0", 4173 - "is-boolean-object": "^1.2.1", 4174 - "is-number-object": "^1.1.1", 4175 - "is-string": "^1.1.1", 4176 - "is-symbol": "^1.1.1" 4177 - }, 4178 - "engines": { 4179 - "node": ">= 0.4" 4180 - }, 4181 - "funding": { 4182 - "url": "https://github.com/sponsors/ljharb" 4183 - } 4184 - }, 4185 - "node_modules/which-builtin-type": { 4186 - "version": "1.2.1", 4187 - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", 4188 - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", 4189 - "dev": true, 4190 - "license": "MIT", 4191 - "dependencies": { 4192 - "call-bound": "^1.0.2", 4193 - "function.prototype.name": "^1.1.6", 4194 - "has-tostringtag": "^1.0.2", 4195 - "is-async-function": "^2.0.0", 4196 - "is-date-object": "^1.1.0", 4197 - "is-finalizationregistry": "^1.1.0", 4198 - "is-generator-function": "^1.0.10", 4199 - "is-regex": "^1.2.1", 4200 - "is-weakref": "^1.0.2", 4201 - "isarray": "^2.0.5", 4202 - "which-boxed-primitive": "^1.1.0", 4203 - "which-collection": "^1.0.2", 4204 - "which-typed-array": "^1.1.16" 4205 - }, 4206 - "engines": { 4207 - "node": ">= 0.4" 4208 - }, 4209 - "funding": { 4210 - "url": "https://github.com/sponsors/ljharb" 4211 - } 4212 - }, 4213 - "node_modules/which-collection": { 4214 - "version": "1.0.2", 4215 - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", 4216 - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", 4217 - "dev": true, 4218 - "license": "MIT", 4219 - "dependencies": { 4220 - "is-map": "^2.0.3", 4221 - "is-set": "^2.0.3", 4222 - "is-weakmap": "^2.0.2", 4223 - "is-weakset": "^2.0.3" 4224 - }, 4225 - "engines": { 4226 - "node": ">= 0.4" 4227 - }, 4228 - "funding": { 4229 - "url": "https://github.com/sponsors/ljharb" 4230 - } 4231 - }, 4232 - "node_modules/which-typed-array": { 4233 - "version": "1.1.19", 4234 - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", 4235 - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", 4236 - "dev": true, 4237 - "license": "MIT", 4238 - "dependencies": { 4239 - "available-typed-arrays": "^1.0.7", 4240 - "call-bind": "^1.0.8", 4241 - "call-bound": "^1.0.4", 4242 - "for-each": "^0.3.5", 4243 - "get-proto": "^1.0.1", 4244 - "gopd": "^1.2.0", 4245 - "has-tostringtag": "^1.0.2" 4246 - }, 4247 - "engines": { 4248 - "node": ">= 0.4" 4249 - }, 4250 - "funding": { 4251 - "url": "https://github.com/sponsors/ljharb" 4252 - } 4253 - }, 4254 - "node_modules/word-wrap": { 4255 - "version": "1.2.5", 4256 - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", 4257 - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", 4258 - "dev": true, 4259 - "license": "MIT", 4260 - "engines": { 4261 - "node": ">=0.10.0" 4262 - } 4263 - }, 4264 - "node_modules/yocto-queue": { 4265 - "version": "0.1.0", 4266 - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 4267 - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 4268 - "dev": true, 4269 - "license": "MIT", 4270 - "engines": { 4271 - "node": ">=10" 4272 - }, 4273 - "funding": { 4274 - "url": "https://github.com/sponsors/sindresorhus" 4275 - } 4276 - }, 4277 - "node_modules/zod": { 4278 - "version": "3.25.76", 4279 - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", 4280 - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", 4281 - "license": "MIT", 4282 - "funding": { 4283 - "url": "https://github.com/sponsors/colinhacks" 4284 - } 4285 - } 4286 - } 4287 - }
-31
web/ui-code/package.json
··· 1 - { 2 - "name": "ui-code", 3 - "private": true, 4 - "version": "0.0.0", 5 - "type": "module", 6 - "scripts": { 7 - "dev": "vite", 8 - "build": "vite build", 9 - "build:watch": "vite build -w", 10 - "preview": "vite preview", 11 - "lint": "eslint src --ext .js", 12 - "lint:fix": "eslint src --ext .js --fix" 13 - }, 14 - "devDependencies": { 15 - "eslint": "^9.34.0", 16 - "eslint-plugin-import": "^2.32.0", 17 - "vite": "^7.1.7" 18 - }, 19 - "dependencies": { 20 - "@atcute/cbor": "^2.2.7", 21 - "@atcute/client": "^4.0.4", 22 - "@atcute/crypto": "^2.2.5", 23 - "@atcute/did-plc": "^0.1.7", 24 - "@atcute/identity-resolver": "^1.1.3", 25 - "@atcute/multibase": "^1.1.6", 26 - "@atproto/api": "^0.16.7", 27 - "alpinejs": "^3.15.0", 28 - "vite-plugin-full-reload": "^1.2.0", 29 - "vite-rs-plugin": "1.0.1" 30 - } 31 - }
-83
web/ui-code/src/atprotoUtils.js
··· 1 - import { 2 - CompositeDidDocumentResolver, CompositeHandleResolver, 3 - DohJsonHandleResolver, 4 - PlcDidDocumentResolver, WebDidDocumentResolver, 5 - WellKnownHandleResolver 6 - } from '@atcute/identity-resolver'; 7 - import {Client, simpleFetchHandler} from '@atcute/client'; 8 - 9 - const handleResolver = new CompositeHandleResolver({ 10 - strategy: 'race', 11 - methods: { 12 - dns: new DohJsonHandleResolver({ 13 - dohUrl: 'https://mozilla.cloudflare-dns.com/dns-query', 14 - }), 15 - http: new WellKnownHandleResolver(), 16 - }, 17 - }); 18 - 19 - const docResolver = new CompositeDidDocumentResolver({ 20 - methods: { 21 - plc: new PlcDidDocumentResolver(), 22 - web: new WebDidDocumentResolver(), 23 - }, 24 - }); 25 - 26 - const cleanHandle = (handle) => 27 - handle.replace('@', '').trim().replace( 28 - /[\u202A\u202C\u200E\u200F\u2066-\u2069]/g, 29 - '', 30 - ); 31 - 32 - 33 - async function handleAndPDSResolver(handle) { 34 - let usersDid = null; 35 - if (handle.startsWith('did:')) { 36 - usersDid = handle; 37 - } else { 38 - const cleanedHandle = cleanHandle(handle); 39 - usersDid = await handleResolver.resolve(cleanedHandle); 40 - } 41 - const didDoc = await docResolver.resolve(usersDid); 42 - 43 - let pds; 44 - try { 45 - pds = didDoc.service?.filter((s) => 46 - s.type === 'AtprotoPersonalDataServer' 47 - )[0].serviceEndpoint; 48 - } catch (error) { 49 - console.error(error); 50 - throw new Error('Could not find a PDS in the DID document.'); 51 - } 52 - return {usersDid, pds}; 53 - } 54 - 55 - 56 - async function fetchPDSMooverDIDWeb(baseUrl) { 57 - const response = await fetch(`${baseUrl}/.well-known/did.json`); 58 - if (!response.ok) { 59 - throw new Error(`Failed to fetch DID document: ${response.status}`); 60 - } 61 - const didDoc = await response.json(); 62 - return didDoc.id; 63 - } 64 - 65 - async function describeServer() { 66 - const baseUrl = window.location.origin; 67 - const handler = simpleFetchHandler({service: baseUrl}); 68 - const rpc = new Client({ 69 - handler 70 - }); 71 - 72 - let describeServerResponse = await rpc.get('com.pdsmoover.backup.describeServer'); 73 - if (describeServerResponse.ok) { 74 - return describeServerResponse.data; 75 - } else { 76 - throw new Error('Could not describe server'); 77 - } 78 - 79 - 80 - } 81 - 82 - 83 - export {handleResolver, docResolver, cleanHandle, handleAndPDSResolver, fetchPDSMooverDIDWeb, describeServer};
-216
web/ui-code/src/backup.js
··· 1 - import {Client, CredentialManager, ok} from '@atcute/client'; 2 - import {fetchPDSMooverDIDWeb, handleAndPDSResolver} from './atprotoUtils.js'; 3 - 4 - 5 - class BackupService { 6 - constructor() { 7 - /** 8 - * 9 - * @type {Client} 10 - */ 11 - this.atCuteClient = null; 12 - /** 13 - * 14 - * @type {CredentialManager} 15 - */ 16 - this.atCuteCredentialManager = null; 17 - } 18 - 19 - // Perform backup sign-up using user credentials. If the server requires 2FA, 20 - // it will throw with error.error === 'AuthFactorTokenRequired'. 21 - async loginAndStatus(identifier, password, onStatus = null, twoFactorCode = null) { 22 - let {pds} = await handleAndPDSResolver(identifier); 23 - 24 - 25 - const manager = new CredentialManager({ 26 - service: pds 27 - }); 28 - 29 - //TODO prob should be a function param, but eh 30 - const didWeb = await fetchPDSMooverDIDWeb(window.location.origin); 31 - 32 - const rpc = new Client({ 33 - handler: manager, 34 - proxy: { 35 - did: didWeb, 36 - serviceId: '#repo_backup' 37 - } 38 - }); 39 - 40 - try { 41 - if (onStatus) onStatus('Signing in…'); 42 - 43 - let loginInput = { 44 - identifier, 45 - password, 46 - 47 - }; 48 - if (twoFactorCode) { 49 - loginInput.code = twoFactorCode; 50 - } 51 - await manager.login(loginInput); 52 - 53 - 54 - // Make the client/manager available regardless of repo status so we can sign up if needed. 55 - this.atCuteClient = rpc; 56 - this.atCuteCredentialManager = manager; 57 - 58 - if (onStatus) onStatus('Checking backup status'); 59 - const result = await rpc.get('com.pdsmoover.backup.getRepoStatus', { 60 - params: { 61 - did: manager.session.did.toString() 62 - } 63 - }); 64 - if (result.ok) { 65 - return result.data; 66 - } else { 67 - switch (result.data.error) { 68 - case 'RepoNotFound': 69 - console.log('Repo not found'); 70 - return null; 71 - default: 72 - throw result.data.error; 73 - } 74 - 75 - } 76 - } catch (err) { 77 - throw err; 78 - } 79 - } 80 - 81 - // Sign up for backups. Optionally create a new recovery key on signup. 82 - async signUp(onStatus = null) { 83 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 84 - throw new Error('Not signed in'); 85 - } 86 - if (onStatus) onStatus('Creating backup registration…'); 87 - const data = await ok( 88 - this.atCuteClient.post('com.pdsmoover.backup.signUp', { 89 - as: null, 90 - }) 91 - ); 92 - if (onStatus) onStatus('Backup registration complete'); 93 - return data; 94 - } 95 - 96 - async requestAPlcToken() { 97 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 98 - throw new Error('Not signed in'); 99 - } 100 - const rpc = new Client({ 101 - handler: this.atCuteCredentialManager, 102 - }); 103 - 104 - let response = await rpc.post('com.atproto.identity.requestPlcOperationSignature', { 105 - as: null, 106 - }); 107 - if (!response.ok) { 108 - throw new Error(response.data?.message || 'Failed to request PLC token'); 109 - } 110 - } 111 - 112 - async addANewRotationKey(plcToken, rotationKey) { 113 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 114 - throw new Error('Not signed in'); 115 - } 116 - 117 - 118 - const rpc = new Client({ 119 - handler: this.atCuteCredentialManager, 120 - }); 121 - 122 - let getDidCredentials = await rpc.get('com.atproto.identity.getRecommendedDidCredentials'); 123 - 124 - if (getDidCredentials.ok) { 125 - const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? []; 126 - const updatedRotationKeys = [rotationKey, ...pdsProvidedRotationKeys]; 127 - 128 - const credentials = { 129 - ...getDidCredentials.data, 130 - rotationKeys: updatedRotationKeys, 131 - }; 132 - 133 - const signDocRes = await rpc.post('com.atproto.identity.signPlcOperation', { 134 - input: { 135 - token: plcToken, 136 - ...credentials, 137 - } 138 - }); 139 - 140 - if (signDocRes.ok) { 141 - const submitDocRes = await rpc.post('com.atproto.identity.submitPlcOperation', { 142 - input: signDocRes.data, 143 - as: null, 144 - }); 145 - 146 - if (!submitDocRes.ok) { 147 - throw new Error(submitDocRes.data?.message || 'Failed to submit PLC operation'); 148 - } 149 - 150 - } else { 151 - throw new Error(signDocRes.data?.message || 'Failed to sign PLC operation'); 152 - } 153 - 154 - 155 - } else { 156 - throw new Error(getDidCredentials.data?.message || 'Failed to get status'); 157 - } 158 - } 159 - 160 - async getUsersRepoStatus(onStatus = null) { 161 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 162 - throw new Error('Not signed in'); 163 - } 164 - if (onStatus) onStatus('Refreshing backup status…'); 165 - const result = await this.atCuteClient.get('com.pdsmoover.backup.getRepoStatus', { 166 - params: {did: this.atCuteCredentialManager.session.did.toString()} 167 - }); 168 - if (result.ok) { 169 - return result.data; 170 - } else { 171 - throw new Error(result.data?.message || 'Failed to get status'); 172 - } 173 - } 174 - 175 - async runBackupNow(onStatus = null) { 176 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 177 - throw new Error('Not signed in'); 178 - } 179 - if (onStatus) onStatus('Requesting backup…'); 180 - const res = await this.atCuteClient.post('com.pdsmoover.backup.requestBackup', {as: null, data: {}}); 181 - if (res.ok) { 182 - if (onStatus) onStatus('Backup requested.'); 183 - // After requesting, refresh status to reflect any immediate changes 184 - try { 185 - await this.refreshStatus(onStatus); 186 - } catch (_) { /* ignore */ 187 - } 188 - return true; 189 - } else { 190 - const err = res.data; 191 - if (err?.error === 'Timeout') { 192 - throw {error: 'Timeout', message: err?.message || 'Please wait a few minutes before requesting again.'}; 193 - } 194 - throw new Error(err?.message || 'Failed to request backup'); 195 - } 196 - } 197 - 198 - // Remove (delete) the user's backup repository 199 - async removeRepo(onStatus = null) { 200 - if (!this.atCuteClient || !this.atCuteCredentialManager) { 201 - throw new Error('Not signed in'); 202 - } 203 - if (onStatus) onStatus('Deleting backup repository…'); 204 - const res = await this.atCuteClient.post('com.pdsmoover.backup.removeRepo', {as: null, data: {}}); 205 - if (res.ok) { 206 - if (onStatus) onStatus('Backup repository deleted.'); 207 - return true; 208 - } else { 209 - const err = res.data; 210 - throw new Error(err?.message || 'Failed to delete backup repository'); 211 - } 212 - } 213 - } 214 - 215 - 216 - export {BackupService};
-24
web/ui-code/src/main.js
··· 1 - import {Migrator} from './pdsmoover.js'; 2 - import {MissingBlobs} from './missingBlobs.js'; 3 - import {BackupService} from './backup.js'; 4 - import {PlcOps} from './plc-ops.js'; 5 - import {MooverUtils} from './utils.js'; 6 - import {Restore} from './restore.js'; 7 - import {handleAndPDSResolver} from './atprotoUtils.js'; 8 - import Alpine from 'alpinejs'; 9 - 10 - 11 - window.Migrator = new Migrator(); 12 - window.MissingBlobs = new MissingBlobs(); 13 - window.BackupService = new BackupService(); 14 - window.PlcOps = new PlcOps(); 15 - window.MooverUtils = new MooverUtils(); 16 - window.Restore = new Restore(); 17 - window.handleAndPDSResolver = handleAndPDSResolver; 18 - 19 - window.Alpine = Alpine; 20 - 21 - Alpine.start(); 22 - 23 - export {Migrator, MissingBlobs}; 24 -
-153
web/ui-code/src/missingBlobs.js
··· 1 - //I need to condense this code with the rest of PDS MOOver cause it has a lot of overlap 2 - import {AtpAgent} from '@atproto/api'; 3 - import {handleAndPDSResolver} from './atprotoUtils.js'; 4 - 5 - 6 - class MissingBlobs { 7 - 8 - constructor() { 9 - this.currentPdsAgent = null; 10 - this.oldPdsAgent = null; 11 - this.did = null; 12 - this.currentPdsUrl = null; 13 - this.missingBlobs = []; 14 - 15 - } 16 - 17 - async currentAgentLogin( 18 - handle, 19 - password, 20 - twoFactorCode = null, 21 - ) { 22 - let {usersDid, pds} = await handleAndPDSResolver(handle); 23 - this.did = usersDid; 24 - this.currentPdsUrl = pds; 25 - const agent = new AtpAgent({ 26 - service: pds, 27 - }); 28 - 29 - if (twoFactorCode === null) { 30 - await agent.login({identifier: usersDid, password}); 31 - } else { 32 - await agent.login({identifier: usersDid, password: password, authFactorToken: twoFactorCode}); 33 - } 34 - 35 - this.currentPdsAgent = agent; 36 - 37 - const result = await agent.com.atproto.server.checkAccountStatus(); 38 - const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 39 - limit: 10, 40 - }); 41 - return {accountStatus: result.data, missingBlobsCount: missingBlobs.data.blobs.length}; 42 - } 43 - 44 - async oldAgentLogin( 45 - password, 46 - twoFactorCode = null, 47 - pdsUrl = null, 48 - ) { 49 - let oldPds = null; 50 - 51 - if (pdsUrl === null) { 52 - const response = await fetch(`https://plc.directory/${this.did}/log`); 53 - let auditLog = await response.json(); 54 - auditLog = auditLog.reverse(); 55 - let debugCount = 0; 56 - for (const entry of auditLog) { 57 - console.log(`Loop: ${debugCount++}`); 58 - console.log(entry); 59 - if (entry.services) { 60 - if (entry.services.atproto_pds) { 61 - if (entry.services.atproto_pds.type === 'AtprotoPersonalDataServer') { 62 - const pds = entry.services.atproto_pds.endpoint; 63 - console.log(`Found PDS: ${pds}`); 64 - if (pds.toLowerCase() !== this.currentPdsUrl.toLowerCase()) { 65 - oldPds = pds; 66 - break; 67 - } 68 - } 69 - } 70 - } 71 - } 72 - if (oldPds === null) { 73 - throw new Error('Could not find your old PDS'); 74 - } 75 - } else { 76 - oldPds = pdsUrl; 77 - } 78 - 79 - const agent = new AtpAgent({ 80 - service: oldPds, 81 - }); 82 - 83 - if (twoFactorCode === null) { 84 - await agent.login({identifier: this.did, password}); 85 - } else { 86 - await agent.login({identifier: this.did, password: password, authFactorToken: twoFactorCode}); 87 - } 88 - this.oldPdsAgent = agent; 89 - } 90 - 91 - async migrateMissingBlobs(statusUpdateHandler) { 92 - if (this.currentPdsAgent === null) { 93 - throw new Error('Current PDS agent is not set'); 94 - } 95 - if (this.oldPdsAgent === null) { 96 - throw new Error('Old PDS agent is not set'); 97 - } 98 - statusUpdateHandler('Starting to import blobs...'); 99 - 100 - // const currentAccountStatus = await this.currentPdsAgent.com.atproto.server.checkAccountStatus(); 101 - // let totalMissingBlobs = currentAccountStatus.data.expectedBlobs - currentAccountStatus.data.importedBlobs; 102 - let totalMissingBlobs = 0; 103 - let missingBlobCursor = undefined; 104 - let missingUploadedBlobs = 0; 105 - 106 - do { 107 - 108 - const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 109 - cursor: missingBlobCursor, 110 - //Test this cause it may be a big update 111 - limit: 1000, 112 - }); 113 - totalMissingBlobs += missingBlobs.data.blobs.length; 114 - 115 - for (const recordBlob of missingBlobs.data.blobs) { 116 - try { 117 - 118 - const blobRes = await this.oldPdsAgent.com.atproto.sync.getBlob({ 119 - did: this.did, 120 - cid: recordBlob.cid, 121 - }); 122 - let result = await this.currentPdsAgent.com.atproto.repo.uploadBlob(blobRes.data, { 123 - encoding: blobRes.headers['content-type'], 124 - }); 125 - 126 - if (result.status === 429) { 127 - statusUpdateHandler(`You are being rate limited. Will need to try again later to get the rest of the blobs. Migrated blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 128 - } 129 - 130 - if (missingUploadedBlobs % 2 === 0) { 131 - statusUpdateHandler(`Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs} (The total may increase as we find more)`); 132 - } 133 - missingUploadedBlobs++; 134 - } catch (error) { 135 - console.error(error); 136 - this.missingBlobs.push(recordBlob.cid); 137 - } 138 - } 139 - missingBlobCursor = missingBlobs.data.cursor; 140 - } while (missingBlobCursor); 141 - 142 - const accountStatus = await this.currentPdsAgent.com.atproto.server.checkAccountStatus(); 143 - const missingBlobs = await this.currentPdsAgent.com.atproto.repo.listMissingBlobs({ 144 - limit: 10, 145 - }); 146 - return {accountStatus: accountStatus.data, missingBlobsCount: missingBlobs.data.blobs.length}; 147 - 148 - 149 - } 150 - 151 - } 152 - 153 - export {MissingBlobs};
-361
web/ui-code/src/pdsmoover.js
··· 1 - import {docResolver, fetchPDSMooverDIDWeb, handleResolver} from './atprotoUtils.js'; 2 - import {AtpAgent} from '@atproto/api'; 3 - 4 - 5 - function safeStatusUpdate(statusUpdateHandler, status) { 6 - if (statusUpdateHandler) { 7 - statusUpdateHandler(status); 8 - } 9 - } 10 - 11 - 12 - class Migrator { 13 - constructor() { 14 - /** @type {AtpAgent} */ 15 - this.oldAgent = null; 16 - /** @type {AtpAgent} */ 17 - this.newAgent = null; 18 - this.missingBlobs = []; 19 - //State for reruns 20 - this.createNewAccount = true; 21 - this.migrateRepo = true; 22 - this.migrateBlobs = true; 23 - this.migrateMissingBlobs = true; 24 - this.migratePrefs = true; 25 - this.migratePlcRecord = true; 26 - } 27 - 28 - /** 29 - * This migrator is pretty cut and dry and makes a few assumptions 30 - * 1. You are using the same password between each account 31 - * 2. If this command fails for something like oauth 2fa code it throws an error and expects the same values when ran again. 32 - * @param {string} oldHandle - The handle you use on your old pds, something like alice.bsky.social 33 - * @param {string} password - Your password for your current login. Has to be your real password, no app password. When setting up a new account we reuse it as well for that account 34 - * @param {string} newPdsUrl - The new URL for your pds. Like https://coolnewpds.com 35 - * @param {string} newEmail - The email you want to use on the new pds (can be the same as the previous one as long as it's not already being used on the new pds) 36 - * @param {string} newHandle - The new handle you want, like alice.bsky.social, or if you already have a domain name set as a handle can use it myname.com. 37 - * @param {string|null} inviteCode - The invite code you got from the PDS you are migrating to. If null does not include one 38 - * @param {function|null} statusUpdateHandler - a function that takes a string used to update the UI. Like (status) => console.log(status) 39 - * @param {string|null} twoFactorCode - Optional, but needed if it fails with 2fa required 40 - */ 41 - async migrate(oldHandle, password, newPdsUrl, newEmail, newHandle, inviteCode, statusUpdateHandler = null, twoFactorCode = null) { 42 - 43 - //Copying the handle from bsky website adds some random unicodes on 44 - oldHandle = oldHandle.replace('@', '').trim().replace(/[\u202A\u202C\u200E\u200F\u2066-\u2069]/g, ''); 45 - let oldAgent; 46 - let usersDid; 47 - //If it's a bsky handle just go with the entryway and let it sort everything 48 - if (oldHandle.endsWith('.bsky.social')) { 49 - oldAgent = new AtpAgent({service: 'https://bsky.social'}); 50 - const publicAgent = new AtpAgent({service: 'https://public.api.bsky.app'}); 51 - const resolveIdentityFromEntryway = await publicAgent.com.atproto.identity.resolveHandle({handle: oldHandle}); 52 - usersDid = resolveIdentityFromEntryway.data.did; 53 - 54 - } else { 55 - //Resolves the did and finds the did document for the old PDS 56 - safeStatusUpdate(statusUpdateHandler, 'Resolving old PDS'); 57 - usersDid = await handleResolver.resolve(oldHandle); 58 - const didDoc = await docResolver.resolve(usersDid); 59 - safeStatusUpdate(statusUpdateHandler, 'Resolving did document and finding your current PDS URL'); 60 - 61 - let oldPds; 62 - try { 63 - oldPds = didDoc.service.filter(s => s.type === 'AtprotoPersonalDataServer')[0].serviceEndpoint; 64 - } catch (error) { 65 - console.error(error); 66 - throw new Error('Could not find a PDS in the DID document.'); 67 - } 68 - 69 - oldAgent = new AtpAgent({ 70 - service: oldPds, 71 - }); 72 - 73 - } 74 - 75 - safeStatusUpdate(statusUpdateHandler, 'Logging you in to the old PDS'); 76 - //Login to the old PDS 77 - if (twoFactorCode === null) { 78 - await oldAgent.login({identifier: oldHandle, password}); 79 - } else { 80 - await oldAgent.login({identifier: oldHandle, password: password, authFactorToken: twoFactorCode}); 81 - } 82 - 83 - safeStatusUpdate(statusUpdateHandler, 'Checking that the new PDS is an actual PDS (if the url is wrong this takes a while to error out)'); 84 - const newAgent = new AtpAgent({service: newPdsUrl}); 85 - const newHostDesc = await newAgent.com.atproto.server.describeServer(); 86 - if (this.createNewAccount) { 87 - const newHostWebDid = newHostDesc.data.did; 88 - 89 - safeStatusUpdate(statusUpdateHandler, 'Creating a new account on the new PDS'); 90 - 91 - const createAuthResp = await oldAgent.com.atproto.server.getServiceAuth({ 92 - aud: newHostWebDid, 93 - lxm: 'com.atproto.server.createAccount', 94 - }); 95 - const serviceJwt = createAuthResp.data.token; 96 - 97 - let createAccountRequest = { 98 - did: usersDid, 99 - handle: newHandle, 100 - email: newEmail, 101 - password: password, 102 - }; 103 - if (inviteCode) { 104 - createAccountRequest.inviteCode = inviteCode; 105 - } 106 - const createNewAccount = await newAgent.com.atproto.server.createAccount( 107 - createAccountRequest, 108 - { 109 - headers: {authorization: `Bearer ${serviceJwt}`}, 110 - encoding: 'application/json', 111 - }); 112 - 113 - if (createNewAccount.data.did !== usersDid.toString()) { 114 - throw new Error('Did not create the new account with the same did as the old account'); 115 - } 116 - } 117 - safeStatusUpdate(statusUpdateHandler, 'Logging in with the new account'); 118 - 119 - await newAgent.login({ 120 - identifier: usersDid, 121 - password: password, 122 - }); 123 - 124 - if (this.migrateRepo) { 125 - safeStatusUpdate(statusUpdateHandler, 'Migrating your repo'); 126 - const repoRes = await oldAgent.com.atproto.sync.getRepo({did: usersDid}); 127 - await newAgent.com.atproto.repo.importRepo(repoRes.data, { 128 - encoding: 'application/vnd.ipld.car', 129 - }); 130 - } 131 - 132 - let newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus(); 133 - 134 - if (this.migrateBlobs) { 135 - safeStatusUpdate(statusUpdateHandler, 'Migrating your blobs'); 136 - 137 - let blobCursor = undefined; 138 - let uploadedBlobs = 0; 139 - do { 140 - safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`); 141 - 142 - const listedBlobs = await oldAgent.com.atproto.sync.listBlobs({ 143 - did: usersDid, 144 - cursor: blobCursor, 145 - limit: 100, 146 - }); 147 - 148 - for (const cid of listedBlobs.data.cids) { 149 - try { 150 - const blobRes = await oldAgent.com.atproto.sync.getBlob({ 151 - did: usersDid, 152 - cid, 153 - }); 154 - await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 155 - encoding: blobRes.headers['content-type'], 156 - }); 157 - uploadedBlobs++; 158 - if (uploadedBlobs % 10 === 0) { 159 - safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${uploadedBlobs}/${newAccountStatus.data.expectedBlobs}`); 160 - } 161 - } catch (error) { 162 - console.error(error); 163 - } 164 - } 165 - blobCursor = listedBlobs.data.cursor; 166 - } while (blobCursor); 167 - } 168 - 169 - if (this.migrateMissingBlobs) { 170 - newAccountStatus = await newAgent.com.atproto.server.checkAccountStatus(); 171 - if (newAccountStatus.data.expectedBlobs !== newAccountStatus.data.importedBlobs) { 172 - let totalMissingBlobs = newAccountStatus.data.expectedBlobs - newAccountStatus.data.importedBlobs; 173 - safeStatusUpdate(statusUpdateHandler, 'Looks like there are some missing blobs. Going to try and upload them now.'); 174 - //Probably should be shared between main blob uploader, but eh 175 - let missingBlobCursor = undefined; 176 - let missingUploadedBlobs = 0; 177 - do { 178 - safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 179 - 180 - const missingBlobs = await newAgent.com.atproto.repo.listMissingBlobs({ 181 - cursor: missingBlobCursor, 182 - limit: 100, 183 - }); 184 - 185 - for (const recordBlob of missingBlobs.data.blobs) { 186 - try { 187 - 188 - const blobRes = await oldAgent.com.atproto.sync.getBlob({ 189 - did: usersDid, 190 - cid: recordBlob.cid, 191 - }); 192 - await newAgent.com.atproto.repo.uploadBlob(blobRes.data, { 193 - encoding: blobRes.headers['content-type'], 194 - }); 195 - if (missingUploadedBlobs % 10 === 0) { 196 - safeStatusUpdate(statusUpdateHandler, `Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs}`); 197 - } 198 - missingUploadedBlobs++; 199 - } catch (error) { 200 - //TODO silently logging prob should list them so user can manually download 201 - console.error(error); 202 - this.missingBlobs.push(recordBlob.cid); 203 - } 204 - } 205 - missingBlobCursor = missingBlobs.data.cursor; 206 - } while (missingBlobCursor); 207 - 208 - } 209 - } 210 - if (this.migratePrefs) { 211 - const prefs = await oldAgent.app.bsky.actor.getPreferences(); 212 - await newAgent.app.bsky.actor.putPreferences(prefs.data); 213 - } 214 - 215 - this.oldAgent = oldAgent; 216 - this.newAgent = newAgent; 217 - 218 - if (this.migratePlcRecord) { 219 - await oldAgent.com.atproto.identity.requestPlcOperationSignature(); 220 - safeStatusUpdate(statusUpdateHandler, 'Please check your email for a PLC token'); 221 - } 222 - } 223 - 224 - /** 225 - * Sign and submits the PLC operation to officially migrate the account 226 - * @param {string} token - the PLC token sent in the email. If you're just wanting to run this rerun migrate with all the flags set as false except for migratePlcRecord 227 - * @param additionalRotationKeysToAdd {[string]} - additional rotation keys to add in addition to the ones provided by the new PDS. 228 - * @returns {Promise<void>} 229 - */ 230 - async signPlcOperation(token, additionalRotationKeysToAdd = []) { 231 - const getDidCredentials = 232 - await this.newAgent.com.atproto.identity.getRecommendedDidCredentials(); 233 - const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? []; 234 - // Prepend any additional rotation keys (e.g., user-added keys, newly created key) so they appear above the new PDS rotation key 235 - const rotationKeys = [...(additionalRotationKeysToAdd || []), ...pdsProvidedRotationKeys]; 236 - if (!rotationKeys) { 237 - throw new Error('No rotation key provided from the new PDS'); 238 - } 239 - const credentials = { 240 - ...getDidCredentials.data, 241 - rotationKeys: rotationKeys, 242 - }; 243 - 244 - 245 - const plcOp = await this.oldAgent.com.atproto.identity.signPlcOperation({ 246 - token: token, 247 - ...credentials, 248 - }); 249 - 250 - await this.newAgent.com.atproto.identity.submitPlcOperation({ 251 - operation: plcOp.data.operation, 252 - }); 253 - 254 - await this.newAgent.com.atproto.server.activateAccount(); 255 - await this.oldAgent.com.atproto.server.deactivateAccount({}); 256 - } 257 - 258 - // Quick and dirty copy and paste of the above to get a fix out to help people without breaking or introducing any bugs to the migration service...hopefully 259 - async deactivateOldAccount(oldHandle, oldPassword, statusUpdateHandler = null, twoFactorCode = null) { 260 - //Copying the handle from bsky website adds some random unicodes on 261 - oldHandle = oldHandle.replace('@', '').trim().replace(/[\u202A\u202C\u200E\u200F\u2066-\u2069]/g, ''); 262 - let usersDid; 263 - //If it's a bsky handle just go with the entryway and let it sort everything 264 - if (oldHandle.endsWith('.bsky.social')) { 265 - const publicAgent = new AtpAgent({service: 'https://public.api.bsky.app'}); 266 - const resolveIdentityFromEntryway = await publicAgent.com.atproto.identity.resolveHandle({handle: oldHandle}); 267 - usersDid = resolveIdentityFromEntryway.data.did; 268 - } else { 269 - //Resolves the did and finds the did document for the old PDS 270 - safeStatusUpdate(statusUpdateHandler, 'Resolving did from handle'); 271 - usersDid = await handleResolver.resolve(oldHandle); 272 - } 273 - 274 - const didDoc = await docResolver.resolve(usersDid); 275 - let currentPds; 276 - try { 277 - currentPds = didDoc.service.filter(s => s.type === 'AtprotoPersonalDataServer')[0].serviceEndpoint; 278 - } catch (error) { 279 - console.error(error); 280 - throw new Error('Could not find a PDS in the DID document.'); 281 - } 282 - 283 - const plcLogRequest = await fetch(`https://plc.directory/${usersDid}/log`); 284 - const plcLog = await plcLogRequest.json(); 285 - let pdsBeforeCurrent = ''; 286 - for (const log of plcLog) { 287 - try { 288 - const pds = log.services.atproto_pds.endpoint; 289 - console.log(pds); 290 - if (pds.toLowerCase() === currentPds.toLowerCase()) { 291 - console.log('Found the PDS before the current one'); 292 - break; 293 - } 294 - pdsBeforeCurrent = pds; 295 - } catch (e) { 296 - console.log(e); 297 - } 298 - } 299 - if (pdsBeforeCurrent === '') { 300 - throw new Error('Could not find the PDS before the current one'); 301 - } 302 - 303 - let oldAgent = new AtpAgent({service: pdsBeforeCurrent}); 304 - safeStatusUpdate(statusUpdateHandler, `Logging you in to the old PDS: ${pdsBeforeCurrent}`); 305 - //Login to the old PDS 306 - if (twoFactorCode === null) { 307 - await oldAgent.login({identifier: oldHandle, password: oldPassword}); 308 - } else { 309 - await oldAgent.login({identifier: oldHandle, password: oldPassword, authFactorToken: twoFactorCode}); 310 - } 311 - safeStatusUpdate(statusUpdateHandler, 'Checking this isn\'t your current PDS'); 312 - if (pdsBeforeCurrent === currentPds) { 313 - throw new Error('This is your current PDS. Login to your old account username and password'); 314 - } 315 - 316 - let currentAccountStatus = await oldAgent.com.atproto.server.checkAccountStatus(); 317 - if (!currentAccountStatus.data.activated) { 318 - safeStatusUpdate(statusUpdateHandler, 'All good. Your old account is not activated.'); 319 - } 320 - safeStatusUpdate(statusUpdateHandler, 'Deactivating your OLD account'); 321 - await oldAgent.com.atproto.server.deactivateAccount({}); 322 - safeStatusUpdate(statusUpdateHandler, 'Successfully deactivated your OLD account'); 323 - } 324 - 325 - async signUpForBackupsFromMigration() { 326 - // Use a plain fetch POST with atproto-proxy header and bearer from the new agent 327 - const didWeb = await fetchPDSMooverDIDWeb(window.location.origin); 328 - 329 - const url = `${this.newAgent.serviceUrl.origin}/xrpc/com.pdsmoover.backup.signUp`; 330 - 331 - const accessJwt = this.newAgent?.session?.accessJwt; 332 - if (!accessJwt) { 333 - throw new Error('Missing access token for authorization'); 334 - } 335 - 336 - const res = await fetch(url, { 337 - method: 'POST', 338 - headers: { 339 - 'Authorization': `Bearer ${accessJwt}`, 340 - 'Content-Type': 'application/json', 341 - 'Accept': 'application/json', 342 - 'atproto-proxy': `${didWeb}#repo_backup`, 343 - }, 344 - body: JSON.stringify({}), 345 - }); 346 - 347 - if (!res.ok) { 348 - let bodyText = ''; 349 - try { 350 - bodyText = await res.text(); 351 - } catch { 352 - } 353 - throw new Error(`Backup signup failed: ${res.status} ${res.statusText}${bodyText ? ` - ${bodyText}` : ''}`); 354 - } 355 - 356 - //No return the success is all that is needed, if there's an error it will throw 357 - } 358 - } 359 - 360 - export {Migrator}; 361 -
-280
web/ui-code/src/plc-ops.js
··· 1 - import {defs, normalizeOp} from '@atcute/did-plc'; 2 - import {P256PrivateKey, parsePrivateMultikey, Secp256k1PrivateKey, Secp256k1PrivateKeyExportable} from '@atcute/crypto'; 3 - import * as CBOR from '@atcute/cbor'; 4 - import {fromBase16, toBase64Url} from '@atcute/multibase'; 5 - 6 - //NOTES 7 - // Don't forget disputes can check https://github.dev/mary-ext/boat/blob/trunk/src/views/identity/plc-applicator/steps/step1_handle-input.tsx 8 - //This is if a previous operation should be disputed, it will be a check box on recovery 9 - 10 - const PLC_DIRECTORY_URL = 'https://plc.directory'; 11 - 12 - 13 - // Helper to base64url-encode JSON 14 - const jsonToB64Url = (obj) => { 15 - const enc = new TextEncoder(); 16 - const json = JSON.stringify(obj); 17 - return toBase64Url(enc.encode(json)); 18 - }; 19 - 20 - class PlcOps { 21 - constructor() { 22 - 23 - } 24 - 25 - //TODO ui 26 - // For unvaldiating a record in 72hr window add an advance option input for that cid 27 - //that will be the easiest way at launch and just help ppl 28 - 29 - //NEEDS 30 - // Function to get current rotation keys 31 - // function to create a new key 32 - // function to add a new key and save and submit 33 - // Can use that same function for signing a recover 34 - 35 - async testSignAServiceAuthToken(did) { 36 - // 37 - const testPrivateKey = 'z3vLhP9c6gwUUwA4jkyYE9SXifE7wD1f3rVMMLmqBq8TaT2B'; 38 - let signingKeypair = await this.getKeyPair(testPrivateKey); 39 - let test = await this.createANewServiceAuthToken(did, 'did:web:dev.pdsmoover.com', signingKeypair.keypair, 'com.pdsmoover.backup.requestBackup'); 40 - console.log(test); 41 - } 42 - 43 - 44 - async exampleOfSigningAPLCOPManuallyThatWorks() { 45 - //dev keys 46 - // New Rotation Key: did:key:zQ3shXuksWLbyTTbWrSJ41qZvR2eyNFGTdbjjG3b2MWRo5cSx 47 - // New Private Key: z3vLhP9c6gwUUwA4jkyYE9SXifE7wD1f3rVMMLmqBq8TaT2B 48 - const testPrivateKey = 'z3vLhP9c6gwUUwA4jkyYE9SXifE7wD1f3rVMMLmqBq8TaT2B'; 49 - let signingKeypair = await this.getKeyPair(testPrivateKey); 50 - let {lastOperation, base} = await this.getLastPlcOpFromPlc(did); 51 - console.log(lastOperation); 52 - let {privateKey, publicKey} = await this.createANewSecp256k1(); 53 - console.log('New Rotation Key:', publicKey); 54 - console.log('New Private Key:', privateKey); 55 - let newRotationKeys = lastOperation.rotationKeys || []; 56 - //Adds the new one to the top 57 - newRotationKeys.unshift(publicKey); 58 - await this.signAndPublishNewOp(did, signingKeypair.keypair, lastOperation.alsoKnownAs, newRotationKeys, lastOperation.services.atproto_pds.endpoint, lastOperation.verificationMethods.atproto, base.cid); 59 - 60 - } 61 - 62 - async getCurrentRotationKeysForUser(did) { 63 - const logs = await this.getPlcAuditLogs(did); 64 - const {rotationKeys} = this.getLastPlcOp(logs); 65 - return rotationKeys; 66 - } 67 - 68 - async getLastPlcOpFromPlc(did) { 69 - const logs = await this.getPlcAuditLogs(did); 70 - return this.getLastPlcOp(logs); 71 - } 72 - 73 - getLastPlcOp(logs) { 74 - const lastOp = logs.at(-1); 75 - return {lastOperation: normalizeOp(lastOp.operation), base: lastOp}; 76 - } 77 - 78 - 79 - async getPlcAuditLogs(did) { 80 - const response = await fetch(`${PLC_DIRECTORY_URL}/${did}/log/audit`); 81 - if (!response.ok) { 82 - throw new Error(`got resposne ${response.status}`); 83 - } 84 - 85 - const json = await response.json(); 86 - return defs.indexedEntryLog.parse(json); 87 - } 88 - 89 - /** 90 - * Creates a new secp256k1 key that can be used for either rotation or verification key 91 - * @returns {Promise<{privateKey: string, publicKey: `did:key:${string}`}>} 92 - */ 93 - 94 - async createANewSecp256k1() { 95 - let keypair = await Secp256k1PrivateKeyExportable.createKeypair(); 96 - let publicKey = await keypair.exportPublicKey('did'); 97 - let privateKey = await keypair.exportPrivateKey('multikey'); 98 - return { 99 - privateKey, 100 - publicKey 101 - }; 102 - } 103 - 104 - 105 - /** 106 - * Signs a new operation with the provided signing key and submits it 107 - * @param did 108 - * @param signingRotationKey 109 - * @param alsoKnownAs 110 - * @param rotationKeys 111 - * @param pds 112 - * @param verificationKey 113 - * @returns {Promise<void>} 114 - */ 115 - async signAndPublishNewOp(did, signingRotationKey, alsoKnownAs, rotationKeys, pds, verificationKey, prev) { 116 - const operation = { 117 - type: 'plc_operation', 118 - // prev: prev!.cid, 119 - prev, 120 - alsoKnownAs, 121 - rotationKeys, 122 - services: { 123 - atproto_pds: { 124 - type: 'AtprotoPersonalDataServer', 125 - endpoint: pds 126 - } 127 - }, 128 - verificationMethods: { 129 - atproto: verificationKey 130 - } 131 - }; 132 - const opBytes = CBOR.encode(operation); 133 - const sigBytes = await signingRotationKey.sign(opBytes); 134 - 135 - const signature = toBase64Url(sigBytes); 136 - 137 - const signedOperation = { 138 - ...operation, 139 - sig: signature, 140 - }; 141 - 142 - await this.pushPlcOperation(did, signedOperation); 143 - } 144 - 145 - /** 146 - * Takes a multi or hexbased private key and returns a keypair 147 - * @param privateKeyString 148 - * @param type 149 - * @returns {Promise<{type: string, didPublicKey: `did:key:${string}`, keypair: P256PrivateKey|Secp256k1PrivateKey}>} 150 - */ 151 - async getKeyPair(privateKeyString, type = 'p256') { 152 - const HEX_REGEX = /^[0-9a-f]+$/i; 153 - const MULTIKEY_REGEX = /^z[a-km-zA-HJ-NP-Z1-9]+$/; 154 - let keypair = undefined; 155 - 156 - if (HEX_REGEX.test(privateKeyString)) { 157 - const privateKeyBytes = fromBase16(privateKeyString); 158 - 159 - switch (type) { 160 - case 'p256': { 161 - keypair = await P256PrivateKey.importRaw(privateKeyBytes); 162 - break; 163 - } 164 - case 'secp256k1': { 165 - keypair = await Secp256k1PrivateKey.importRaw(privateKeyBytes); 166 - break; 167 - } 168 - default: { 169 - throw new Error(`unsupported "${type}" type`); 170 - } 171 - } 172 - } else if (MULTIKEY_REGEX.test(privateKeyString)) { 173 - 174 - const match = parsePrivateMultikey(privateKeyString); 175 - console.log(match); 176 - const privateKeyBytes = match.privateKeyBytes; 177 - 178 - switch (match.type) { 179 - case 'p256': { 180 - keypair = await P256PrivateKey.importRaw(privateKeyBytes); 181 - console.log(keypair); 182 - break; 183 - } 184 - case 'secp256k1': { 185 - keypair = await Secp256k1PrivateKey.importRaw(privateKeyBytes); 186 - break; 187 - } 188 - default: { 189 - throw new Error(`unsupported "${type}" type`); 190 - } 191 - } 192 - } else { 193 - throw new Error('unknown input format'); 194 - } 195 - 196 - return { 197 - type: 'private_key', 198 - didPublicKey: await keypair.exportPublicKey('did'), 199 - keypair: keypair, 200 - }; 201 - } 202 - 203 - async pushPlcOperation(did, operation) { 204 - const response = await fetch(`${PLC_DIRECTORY_URL}/${did}`, { 205 - method: 'post', 206 - headers: { 207 - 'content-type': 'application/json', 208 - }, 209 - body: JSON.stringify(operation), 210 - }); 211 - 212 - const headers = response.headers; 213 - if (!response.ok) { 214 - const type = headers.get('content-type'); 215 - 216 - if (type?.includes('application/json')) { 217 - const json = await response.json(); 218 - if (typeof json === 'object' && json !== null && typeof json.message === 'string') { 219 - throw new Error(json.message); 220 - } 221 - } 222 - 223 - throw new Error(`got http ${response.status} from plc`); 224 - } 225 - }; 226 - 227 - 228 - /** 229 - * 230 - * @param iss The user's did 231 - * @param aud The did:web, if it's a PDS it's usually from /xrpc/com.atproto.server.describeServer 232 - * @param keypair The keypair to sign with only supporting ES256K atm 233 - * @param lxm The lxm which is usually com.atproto.server.createAccount for creating a new account 234 - * @returns {Promise<string>} 235 - */ 236 - async createANewServiceAuthToken(iss, aud, keypair, lxm) { 237 - 238 - 239 - // Compute iat/exp defaults (60s window like reference: MINUTE/1e3) 240 - const iat = Math.floor(Date.now() / 1e3); 241 - const exp = iat + 60; 242 - 243 - // Generate a 16-byte hex jti 244 - const jti = (() => { 245 - const bytes = new Uint8Array(16); 246 - // crypto in browser or node; fall back safely 247 - (globalThis.crypto || window.crypto).getRandomValues(bytes); 248 - return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''); 249 - })(); 250 - 251 - 252 - // Build header and payload (omit undefined fields) 253 - // Just defaulting to ES256K since p256 was not importing on firefox 254 - const header = {typ: 'JWT', alg: 'ES256K'}; 255 - const payload = {}; 256 - payload.iat = iat; 257 - payload.iss = iss; 258 - payload.aud = aud; 259 - payload.exp = exp; 260 - payload.lxm = lxm; 261 - payload.jti = jti; 262 - 263 - const headerB64 = jsonToB64Url(header); 264 - const payloadB64 = jsonToB64Url(payload); 265 - const toSignStr = `${headerB64}.${payloadB64}`; 266 - 267 - // Sign 268 - const toSignBytes = new TextEncoder().encode(toSignStr); 269 - const sigBytes = await keypair.sign(toSignBytes); 270 - 271 - // Return compact JWS 272 - const sigB64 = toBase64Url(sigBytes); 273 - return `${toSignStr}.${sigB64}`; 274 - } 275 - 276 - 277 - } 278 - 279 - 280 - export {PlcOps};
-254
web/ui-code/src/restore.js
··· 1 - import {handleAndPDSResolver} from './atprotoUtils.js'; 2 - import {PlcOps} from './plc-ops.js'; 3 - import {normalizeOp, Operation} from '@atcute/did-plc'; 4 - import {AtpAgent} from '@atproto/api'; 5 - import {Secp256k1PrivateKeyExportable} from '@atcute/crypto'; 6 - import * as CBOR from '@atcute/cbor'; 7 - import {toBase64Url} from '@atcute/multibase'; 8 - 9 - class Restore { 10 - 11 - constructor() { 12 - /** @type {PlcOps} */ 13 - this.plcOps = new PlcOps(); 14 - this.tempVerificationKeypair = null; 15 - /** @type {AtpAgent} */ 16 - this.atpAgent = null; 17 - this.recoveryRotationKeyPair = null; 18 - //Feature flags 19 - this.justRestoreFiles = false; 20 - } 21 - 22 - async recover( 23 - rotationKey, 24 - currentHandle, 25 - newPDS, 26 - newHandle, 27 - newPassword, 28 - newEmail, 29 - inviteCode, 30 - cidToRestoreTo = null, 31 - onStatus = null) { 32 - 33 - 34 - if (onStatus) onStatus('Resolving your handle...'); 35 - 36 - let {usersDid} = await handleAndPDSResolver(currentHandle); 37 - 38 - if (onStatus) onStatus('Checking that the new PDS is an actual PDS (if the url is wrong, this takes a while to error out)'); 39 - this.atpAgent = new AtpAgent({service: newPDS}); 40 - const newHostDesc = await this.atpAgent.com.atproto.server.describeServer(); 41 - 42 - 43 - //Check to see if the user already has a repo on the new PDS, if they do no reason to try and restore via the plc operations 44 - try { 45 - await this.atpAgent.com.atproto.repo.describeRepo({repo: usersDid.toString()}); 46 - //If we got this far and there is a repo on the new PDS with the users did, we can just move on and restore the files 47 - this.justRestoreFiles = true; 48 - 49 - } catch (error) { 50 - console.error(error); 51 - let parsedError = error.error; 52 - if (parsedError === 'RepoDeactivated') { 53 - //Ideally should mean they already have a repo on the new PDS and we just need to restore the files 54 - this.justRestoreFiles = true; 55 - } 56 - //This is the error we want to see, anything else throw 57 - if (parsedError !== 'RepoNotFound') { 58 - throw error; 59 - } 60 - } 61 - 62 - //We need to double check that the new handle has not been taken, if it has we need to throw an error 63 - //We care a bit more because we do not want any unnecessary plc ops to be created 64 - try { 65 - let resolveHandle = await this.atpAgent.com.atproto.identity.resolveHandle({handle: newHandle}); 66 - if (resolveHandle.data.did === usersDid.toString()) { 67 - //Ideally shouldn't get here without the checks above. But we do not need to create a new account or do plc ops. It should already be there 68 - this.justRestoreFiles = true; 69 - } else { 70 - //There is a repo with that name and it's not the users did, 71 - throw new Error('The new handle is already taken, please select a different handle'); 72 - } 73 - } catch (error) { 74 - // Going to silently log this and just assume the handle has not been taken. 75 - console.error(error); 76 - if (error.message.startsWith('The new handle')) { 77 - //it's not our custom error, so we can just throw it 78 - throw error; 79 - } 80 - 81 - } 82 - 83 - if (!this.justRestoreFiles) { 84 - 85 - if (onStatus) onStatus('Validating your private rotation key is in the correct format...'); 86 - this.recoveryRotationKeyPair = await this.plcOps.getKeyPair(rotationKey); 87 - 88 - 89 - if (onStatus) onStatus('Resolving PlC operation logs...'); 90 - 91 - /** @type {Operation} */ 92 - let baseOpForSigning = null; 93 - let opPrevCid = null; 94 - 95 - //This is for reversals against a rogue plc op and you want to restore to a specific cid in the audit log 96 - if (cidToRestoreTo) { 97 - let auditLogs = await this.plcOps.getPlcAuditLogs(usersDid); 98 - for (const log of auditLogs) { 99 - if (log.cid === cidToRestoreTo) { 100 - baseOpForSigning = normalizeOp(log.operation); 101 - opPrevCid = log.cid; 102 - break; 103 - } 104 - } 105 - if (!baseOpForSigning) { 106 - throw new Error('Could not find the cid in the audit logs'); 107 - } 108 - } else { 109 - let {lastOperation, base} = await this.plcOps.getLastPlcOpFromPlc(usersDid); 110 - opPrevCid = base.cid; 111 - baseOpForSigning = lastOperation; 112 - } 113 - 114 - if (onStatus) onStatus('Preparing to switch to a temp atproto key...'); 115 - if (this.tempVerificationKeypair == null) { 116 - if (onStatus) onStatus('Creating a new temp atproto key...'); 117 - this.tempVerificationKeypair = await Secp256k1PrivateKeyExportable.createKeypair(); 118 - } 119 - //Just defaulting to the user's recovery key for now. Advance cases will be something else 120 - //Maybe just a new ui to edit the PLC doc in a limited capacity, but sinc ethis is a temp plc op i don't think it's needed 121 - let tempRotationKeys = [this.recoveryRotationKeyPair.didPublicKey]; 122 - 123 - if (onStatus) onStatus('Modifying the PLC OP for recovery...'); 124 - //A temp plc op for control of the atproto key to create a serviceAuth and new account on the new PDS 125 - await this.plcOps.signAndPublishNewOp( 126 - usersDid, 127 - this.recoveryRotationKeyPair.keypair, 128 - baseOpForSigning.alsoKnownAs, 129 - tempRotationKeys, 130 - newPDS, 131 - await this.tempVerificationKeypair.exportPublicKey('did'), 132 - opPrevCid); 133 - 134 - 135 - if (onStatus) onStatus('Creating your new account on the new PDS...'); 136 - let serviceAuthToken = await this.plcOps.createANewServiceAuthToken(usersDid, newHostDesc.data.did, this.tempVerificationKeypair, 'com.atproto.server.createAccount'); 137 - 138 - let createAccountRequest = { 139 - did: usersDid, 140 - handle: newHandle, 141 - email: newEmail, 142 - password: newPassword, 143 - }; 144 - if (inviteCode) { 145 - createAccountRequest.inviteCode = inviteCode; 146 - } 147 - const _ = await this.atpAgent.com.atproto.server.createAccount( 148 - createAccountRequest, 149 - { 150 - headers: {authorization: `Bearer ${serviceAuthToken}`}, 151 - encoding: 'application/json', 152 - }); 153 - } 154 - 155 - await this.atpAgent.login({ 156 - identifier: usersDid, 157 - password: newPassword, 158 - }); 159 - 160 - if (!this.justRestoreFiles) { 161 - //Moving the user offically to the new PDS 162 - if (onStatus) onStatus('Signing the papers...'); 163 - let {base} = await this.plcOps.getLastPlcOpFromPlc(usersDid); 164 - await this.signRestorePlcOperation(usersDid, [this.recoveryRotationKeyPair.didPublicKey], base.cid); 165 - } 166 - 167 - if (onStatus) onStatus('Success! Restoring your repo...'); 168 - const pdsMoover = new AtpAgent({service: window.location.origin}); 169 - const repoRes = await pdsMoover.com.atproto.sync.getRepo({did: usersDid}); 170 - await this.atpAgent.com.atproto.repo.importRepo(repoRes.data, { 171 - encoding: 'application/vnd.ipld.car', 172 - }); 173 - 174 - if (onStatus) onStatus('Restoring your blobs...'); 175 - 176 - //Using the missing endpoint to findout what's missing then the PDS MOOver endpoint to restore 177 - let totalMissingBlobs = 0; 178 - let missingBlobCursor = undefined; 179 - let missingUploadedBlobs = 0; 180 - 181 - do { 182 - 183 - const missingBlobs = await this.atpAgent.com.atproto.repo.listMissingBlobs({ 184 - cursor: missingBlobCursor, 185 - limit: 1000, 186 - }); 187 - totalMissingBlobs += missingBlobs.data.blobs.length; 188 - 189 - for (const recordBlob of missingBlobs.data.blobs) { 190 - try { 191 - 192 - const blobRes = await pdsMoover.com.atproto.sync.getBlob({ 193 - did: usersDid, 194 - cid: recordBlob.cid, 195 - }); 196 - let result = await this.atpAgent.com.atproto.repo.uploadBlob(blobRes.data, { 197 - encoding: blobRes.headers['content-type'], 198 - }); 199 - 200 - 201 - if (missingUploadedBlobs % 2 === 0) { 202 - if (onStatus) onStatus(`Migrating blobs: ${missingUploadedBlobs}/${totalMissingBlobs} (The total may increase as we find more)`); 203 - } 204 - missingUploadedBlobs++; 205 - } catch (error) { 206 - console.error(error); 207 - } 208 - } 209 - missingBlobCursor = missingBlobs.data.cursor; 210 - } while (missingBlobCursor); 211 - 212 - const accountStatus = await this.atpAgent.com.atproto.server.checkAccountStatus(); 213 - if (!accountStatus.data.activated) { 214 - if (onStatus) onStatus('Activating your account...'); 215 - await this.atpAgent.com.atproto.server.activateAccount(); 216 - } 217 - } 218 - 219 - async signRestorePlcOperation(usersDid, additionalRotationKeysToAdd = [], prevCid) { 220 - const getDidCredentials = 221 - await this.atpAgent.com.atproto.identity.getRecommendedDidCredentials(); 222 - console.log(getDidCredentials); 223 - 224 - const pdsProvidedRotationKeys = getDidCredentials.data.rotationKeys ?? []; 225 - // Prepend any additional rotation keys (e.g., user-added keys, newly created key) so they appear above the new PDS rotation key 226 - const rotationKeys = [...(additionalRotationKeysToAdd || []), ...pdsProvidedRotationKeys]; 227 - if (!rotationKeys) { 228 - throw new Error('No rotation key provided from the new PDS'); 229 - } 230 - const plcOpToSubmit = { 231 - type: 'plc_operation', 232 - ...getDidCredentials.data, 233 - prev: prevCid, 234 - rotationKeys: rotationKeys, 235 - }; 236 - 237 - 238 - const opBytes = CBOR.encode(plcOpToSubmit); 239 - const sigBytes = await this.recoveryRotationKeyPair.keypair.sign(opBytes); 240 - 241 - const signature = toBase64Url(sigBytes); 242 - 243 - const signedOperation = { 244 - ...plcOpToSubmit, 245 - sig: signature, 246 - }; 247 - 248 - await this.plcOps.pushPlcOperation(usersDid, signedOperation); 249 - await this.atpAgent.com.atproto.server.activateAccount(); 250 - 251 - } 252 - } 253 - 254 - export {Restore};
-28
web/ui-code/src/utils.js
··· 1 - class MooverUtils { 2 - formatDate(value) { 3 - if (!value) return '—'; 4 - try { 5 - const d = new Date(value); 6 - return d.toLocaleString(); 7 - } catch (err) { 8 - console.error(err); 9 - return String(value); 10 - } 11 - } 12 - 13 - formatBytes(bytes) { 14 - if (bytes == null) return '—'; 15 - const units = ['B', 'KB', 'MB', 'GB', 'TB']; 16 - let i = 0; 17 - let v = Number(bytes); 18 - while (v >= 1024 && i < units.length - 1) { 19 - v /= 1024; 20 - i++; 21 - } 22 - return v.toFixed(1) + ' ' + units[i]; 23 - } 24 - 25 - } 26 - 27 - 28 - export {MooverUtils};
-31
web/ui-code/vite.config.js
··· 1 - import {defineConfig} from 'vite'; 2 - import rustVitePlugin from 'vite-rs-plugin'; 3 - 4 - 5 - export default defineConfig({ 6 - port: 5173, 7 - plugins: [ 8 - rustVitePlugin({ 9 - refresh: './src/**.*', 10 - entrypoints: [ 11 - 'src/main.js', 12 - 'src/pdsmoover.js', 13 - 'src/missingBlobs.js', 14 - 'src/atprotoUtils.js', 15 - 'src/backup.js', 16 - ], 17 - assetsEndpoint: '/', 18 - }) 19 - ], 20 - build: { 21 - rollupOptions: { 22 - input: ['index.html'], 23 - }, 24 - }, 25 - // Uncomment this section if you're going to use `start_dev_server` in Rust: 26 - // server: { 27 - // hmr: { 28 - // port: 21012, 29 - // }, 30 - // }, 31 - });