Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol. wisp.place

Compare changes

Choose any two refs to compare.

+72
.env.grafana.example
··· 1 + # Grafana Cloud Configuration for wisp.place monorepo 2 + # Copy these variables to your .env file to enable Grafana integration 3 + # The observability package will automatically pick up these environment variables 4 + 5 + # ============================================================================ 6 + # Grafana Loki (for logs) 7 + # ============================================================================ 8 + # Get this from your Grafana Cloud portal under Loki โ†’ Details 9 + # Example: https://logs-prod-012.grafana.net 10 + GRAFANA_LOKI_URL=https://logs-prod-xxx.grafana.net 11 + 12 + # Authentication Option 1: Bearer Token (Grafana Cloud) 13 + GRAFANA_LOKI_TOKEN=glc_xxx 14 + 15 + # Authentication Option 2: Username/Password (Self-hosted or some Grafana setups) 16 + # GRAFANA_LOKI_USERNAME=your-username 17 + # GRAFANA_LOKI_PASSWORD=your-password 18 + 19 + # ============================================================================ 20 + # Grafana Prometheus (for metrics) 21 + # ============================================================================ 22 + # Get this from your Grafana Cloud portal under Prometheus โ†’ Details 23 + # Note: You need to add /api/prom to the base URL for OTLP export 24 + # Example: https://prometheus-prod-10-prod-us-central-0.grafana.net/api/prom 25 + GRAFANA_PROMETHEUS_URL=https://prometheus-prod-xxx.grafana.net/api/prom 26 + 27 + # Authentication Option 1: Bearer Token (Grafana Cloud) 28 + GRAFANA_PROMETHEUS_TOKEN=glc_xxx 29 + 30 + # Authentication Option 2: Username/Password (Self-hosted or some Grafana setups) 31 + # GRAFANA_PROMETHEUS_USERNAME=your-username 32 + # GRAFANA_PROMETHEUS_PASSWORD=your-password 33 + 34 + # ============================================================================ 35 + # Optional Configuration 36 + # ============================================================================ 37 + # These will be used by both main-app and hosting-service if not overridden 38 + 39 + # Service metadata (optional - defaults are provided in code) 40 + # SERVICE_NAME=wisp-app 41 + # SERVICE_VERSION=1.0.0 42 + 43 + # Batching configuration (optional) 44 + # GRAFANA_BATCH_SIZE=100 # Flush after this many entries 45 + # GRAFANA_FLUSH_INTERVAL=5000 # Flush every 5 seconds 46 + 47 + # ============================================================================ 48 + # How to get these values: 49 + # ============================================================================ 50 + # 1. Sign up for Grafana Cloud at https://grafana.com/ 51 + # 2. Go to your Grafana Cloud portal 52 + # 3. For Loki: 53 + # - Navigate to "Connections" โ†’ "Loki" 54 + # - Click "Details" 55 + # - Copy the Push endpoint URL (without /loki/api/v1/push) 56 + # - Create an API token with push permissions 57 + # 4. For Prometheus: 58 + # - Navigate to "Connections" โ†’ "Prometheus" 59 + # - Click "Details" 60 + # - Copy the Remote Write endpoint (add /api/prom for OTLP) 61 + # - Create an API token with write permissions 62 + 63 + # ============================================================================ 64 + # Testing the integration: 65 + # ============================================================================ 66 + # 1. Copy this file's contents to your .env file 67 + # 2. Fill in the actual values 68 + # 3. Restart your services (main-app and hosting-service) 69 + # 4. Check your Grafana Cloud dashboard for incoming data 70 + # 5. Use Grafana Explore to query: 71 + # - Loki: {job="main-app"} or {job="hosting-service"} 72 + # - Prometheus: http_requests_total{service="main-app"}
+1
.gitignore
··· 1 + .research/ 1 2 # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 3 .env 3 4 # dependencies
+2
.tangled/workflows/deploy-wisp.yml
··· 49 49 - name: Build docs 50 50 command: | 51 51 cd docs 52 + export PATH="$HOME/.nix-profile/bin:$PATH" 53 + bun install 52 54 bun run build 53 55 - name: Deploy to Wisp.place 54 56 command: |
+2 -1
.tangled/workflows/test.yml
··· 7 7 dependencies: 8 8 nixpkgs: 9 9 - git 10 + - findutils 10 11 github:NixOS/nixpkgs/nixpkgs-unstable: 11 12 - bun 12 13 ··· 16 17 export PATH="$HOME/.nix-profile/bin:$PATH" 17 18 18 19 # have to regenerate otherwise it wont install necessary dependencies to run 19 - rm -rf bun.lock package-lock.json 20 + find . -type f \( -name "bun.lock" -o -name "package-lock.json" \) -delete 20 21 bun install 21 22 22 23 - name: run all tests
+15 -58
Dockerfile
··· 1 - # Build stage 2 - FROM oven/bun:1.3 AS build 1 + # Production stage 2 + FROM oven/bun:1.3 3 3 4 4 WORKDIR /app 5 5 ··· 7 7 COPY package.json bunfig.toml tsconfig.json bun.lock* ./ 8 8 9 9 # Copy all workspace package.json files first (for dependency resolution) 10 - COPY packages ./packages 10 + COPY packages/@wisp/atproto-utils/package.json ./packages/@wisp/atproto-utils/package.json 11 + COPY packages/@wisp/constants/package.json ./packages/@wisp/constants/package.json 12 + COPY packages/@wisp/database/package.json ./packages/@wisp/database/package.json 13 + COPY packages/@wisp/fs-utils/package.json ./packages/@wisp/fs-utils/package.json 14 + COPY packages/@wisp/lexicons/package.json ./packages/@wisp/lexicons/package.json 15 + COPY packages/@wisp/observability/package.json ./packages/@wisp/observability/package.json 16 + COPY packages/@wisp/safe-fetch/package.json ./packages/@wisp/safe-fetch/package.json 11 17 COPY apps/main-app/package.json ./apps/main-app/package.json 12 18 COPY apps/hosting-service/package.json ./apps/hosting-service/package.json 13 19 14 - # Install all dependencies (including workspaces) 15 - RUN bun install --frozen-lockfile 20 + # Install dependencies 21 + RUN bun install --frozen-lockfile --production 16 22 17 - # Copy source files 18 - COPY apps/main-app ./apps/main-app 19 - 20 - # Build compiled server 21 - RUN bun build \ 22 - --compile \ 23 - --target bun \ 24 - --minify \ 25 - --outfile server \ 26 - apps/main-app/src/index.ts 27 - 28 - # Production dependencies stage 29 - FROM oven/bun:1.3 AS prod-deps 30 - 31 - WORKDIR /app 32 - 33 - COPY package.json bunfig.toml tsconfig.json bun.lock* ./ 23 + # Copy workspace source files 34 24 COPY packages ./packages 35 - COPY apps/main-app/package.json ./apps/main-app/package.json 36 - COPY apps/hosting-service/package.json ./apps/hosting-service/package.json 37 25 38 - # Install only production dependencies 39 - RUN bun install --frozen-lockfile --production 40 - 41 - # Remove unnecessary large packages (bun is already in base image, these are dev tools) 42 - RUN rm -rf /app/node_modules/bun \ 43 - /app/node_modules/@oven \ 44 - /app/node_modules/prettier \ 45 - /app/node_modules/@ts-morph 46 - 47 - # Final stage - use distroless or slim debian-based image 48 - FROM debian:bookworm-slim 49 - 50 - # Install Bun runtime 51 - COPY --from=oven/bun:1.3 /usr/local/bin/bun /usr/local/bin/bun 52 - 53 - WORKDIR /app 54 - 55 - # Copy compiled server 56 - COPY --from=build /app/server /app/server 57 - 58 - # Copy public files 59 - COPY apps/main-app/public apps/main-app/public 60 - 61 - # Copy production dependencies only 62 - COPY --from=prod-deps /app/node_modules /app/node_modules 63 - 64 - # Copy configs 65 - COPY package.json bunfig.toml tsconfig.json /app/ 66 - COPY apps/main-app/tsconfig.json /app/apps/main-app/tsconfig.json 67 - COPY apps/main-app/package.json /app/apps/main-app/package.json 68 - 69 - # Create symlink for module resolution 70 - RUN ln -s /app/node_modules /app/apps/main-app/node_modules 26 + # Copy app source and public files 27 + COPY apps/main-app ./apps/main-app 71 28 72 29 ENV PORT=8000 73 30 74 31 EXPOSE 8000 75 32 76 - CMD ["./server"] 33 + CMD ["bun", "run", "apps/main-app/src/index.ts"]
+9 -8
apps/hosting-service/package.json
··· 10 10 "backfill": "tsx src/index.ts --backfill" 11 11 }, 12 12 "dependencies": { 13 - "@wisp/lexicons": "workspace:*", 14 - "@wisp/constants": "workspace:*", 15 - "@wisp/observability": "workspace:*", 16 - "@wisp/atproto-utils": "workspace:*", 17 - "@wisp/database": "workspace:*", 18 - "@wisp/fs-utils": "workspace:*", 19 - "@wisp/safe-fetch": "workspace:*", 20 13 "@atproto/api": "^0.17.4", 21 14 "@atproto/identity": "^0.4.9", 22 15 "@atproto/lexicon": "^0.5.2", 23 16 "@atproto/sync": "^0.1.36", 24 17 "@atproto/xrpc": "^0.7.5", 25 18 "@hono/node-server": "^1.19.6", 19 + "@wisp/atproto-utils": "workspace:*", 20 + "@wisp/constants": "workspace:*", 21 + "@wisp/database": "workspace:*", 22 + "@wisp/fs-utils": "workspace:*", 23 + "@wisp/lexicons": "workspace:*", 24 + "@wisp/observability": "workspace:*", 25 + "@wisp/safe-fetch": "workspace:*", 26 26 "hono": "^4.10.4", 27 27 "mime-types": "^2.1.35", 28 28 "multiformats": "^13.4.1", 29 - "postgres": "^3.4.5" 29 + "postgres": "^3.4.5", 30 + "tiered-storage": "1.0.3" 30 31 }, 31 32 "devDependencies": { 32 33 "@types/bun": "^1.3.1",
+46 -6
apps/hosting-service/src/index.ts
··· 1 1 import app from './server'; 2 2 import { serve } from '@hono/node-server'; 3 3 import { FirehoseWorker } from './lib/firehose'; 4 - import { createLogger } from '@wisp/observability'; 4 + import { createLogger, initializeGrafanaExporters } from '@wisp/observability'; 5 5 import { mkdirSync, existsSync } from 'fs'; 6 6 import { backfillCache } from './lib/backfill'; 7 - import { startDomainCacheCleanup, stopDomainCacheCleanup, setCacheOnlyMode } from './lib/db'; 7 + import { startDomainCacheCleanup, stopDomainCacheCleanup, setCacheOnlyMode, closeDatabase } from './lib/db'; 8 + import { storage, getStorageConfig } from './lib/storage'; 9 + 10 + // Initialize Grafana exporters if configured 11 + initializeGrafanaExporters({ 12 + serviceName: 'hosting-service', 13 + serviceVersion: '1.0.0' 14 + }); 8 15 9 16 const logger = createLogger('hosting-service'); 10 17 11 18 const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3001; 12 19 const CACHE_DIR = process.env.CACHE_DIR || './cache/sites'; 20 + const BACKFILL_CONCURRENCY = process.env.BACKFILL_CONCURRENCY 21 + ? parseInt(process.env.BACKFILL_CONCURRENCY) 22 + : undefined; // Let backfill.ts default (10) apply 13 23 14 24 // Parse CLI arguments 15 25 const args = process.argv.slice(2); ··· 41 51 42 52 firehose.start(); 43 53 54 + // Optional: Bootstrap hot cache from warm tier on startup 55 + const BOOTSTRAP_HOT_ON_STARTUP = process.env.BOOTSTRAP_HOT_ON_STARTUP === 'true'; 56 + const BOOTSTRAP_HOT_LIMIT = process.env.BOOTSTRAP_HOT_LIMIT ? parseInt(process.env.BOOTSTRAP_HOT_LIMIT) : 100; 57 + 58 + if (BOOTSTRAP_HOT_ON_STARTUP) { 59 + console.log(`๐Ÿ”ฅ Bootstrapping hot cache (top ${BOOTSTRAP_HOT_LIMIT} items)...`); 60 + storage.bootstrapHot(BOOTSTRAP_HOT_LIMIT) 61 + .then((loaded: number) => { 62 + console.log(`โœ… Bootstrapped ${loaded} items into hot cache`); 63 + }) 64 + .catch((err: unknown) => { 65 + console.error('โŒ Hot cache bootstrap error:', err); 66 + }); 67 + } 68 + 44 69 // Run backfill if requested 45 70 if (backfillOnStartup) { 46 71 console.log('๐Ÿ”„ Backfill requested, starting cache backfill...'); 47 72 backfillCache({ 48 73 skipExisting: true, 49 - concurrency: 3, 74 + concurrency: BACKFILL_CONCURRENCY, 50 75 }).then((stats) => { 51 76 console.log('โœ… Cache backfill completed'); 52 77 }).catch((err) => { ··· 69 94 port: PORT, 70 95 }); 71 96 97 + // Get storage configuration for display 98 + const storageConfig = getStorageConfig(); 99 + 72 100 console.log(` 73 - Wisp Hosting Service 101 + Wisp Hosting Service with Tiered Storage 74 102 75 103 Server: http://localhost:${PORT} 76 104 Health: http://localhost:${PORT}/health 77 - Cache: ${CACHE_DIR} 78 - Firehose: Connected to Firehose 79 105 Cache-Only: ${CACHE_ONLY_MODE ? 'ENABLED (no DB writes)' : 'DISABLED'} 106 + Backfill: ${backfillOnStartup ? `ENABLED (concurrency: ${BACKFILL_CONCURRENCY || 10})` : 'DISABLED'} 107 + 108 + Tiered Storage Configuration: 109 + Hot Cache: ${storageConfig.hotCacheSize} (${storageConfig.hotCacheCount} items max) 110 + Warm Cache: ${storageConfig.warmCacheSize} (${storageConfig.warmEvictionPolicy} eviction) 111 + Cold Storage: S3 - ${storageConfig.s3Bucket} 112 + S3 Region: ${storageConfig.s3Region} 113 + S3 Endpoint: ${storageConfig.s3Endpoint} 114 + S3 Prefix: ${storageConfig.s3Prefix} 115 + Metadata Bucket: ${storageConfig.metadataBucket} 116 + 117 + Firehose: Connecting... 80 118 `); 81 119 82 120 // Graceful shutdown ··· 84 122 console.log('\n๐Ÿ›‘ Shutting down...'); 85 123 firehose.stop(); 86 124 stopDomainCacheCleanup(); 125 + await closeDatabase(); 87 126 server.close(); 88 127 process.exit(0); 89 128 }); ··· 92 131 console.log('\n๐Ÿ›‘ Shutting down...'); 93 132 firehose.stop(); 94 133 stopDomainCacheCleanup(); 134 + await closeDatabase(); 95 135 server.close(); 96 136 process.exit(0); 97 137 });
+65 -57
apps/hosting-service/src/lib/backfill.ts
··· 60 60 console.log(`โš™๏ธ Limited to ${maxSites} sites for backfill`); 61 61 } 62 62 63 - // Process sites in batches 64 - const batches: typeof sites[] = []; 65 - for (let i = 0; i < sites.length; i += concurrency) { 66 - batches.push(sites.slice(i, i + concurrency)); 67 - } 68 - 63 + // Process sites with sliding window concurrency pool 64 + const executing = new Set<Promise<void>>(); 69 65 let processed = 0; 70 - for (const batch of batches) { 71 - await Promise.all( 72 - batch.map(async (site) => { 73 - try { 74 - // Check if already cached 75 - if (skipExisting && isCached(site.did, site.rkey)) { 76 - stats.skipped++; 77 - processed++; 78 - logger.debug(`Skipping already cached site`, { did: site.did, rkey: site.rkey }); 79 - console.log(`โญ๏ธ [${processed}/${sites.length}] Skipped (cached): ${site.display_name || site.rkey}`); 80 - return; 81 - } 82 66 83 - // Fetch site record 84 - const siteData = await fetchSiteRecord(site.did, site.rkey); 85 - if (!siteData) { 86 - stats.failed++; 87 - processed++; 88 - logger.error('Site record not found during backfill', null, { did: site.did, rkey: site.rkey }); 89 - console.log(`โŒ [${processed}/${sites.length}] Failed (not found): ${site.display_name || site.rkey}`); 90 - return; 91 - } 92 - 93 - // Get PDS endpoint 94 - const pdsEndpoint = await getPdsForDid(site.did); 95 - if (!pdsEndpoint) { 96 - stats.failed++; 97 - processed++; 98 - logger.error('PDS not found during backfill', null, { did: site.did }); 99 - console.log(`โŒ [${processed}/${sites.length}] Failed (no PDS): ${site.display_name || site.rkey}`); 100 - return; 101 - } 67 + for (const site of sites) { 68 + // Create task for this site 69 + const processSite = async () => { 70 + try { 71 + // Check if already cached 72 + if (skipExisting && await isCached(site.did, site.rkey)) { 73 + stats.skipped++; 74 + processed++; 75 + logger.debug(`Skipping already cached site`, { did: site.did, rkey: site.rkey }); 76 + console.log(`โญ๏ธ [${processed}/${sites.length}] Skipped (cached): ${site.display_name || site.rkey}`); 77 + return; 78 + } 102 79 103 - // Mark site as being cached to prevent serving stale content during update 104 - markSiteAsBeingCached(site.did, site.rkey); 80 + // Fetch site record 81 + const siteData = await fetchSiteRecord(site.did, site.rkey); 82 + if (!siteData) { 83 + stats.failed++; 84 + processed++; 85 + logger.error('Site record not found during backfill', null, { did: site.did, rkey: site.rkey }); 86 + console.log(`โŒ [${processed}/${sites.length}] Failed (not found): ${site.display_name || site.rkey}`); 87 + return; 88 + } 105 89 106 - try { 107 - // Download and cache site 108 - await downloadAndCacheSite(site.did, site.rkey, siteData.record, pdsEndpoint, siteData.cid); 109 - // Clear redirect rules cache since the site was updated 110 - clearRedirectRulesCache(site.did, site.rkey); 111 - stats.cached++; 112 - processed++; 113 - logger.info('Successfully cached site during backfill', { did: site.did, rkey: site.rkey }); 114 - console.log(`โœ… [${processed}/${sites.length}] Cached: ${site.display_name || site.rkey}`); 115 - } finally { 116 - // Always unmark, even if caching fails 117 - unmarkSiteAsBeingCached(site.did, site.rkey); 118 - } 119 - } catch (err) { 90 + // Get PDS endpoint 91 + const pdsEndpoint = await getPdsForDid(site.did); 92 + if (!pdsEndpoint) { 120 93 stats.failed++; 121 94 processed++; 122 - logger.error('Failed to cache site during backfill', err, { did: site.did, rkey: site.rkey }); 123 - console.log(`โŒ [${processed}/${sites.length}] Failed: ${site.display_name || site.rkey}`); 95 + logger.error('PDS not found during backfill', null, { did: site.did }); 96 + console.log(`โŒ [${processed}/${sites.length}] Failed (no PDS): ${site.display_name || site.rkey}`); 97 + return; 98 + } 99 + 100 + // Mark site as being cached to prevent serving stale content during update 101 + markSiteAsBeingCached(site.did, site.rkey); 102 + 103 + try { 104 + // Download and cache site 105 + await downloadAndCacheSite(site.did, site.rkey, siteData.record, pdsEndpoint, siteData.cid); 106 + // Clear redirect rules cache since the site was updated 107 + clearRedirectRulesCache(site.did, site.rkey); 108 + stats.cached++; 109 + processed++; 110 + logger.info('Successfully cached site during backfill', { did: site.did, rkey: site.rkey }); 111 + console.log(`โœ… [${processed}/${sites.length}] Cached: ${site.display_name || site.rkey}`); 112 + } finally { 113 + // Always unmark, even if caching fails 114 + unmarkSiteAsBeingCached(site.did, site.rkey); 124 115 } 125 - }) 126 - ); 116 + } catch (err) { 117 + stats.failed++; 118 + processed++; 119 + logger.error('Failed to cache site during backfill', err, { did: site.did, rkey: site.rkey }); 120 + console.log(`โŒ [${processed}/${sites.length}] Failed: ${site.display_name || site.rkey}`); 121 + } 122 + }; 123 + 124 + // Add to executing pool and remove when done 125 + const promise = processSite().finally(() => executing.delete(promise)); 126 + executing.add(promise); 127 + 128 + // When pool is full, wait for at least one to complete 129 + if (executing.size >= concurrency) { 130 + await Promise.race(executing); 131 + } 127 132 } 133 + 134 + // Wait for all remaining tasks to complete 135 + await Promise.all(executing); 128 136 129 137 stats.duration = Date.now() - startTime; 130 138
+35 -43
apps/hosting-service/src/lib/cache.ts
··· 1 - // In-memory LRU cache for file contents and metadata 1 + /** 2 + * Cache management for wisp-hosting-service 3 + * 4 + * With tiered storage, most caching is handled transparently. 5 + * This module tracks sites being cached and manages rewritten HTML cache. 6 + */ 2 7 8 + import { storage } from './storage'; 9 + 10 + // In-memory LRU cache for rewritten HTML (for path rewriting in subdomain routes) 3 11 interface CacheEntry<T> { 4 12 value: T; 5 13 size: number; ··· 96 104 return true; 97 105 } 98 106 99 - // Invalidate all entries for a specific site 100 - invalidateSite(did: string, rkey: string): number { 101 - const prefix = `${did}:${rkey}:`; 102 - let count = 0; 103 - 104 - for (const key of Array.from(this.cache.keys())) { 105 - if (key.startsWith(prefix)) { 106 - this.delete(key); 107 - count++; 108 - } 109 - } 110 - 111 - return count; 112 - } 113 - 114 - // Get cache size 115 107 size(): number { 116 108 return this.cache.size; 117 109 } ··· 127 119 return { ...this.stats }; 128 120 } 129 121 130 - // Get cache hit rate 131 122 getHitRate(): number { 132 123 const total = this.stats.hits + this.stats.misses; 133 124 return total === 0 ? 0 : (this.stats.hits / total) * 100; 134 125 } 135 126 } 136 127 137 - // File metadata cache entry 138 - export interface FileMetadata { 139 - encoding?: 'gzip'; 140 - mimeType: string; 141 - } 142 - 143 - // Global cache instances 144 - const FILE_CACHE_SIZE = 100 * 1024 * 1024; // 100MB 145 - const FILE_CACHE_COUNT = 500; 146 - const METADATA_CACHE_COUNT = 2000; 147 - 148 - export const fileCache = new LRUCache<Buffer>(FILE_CACHE_SIZE, FILE_CACHE_COUNT); 149 - export const metadataCache = new LRUCache<FileMetadata>(1024 * 1024, METADATA_CACHE_COUNT); // 1MB for metadata 128 + // Rewritten HTML cache: stores HTML after path rewriting for subdomain routes 150 129 export const rewrittenHtmlCache = new LRUCache<Buffer>(50 * 1024 * 1024, 200); // 50MB for rewritten HTML 151 130 152 - // Helper to generate cache keys 131 + // Helper to generate cache keys for rewritten HTML 153 132 export function getCacheKey(did: string, rkey: string, filePath: string, suffix?: string): string { 154 133 const base = `${did}:${rkey}:${filePath}`; 155 134 return suffix ? `${base}:${suffix}` : base; 156 135 } 157 136 158 - // Invalidate all caches for a site 159 - export function invalidateSiteCache(did: string, rkey: string): void { 160 - const fileCount = fileCache.invalidateSite(did, rkey); 161 - const metaCount = metadataCache.invalidateSite(did, rkey); 162 - const htmlCount = rewrittenHtmlCache.invalidateSite(did, rkey); 137 + /** 138 + * Invalidate site cache via tiered storage 139 + * Also invalidates locally cached rewritten HTML 140 + */ 141 + export async function invalidateSiteCache(did: string, rkey: string): Promise<void> { 142 + // Invalidate in tiered storage 143 + const prefix = `${did}/${rkey}/`; 144 + const deleted = await storage.invalidate(prefix); 163 145 164 - console.log(`[Cache] Invalidated site ${did}:${rkey} - ${fileCount} files, ${metaCount} metadata, ${htmlCount} HTML`); 146 + // Invalidate rewritten HTML cache for this site 147 + const sitePrefix = `${did}:${rkey}:`; 148 + let htmlCount = 0; 149 + const cacheKeys = Array.from((rewrittenHtmlCache as any).cache?.keys() || []) as string[]; 150 + for (const key of cacheKeys) { 151 + if (key.startsWith(sitePrefix)) { 152 + rewrittenHtmlCache.delete(key); 153 + htmlCount++; 154 + } 155 + } 156 + 157 + console.log(`[Cache] Invalidated site ${did}:${rkey} - ${deleted} files in tiered storage, ${htmlCount} rewritten HTML`); 165 158 } 166 159 167 160 // Track sites currently being cached (to prevent serving stale cache during updates) ··· 183 176 } 184 177 185 178 // Get overall cache statistics 186 - export function getCacheStats() { 179 + export async function getCacheStats() { 180 + const tieredStats = await storage.getStats(); 181 + 187 182 return { 188 - files: fileCache.getStats(), 189 - fileHitRate: fileCache.getHitRate(), 190 - metadata: metadataCache.getStats(), 191 - metadataHitRate: metadataCache.getHitRate(), 183 + tieredStorage: tieredStats, 192 184 rewrittenHtml: rewrittenHtmlCache.getStats(), 193 185 rewrittenHtmlHitRate: rewrittenHtmlCache.getHitRate(), 194 186 sitesBeingCached: sitesBeingCached.size,
+32 -1
apps/hosting-service/src/lib/db.ts
··· 183 183 return hashNum & 0x7FFFFFFFFFFFFFFFn; 184 184 } 185 185 186 + // Track active locks for cleanup on shutdown 187 + const activeLocks = new Set<string>(); 188 + 186 189 /** 187 190 * Acquire a distributed lock using PostgreSQL advisory locks 188 191 * Returns true if lock was acquired, false if already held by another instance ··· 193 196 194 197 try { 195 198 const result = await sql`SELECT pg_try_advisory_lock(${Number(lockId)}) as acquired`; 196 - return result[0]?.acquired === true; 199 + const acquired = result[0]?.acquired === true; 200 + if (acquired) { 201 + activeLocks.add(key); 202 + } 203 + return acquired; 197 204 } catch (err) { 198 205 console.error('Failed to acquire lock', { key, error: err }); 199 206 return false; ··· 208 215 209 216 try { 210 217 await sql`SELECT pg_advisory_unlock(${Number(lockId)})`; 218 + activeLocks.delete(key); 211 219 } catch (err) { 212 220 console.error('Failed to release lock', { key, error: err }); 221 + // Still remove from tracking even if unlock fails 222 + activeLocks.delete(key); 223 + } 224 + } 225 + 226 + /** 227 + * Close all database connections 228 + * Call this during graceful shutdown 229 + */ 230 + export async function closeDatabase(): Promise<void> { 231 + try { 232 + // Release all active advisory locks before closing connections 233 + if (activeLocks.size > 0) { 234 + console.log(`[DB] Releasing ${activeLocks.size} active advisory locks before shutdown`); 235 + for (const key of activeLocks) { 236 + await releaseLock(key); 237 + } 238 + } 239 + 240 + await sql.end({ timeout: 5 }); 241 + console.log('[DB] Database connections closed'); 242 + } catch (err) { 243 + console.error('[DB] Error closing database connections:', err); 213 244 } 214 245 } 215 246
+64 -76
apps/hosting-service/src/lib/file-serving.ts
··· 7 7 import { lookup } from 'mime-types'; 8 8 import type { Record as WispSettings } from '@wisp/lexicons/types/place/wisp/settings'; 9 9 import { shouldCompressMimeType } from '@wisp/atproto-utils/compression'; 10 - import { fileCache, metadataCache, rewrittenHtmlCache, getCacheKey, isSiteBeingCached } from './cache'; 10 + import { rewrittenHtmlCache, getCacheKey, isSiteBeingCached } from './cache'; 11 11 import { getCachedFilePath, getCachedSettings } from './utils'; 12 12 import { loadRedirectRules, matchRedirectRule, parseCookies, parseQueryString } from './redirects'; 13 13 import { rewriteHtmlPaths, isHtmlContent } from './html-rewriter'; 14 14 import { generate404Page, generateDirectoryListing, siteUpdatingResponse } from './page-generators'; 15 15 import { getIndexFiles, applyCustomHeaders, fileExists } from './request-utils'; 16 16 import { getRedirectRulesFromCache, setRedirectRulesInCache } from './site-cache'; 17 + import { storage } from './storage'; 18 + 19 + /** 20 + * Helper to retrieve a file with metadata from tiered storage 21 + */ 22 + async function getFileWithMetadata(did: string, rkey: string, filePath: string) { 23 + const key = `${did}/${rkey}/${filePath}`; 24 + return await storage.getWithMetadata(key); 25 + } 17 26 18 27 /** 19 28 * Helper to serve files from cache (for custom domains and subdomains) ··· 176 185 177 186 // Not a directory, try to serve as a file 178 187 const fileRequestPath: string = requestPath || indexFiles[0] || 'index.html'; 179 - const cacheKey = getCacheKey(did, rkey, fileRequestPath); 180 - const cachedFile = getCachedFilePath(did, rkey, fileRequestPath); 181 188 182 - // Check in-memory cache first 183 - let content = fileCache.get(cacheKey); 184 - let meta = metadataCache.get(cacheKey); 189 + // Retrieve from tiered storage 190 + const result = await getFileWithMetadata(did, rkey, fileRequestPath); 185 191 186 - if (!content && await fileExists(cachedFile)) { 187 - // Read from disk and cache 188 - content = await readFile(cachedFile); 189 - fileCache.set(cacheKey, content, content.length); 192 + if (result) { 193 + const content = Buffer.from(result.data); 194 + const meta = result.metadata.customMetadata as { encoding?: string; mimeType?: string } | undefined; 190 195 191 - const metaFile = `${cachedFile}.meta`; 192 - if (await fileExists(metaFile)) { 193 - const metaJson = await readFile(metaFile, 'utf-8'); 194 - meta = JSON.parse(metaJson); 195 - metadataCache.set(cacheKey, meta!, JSON.stringify(meta).length); 196 - } 197 - } 198 - 199 - if (content) { 200 196 // Build headers with caching 201 - const headers: Record<string, string> = {}; 197 + const headers: Record<string, string> = { 198 + 'X-Cache-Tier': result.source, 199 + }; 202 200 203 - if (meta && meta.encoding === 'gzip' && meta.mimeType) { 201 + if (meta?.encoding === 'gzip' && meta.mimeType) { 204 202 const shouldServeCompressed = shouldCompressMimeType(meta.mimeType); 205 203 206 204 if (!shouldServeCompressed) { ··· 233 231 } 234 232 235 233 // Non-compressed files 236 - const mimeType = lookup(cachedFile) || 'application/octet-stream'; 234 + const mimeType = meta?.mimeType || lookup(fileRequestPath) || 'application/octet-stream'; 237 235 headers['Content-Type'] = mimeType; 238 236 headers['Cache-Control'] = mimeType.startsWith('text/html') 239 237 ? 'public, max-age=300' ··· 246 244 if (!fileRequestPath.includes('.')) { 247 245 for (const indexFileName of indexFiles) { 248 246 const indexPath = fileRequestPath ? `${fileRequestPath}/${indexFileName}` : indexFileName; 249 - const indexCacheKey = getCacheKey(did, rkey, indexPath); 250 - const indexFile = getCachedFilePath(did, rkey, indexPath); 251 247 252 - let indexContent = fileCache.get(indexCacheKey); 253 - let indexMeta = metadataCache.get(indexCacheKey); 248 + const indexResult = await getFileWithMetadata(did, rkey, indexPath); 254 249 255 - if (!indexContent && await fileExists(indexFile)) { 256 - indexContent = await readFile(indexFile); 257 - fileCache.set(indexCacheKey, indexContent, indexContent.length); 250 + if (indexResult) { 251 + const indexContent = Buffer.from(indexResult.data); 252 + const indexMeta = indexResult.metadata.customMetadata as { encoding?: string; mimeType?: string } | undefined; 258 253 259 - const indexMetaFile = `${indexFile}.meta`; 260 - if (await fileExists(indexMetaFile)) { 261 - const metaJson = await readFile(indexMetaFile, 'utf-8'); 262 - indexMeta = JSON.parse(metaJson); 263 - metadataCache.set(indexCacheKey, indexMeta!, JSON.stringify(indexMeta).length); 264 - } 265 - } 266 - 267 - if (indexContent) { 268 254 const headers: Record<string, string> = { 269 255 'Content-Type': 'text/html; charset=utf-8', 270 256 'Cache-Control': 'public, max-age=300', 257 + 'X-Cache-Tier': indexResult.source, 271 258 }; 272 259 273 - if (indexMeta && indexMeta.encoding === 'gzip') { 260 + if (indexMeta?.encoding === 'gzip') { 274 261 headers['Content-Encoding'] = 'gzip'; 275 262 } 276 263 ··· 556 543 557 544 // Not a directory, try to serve as a file 558 545 const fileRequestPath: string = requestPath || indexFiles[0] || 'index.html'; 559 - const cacheKey = getCacheKey(did, rkey, fileRequestPath); 560 - const cachedFile = getCachedFilePath(did, rkey, fileRequestPath); 561 546 562 547 // Check for rewritten HTML in cache first (if it's HTML) 563 548 const mimeTypeGuess = lookup(fileRequestPath) || 'application/octet-stream'; ··· 565 550 const rewrittenKey = getCacheKey(did, rkey, fileRequestPath, `rewritten:${basePath}`); 566 551 const rewrittenContent = rewrittenHtmlCache.get(rewrittenKey); 567 552 if (rewrittenContent) { 553 + console.log(`[HTML Rewrite] Serving from rewritten cache: ${rewrittenKey}`); 568 554 const headers: Record<string, string> = { 569 555 'Content-Type': 'text/html; charset=utf-8', 570 556 'Content-Encoding': 'gzip', 571 557 'Cache-Control': 'public, max-age=300', 558 + 'X-Cache-Tier': 'local', // Rewritten HTML is stored locally 572 559 }; 573 560 applyCustomHeaders(headers, fileRequestPath, settings); 574 561 return new Response(rewrittenContent, { headers }); 575 562 } 576 563 } 577 564 578 - // Check in-memory file cache 579 - let content = fileCache.get(cacheKey); 580 - let meta = metadataCache.get(cacheKey); 565 + // Retrieve from tiered storage 566 + const result = await getFileWithMetadata(did, rkey, fileRequestPath); 581 567 582 - if (!content && await fileExists(cachedFile)) { 583 - // Read from disk and cache 584 - content = await readFile(cachedFile); 585 - fileCache.set(cacheKey, content, content.length); 568 + if (result) { 569 + const content = Buffer.from(result.data); 570 + const meta = result.metadata.customMetadata as { encoding?: string; mimeType?: string } | undefined; 571 + const mimeType = meta?.mimeType || lookup(fileRequestPath) || 'application/octet-stream'; 572 + const isGzipped = meta?.encoding === 'gzip'; 586 573 587 - const metaFile = `${cachedFile}.meta`; 588 - if (await fileExists(metaFile)) { 589 - const metaJson = await readFile(metaFile, 'utf-8'); 590 - meta = JSON.parse(metaJson); 591 - metadataCache.set(cacheKey, meta!, JSON.stringify(meta).length); 592 - } 593 - } 594 - 595 - if (content) { 596 - const mimeType = meta?.mimeType || lookup(cachedFile) || 'application/octet-stream'; 597 - const isGzipped = meta?.encoding === 'gzip'; 574 + console.log(`[File Serve] Serving ${fileRequestPath}, mimeType: ${mimeType}, isHTML: ${isHtmlContent(fileRequestPath, mimeType)}, basePath: ${basePath}`); 598 575 599 576 // Check if this is HTML content that needs rewriting 600 577 if (isHtmlContent(fileRequestPath, mimeType)) { 578 + console.log(`[HTML Rewrite] Processing ${fileRequestPath}, basePath: ${basePath}, mimeType: ${mimeType}, isGzipped: ${isGzipped}`); 601 579 let htmlContent: string; 602 580 if (isGzipped) { 603 581 // Verify content is actually gzipped ··· 612 590 } else { 613 591 htmlContent = content.toString('utf-8'); 614 592 } 593 + // Check for <base> tag which can override paths 594 + const baseTagMatch = htmlContent.match(/<base\s+[^>]*href=["'][^"']+["'][^>]*>/i); 595 + if (baseTagMatch) { 596 + console.warn(`[HTML Rewrite] WARNING: <base> tag found: ${baseTagMatch[0]} - this may override path rewrites`); 597 + } 598 + 599 + // Find src/href attributes (quoted and unquoted) to debug 600 + const allMatches = htmlContent.match(/(?:src|href)\s*=\s*["']?\/[^"'\s>]+/g); 601 + console.log(`[HTML Rewrite] Found ${allMatches ? allMatches.length : 0} local path attrs`); 602 + if (allMatches && allMatches.length > 0) { 603 + console.log(`[HTML Rewrite] Sample paths: ${allMatches.slice(0, 5).join(', ')}`); 604 + } 605 + 615 606 const rewritten = rewriteHtmlPaths(htmlContent, basePath, fileRequestPath); 616 607 608 + const rewrittenMatches = rewritten.match(/(?:src|href)\s*=\s*["']?\/[^"'\s>]+/g); 609 + console.log(`[HTML Rewrite] After rewrite, found ${rewrittenMatches ? rewrittenMatches.length : 0} local paths`); 610 + if (rewrittenMatches && rewrittenMatches.length > 0) { 611 + console.log(`[HTML Rewrite] Sample rewritten: ${rewrittenMatches.slice(0, 5).join(', ')}`); 612 + } 613 + 617 614 // Recompress and cache the rewritten HTML 618 615 const { gzipSync } = await import('zlib'); 619 616 const recompressed = gzipSync(Buffer.from(rewritten, 'utf-8')); ··· 625 622 'Content-Type': 'text/html; charset=utf-8', 626 623 'Content-Encoding': 'gzip', 627 624 'Cache-Control': 'public, max-age=300', 625 + 'X-Cache-Tier': result.source, 628 626 }; 629 627 applyCustomHeaders(htmlHeaders, fileRequestPath, settings); 630 628 return new Response(recompressed, { headers: htmlHeaders }); ··· 634 632 const headers: Record<string, string> = { 635 633 'Content-Type': mimeType, 636 634 'Cache-Control': 'public, max-age=31536000, immutable', 635 + 'X-Cache-Tier': result.source, 637 636 }; 638 637 639 638 if (isGzipped) { ··· 663 662 if (!fileRequestPath.includes('.')) { 664 663 for (const indexFileName of indexFiles) { 665 664 const indexPath = fileRequestPath ? `${fileRequestPath}/${indexFileName}` : indexFileName; 666 - const indexCacheKey = getCacheKey(did, rkey, indexPath); 667 - const indexFile = getCachedFilePath(did, rkey, indexPath); 668 665 669 666 // Check for rewritten index file in cache 670 667 const rewrittenKey = getCacheKey(did, rkey, indexPath, `rewritten:${basePath}`); ··· 674 671 'Content-Type': 'text/html; charset=utf-8', 675 672 'Content-Encoding': 'gzip', 676 673 'Cache-Control': 'public, max-age=300', 674 + 'X-Cache-Tier': 'local', // Rewritten HTML is stored locally 677 675 }; 678 676 applyCustomHeaders(headers, indexPath, settings); 679 677 return new Response(rewrittenContent, { headers }); 680 678 } 681 679 682 - let indexContent = fileCache.get(indexCacheKey); 683 - let indexMeta = metadataCache.get(indexCacheKey); 680 + const indexResult = await getFileWithMetadata(did, rkey, indexPath); 684 681 685 - if (!indexContent && await fileExists(indexFile)) { 686 - indexContent = await readFile(indexFile); 687 - fileCache.set(indexCacheKey, indexContent, indexContent.length); 688 - 689 - const indexMetaFile = `${indexFile}.meta`; 690 - if (await fileExists(indexMetaFile)) { 691 - const metaJson = await readFile(indexMetaFile, 'utf-8'); 692 - indexMeta = JSON.parse(metaJson); 693 - metadataCache.set(indexCacheKey, indexMeta!, JSON.stringify(indexMeta).length); 694 - } 695 - } 696 - 697 - if (indexContent) { 682 + if (indexResult) { 683 + const indexContent = Buffer.from(indexResult.data); 684 + const indexMeta = indexResult.metadata.customMetadata as { encoding?: string; mimeType?: string } | undefined; 698 685 const isGzipped = indexMeta?.encoding === 'gzip'; 699 686 700 687 let htmlContent: string; ··· 722 709 'Content-Type': 'text/html; charset=utf-8', 723 710 'Content-Encoding': 'gzip', 724 711 'Cache-Control': 'public, max-age=300', 712 + 'X-Cache-Tier': indexResult.source, 725 713 }; 726 714 applyCustomHeaders(headers, indexPath, settings); 727 715 return new Response(recompressed, { headers });
+46 -35
apps/hosting-service/src/lib/firehose.ts
··· 1 - import { existsSync, rmSync } from 'fs' 1 + import { existsSync } from 'fs' 2 2 import { 3 3 getPdsForDid, 4 4 downloadAndCacheSite, ··· 13 13 import { invalidateSiteCache, markSiteAsBeingCached, unmarkSiteAsBeingCached } from './cache' 14 14 import { clearRedirectRulesCache } from './site-cache' 15 15 16 - const CACHE_DIR = './cache/sites' 16 + const CACHE_DIR = process.env.CACHE_DIR || './cache/sites' 17 17 18 18 export class FirehoseWorker { 19 19 private firehose: Firehose | null = null 20 20 private idResolver: IdResolver 21 21 private isShuttingDown = false 22 22 private lastEventTime = Date.now() 23 + private eventCount = 0 23 24 private cacheCleanupInterval: NodeJS.Timeout | null = null 25 + private healthCheckInterval: NodeJS.Timeout | null = null 24 26 25 27 constructor( 26 28 private logger?: (msg: string, data?: Record<string, unknown>) => void ··· 47 49 48 50 this.log('IdResolver cache cleared') 49 51 }, 60 * 60 * 1000) // Every hour 52 + 53 + // Health check: log if no events received for 30 seconds 54 + this.healthCheckInterval = setInterval(() => { 55 + if (this.isShuttingDown) return 56 + 57 + const timeSinceLastEvent = Date.now() - this.lastEventTime 58 + if (timeSinceLastEvent > 30000 && this.eventCount === 0) { 59 + this.log('Warning: No firehose events received in the last 30 seconds', { 60 + timeSinceLastEvent, 61 + eventsReceived: this.eventCount 62 + }) 63 + } else if (timeSinceLastEvent > 60000) { 64 + this.log('Firehose status check', { 65 + timeSinceLastEvent, 66 + eventsReceived: this.eventCount 67 + }) 68 + } 69 + }, 30000) // Every 30 seconds 50 70 } 51 71 52 72 start() { ··· 61 81 if (this.cacheCleanupInterval) { 62 82 clearInterval(this.cacheCleanupInterval) 63 83 this.cacheCleanupInterval = null 84 + } 85 + 86 + if (this.healthCheckInterval) { 87 + clearInterval(this.healthCheckInterval) 88 + this.healthCheckInterval = null 64 89 } 65 90 66 91 if (this.firehose) { ··· 80 105 filterCollections: ['place.wisp.fs', 'place.wisp.settings'], 81 106 handleEvent: async (evt: any) => { 82 107 this.lastEventTime = Date.now() 108 + this.eventCount++ 109 + 110 + if (this.eventCount === 1) { 111 + this.log('First firehose event received - connection established', { 112 + eventType: evt.event, 113 + collection: evt.collection 114 + }) 115 + } 83 116 84 117 // Watch for write events 85 118 if (evt.event === 'create' || evt.event === 'update') { ··· 189 222 } 190 223 }) 191 224 192 - this.firehose.start() 193 - this.log('Firehose started') 225 + this.firehose.start().catch((err: unknown) => { 226 + this.log('Fatal firehose error', { 227 + error: err instanceof Error ? err.message : String(err) 228 + }) 229 + console.error('Fatal firehose error:', err) 230 + }) 231 + this.log('Firehose starting') 194 232 } 195 233 196 234 private async handleCreateOrUpdate( ··· 250 288 } 251 289 252 290 // Invalidate in-memory caches before updating 253 - invalidateSiteCache(did, site) 291 + await invalidateSiteCache(did, site) 254 292 255 293 // Mark site as being cached to prevent serving stale content during update 256 294 markSiteAsBeingCached(did, site) ··· 340 378 }) 341 379 } 342 380 343 - // Invalidate in-memory caches 344 - invalidateSiteCache(did, site) 345 - 346 - // Delete disk cache 347 - this.deleteCache(did, site) 381 + // Invalidate all caches (tiered storage invalidation is handled by invalidateSiteCache) 382 + await invalidateSiteCache(did, site) 348 383 349 384 this.log('Successfully processed delete', { did, site }) 350 385 } ··· 353 388 this.log('Processing settings change', { did, rkey }) 354 389 355 390 // Invalidate in-memory caches (includes metadata which stores settings) 356 - invalidateSiteCache(did, rkey) 391 + await invalidateSiteCache(did, rkey) 357 392 358 393 // Check if site is already cached 359 394 const cacheDir = `${CACHE_DIR}/${did}/${rkey}` ··· 413 448 } 414 449 415 450 this.log('Successfully processed settings change', { did, rkey }) 416 - } 417 - 418 - private deleteCache(did: string, site: string) { 419 - const cacheDir = `${CACHE_DIR}/${did}/${site}` 420 - 421 - if (!existsSync(cacheDir)) { 422 - this.log('Cache directory does not exist, nothing to delete', { 423 - did, 424 - site 425 - }) 426 - return 427 - } 428 - 429 - try { 430 - rmSync(cacheDir, { recursive: true, force: true }) 431 - this.log('Cache deleted', { did, site, path: cacheDir }) 432 - } catch (err) { 433 - this.log('Failed to delete cache', { 434 - did, 435 - site, 436 - path: cacheDir, 437 - error: err instanceof Error ? err.message : String(err) 438 - }) 439 - } 440 451 } 441 452 442 453 getHealth() {
+16
apps/hosting-service/src/lib/html-rewriter.ts
··· 189 189 `\\b${attr}[ \\t]{0,5}=[ \\t]{0,5}'([^']*)'`, 190 190 'gi' 191 191 ) 192 + // Unquoted attributes (valid in HTML5 for values without spaces/special chars) 193 + // Match: attr=value where value starts immediately (no quotes) and continues until space or > 194 + // Use negative lookahead to ensure we don't match quoted attributes 195 + const unquotedRegex = new RegExp( 196 + `\\b${attr}[ \\t]{0,5}=[ \\t]{0,5}(?!["'])([^\\s>]+)`, 197 + 'gi' 198 + ) 192 199 193 200 rewritten = rewritten.replace(doubleQuoteRegex, (match, value) => { 194 201 const rewrittenValue = rewritePath( ··· 206 213 documentPath 207 214 ) 208 215 return `${attr}='${rewrittenValue}'` 216 + }) 217 + 218 + rewritten = rewritten.replace(unquotedRegex, (match, value) => { 219 + const rewrittenValue = rewritePath( 220 + value, 221 + normalizedBase, 222 + documentPath 223 + ) 224 + return `${attr}=${rewrittenValue}` 209 225 }) 210 226 } 211 227 }
+1 -1
apps/hosting-service/src/lib/site-cache.ts
··· 42 42 * Returns true if site is successfully cached, false otherwise 43 43 */ 44 44 export async function ensureSiteCached(did: string, rkey: string): Promise<boolean> { 45 - if (isCached(did, rkey)) { 45 + if (await isCached(did, rkey)) { 46 46 return true; 47 47 } 48 48
+270
apps/hosting-service/src/lib/storage.ts
··· 1 + /** 2 + * Tiered storage configuration for wisp-hosting-service 3 + * 4 + * Implements a three-tier caching strategy: 5 + * - Hot (Memory): Instant access for frequently used files (index.html, CSS, JS) 6 + * - Warm (Disk): Local cache with eviction policy 7 + * - Cold (S3/R2): Object storage as source of truth (optional) 8 + * 9 + * When S3 is not configured, falls back to disk-only mode (warm tier acts as source of truth). 10 + * In cache-only mode (non-master nodes), S3 writes are skipped even if configured. 11 + */ 12 + 13 + import { 14 + TieredStorage, 15 + MemoryStorageTier, 16 + DiskStorageTier, 17 + S3StorageTier, 18 + type StorageTier, 19 + type StorageMetadata, 20 + } from 'tiered-storage'; 21 + 22 + const CACHE_DIR = process.env.CACHE_DIR || './cache/sites'; 23 + const HOT_CACHE_SIZE = parseInt(process.env.HOT_CACHE_SIZE || '104857600', 10); // 100MB default 24 + const HOT_CACHE_COUNT = parseInt(process.env.HOT_CACHE_COUNT || '500', 10); 25 + const WARM_CACHE_SIZE = parseInt(process.env.WARM_CACHE_SIZE || '10737418240', 10); // 10GB default 26 + const WARM_EVICTION_POLICY = (process.env.WARM_EVICTION_POLICY || 'lru') as 'lru' | 'fifo' | 'size'; 27 + 28 + // Cache-only mode: skip S3 writes (non-master nodes) 29 + // This is the same flag used to skip database writes 30 + const CACHE_ONLY_MODE = process.env.CACHE_ONLY_MODE === 'true'; 31 + 32 + // S3/Cold tier configuration (optional) 33 + const S3_BUCKET = process.env.S3_BUCKET || ''; 34 + const S3_METADATA_BUCKET = process.env.S3_METADATA_BUCKET; 35 + const S3_REGION = process.env.S3_REGION || 'us-east-1'; 36 + const S3_ENDPOINT = process.env.S3_ENDPOINT; 37 + const S3_FORCE_PATH_STYLE = process.env.S3_FORCE_PATH_STYLE !== 'false'; 38 + const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID; 39 + const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY; 40 + const S3_PREFIX = process.env.S3_PREFIX || 'sites/'; 41 + 42 + // Identity serializers for raw binary data (no JSON transformation) 43 + // Files are stored as-is without any encoding/decoding 44 + const identitySerialize = async (data: unknown): Promise<Uint8Array> => { 45 + if (data instanceof Uint8Array) return data; 46 + if (data instanceof ArrayBuffer) return new Uint8Array(data); 47 + if (Buffer.isBuffer(data)) return new Uint8Array(data); 48 + // For other types, fall back to JSON (shouldn't happen with file storage) 49 + return new TextEncoder().encode(JSON.stringify(data)); 50 + }; 51 + 52 + const identityDeserialize = async (data: Uint8Array): Promise<unknown> => { 53 + // Return as-is for binary file storage 54 + return data; 55 + }; 56 + 57 + /** 58 + * Read-only wrapper for S3 tier in cache-only mode. 59 + * Allows reads from S3 but skips all writes (for non-master nodes). 60 + */ 61 + class ReadOnlyS3Tier implements StorageTier { 62 + private static hasLoggedWriteSkip = false; 63 + 64 + constructor(private tier: StorageTier) {} 65 + 66 + // Read operations - pass through to underlying tier 67 + async get(key: string) { 68 + return this.tier.get(key); 69 + } 70 + 71 + async getWithMetadata(key: string) { 72 + return this.tier.getWithMetadata?.(key) ?? null; 73 + } 74 + 75 + async getStream(key: string) { 76 + return this.tier.getStream?.(key) ?? null; 77 + } 78 + 79 + async exists(key: string) { 80 + return this.tier.exists(key); 81 + } 82 + 83 + async getMetadata(key: string) { 84 + return this.tier.getMetadata(key); 85 + } 86 + 87 + async *listKeys(prefix?: string) { 88 + yield* this.tier.listKeys(prefix); 89 + } 90 + 91 + async getStats() { 92 + return this.tier.getStats(); 93 + } 94 + 95 + // Write operations - no-op in cache-only mode 96 + async set(key: string, _data: Uint8Array, _metadata: StorageMetadata) { 97 + this.logWriteSkip('set', key); 98 + } 99 + 100 + async setStream(key: string, _stream: NodeJS.ReadableStream, _metadata: StorageMetadata) { 101 + this.logWriteSkip('setStream', key); 102 + } 103 + 104 + async setMetadata(key: string, _metadata: StorageMetadata) { 105 + this.logWriteSkip('setMetadata', key); 106 + } 107 + 108 + async delete(key: string) { 109 + this.logWriteSkip('delete', key); 110 + } 111 + 112 + async deleteMany(keys: string[]) { 113 + this.logWriteSkip('deleteMany', `${keys.length} keys`); 114 + } 115 + 116 + async clear() { 117 + this.logWriteSkip('clear', 'all keys'); 118 + } 119 + 120 + private logWriteSkip(operation: string, key: string) { 121 + // Only log once to avoid spam 122 + if (!ReadOnlyS3Tier.hasLoggedWriteSkip) { 123 + console.log(`[Storage] Cache-only mode: skipping S3 writes (operation: ${operation})`); 124 + ReadOnlyS3Tier.hasLoggedWriteSkip = true; 125 + } 126 + } 127 + } 128 + 129 + /** 130 + * Initialize tiered storage 131 + * Must be called before serving requests 132 + */ 133 + function initializeStorage(): TieredStorage<Uint8Array> { 134 + // Determine cold tier: S3 if configured, otherwise disk acts as cold 135 + let coldTier: StorageTier; 136 + let warmTier: StorageTier | undefined; 137 + 138 + const diskTier = new DiskStorageTier({ 139 + directory: CACHE_DIR, 140 + maxSizeBytes: WARM_CACHE_SIZE, 141 + evictionPolicy: WARM_EVICTION_POLICY, 142 + encodeColons: false, // Preserve colons for readable DID paths on Unix/macOS 143 + }); 144 + 145 + if (S3_BUCKET) { 146 + // Full three-tier setup with S3 as cold storage 147 + const s3Tier = new S3StorageTier({ 148 + bucket: S3_BUCKET, 149 + metadataBucket: S3_METADATA_BUCKET, 150 + region: S3_REGION, 151 + endpoint: S3_ENDPOINT, 152 + forcePathStyle: S3_FORCE_PATH_STYLE, 153 + credentials: 154 + AWS_ACCESS_KEY_ID && AWS_SECRET_ACCESS_KEY 155 + ? { accessKeyId: AWS_ACCESS_KEY_ID, secretAccessKey: AWS_SECRET_ACCESS_KEY } 156 + : undefined, 157 + prefix: S3_PREFIX, 158 + }); 159 + 160 + // In cache-only mode, wrap S3 tier to make it read-only 161 + coldTier = CACHE_ONLY_MODE ? new ReadOnlyS3Tier(s3Tier) : s3Tier; 162 + warmTier = diskTier; 163 + 164 + if (CACHE_ONLY_MODE) { 165 + console.log('[Storage] Cache-only mode: S3 as read-only cold tier (no writes), disk as warm tier'); 166 + } else { 167 + console.log('[Storage] Using S3 as cold tier, disk as warm tier'); 168 + } 169 + } else { 170 + // Disk-only mode: disk tier acts as source of truth (cold) 171 + coldTier = diskTier; 172 + warmTier = undefined; 173 + console.log('[Storage] S3 not configured - using disk-only mode (disk as cold tier)'); 174 + } 175 + 176 + const storage = new TieredStorage<Uint8Array>({ 177 + tiers: { 178 + // Hot tier: In-memory LRU for instant serving 179 + hot: new MemoryStorageTier({ 180 + maxSizeBytes: HOT_CACHE_SIZE, 181 + maxItems: HOT_CACHE_COUNT, 182 + }), 183 + 184 + // Warm tier: Disk-based cache (only when S3 is configured) 185 + warm: warmTier, 186 + 187 + // Cold tier: S3/R2 as source of truth, or disk in disk-only mode 188 + cold: coldTier, 189 + }, 190 + 191 + // Placement rules: determine which tiers each file goes to 192 + placementRules: [ 193 + // Metadata is critical: frequently accessed for cache validity checks 194 + { 195 + pattern: '**/.metadata.json', 196 + tiers: ['hot', 'warm', 'cold'], 197 + }, 198 + 199 + // index.html is critical: write to all tiers for instant serving 200 + { 201 + pattern: '**/index.html', 202 + tiers: ['hot', 'warm', 'cold'], 203 + }, 204 + { 205 + pattern: 'index.html', 206 + tiers: ['hot', 'warm', 'cold'], 207 + }, 208 + 209 + // CSS and JS: eligible for hot tier if accessed frequently 210 + { 211 + pattern: '**/*.{css,js}', 212 + tiers: ['hot', 'warm', 'cold'], 213 + }, 214 + 215 + // Media files: never needed in memory, skip hot tier 216 + { 217 + pattern: '**/*.{jpg,jpeg,png,gif,webp,svg,ico,mp4,webm,mp3,woff,woff2,ttf,eot}', 218 + tiers: ['warm', 'cold'], 219 + }, 220 + 221 + // Default: everything else goes to warm and cold 222 + { 223 + pattern: '**', 224 + tiers: ['warm', 'cold'], 225 + }, 226 + ], 227 + 228 + // IMPORTANT: Compression is disabled at the tiered-storage level 229 + // Text files (HTML, CSS, JS, JSON) are pre-compressed with gzip at the app level 230 + // Binary files (images, video) are stored uncompressed as they're already compressed 231 + // The file's compression state is tracked in customMetadata.encoding 232 + compression: false, 233 + 234 + // TTL for cache entries (14 days) 235 + defaultTTL: 14 * 24 * 60 * 60 * 1000, 236 + 237 + // Eager promotion: promote data to upper tiers on read 238 + // This ensures frequently accessed files end up in hot tier 239 + promotionStrategy: 'eager', 240 + 241 + // Identity serialization: store raw binary without JSON transformation 242 + serialization: { 243 + serialize: identitySerialize, 244 + deserialize: identityDeserialize, 245 + }, 246 + }); 247 + 248 + return storage; 249 + } 250 + 251 + // Export singleton instance 252 + export const storage = initializeStorage(); 253 + 254 + /** 255 + * Get storage configuration summary for logging 256 + */ 257 + export function getStorageConfig() { 258 + return { 259 + cacheDir: CACHE_DIR, 260 + hotCacheSize: `${(HOT_CACHE_SIZE / 1024 / 1024).toFixed(0)}MB`, 261 + hotCacheCount: HOT_CACHE_COUNT, 262 + warmCacheSize: `${(WARM_CACHE_SIZE / 1024 / 1024 / 1024).toFixed(1)}GB`, 263 + warmEvictionPolicy: WARM_EVICTION_POLICY, 264 + s3Bucket: S3_BUCKET, 265 + s3Region: S3_REGION, 266 + s3Endpoint: S3_ENDPOINT || '(default AWS S3)', 267 + s3Prefix: S3_PREFIX, 268 + metadataBucket: S3_METADATA_BUCKET || '(embedded in data bucket)', 269 + }; 270 + }
+473 -8
apps/hosting-service/src/lib/utils.test.ts
··· 1 1 import { describe, test, expect } from 'bun:test' 2 - import { sanitizePath, extractBlobCid } from './utils' 2 + import { sanitizePath, extractBlobCid, extractSubfsUris, expandSubfsNodes } from './utils' 3 3 import { CID } from 'multiformats' 4 + import { BlobRef } from '@atproto/lexicon' 5 + import type { 6 + Record as WispFsRecord, 7 + Directory as FsDirectory, 8 + Entry as FsEntry, 9 + File as FsFile, 10 + Subfs as FsSubfs, 11 + } from '@wisp/lexicons/types/place/wisp/fs' 12 + import type { 13 + Record as SubfsRecord, 14 + Directory as SubfsDirectory, 15 + Entry as SubfsEntry, 16 + File as SubfsFile, 17 + Subfs as SubfsSubfs, 18 + } from '@wisp/lexicons/types/place/wisp/subfs' 19 + import type { $Typed } from '@wisp/lexicons/util' 4 20 5 21 describe('sanitizePath', () => { 6 22 test('allows normal file paths', () => { ··· 31 47 32 48 test('blocks directory traversal in middle of path', () => { 33 49 expect(sanitizePath('images/../../../etc/passwd')).toBe('images/etc/passwd') 34 - // Note: sanitizePath only filters out ".." segments, doesn't resolve paths 35 50 expect(sanitizePath('a/b/../c')).toBe('a/b/c') 36 51 expect(sanitizePath('a/../b/../c')).toBe('a/b/c') 37 52 }) ··· 50 65 }) 51 66 52 67 test('blocks null bytes', () => { 53 - // Null bytes cause the entire segment to be filtered out 54 68 expect(sanitizePath('index.html\0.txt')).toBe('') 55 69 expect(sanitizePath('test\0')).toBe('') 56 - // Null byte in middle segment 57 70 expect(sanitizePath('css/bad\0name/styles.css')).toBe('css/styles.css') 58 71 }) 59 72 ··· 89 102 90 103 describe('extractBlobCid', () => { 91 104 const TEST_CID = 'bafkreid7ybejd5s2vv2j7d4aajjlmdgazguemcnuliiyfn6coxpwp2mi6y' 92 - 105 + 93 106 test('extracts CID from IPLD link', () => { 94 107 const blobRef = { $link: TEST_CID } 95 108 expect(extractBlobCid(blobRef)).toBe(TEST_CID) ··· 103 116 }) 104 117 105 118 test('extracts CID from typed BlobRef with IPLD link', () => { 106 - const blobRef = { 119 + const blobRef = { 107 120 ref: { $link: TEST_CID } 108 121 } 109 122 expect(extractBlobCid(blobRef)).toBe(TEST_CID) ··· 129 142 }) 130 143 131 144 test('handles nested structures from AT Proto API', () => { 132 - // Real structure from AT Proto 133 145 const blobRef = { 134 146 $type: 'blob', 135 147 ref: CID.parse(TEST_CID), ··· 150 162 }) 151 163 152 164 test('prioritizes checking IPLD link first', () => { 153 - // Direct $link takes precedence 154 165 const directLink = { $link: TEST_CID } 155 166 expect(extractBlobCid(directLink)).toBe(TEST_CID) 156 167 }) ··· 167 178 expect(extractBlobCid(blobRef)).toBe(cidV1) 168 179 }) 169 180 }) 181 + 182 + const TEST_CID_BASE = 'bafkreid7ybejd5s2vv2j7d4aajjlmdgazguemcnuliiyfn6coxpwp2mi6y' 183 + 184 + function createMockBlobRef(cidSuffix: string = '', size: number = 100, mimeType: string = 'text/plain'): BlobRef { 185 + const cidString = TEST_CID_BASE 186 + return new BlobRef(CID.parse(cidString), mimeType, size) 187 + } 188 + 189 + function createFsFile( 190 + name: string, 191 + options: { mimeType?: string; size?: number; encoding?: 'gzip'; base64?: boolean } = {} 192 + ): FsEntry { 193 + const { mimeType = 'text/plain', size = 100, encoding, base64 } = options 194 + const file: $Typed<FsFile, 'place.wisp.fs#file'> = { 195 + $type: 'place.wisp.fs#file', 196 + type: 'file', 197 + blob: createMockBlobRef(name.replace(/[^a-z0-9]/gi, ''), size, mimeType), 198 + ...(encoding && { encoding }), 199 + ...(mimeType && { mimeType }), 200 + ...(base64 && { base64 }), 201 + } 202 + return { name, node: file } 203 + } 204 + 205 + function createFsDirectory(name: string, entries: FsEntry[]): FsEntry { 206 + const dir: $Typed<FsDirectory, 'place.wisp.fs#directory'> = { 207 + $type: 'place.wisp.fs#directory', 208 + type: 'directory', 209 + entries, 210 + } 211 + return { name, node: dir } 212 + } 213 + 214 + function createFsSubfs(name: string, subject: string, flat: boolean = true): FsEntry { 215 + const subfs: $Typed<FsSubfs, 'place.wisp.fs#subfs'> = { 216 + $type: 'place.wisp.fs#subfs', 217 + type: 'subfs', 218 + subject, 219 + flat, 220 + } 221 + return { name, node: subfs } 222 + } 223 + 224 + function createFsRootDirectory(entries: FsEntry[]): FsDirectory { 225 + return { 226 + $type: 'place.wisp.fs#directory', 227 + type: 'directory', 228 + entries, 229 + } 230 + } 231 + 232 + function createFsRecord(site: string, entries: FsEntry[], fileCount?: number): WispFsRecord { 233 + return { 234 + $type: 'place.wisp.fs', 235 + site, 236 + root: createFsRootDirectory(entries), 237 + ...(fileCount !== undefined && { fileCount }), 238 + createdAt: new Date().toISOString(), 239 + } 240 + } 241 + 242 + function createSubfsFile( 243 + name: string, 244 + options: { mimeType?: string; size?: number; encoding?: 'gzip'; base64?: boolean } = {} 245 + ): SubfsEntry { 246 + const { mimeType = 'text/plain', size = 100, encoding, base64 } = options 247 + const file: $Typed<SubfsFile, 'place.wisp.subfs#file'> = { 248 + $type: 'place.wisp.subfs#file', 249 + type: 'file', 250 + blob: createMockBlobRef(name.replace(/[^a-z0-9]/gi, ''), size, mimeType), 251 + ...(encoding && { encoding }), 252 + ...(mimeType && { mimeType }), 253 + ...(base64 && { base64 }), 254 + } 255 + return { name, node: file } 256 + } 257 + 258 + function createSubfsDirectory(name: string, entries: SubfsEntry[]): SubfsEntry { 259 + const dir: $Typed<SubfsDirectory, 'place.wisp.subfs#directory'> = { 260 + $type: 'place.wisp.subfs#directory', 261 + type: 'directory', 262 + entries, 263 + } 264 + return { name, node: dir } 265 + } 266 + 267 + function createSubfsSubfs(name: string, subject: string): SubfsEntry { 268 + const subfs: $Typed<SubfsSubfs, 'place.wisp.subfs#subfs'> = { 269 + $type: 'place.wisp.subfs#subfs', 270 + type: 'subfs', 271 + subject, 272 + } 273 + return { name, node: subfs } 274 + } 275 + 276 + function createSubfsRootDirectory(entries: SubfsEntry[]): SubfsDirectory { 277 + return { 278 + $type: 'place.wisp.subfs#directory', 279 + type: 'directory', 280 + entries, 281 + } 282 + } 283 + 284 + function createSubfsRecord(entries: SubfsEntry[], fileCount?: number): SubfsRecord { 285 + return { 286 + $type: 'place.wisp.subfs', 287 + root: createSubfsRootDirectory(entries), 288 + ...(fileCount !== undefined && { fileCount }), 289 + createdAt: new Date().toISOString(), 290 + } 291 + } 292 + 293 + describe('extractSubfsUris', () => { 294 + test('extracts subfs URIs from flat directory structure', () => { 295 + const subfsUri = 'at://did:plc:test/place.wisp.subfs/a' 296 + const dir = createFsRootDirectory([ 297 + createFsSubfs('a', subfsUri), 298 + createFsFile('file.txt'), 299 + ]) 300 + 301 + const uris = extractSubfsUris(dir) 302 + 303 + expect(uris).toHaveLength(1) 304 + expect(uris[0]).toEqual({ uri: subfsUri, path: 'a' }) 305 + }) 306 + 307 + test('extracts subfs URIs from nested directory structure', () => { 308 + const subfsAUri = 'at://did:plc:test/place.wisp.subfs/a' 309 + const subfsBUri = 'at://did:plc:test/place.wisp.subfs/b' 310 + 311 + const dir = createFsRootDirectory([ 312 + createFsSubfs('a', subfsAUri), 313 + createFsDirectory('nested', [ 314 + createFsSubfs('b', subfsBUri), 315 + createFsFile('file.txt'), 316 + ]), 317 + ]) 318 + 319 + const uris = extractSubfsUris(dir) 320 + 321 + expect(uris).toHaveLength(2) 322 + expect(uris).toContainEqual({ uri: subfsAUri, path: 'a' }) 323 + expect(uris).toContainEqual({ uri: subfsBUri, path: 'nested/b' }) 324 + }) 325 + 326 + test('returns empty array when no subfs nodes exist', () => { 327 + const dir = createFsRootDirectory([ 328 + createFsFile('file1.txt'), 329 + createFsDirectory('dir', [createFsFile('file2.txt')]), 330 + ]) 331 + 332 + const uris = extractSubfsUris(dir) 333 + expect(uris).toHaveLength(0) 334 + }) 335 + 336 + test('handles deeply nested subfs', () => { 337 + const subfsUri = 'at://did:plc:test/place.wisp.subfs/deep' 338 + const dir = createFsRootDirectory([ 339 + createFsDirectory('a', [ 340 + createFsDirectory('b', [ 341 + createFsDirectory('c', [ 342 + createFsSubfs('deep', subfsUri), 343 + ]), 344 + ]), 345 + ]), 346 + ]) 347 + 348 + const uris = extractSubfsUris(dir) 349 + 350 + expect(uris).toHaveLength(1) 351 + expect(uris[0]).toEqual({ uri: subfsUri, path: 'a/b/c/deep' }) 352 + }) 353 + }) 354 + 355 + describe('expandSubfsNodes caching', () => { 356 + test('cache map is populated after expansion', async () => { 357 + const subfsCache = new Map<string, SubfsRecord | null>() 358 + const dir = createFsRootDirectory([createFsFile('file.txt')]) 359 + 360 + const result = await expandSubfsNodes(dir, 'https://pds.example.com', 0, subfsCache) 361 + 362 + expect(subfsCache.size).toBe(0) 363 + expect(result.entries).toHaveLength(1) 364 + expect(result.entries[0]?.name).toBe('file.txt') 365 + }) 366 + 367 + test('cache is passed through recursion depths', async () => { 368 + const subfsCache = new Map<string, SubfsRecord | null>() 369 + const mockSubfsUri = 'at://did:plc:test/place.wisp.subfs/cached' 370 + const mockRecord = createSubfsRecord([createSubfsFile('cached-file.txt')]) 371 + subfsCache.set(mockSubfsUri, mockRecord) 372 + 373 + const dir = createFsRootDirectory([createFsSubfs('cached', mockSubfsUri)]) 374 + const result = await expandSubfsNodes(dir, 'https://pds.example.com', 0, subfsCache) 375 + 376 + expect(subfsCache.has(mockSubfsUri)).toBe(true) 377 + expect(result.entries).toHaveLength(1) 378 + expect(result.entries[0]?.name).toBe('cached-file.txt') 379 + }) 380 + 381 + test('pre-populated cache prevents re-fetching', async () => { 382 + const subfsCache = new Map<string, SubfsRecord | null>() 383 + const subfsAUri = 'at://did:plc:test/place.wisp.subfs/a' 384 + const subfsBUri = 'at://did:plc:test/place.wisp.subfs/b' 385 + 386 + subfsCache.set(subfsAUri, createSubfsRecord([createSubfsSubfs('b', subfsBUri)])) 387 + subfsCache.set(subfsBUri, createSubfsRecord([createSubfsFile('final.txt')])) 388 + 389 + const dir = createFsRootDirectory([createFsSubfs('a', subfsAUri)]) 390 + const result = await expandSubfsNodes(dir, 'https://pds.example.com', 0, subfsCache) 391 + 392 + expect(result.entries).toHaveLength(1) 393 + expect(result.entries[0]?.name).toBe('final.txt') 394 + }) 395 + 396 + test('diamond dependency uses cache for shared reference', async () => { 397 + const subfsCache = new Map<string, SubfsRecord | null>() 398 + const subfsAUri = 'at://did:plc:test/place.wisp.subfs/a' 399 + const subfsBUri = 'at://did:plc:test/place.wisp.subfs/b' 400 + const subfsCUri = 'at://did:plc:test/place.wisp.subfs/c' 401 + 402 + subfsCache.set(subfsAUri, createSubfsRecord([createSubfsSubfs('c', subfsCUri)])) 403 + subfsCache.set(subfsBUri, createSubfsRecord([createSubfsSubfs('c', subfsCUri)])) 404 + subfsCache.set(subfsCUri, createSubfsRecord([createSubfsFile('shared.txt')])) 405 + 406 + const dir = createFsRootDirectory([ 407 + createFsSubfs('a', subfsAUri), 408 + createFsSubfs('b', subfsBUri), 409 + ]) 410 + const result = await expandSubfsNodes(dir, 'https://pds.example.com', 0, subfsCache) 411 + 412 + expect(result.entries.filter(e => e.name === 'shared.txt')).toHaveLength(2) 413 + }) 414 + 415 + test('handles null records in cache gracefully', async () => { 416 + const subfsCache = new Map<string, SubfsRecord | null>() 417 + const subfsUri = 'at://did:plc:test/place.wisp.subfs/missing' 418 + subfsCache.set(subfsUri, null) 419 + 420 + const dir = createFsRootDirectory([ 421 + createFsFile('file.txt'), 422 + createFsSubfs('missing', subfsUri), 423 + ]) 424 + const result = await expandSubfsNodes(dir, 'https://pds.example.com', 0, subfsCache) 425 + 426 + expect(result.entries.some(e => e.name === 'file.txt')).toBe(true) 427 + expect(result.entries.some(e => e.name === 'missing')).toBe(true) 428 + }) 429 + 430 + test('non-flat subfs merge creates directory instead of hoisting', async () => { 431 + const subfsCache = new Map<string, SubfsRecord | null>() 432 + const subfsUri = 'at://did:plc:test/place.wisp.subfs/nested' 433 + subfsCache.set(subfsUri, createSubfsRecord([createSubfsFile('nested-file.txt')])) 434 + 435 + const dir = createFsRootDirectory([ 436 + createFsFile('root.txt'), 437 + createFsSubfs('subdir', subfsUri, false), 438 + ]) 439 + const result = await expandSubfsNodes(dir, 'https://pds.example.com', 0, subfsCache) 440 + 441 + expect(result.entries).toHaveLength(2) 442 + 443 + const rootFile = result.entries.find(e => e.name === 'root.txt') 444 + expect(rootFile).toBeDefined() 445 + 446 + const subdir = result.entries.find(e => e.name === 'subdir') 447 + expect(subdir).toBeDefined() 448 + 449 + if (subdir && 'entries' in subdir.node) { 450 + expect(subdir.node.type).toBe('directory') 451 + expect(subdir.node.entries).toHaveLength(1) 452 + expect(subdir.node.entries[0]?.name).toBe('nested-file.txt') 453 + } 454 + }) 455 + }) 456 + 457 + describe('WispFsRecord mock builders', () => { 458 + test('createFsRecord creates valid record structure', () => { 459 + const record = createFsRecord('my-site', [ 460 + createFsFile('index.html', { mimeType: 'text/html' }), 461 + createFsDirectory('assets', [ 462 + createFsFile('style.css', { mimeType: 'text/css' }), 463 + ]), 464 + ]) 465 + 466 + expect(record.$type).toBe('place.wisp.fs') 467 + expect(record.site).toBe('my-site') 468 + expect(record.root.type).toBe('directory') 469 + expect(record.root.entries).toHaveLength(2) 470 + expect(record.createdAt).toBeDefined() 471 + }) 472 + 473 + test('createFsFile creates valid file entry', () => { 474 + const entry = createFsFile('test.html', { mimeType: 'text/html', size: 500 }) 475 + 476 + expect(entry.name).toBe('test.html') 477 + 478 + const file = entry.node 479 + if ('blob' in file) { 480 + expect(file.$type).toBe('place.wisp.fs#file') 481 + expect(file.type).toBe('file') 482 + expect(file.blob).toBeDefined() 483 + expect(file.mimeType).toBe('text/html') 484 + } 485 + }) 486 + 487 + test('createFsFile with gzip encoding', () => { 488 + const entry = createFsFile('bundle.js', { mimeType: 'application/javascript', encoding: 'gzip' }) 489 + 490 + const file = entry.node 491 + if ('encoding' in file) { 492 + expect(file.encoding).toBe('gzip') 493 + } 494 + }) 495 + 496 + test('createFsFile with base64 flag', () => { 497 + const entry = createFsFile('data.bin', { base64: true }) 498 + 499 + const file = entry.node 500 + if ('base64' in file) { 501 + expect(file.base64).toBe(true) 502 + } 503 + }) 504 + 505 + test('createFsDirectory creates valid directory entry', () => { 506 + const entry = createFsDirectory('assets', [ 507 + createFsFile('file1.txt'), 508 + createFsFile('file2.txt'), 509 + ]) 510 + 511 + expect(entry.name).toBe('assets') 512 + 513 + const dir = entry.node 514 + if ('entries' in dir) { 515 + expect(dir.$type).toBe('place.wisp.fs#directory') 516 + expect(dir.type).toBe('directory') 517 + expect(dir.entries).toHaveLength(2) 518 + } 519 + }) 520 + 521 + test('createFsSubfs creates valid subfs entry with flat=true', () => { 522 + const entry = createFsSubfs('external', 'at://did:plc:test/place.wisp.subfs/ext') 523 + 524 + expect(entry.name).toBe('external') 525 + 526 + const subfs = entry.node 527 + if ('subject' in subfs) { 528 + expect(subfs.$type).toBe('place.wisp.fs#subfs') 529 + expect(subfs.type).toBe('subfs') 530 + expect(subfs.subject).toBe('at://did:plc:test/place.wisp.subfs/ext') 531 + expect(subfs.flat).toBe(true) 532 + } 533 + }) 534 + 535 + test('createFsSubfs creates valid subfs entry with flat=false', () => { 536 + const entry = createFsSubfs('external', 'at://did:plc:test/place.wisp.subfs/ext', false) 537 + 538 + const subfs = entry.node 539 + if ('subject' in subfs) { 540 + expect(subfs.flat).toBe(false) 541 + } 542 + }) 543 + 544 + test('createFsRecord with fileCount', () => { 545 + const record = createFsRecord('my-site', [createFsFile('index.html')], 1) 546 + expect(record.fileCount).toBe(1) 547 + }) 548 + }) 549 + 550 + describe('SubfsRecord mock builders', () => { 551 + test('createSubfsRecord creates valid record structure', () => { 552 + const record = createSubfsRecord([ 553 + createSubfsFile('file1.txt'), 554 + createSubfsDirectory('nested', [ 555 + createSubfsFile('file2.txt'), 556 + ]), 557 + ]) 558 + 559 + expect(record.$type).toBe('place.wisp.subfs') 560 + expect(record.root.type).toBe('directory') 561 + expect(record.root.entries).toHaveLength(2) 562 + expect(record.createdAt).toBeDefined() 563 + }) 564 + 565 + test('createSubfsFile creates valid file entry', () => { 566 + const entry = createSubfsFile('data.json', { mimeType: 'application/json', size: 1024 }) 567 + 568 + expect(entry.name).toBe('data.json') 569 + 570 + const file = entry.node 571 + if ('blob' in file) { 572 + expect(file.$type).toBe('place.wisp.subfs#file') 573 + expect(file.type).toBe('file') 574 + expect(file.blob).toBeDefined() 575 + expect(file.mimeType).toBe('application/json') 576 + } 577 + }) 578 + 579 + test('createSubfsDirectory creates valid directory entry', () => { 580 + const entry = createSubfsDirectory('subdir', [createSubfsFile('inner.txt')]) 581 + 582 + expect(entry.name).toBe('subdir') 583 + 584 + const dir = entry.node 585 + if ('entries' in dir) { 586 + expect(dir.$type).toBe('place.wisp.subfs#directory') 587 + expect(dir.type).toBe('directory') 588 + expect(dir.entries).toHaveLength(1) 589 + } 590 + }) 591 + 592 + test('createSubfsSubfs creates valid nested subfs entry', () => { 593 + const entry = createSubfsSubfs('deeper', 'at://did:plc:test/place.wisp.subfs/deeper') 594 + 595 + expect(entry.name).toBe('deeper') 596 + 597 + const subfs = entry.node 598 + if ('subject' in subfs) { 599 + expect(subfs.$type).toBe('place.wisp.subfs#subfs') 600 + expect(subfs.type).toBe('subfs') 601 + expect(subfs.subject).toBe('at://did:plc:test/place.wisp.subfs/deeper') 602 + expect('flat' in subfs).toBe(false) 603 + } 604 + }) 605 + 606 + test('createSubfsRecord with fileCount', () => { 607 + const record = createSubfsRecord([createSubfsFile('file.txt')], 1) 608 + expect(record.fileCount).toBe(1) 609 + }) 610 + }) 611 + 612 + describe('extractBlobCid with typed mock data', () => { 613 + test('extracts CID from FsFile blob', () => { 614 + const entry = createFsFile('test.txt') 615 + const file = entry.node 616 + 617 + if ('blob' in file) { 618 + const cid = extractBlobCid(file.blob) 619 + expect(cid).toBeDefined() 620 + expect(cid).toContain('bafkrei') 621 + } 622 + }) 623 + 624 + test('extracts CID from SubfsFile blob', () => { 625 + const entry = createSubfsFile('test.txt') 626 + const file = entry.node 627 + 628 + if ('blob' in file) { 629 + const cid = extractBlobCid(file.blob) 630 + expect(cid).toBeDefined() 631 + expect(cid).toContain('bafkrei') 632 + } 633 + }) 634 + })
+128 -167
apps/hosting-service/src/lib/utils.ts
··· 4 4 import type { Record as WispSettings } from '@wisp/lexicons/types/place/wisp/settings'; 5 5 import { existsSync, mkdirSync, readFileSync, rmSync } from 'fs'; 6 6 import { writeFile, readFile, rename } from 'fs/promises'; 7 + import { Readable } from 'stream'; 7 8 import { safeFetchJson, safeFetchBlob } from '@wisp/safe-fetch'; 8 9 import { CID } from 'multiformats'; 9 10 import { extractBlobCid } from '@wisp/atproto-utils'; 10 11 import { sanitizePath, collectFileCidsFromEntries, countFilesInDirectory } from '@wisp/fs-utils'; 11 12 import { shouldCompressMimeType } from '@wisp/atproto-utils/compression'; 12 13 import { MAX_BLOB_SIZE, MAX_FILE_COUNT, MAX_SITE_SIZE } from '@wisp/constants'; 14 + import { storage } from './storage'; 13 15 14 16 // Re-export shared utilities for local usage and tests 15 17 export { extractBlobCid, sanitizePath }; ··· 90 92 export async function fetchSiteRecord(did: string, rkey: string): Promise<{ record: WispFsRecord; cid: string } | null> { 91 93 try { 92 94 const pdsEndpoint = await getPdsForDid(did); 93 - if (!pdsEndpoint) return null; 95 + if (!pdsEndpoint) { 96 + console.error('[hosting-service] Failed to get PDS endpoint for DID', { did, rkey }); 97 + return null; 98 + } 94 99 95 100 const url = `${pdsEndpoint}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(did)}&collection=place.wisp.fs&rkey=${encodeURIComponent(rkey)}`; 96 101 const data = await safeFetchJson(url); ··· 100 105 cid: data.cid || '' 101 106 }; 102 107 } catch (err) { 103 - console.error('Failed to fetch site record', did, rkey, err); 108 + const errorCode = (err as any)?.code; 109 + const errorMsg = err instanceof Error ? err.message : String(err); 110 + 111 + // Better error logging to distinguish between network errors and 404s 112 + if (errorMsg.includes('HTTP 404') || errorMsg.includes('Not Found')) { 113 + console.log('[hosting-service] Site record not found', { did, rkey }); 114 + } else if (errorCode && ['ECONNRESET', 'ERR_SSL_TLSV1_ALERT_INTERNAL_ERROR', 'ETIMEDOUT'].includes(errorCode)) { 115 + console.error('[hosting-service] Network/SSL error fetching site record (after retries)', { 116 + did, 117 + rkey, 118 + error: errorMsg, 119 + code: errorCode 120 + }); 121 + } else { 122 + console.error('[hosting-service] Failed to fetch site record', { 123 + did, 124 + rkey, 125 + error: errorMsg, 126 + code: errorCode 127 + }); 128 + } 129 + 104 130 return null; 105 131 } 106 132 } ··· 149 175 /** 150 176 * Extract all subfs URIs from a directory tree with their mount paths 151 177 */ 152 - function extractSubfsUris(directory: Directory, currentPath: string = ''): Array<{ uri: string; path: string }> { 178 + export function extractSubfsUris(directory: Directory, currentPath: string = ''): Array<{ uri: string; path: string }> { 153 179 const uris: Array<{ uri: string; path: string }> = []; 154 180 155 181 for (const entry of directory.entries) { ··· 209 235 * Replace subfs nodes in a directory tree with their actual content 210 236 * Subfs entries are "merged" - their root entries are hoisted into the parent directory 211 237 * This function is recursive - it will keep expanding until no subfs nodes remain 238 + * Uses a cache to avoid re-fetching the same subfs records across recursion depths 212 239 */ 213 - async function expandSubfsNodes(directory: Directory, pdsEndpoint: string, depth: number = 0): Promise<Directory> { 240 + export async function expandSubfsNodes( 241 + directory: Directory, 242 + pdsEndpoint: string, 243 + depth: number = 0, 244 + subfsCache: Map<string, SubfsRecord | null> = new Map() 245 + ): Promise<Directory> { 214 246 const MAX_DEPTH = 10; // Prevent infinite loops 215 247 216 248 if (depth >= MAX_DEPTH) { ··· 226 258 return directory; 227 259 } 228 260 229 - console.log(`[Depth ${depth}] Found ${subfsUris.length} subfs records, fetching...`); 261 + // Filter to only URIs we haven't fetched yet 262 + const uncachedUris = subfsUris.filter(({ uri }) => !subfsCache.has(uri)); 230 263 231 - // Fetch all subfs records in parallel 232 - const subfsRecords = await Promise.all( 233 - subfsUris.map(async ({ uri, path }) => { 234 - const record = await fetchSubfsRecord(uri, pdsEndpoint); 235 - return { record, path }; 236 - }) 237 - ); 264 + if (uncachedUris.length > 0) { 265 + console.log(`[Depth ${depth}] Found ${subfsUris.length} subfs references, fetching ${uncachedUris.length} new records (${subfsUris.length - uncachedUris.length} cached)...`); 238 266 239 - // Build a map of path -> root entries to merge 267 + // Fetch only uncached subfs records in parallel 268 + const fetchedRecords = await Promise.all( 269 + uncachedUris.map(async ({ uri }) => { 270 + const record = await fetchSubfsRecord(uri, pdsEndpoint); 271 + return { uri, record }; 272 + }) 273 + ); 274 + 275 + // Add fetched records to cache 276 + for (const { uri, record } of fetchedRecords) { 277 + subfsCache.set(uri, record); 278 + } 279 + } else { 280 + console.log(`[Depth ${depth}] Found ${subfsUris.length} subfs references, all cached`); 281 + } 282 + 283 + // Build a map of path -> root entries to merge using the cache 240 284 // Note: SubFS entries are compatible with FS entries at runtime 241 285 const subfsMap = new Map<string, Entry[]>(); 242 - for (const { record, path } of subfsRecords) { 286 + for (const { uri, path } of subfsUris) { 287 + const record = subfsCache.get(uri); 243 288 if (record && record.root && record.root.entries) { 244 289 subfsMap.set(path, record.root.entries as unknown as Entry[]); 245 290 } ··· 307 352 }; 308 353 309 354 // Recursively expand any remaining subfs nodes (e.g., nested subfs inside parent subfs) 310 - return expandSubfsNodes(partiallyExpanded, pdsEndpoint, depth + 1); 355 + // Pass the cache to avoid re-fetching records 356 + return expandSubfsNodes(partiallyExpanded, pdsEndpoint, depth + 1, subfsCache); 311 357 } 312 358 313 359 ··· 351 397 const existingMetadata = await getCacheMetadata(did, rkey); 352 398 const existingFileCids = existingMetadata?.fileCids || {}; 353 399 354 - // Use a temporary directory with timestamp to avoid collisions 355 - const tempSuffix = `.tmp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; 356 - const tempDir = `${CACHE_DIR}/${did}/${rkey}${tempSuffix}`; 357 - const finalDir = `${CACHE_DIR}/${did}/${rkey}`; 358 - 359 - try { 360 - // Collect file CIDs from the new record (using expanded root) 361 - const newFileCids: Record<string, string> = {}; 362 - collectFileCidsFromEntries(expandedRoot.entries, '', newFileCids); 400 + // Collect file CIDs from the new record (using expanded root) 401 + const newFileCids: Record<string, string> = {}; 402 + collectFileCidsFromEntries(expandedRoot.entries, '', newFileCids); 363 403 364 - // Fetch site settings (optional) 365 - const settings = await fetchSiteSettings(did, rkey); 404 + // Fetch site settings (optional) 405 + const settings = await fetchSiteSettings(did, rkey); 366 406 367 - // Download/copy files to temporary directory (with incremental logic, using expanded root) 368 - await cacheFiles(did, rkey, expandedRoot.entries, pdsEndpoint, '', tempSuffix, existingFileCids, finalDir); 369 - await saveCacheMetadata(did, rkey, recordCid, tempSuffix, newFileCids, settings); 370 - 371 - // Atomically replace old cache with new cache 372 - // On POSIX systems (Linux/macOS), rename is atomic 373 - if (existsSync(finalDir)) { 374 - // Rename old directory to backup 375 - const backupDir = `${finalDir}.old-${Date.now()}`; 376 - await rename(finalDir, backupDir); 377 - 378 - try { 379 - // Rename new directory to final location 380 - await rename(tempDir, finalDir); 381 - 382 - // Clean up old backup 383 - rmSync(backupDir, { recursive: true, force: true }); 384 - } catch (err) { 385 - // If rename failed, restore backup 386 - if (existsSync(backupDir) && !existsSync(finalDir)) { 387 - await rename(backupDir, finalDir); 388 - } 389 - throw err; 390 - } 391 - } else { 392 - // No existing cache, just rename temp to final 393 - await rename(tempDir, finalDir); 394 - } 407 + // Download files directly to tiered storage (with incremental logic) 408 + await cacheFiles(did, rkey, expandedRoot.entries, pdsEndpoint, '', existingFileCids); 409 + await saveCacheMetadata(did, rkey, recordCid, newFileCids, settings); 395 410 396 - console.log('Successfully cached site atomically', did, rkey); 397 - } catch (err) { 398 - // Clean up temp directory on failure 399 - if (existsSync(tempDir)) { 400 - rmSync(tempDir, { recursive: true, force: true }); 401 - } 402 - throw err; 403 - } 411 + console.log('Successfully cached site', did, rkey); 404 412 } 405 413 406 414 ··· 410 418 entries: Entry[], 411 419 pdsEndpoint: string, 412 420 pathPrefix: string, 413 - dirSuffix: string = '', 414 - existingFileCids: Record<string, string> = {}, 415 - existingCacheDir?: string 421 + existingFileCids: Record<string, string> = {} 416 422 ): Promise<void> { 417 - // Collect file tasks, separating unchanged files from new/changed files 423 + // Collect file download tasks (skip unchanged files) 418 424 const downloadTasks: Array<() => Promise<void>> = []; 419 - const copyTasks: Array<() => Promise<void>> = []; 420 425 421 426 function collectFileTasks( 422 427 entries: Entry[], ··· 433 438 const cid = extractBlobCid(fileNode.blob); 434 439 435 440 // Check if file is unchanged (same CID as existing cache) 436 - if (cid && existingFileCids[currentPath] === cid && existingCacheDir) { 437 - // File unchanged - copy from existing cache instead of downloading 438 - copyTasks.push(() => copyExistingFile( 439 - did, 440 - site, 441 - currentPath, 442 - dirSuffix, 443 - existingCacheDir 444 - )); 441 + if (cid && existingFileCids[currentPath] === cid) { 442 + // File unchanged - skip download (already in tiered storage) 443 + console.log(`Skipping unchanged file: ${currentPath}`); 445 444 } else { 446 445 // File new or changed - download it 447 446 downloadTasks.push(() => cacheFileBlob( ··· 452 451 pdsEndpoint, 453 452 fileNode.encoding, 454 453 fileNode.mimeType, 455 - fileNode.base64, 456 - dirSuffix 454 + fileNode.base64 457 455 )); 458 456 } 459 457 } ··· 462 460 463 461 collectFileTasks(entries, pathPrefix); 464 462 465 - console.log(`[Incremental Update] Files to copy: ${copyTasks.length}, Files to download: ${downloadTasks.length}`); 463 + console.log(`[Incremental Update] Files to download: ${downloadTasks.length}`); 466 464 467 - // Copy unchanged files in parallel (fast local operations) - increased limit for better performance 468 - const copyLimit = 50; 469 - for (let i = 0; i < copyTasks.length; i += copyLimit) { 470 - const batch = copyTasks.slice(i, i + copyLimit); 471 - await Promise.all(batch.map(task => task())); 472 - if (copyTasks.length > copyLimit) { 473 - console.log(`[Cache Progress] Copied ${Math.min(i + copyLimit, copyTasks.length)}/${copyTasks.length} unchanged files`); 474 - } 475 - } 476 - 477 - // Download new/changed files concurrently - increased from 3 to 20 for much better performance 465 + // Download new/changed files concurrently 478 466 const downloadLimit = 20; 479 467 let successCount = 0; 480 468 let failureCount = 0; ··· 503 491 } 504 492 } 505 493 506 - /** 507 - * Copy an unchanged file from existing cache to new cache location 508 - */ 509 - async function copyExistingFile( 510 - did: string, 511 - site: string, 512 - filePath: string, 513 - dirSuffix: string, 514 - existingCacheDir: string 515 - ): Promise<void> { 516 - const { copyFile } = await import('fs/promises'); 517 - 518 - const sourceFile = `${existingCacheDir}/${filePath}`; 519 - const destFile = `${CACHE_DIR}/${did}/${site}${dirSuffix}/${filePath}`; 520 - const destDir = destFile.substring(0, destFile.lastIndexOf('/')); 521 - 522 - // Create destination directory if needed 523 - if (destDir && !existsSync(destDir)) { 524 - mkdirSync(destDir, { recursive: true }); 525 - } 526 - 527 - try { 528 - // Copy the file 529 - await copyFile(sourceFile, destFile); 530 - 531 - // Copy metadata file if it exists 532 - const sourceMetaFile = `${sourceFile}.meta`; 533 - const destMetaFile = `${destFile}.meta`; 534 - if (existsSync(sourceMetaFile)) { 535 - await copyFile(sourceMetaFile, destMetaFile); 536 - } 537 - } catch (err) { 538 - console.error(`Failed to copy cached file ${filePath}, will attempt download:`, err); 539 - throw err; 540 - } 541 - } 542 - 543 494 async function cacheFileBlob( 544 495 did: string, 545 496 site: string, ··· 548 499 pdsEndpoint: string, 549 500 encoding?: 'gzip', 550 501 mimeType?: string, 551 - base64?: boolean, 552 - dirSuffix: string = '' 502 + base64?: boolean 553 503 ): Promise<void> { 554 504 const cid = extractBlobCid(blobRef); 555 505 if (!cid) { ··· 572 522 content = Buffer.from(base64String, 'base64'); 573 523 } 574 524 575 - const cacheFile = `${CACHE_DIR}/${did}/${site}${dirSuffix}/${filePath}`; 576 - const fileDir = cacheFile.substring(0, cacheFile.lastIndexOf('/')); 577 - 578 - if (fileDir && !existsSync(fileDir)) { 579 - mkdirSync(fileDir, { recursive: true }); 580 - } 581 - 582 525 // Use the shared function to determine if this should remain compressed 583 526 const shouldStayCompressed = shouldCompressMimeType(mimeType); 584 527 ··· 596 539 } 597 540 } 598 541 599 - await writeFile(cacheFile, content); 542 + // Write to tiered storage with metadata 543 + const stream = Readable.from([content]); 544 + const key = `${did}/${site}/${filePath}`; 545 + 546 + // Build metadata object, only including defined values 547 + const customMetadata: Record<string, string> = {}; 548 + if (encoding) customMetadata.encoding = encoding; 549 + if (mimeType) customMetadata.mimeType = mimeType; 600 550 601 - // Store metadata only if file is still compressed 551 + await storage.setStream(key, stream, { 552 + size: content.length, 553 + skipTiers: ['hot'], // Don't put in memory on ingest, only on access 554 + metadata: customMetadata, 555 + }); 556 + 557 + // Log completion 602 558 if (encoding === 'gzip' && mimeType) { 603 - const metaFile = `${cacheFile}.meta`; 604 - await writeFile(metaFile, JSON.stringify({ encoding, mimeType })); 605 559 console.log('Cached file', filePath, content.length, 'bytes (gzipped,', mimeType + ')'); 606 560 } else { 607 561 console.log('Cached file', filePath, content.length, 'bytes'); ··· 614 568 return `${CACHE_DIR}/${did}/${site}/${sanitizedPath}`; 615 569 } 616 570 617 - export function isCached(did: string, site: string): boolean { 618 - return existsSync(`${CACHE_DIR}/${did}/${site}`); 571 + /** 572 + * Check if a site exists in any tier of the cache (without checking metadata) 573 + * This is a quick existence check - for actual retrieval, use storage.get() 574 + */ 575 + export async function isCached(did: string, site: string): Promise<boolean> { 576 + // Check if any file exists for this site by checking for the index.html 577 + // If index.html exists, the site is cached 578 + const indexKey = `${did}/${site}/index.html`; 579 + return await storage.exists(indexKey); 619 580 } 620 581 621 - async function saveCacheMetadata(did: string, rkey: string, recordCid: string, dirSuffix: string = '', fileCids?: Record<string, string>, settings?: WispSettings | null): Promise<void> { 582 + async function saveCacheMetadata(did: string, rkey: string, recordCid: string, fileCids?: Record<string, string>, settings?: WispSettings | null): Promise<void> { 622 583 const metadata: CacheMetadata = { 623 584 recordCid, 624 585 cachedAt: Date.now(), ··· 628 589 settings: settings || undefined 629 590 }; 630 591 631 - const metadataPath = `${CACHE_DIR}/${did}/${rkey}${dirSuffix}/.metadata.json`; 632 - const metadataDir = metadataPath.substring(0, metadataPath.lastIndexOf('/')); 633 - 634 - if (!existsSync(metadataDir)) { 635 - mkdirSync(metadataDir, { recursive: true }); 636 - } 637 - 638 - await writeFile(metadataPath, JSON.stringify(metadata, null, 2)); 592 + // Store through tiered storage for persistence to S3/cold tier 593 + const metadataKey = `${did}/${rkey}/.metadata.json`; 594 + const metadataBytes = new TextEncoder().encode(JSON.stringify(metadata, null, 2)); 595 + await storage.set(metadataKey, metadataBytes); 639 596 } 640 597 641 598 async function getCacheMetadata(did: string, rkey: string): Promise<CacheMetadata | null> { 642 599 try { 643 - const metadataPath = `${CACHE_DIR}/${did}/${rkey}/.metadata.json`; 644 - if (!existsSync(metadataPath)) return null; 600 + // Retrieve metadata from tiered storage 601 + const metadataKey = `${did}/${rkey}/.metadata.json`; 602 + const data = await storage.get(metadataKey); 645 603 646 - const content = await readFile(metadataPath, 'utf-8'); 647 - return JSON.parse(content) as CacheMetadata; 604 + if (!data) return null; 605 + 606 + // Deserialize from Uint8Array to JSON (storage uses identity serialization) 607 + const jsonString = new TextDecoder().decode(data as Uint8Array); 608 + return JSON.parse(jsonString) as CacheMetadata; 648 609 } catch (err) { 649 610 console.error('Failed to read cache metadata', err); 650 611 return null; ··· 678 639 } 679 640 680 641 export async function updateCacheMetadataSettings(did: string, rkey: string, settings: WispSettings | null): Promise<void> { 681 - const metadataPath = `${CACHE_DIR}/${did}/${rkey}/.metadata.json`; 682 - 683 - if (!existsSync(metadataPath)) { 684 - console.warn('Metadata file does not exist, cannot update settings', { did, rkey }); 685 - return; 686 - } 687 - 688 642 try { 689 - // Read existing metadata 690 - const content = await readFile(metadataPath, 'utf-8'); 691 - const metadata = JSON.parse(content) as CacheMetadata; 643 + // Read existing metadata from tiered storage 644 + const metadata = await getCacheMetadata(did, rkey); 645 + 646 + if (!metadata) { 647 + console.warn('Metadata does not exist, cannot update settings', { did, rkey }); 648 + return; 649 + } 692 650 693 651 // Update settings field 694 652 // Store null explicitly to cache "no settings" state and avoid repeated fetches 695 653 metadata.settings = settings ?? null; 696 654 697 - // Write back to disk 698 - await writeFile(metadataPath, JSON.stringify(metadata, null, 2), 'utf-8'); 655 + // Write back through tiered storage 656 + // Convert to Uint8Array since storage is typed for binary data 657 + const metadataKey = `${did}/${rkey}/.metadata.json`; 658 + const metadataBytes = new TextEncoder().encode(JSON.stringify(metadata, null, 2)); 659 + await storage.set(metadataKey, metadataBytes); 699 660 console.log('Updated metadata settings', { did, rkey, hasSettings: !!settings }); 700 661 } catch (err) { 701 662 console.error('Failed to update metadata settings', err);
+4 -1
apps/hosting-service/src/server.ts
··· 80 80 return c.text('Invalid identifier', 400); 81 81 } 82 82 83 + console.log(`[Server] sites.wisp.place request: identifier=${identifier}, site=${site}, filePath=${filePath}`); 84 + 83 85 // Check if site is currently being cached - return updating response early 84 86 if (isSiteBeingCached(did, site)) { 85 87 return siteUpdatingResponse(); ··· 93 95 94 96 // Serve with HTML path rewriting to handle absolute paths 95 97 const basePath = `/${identifier}/${site}/`; 98 + console.log(`[Server] Serving with basePath: ${basePath}`); 96 99 const headers = extractHeaders(c.req.raw.headers); 97 100 return serveFromCacheWithRewrite(did, site, filePath, basePath, c.req.url, headers); 98 101 } ··· 227 230 228 231 app.get('/__internal__/observability/cache', async (c) => { 229 232 const { getCacheStats } = await import('./lib/cache'); 230 - const stats = getCacheStats(); 233 + const stats = await getCacheStats(); 231 234 return c.json({ cache: stats }); 232 235 }); 233 236
+7 -6
apps/main-app/package.json
··· 11 11 "screenshot": "bun run scripts/screenshot-sites.ts" 12 12 }, 13 13 "dependencies": { 14 - "@atproto/api": "^0.17.3", 15 - "@atproto/common-web": "^0.4.5", 14 + "@atproto-labs/did-resolver": "^0.2.4", 15 + "@atproto/api": "^0.17.7", 16 + "@atproto/common-web": "^0.4.6", 16 17 "@atproto/jwk-jose": "^0.1.11", 17 - "@atproto/lex-cli": "^0.9.5", 18 - "@atproto/oauth-client-node": "^0.3.9", 19 - "@atproto/xrpc-server": "^0.9.5", 18 + "@atproto/lex-cli": "^0.9.7", 19 + "@atproto/oauth-client-node": "^0.3.12", 20 + "@atproto/xrpc-server": "^0.9.6", 20 21 "@elysiajs/cors": "^1.4.0", 21 22 "@elysiajs/eden": "^1.4.3", 22 23 "@elysiajs/openapi": "^1.4.11", ··· 40 41 "bun-plugin-tailwind": "^0.1.2", 41 42 "class-variance-authority": "^0.7.1", 42 43 "clsx": "^2.1.1", 43 - "elysia": "latest", 44 + "elysia": "^1.4.18", 44 45 "ignore": "^7.0.5", 45 46 "iron-session": "^8.0.4", 46 47 "lucide-react": "^0.546.0",
+4 -4
apps/main-app/public/acceptable-use/acceptable-use.tsx
··· 6 6 7 7 function AcceptableUsePage() { 8 8 return ( 9 - <div className="min-h-screen bg-background"> 9 + <div className="w-full min-h-screen bg-background flex flex-col"> 10 10 {/* Header */} 11 - <header className="border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 12 - <div className="container mx-auto px-4 py-4 flex items-center justify-between"> 11 + <header className="w-full border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 12 + <div className="max-w-6xl w-full mx-auto px-4 h-16 flex items-center justify-between"> 13 13 <div className="flex items-center gap-2"> 14 14 <img src="/transparent-full-size-ico.png" alt="wisp.place" className="w-8 h-8" /> 15 15 <span className="text-xl font-semibold text-foreground"> ··· 326 326 </div> 327 327 328 328 {/* Footer */} 329 - <footer className="border-t border-border/40 bg-muted/20 mt-12"> 329 + <footer className="border-t border-border/40 bg-muted/20 mt-auto"> 330 330 <div className="container mx-auto px-4 py-8"> 331 331 <div className="text-center text-sm text-muted-foreground"> 332 332 <p>
+1 -1
apps/main-app/public/components/ui/checkbox.tsx
··· 12 12 <CheckboxPrimitive.Root 13 13 data-slot="checkbox" 14 14 className={cn( 15 - "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", 15 + "peer border-border bg-background dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", 16 16 className 17 17 )} 18 18 {...props}
+6 -6
apps/main-app/public/editor/editor.tsx
··· 302 302 return ( 303 303 <div className="w-full min-h-screen bg-background"> 304 304 {/* Header Skeleton */} 305 - <header className="border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 306 - <div className="container mx-auto px-4 py-4 flex items-center justify-between"> 305 + <header className="w-full border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 306 + <div className="max-w-6xl w-full mx-auto px-4 h-16 flex items-center justify-between"> 307 307 <div className="flex items-center gap-2"> 308 308 <img src="/transparent-full-size-ico.png" alt="wisp.place" className="w-8 h-8" /> 309 309 <span className="text-xl font-semibold text-foreground"> ··· 366 366 } 367 367 368 368 return ( 369 - <div className="w-full min-h-screen bg-background"> 369 + <div className="w-full min-h-screen bg-background flex flex-col"> 370 370 {/* Header */} 371 - <header className="border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 372 - <div className="container mx-auto px-4 py-4 flex items-center justify-between"> 371 + <header className="w-full border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 372 + <div className="max-w-6xl w-full mx-auto px-4 h-16 flex items-center justify-between"> 373 373 <div className="flex items-center gap-2"> 374 374 <img src="/transparent-full-size-ico.png" alt="wisp.place" className="w-8 h-8" /> 375 375 <span className="text-xl font-semibold text-foreground"> ··· 454 454 </div> 455 455 456 456 {/* Footer */} 457 - <footer className="border-t border-border/40 bg-muted/20 mt-12"> 457 + <footer className="border-t border-border/40 bg-muted/20 mt-auto"> 458 458 <div className="container mx-auto px-4 py-8"> 459 459 <div className="text-center text-sm text-muted-foreground"> 460 460 <p>
+74 -66
apps/main-app/public/index.tsx
··· 88 88 89 89 const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { 90 90 const navigationKeys = ['ArrowDown', 'ArrowUp', 'PageDown', 'PageUp', 'Enter', 'Escape'] 91 - 91 + 92 92 // Mark that we should preserve the index for navigation keys 93 93 if (navigationKeys.includes(e.key)) { 94 94 preserveIndexRef.current = true ··· 142 142 setIndex(-1) 143 143 setIsOpen(false) 144 144 onSelect?.(handle) 145 - 145 + 146 146 // Auto-submit the form if enabled 147 147 if (autoSubmit && inputRef.current) { 148 148 const form = inputRef.current.closest('form') ··· 236 236 height: 'calc(1.5rem + 12px)', 237 237 borderRadius: '4px', 238 238 cursor: 'pointer', 239 - backgroundColor: i === index ? 'hsl(var(--accent) / 0.5)' : 'transparent', 239 + backgroundColor: i === index ? 'color-mix(in oklch, var(--accent) 50%, transparent)' : 'transparent', 240 240 transition: 'background-color 0.1s' 241 241 }} 242 242 onMouseEnter={() => setIndex(i)} ··· 246 246 width: '1.5rem', 247 247 height: '1.5rem', 248 248 borderRadius: '50%', 249 - backgroundColor: 'hsl(var(--muted))', 249 + backgroundColor: 'var(--muted)', 250 250 overflow: 'hidden', 251 251 flexShrink: 0 252 252 }} ··· 255 255 <img 256 256 src={actor.avatar} 257 257 alt="" 258 + loading="lazy" 258 259 style={{ 259 260 display: 'block', 260 261 width: '100%', ··· 359 360 360 361 return ( 361 362 <> 362 - <div className="min-h-screen"> 363 + <div className="w-full min-h-screen flex flex-col"> 363 364 {/* Header */} 364 - <header className="border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 365 - <div className="container mx-auto px-4 py-4 flex items-center justify-between"> 365 + <header className="w-full border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 366 + <div className="max-w-6xl w-full mx-auto px-4 h-16 flex items-center justify-between"> 366 367 <div className="flex items-center gap-2"> 367 368 <img src="/transparent-full-size-ico.png" alt="wisp.place" className="w-8 h-8" /> 368 - <span className="text-xl font-semibold text-foreground"> 369 + <span className="text-lg font-semibold text-foreground"> 369 370 wisp.place 370 371 </span> 371 372 </div> 372 - <div className="flex items-center gap-3"> 373 + <div className="flex items-center gap-4"> 374 + <a 375 + href="https://docs.wisp.place" 376 + target="_blank" 377 + rel="noopener noreferrer" 378 + className="text-sm text-muted-foreground hover:text-foreground transition-colors" 379 + > 380 + Read the Docs 381 + </a> 373 382 <Button 374 - variant="ghost" 383 + variant="outline" 375 384 size="sm" 385 + className="btn-hover-lift" 376 386 onClick={() => setShowForm(true)} 377 387 > 378 388 Sign In 379 389 </Button> 380 - <Button 381 - size="sm" 382 - className="bg-accent text-accent-foreground hover:bg-accent/90" 383 - asChild 384 - > 385 - <a href="https://docs.wisp.place" target="_blank" rel="noopener noreferrer"> 386 - Read the Docs 387 - </a> 388 - </Button> 389 390 </div> 390 391 </div> 391 392 </header> 392 393 393 394 {/* Hero Section */} 394 - <section className="container mx-auto px-4 py-20 md:py-32"> 395 + <section className="container mx-auto px-4 py-24 md:py-36"> 395 396 <div className="max-w-4xl mx-auto text-center"> 396 - <div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-accent/10 border border-accent/20 mb-8"> 397 - <span className="w-2 h-2 bg-accent rounded-full animate-pulse"></span> 398 - <span className="text-sm text-foreground"> 399 - Built on AT Protocol 400 - </span> 401 - </div> 402 - 403 - <h1 className="text-5xl md:text-7xl font-bold text-balance mb-6 leading-tight"> 404 - Your Website.Your Control. Lightning Fast. 397 + {/* Main Headline */} 398 + <h1 className="animate-fade-in-up animate-delay-100 text-5xl md:text-7xl font-bold mb-2 leading-tight tracking-tight"> 399 + Deploy Anywhere. 400 + </h1> 401 + <h1 className="animate-fade-in-up animate-delay-200 text-5xl md:text-7xl font-bold mb-8 leading-tight tracking-tight text-gradient-animate"> 402 + For Free. Forever. 405 403 </h1> 406 404 407 - <p className="text-xl md:text-2xl text-muted-foreground text-balance mb-10 leading-relaxed max-w-3xl mx-auto"> 408 - Host static sites in your AT Protocol account. You 409 - keep ownership and control. We just serve them fast 410 - through our CDN. 405 + {/* Subheadline */} 406 + <p className="animate-fade-in-up animate-delay-300 text-lg md:text-xl text-muted-foreground mb-12 leading-relaxed max-w-2xl mx-auto"> 407 + The easiest way to deploy and orchestrate static sites. 408 + Push updates instantly. Host on our infrastructure or yours. 409 + All powered by AT Protocol. 411 410 </p> 412 411 413 - <div className="max-w-md mx-auto relative"> 412 + {/* CTA Buttons */} 413 + <div className="animate-fade-in-up animate-delay-400 max-w-lg mx-auto relative"> 414 414 <div 415 - className={`transition-all duration-500 ease-in-out ${ 416 - showForm 417 - ? 'opacity-0 -translate-y-5 pointer-events-none' 418 - : 'opacity-100 translate-y-0' 419 - }`} 415 + className={`transition-all duration-500 ease-in-out ${showForm 416 + ? 'opacity-0 -translate-y-5 pointer-events-none absolute inset-0' 417 + : 'opacity-100 translate-y-0' 418 + }`} 420 419 > 421 - <Button 422 - size="lg" 423 - className="bg-primary text-primary-foreground hover:bg-primary/90 text-lg px-8 py-6 w-full" 424 - onClick={() => setShowForm(true)} 425 - > 426 - Log in with AT Proto 427 - <ArrowRight className="ml-2 w-5 h-5" /> 428 - </Button> 420 + <div className="flex flex-col sm:flex-row gap-3 justify-center"> 421 + <Button 422 + size="lg" 423 + className="bg-foreground text-background hover:bg-foreground/90 text-base px-6 py-5 btn-hover-lift" 424 + onClick={() => setShowForm(true)} 425 + > 426 + <span className="mr-2 font-bold">@</span> 427 + Deploy with AT 428 + </Button> 429 + <Button 430 + variant="outline" 431 + size="lg" 432 + className="text-base px-6 py-5 btn-hover-lift" 433 + asChild 434 + > 435 + <a href="https://docs.wisp.place/cli/" target="_blank" rel="noopener noreferrer"> 436 + <span className="font-mono mr-2 text-muted-foreground">&gt;_</span> 437 + Install wisp-cli 438 + </a> 439 + </Button> 440 + </div> 429 441 </div> 430 442 431 443 <div 432 - className={`transition-all duration-500 ease-in-out absolute inset-0 ${ 433 - showForm 434 - ? 'opacity-100 translate-y-0' 435 - : 'opacity-0 translate-y-5 pointer-events-none' 436 - }`} 444 + className={`transition-all duration-500 ease-in-out ${showForm 445 + ? 'opacity-100 translate-y-0' 446 + : 'opacity-0 translate-y-5 pointer-events-none absolute inset-0' 447 + }`} 437 448 > 438 449 <form 439 450 onSubmit={async (e) => { ··· 494 505 </ActorTypeahead> 495 506 <button 496 507 type="submit" 497 - className="w-full bg-accent hover:bg-accent/90 text-accent-foreground font-semibold py-4 px-6 text-lg rounded-lg inline-flex items-center justify-center transition-colors" 508 + className="w-full bg-foreground text-background hover:bg-foreground/90 font-semibold py-4 px-6 text-lg rounded-lg inline-flex items-center justify-center transition-colors btn-hover-lift" 498 509 > 499 510 Continue 500 511 <ArrowRight className="ml-2 w-5 h-5" /> ··· 518 529 </div> 519 530 <div> 520 531 <h3 className="text-xl font-semibold mb-2"> 521 - Upload your static site 532 + Drop in your files 522 533 </h3> 523 534 <p className="text-muted-foreground"> 524 - Your HTML, CSS, and JavaScript files are 525 - stored in your AT Protocol account as 526 - gzipped blobs and a manifest record. 535 + Upload your site through our dashboard or push with the CLI. 536 + Everything gets stored directly in your AT Protocol account. 527 537 </p> 528 538 </div> 529 539 </div> ··· 533 543 </div> 534 544 <div> 535 545 <h3 className="text-xl font-semibold mb-2"> 536 - We serve it globally 546 + We handle the rest 537 547 </h3> 538 548 <p className="text-muted-foreground"> 539 - Wisp.place reads your site from your 540 - account and delivers it through our CDN 541 - for fast loading anywhere. 549 + Your site goes live instantly on our global CDN. 550 + Custom domains, HTTPS, cachingโ€”all automatic. 542 551 </p> 543 552 </div> 544 553 </div> ··· 548 557 </div> 549 558 <div> 550 559 <h3 className="text-xl font-semibold mb-2"> 551 - You stay in control 560 + Push updates instantly 552 561 </h3> 553 562 <p className="text-muted-foreground"> 554 - Update or remove your site anytime 555 - through your AT Protocol account. No 556 - lock-in, no middleman ownership. 563 + Ship changes in seconds. Update through the dashboard, 564 + run wisp-cli deploy, or wire up your CI/CD pipeline. 557 565 </p> 558 566 </div> 559 567 </div> ··· 686 694 </section> 687 695 688 696 {/* Footer */} 689 - <footer className="border-t border-border/40 bg-muted/20"> 697 + <footer className="border-t border-border/40 bg-muted/20 mt-auto"> 690 698 <div className="container mx-auto px-4 py-8"> 691 699 <div className="text-center text-sm text-muted-foreground"> 692 700 <p>
+16 -19
apps/main-app/public/onboarding/onboarding.tsx
··· 161 161 return ( 162 162 <div className="w-full min-h-screen bg-background"> 163 163 {/* Header */} 164 - <header className="border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 165 - <div className="container mx-auto px-4 py-4 flex items-center justify-between"> 164 + <header className="w-full border-b border-border/40 bg-background/80 backdrop-blur-sm sticky top-0 z-50"> 165 + <div className="max-w-6xl w-full mx-auto px-4 h-16 flex items-center justify-between"> 166 166 <div className="flex items-center gap-2"> 167 167 <div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center"> 168 168 <Globe className="w-5 h-5 text-primary-foreground" /> ··· 179 179 <div className="mb-8"> 180 180 <div className="flex items-center justify-center gap-2 mb-4"> 181 181 <div 182 - className={`w-8 h-8 rounded-full flex items-center justify-center ${ 183 - step === 'domain' 184 - ? 'bg-primary text-primary-foreground' 185 - : 'bg-green-500 text-white' 186 - }`} 182 + className={`w-8 h-8 rounded-full flex items-center justify-center ${step === 'domain' 183 + ? 'bg-primary text-primary-foreground' 184 + : 'bg-green-500 text-white' 185 + }`} 187 186 > 188 187 {step === 'domain' ? ( 189 188 '1' ··· 193 192 </div> 194 193 <div className="w-16 h-0.5 bg-border"></div> 195 194 <div 196 - className={`w-8 h-8 rounded-full flex items-center justify-center ${ 197 - step === 'upload' 198 - ? 'bg-primary text-primary-foreground' 199 - : step === 'domain' 200 - ? 'bg-muted text-muted-foreground' 201 - : 'bg-green-500 text-white' 202 - }`} 195 + className={`w-8 h-8 rounded-full flex items-center justify-center ${step === 'upload' 196 + ? 'bg-primary text-primary-foreground' 197 + : step === 'domain' 198 + ? 'bg-muted text-muted-foreground' 199 + : 'bg-green-500 text-white' 200 + }`} 203 201 > 204 202 {step === 'complete' ? ( 205 203 <CheckCircle2 className="w-5 h-5" /> ··· 258 256 {!isCheckingAvailability && 259 257 isAvailable !== null && ( 260 258 <div 261 - className={`absolute right-3 top-1/2 -translate-y-1/2 ${ 262 - isAvailable 263 - ? 'text-green-500' 264 - : 'text-red-500' 265 - }`} 259 + className={`absolute right-3 top-1/2 -translate-y-1/2 ${isAvailable 260 + ? 'text-green-500' 261 + : 'text-red-500' 262 + }`} 266 263 > 267 264 {isAvailable ? 'โœ“' : 'โœ—'} 268 265 </div>
+212 -39
apps/main-app/public/styles/global.css
··· 6 6 :root { 7 7 color-scheme: light; 8 8 9 - /* Warm beige background inspired by Sunset design #E9DDD8 */ 10 - --background: oklch(0.90 0.012 35); 11 - /* Very dark brown text for strong contrast #2A2420 */ 12 - --foreground: oklch(0.18 0.01 30); 9 + /* Warm beige background inspired by Sunset design */ 10 + --background: oklch(0.92 0.012 35); 11 + /* Very dark brown text for strong contrast */ 12 + --foreground: oklch(0.15 0.015 30); 13 13 14 - /* Slightly lighter card background */ 15 - --card: oklch(0.93 0.01 35); 16 - --card-foreground: oklch(0.18 0.01 30); 14 + /* Slightly lighter card background for elevation */ 15 + --card: oklch(0.95 0.008 35); 16 + --card-foreground: oklch(0.15 0.015 30); 17 17 18 - --popover: oklch(0.93 0.01 35); 19 - --popover-foreground: oklch(0.18 0.01 30); 18 + --popover: oklch(0.96 0.006 35); 19 + --popover-foreground: oklch(0.15 0.015 30); 20 20 21 - /* Dark brown primary inspired by #645343 */ 22 - --primary: oklch(0.35 0.02 35); 23 - --primary-foreground: oklch(0.95 0.01 35); 21 + /* Dark brown primary - darker for better contrast */ 22 + --primary: oklch(0.30 0.025 35); 23 + --primary-foreground: oklch(0.96 0.008 35); 24 24 25 - /* Bright pink accent for links #FFAAD2 */ 26 - --accent: oklch(0.78 0.15 345); 27 - --accent-foreground: oklch(0.18 0.01 30); 25 + /* Deeper pink accent for better visibility */ 26 + --accent: oklch(0.65 0.18 345); 27 + --accent-foreground: oklch(0.15 0.015 30); 28 28 29 - /* Medium taupe secondary inspired by #867D76 */ 30 - --secondary: oklch(0.52 0.015 30); 31 - --secondary-foreground: oklch(0.95 0.01 35); 29 + /* Darker taupe secondary for better contrast */ 30 + --secondary: oklch(0.85 0.012 30); 31 + --secondary-foreground: oklch(0.25 0.02 30); 32 32 33 - /* Light warm muted background */ 33 + /* Muted areas with better distinction */ 34 34 --muted: oklch(0.88 0.01 35); 35 - --muted-foreground: oklch(0.42 0.015 30); 35 + --muted-foreground: oklch(0.35 0.02 30); 36 36 37 - --border: oklch(0.75 0.015 30); 38 - --input: oklch(0.92 0.01 35); 39 - --ring: oklch(0.72 0.08 15); 37 + /* Significantly darker border for visibility */ 38 + --border: oklch(0.65 0.02 30); 39 + /* Input backgrounds lighter than cards */ 40 + --input: oklch(0.97 0.005 35); 41 + --ring: oklch(0.55 0.12 345); 40 42 41 - --destructive: oklch(0.577 0.245 27.325); 42 - --destructive-foreground: oklch(0.985 0 0); 43 + --destructive: oklch(0.50 0.20 25); 44 + --destructive-foreground: oklch(0.98 0 0); 43 45 44 - --chart-1: oklch(0.78 0.15 345); 46 + --chart-1: oklch(0.65 0.18 345); 45 47 --chart-2: oklch(0.32 0.04 285); 46 - --chart-3: oklch(0.56 0.08 220); 47 - --chart-4: oklch(0.85 0.02 130); 48 - --chart-5: oklch(0.93 0.03 85); 48 + --chart-3: oklch(0.50 0.10 220); 49 + --chart-4: oklch(0.70 0.08 130); 50 + --chart-5: oklch(0.75 0.06 85); 49 51 50 52 --radius: 0.75rem; 51 - --sidebar: oklch(0.985 0 0); 52 - --sidebar-foreground: oklch(0.145 0 0); 53 - --sidebar-primary: oklch(0.205 0 0); 54 - --sidebar-primary-foreground: oklch(0.985 0 0); 55 - --sidebar-accent: oklch(0.97 0 0); 56 - --sidebar-accent-foreground: oklch(0.205 0 0); 57 - --sidebar-border: oklch(0.922 0 0); 58 - --sidebar-ring: oklch(0.708 0 0); 53 + --sidebar: oklch(0.94 0.008 35); 54 + --sidebar-foreground: oklch(0.15 0.015 30); 55 + --sidebar-primary: oklch(0.30 0.025 35); 56 + --sidebar-primary-foreground: oklch(0.96 0.008 35); 57 + --sidebar-accent: oklch(0.90 0.01 35); 58 + --sidebar-accent-foreground: oklch(0.20 0.02 30); 59 + --sidebar-border: oklch(0.65 0.02 30); 60 + --sidebar-ring: oklch(0.55 0.12 345); 59 61 } 60 62 61 63 .dark { ··· 160 162 * { 161 163 @apply border-border outline-ring/50; 162 164 } 165 + 166 + html { 167 + scrollbar-gutter: stable; 168 + } 169 + 163 170 body { 164 171 @apply bg-background text-foreground; 165 172 } 166 173 } 167 174 168 175 @keyframes arrow-bounce { 169 - 0%, 100% { 176 + 177 + 0%, 178 + 100% { 170 179 transform: translateX(0); 171 180 } 181 + 172 182 50% { 173 183 transform: translateX(4px); 174 184 } ··· 189 199 border-radius: 0.5rem; 190 200 padding: 1rem; 191 201 overflow-x: auto; 192 - border: 1px solid hsl(var(--border)); 202 + border: 1px solid var(--border); 193 203 } 194 204 195 205 .shiki-wrapper pre { 196 206 margin: 0 !important; 197 207 padding: 0 !important; 198 208 } 209 + 210 + /* ========== Landing Page Animations ========== */ 211 + 212 + /* Animated gradient for headline text */ 213 + @keyframes gradient-shift { 214 + 215 + 0%, 216 + 100% { 217 + background-position: 0% 50%; 218 + } 219 + 220 + 50% { 221 + background-position: 100% 50%; 222 + } 223 + } 224 + 225 + .text-gradient-animate { 226 + background: linear-gradient(90deg, 227 + oklch(0.55 0.22 350), 228 + oklch(0.60 0.24 10), 229 + oklch(0.55 0.22 350)); 230 + background-size: 200% auto; 231 + -webkit-background-clip: text; 232 + background-clip: text; 233 + -webkit-text-fill-color: transparent; 234 + animation: gradient-shift 4s ease-in-out infinite; 235 + } 236 + 237 + .dark .text-gradient-animate { 238 + background: linear-gradient(90deg, 239 + oklch(0.75 0.12 295), 240 + oklch(0.85 0.10 5), 241 + oklch(0.75 0.12 295)); 242 + background-size: 200% auto; 243 + -webkit-background-clip: text; 244 + background-clip: text; 245 + -webkit-text-fill-color: transparent; 246 + } 247 + 248 + /* Floating/breathing animation for hero elements */ 249 + @keyframes float { 250 + 251 + 0%, 252 + 100% { 253 + transform: translateY(0); 254 + } 255 + 256 + 50% { 257 + transform: translateY(-8px); 258 + } 259 + } 260 + 261 + .animate-float { 262 + animation: float 3s ease-in-out infinite; 263 + } 264 + 265 + .animate-float-delayed { 266 + animation: float 3s ease-in-out infinite; 267 + animation-delay: 0.5s; 268 + } 269 + 270 + /* Staggered fade-in animation */ 271 + @keyframes fade-in-up { 272 + from { 273 + opacity: 0; 274 + transform: translateY(20px); 275 + } 276 + 277 + to { 278 + opacity: 1; 279 + transform: translateY(0); 280 + } 281 + } 282 + 283 + .animate-fade-in-up { 284 + animation: fade-in-up 0.6s ease-out forwards; 285 + opacity: 0; 286 + } 287 + 288 + .animate-delay-100 { 289 + animation-delay: 0.1s; 290 + } 291 + 292 + .animate-delay-200 { 293 + animation-delay: 0.2s; 294 + } 295 + 296 + .animate-delay-300 { 297 + animation-delay: 0.3s; 298 + } 299 + 300 + .animate-delay-400 { 301 + animation-delay: 0.4s; 302 + } 303 + 304 + .animate-delay-500 { 305 + animation-delay: 0.5s; 306 + } 307 + 308 + .animate-delay-600 { 309 + animation-delay: 0.6s; 310 + } 311 + 312 + /* Terminal cursor blink */ 313 + @keyframes cursor-blink { 314 + 315 + 0%, 316 + 50% { 317 + opacity: 1; 318 + } 319 + 320 + 51%, 321 + 100% { 322 + opacity: 0; 323 + } 324 + } 325 + 326 + .animate-cursor-blink { 327 + animation: cursor-blink 1s step-end infinite; 328 + } 329 + 330 + /* Button hover scale effect */ 331 + .btn-hover-lift { 332 + transition: all 0.2s ease-out; 333 + } 334 + 335 + .btn-hover-lift:hover { 336 + transform: translateY(-2px); 337 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 338 + } 339 + 340 + .btn-hover-lift:active { 341 + transform: translateY(0); 342 + } 343 + 344 + /* Subtle pulse for feature dots */ 345 + @keyframes dot-pulse { 346 + 347 + 0%, 348 + 100% { 349 + transform: scale(1); 350 + opacity: 1; 351 + } 352 + 353 + 50% { 354 + transform: scale(1.2); 355 + opacity: 0.8; 356 + } 357 + } 358 + 359 + .animate-dot-pulse { 360 + animation: dot-pulse 2s ease-in-out infinite; 361 + } 362 + 363 + .animate-dot-pulse-delayed-1 { 364 + animation: dot-pulse 2s ease-in-out infinite; 365 + animation-delay: 0.3s; 366 + } 367 + 368 + .animate-dot-pulse-delayed-2 { 369 + animation: dot-pulse 2s ease-in-out infinite; 370 + animation-delay: 0.6s; 371 + }
+30 -4
apps/main-app/src/index.ts
··· 12 12 cleanupExpiredSessions, 13 13 rotateKeysIfNeeded 14 14 } from './lib/oauth-client' 15 - import { getCookieSecret } from './lib/db' 15 + import { getCookieSecret, closeDatabase } from './lib/db' 16 16 import { authRoutes } from './routes/auth' 17 17 import { wispRoutes } from './routes/wisp' 18 18 import { domainRoutes } from './routes/domain' ··· 20 20 import { siteRoutes } from './routes/site' 21 21 import { csrfProtection } from './lib/csrf' 22 22 import { DNSVerificationWorker } from './lib/dns-verification-worker' 23 - import { createLogger, logCollector } from '@wisp/observability' 23 + import { createLogger, logCollector, initializeGrafanaExporters } from '@wisp/observability' 24 24 import { observabilityMiddleware } from '@wisp/observability/middleware/elysia' 25 25 import { promptAdminSetup } from './lib/admin-auth' 26 26 import { adminRoutes } from './routes/admin' 27 + 28 + // Initialize Grafana exporters if configured 29 + initializeGrafanaExporters({ 30 + serviceName: 'main-app', 31 + serviceVersion: '1.0.50' 32 + }) 27 33 28 34 const logger = createLogger('main-app') 29 35 ··· 55 61 setInterval(runMaintenance, 60 * 60 * 1000) 56 62 57 63 // Start DNS verification worker (runs every 10 minutes) 64 + // Can be disabled via DISABLE_DNS_WORKER=true environment variable 58 65 const dnsVerifier = new DNSVerificationWorker( 59 66 10 * 60 * 1000, // 10 minutes 60 67 (msg, data) => { ··· 62 69 } 63 70 ) 64 71 65 - dnsVerifier.start() 66 - logger.info('DNS Verifier Started - checking custom domains every 10 minutes') 72 + if (Bun.env.DISABLE_DNS_WORKER !== 'true') { 73 + dnsVerifier.start() 74 + logger.info('DNS Verifier Started - checking custom domains every 10 minutes') 75 + } else { 76 + logger.info('DNS Verifier disabled via DISABLE_DNS_WORKER environment variable') 77 + } 67 78 68 79 export const app = new Elysia({ 69 80 serve: { ··· 194 205 console.log( 195 206 `๐ŸฆŠ Elysia is running at ${app.server?.hostname}:${app.server?.port}` 196 207 ) 208 + 209 + // Graceful shutdown 210 + process.on('SIGINT', async () => { 211 + console.log('\n๐Ÿ›‘ Shutting down...') 212 + dnsVerifier.stop() 213 + await closeDatabase() 214 + process.exit(0) 215 + }) 216 + 217 + process.on('SIGTERM', async () => { 218 + console.log('\n๐Ÿ›‘ Shutting down...') 219 + dnsVerifier.stop() 220 + await closeDatabase() 221 + process.exit(0) 222 + })
+13
apps/main-app/src/lib/db.ts
··· 526 526 console.log('[CookieSecret] Generated new cookie signing secret'); 527 527 return secret; 528 528 }; 529 + 530 + /** 531 + * Close database connection 532 + * Call this during graceful shutdown 533 + */ 534 + export const closeDatabase = async (): Promise<void> => { 535 + try { 536 + await db.end(); 537 + console.log('[DB] Database connection closed'); 538 + } catch (err) { 539 + console.error('[DB] Error closing database connection:', err); 540 + } 541 + };
+5 -4
apps/main-app/src/lib/oauth-client.ts
··· 4 4 import { logger } from "./logger"; 5 5 import { SlingshotHandleResolver } from "./slingshot-handle-resolver"; 6 6 7 + // OAuth scope for all client types 8 + const OAUTH_SCOPE = 'atproto repo:place.wisp.fs repo:place.wisp.domain repo:place.wisp.subfs repo:place.wisp.settings blob:*/*'; 7 9 // Session timeout configuration (30 days in seconds) 8 10 const SESSION_TIMEOUT = 30 * 24 * 60 * 60; // 2592000 seconds 9 11 // OAuth state timeout (1 hour in seconds) ··· 110 112 // Loopback client for local development 111 113 // For loopback, scopes and redirect_uri must be in client_id query string 112 114 const redirectUri = 'http://127.0.0.1:8000/api/auth/callback'; 113 - const scope = 'atproto repo:place.wisp.fs repo:place.wisp.domain repo:place.wisp.subfs repo:place.wisp.settings blob:*/* rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app#bsky_appview'; 114 115 const params = new URLSearchParams(); 115 116 params.append('redirect_uri', redirectUri); 116 - params.append('scope', scope); 117 + params.append('scope', OAUTH_SCOPE); 117 118 118 119 return { 119 120 client_id: `http://localhost?${params.toString()}`, ··· 124 125 response_types: ['code'], 125 126 application_type: 'web', 126 127 token_endpoint_auth_method: 'none', 127 - scope: scope, 128 + scope: OAUTH_SCOPE, 128 129 dpop_bound_access_tokens: false, 129 130 subject_type: 'public', 130 131 authorization_signed_response_alg: 'ES256' ··· 145 146 application_type: 'web', 146 147 token_endpoint_auth_method: 'private_key_jwt', 147 148 token_endpoint_auth_signing_alg: "ES256", 148 - scope: "atproto repo:place.wisp.fs repo:place.wisp.domain repo:place.wisp.subfs repo:place.wisp.settings blob:*/* rpc:app.bsky.actor.getProfile?aud=did:web:api.bsky.app#bsky_appview", 149 + scope: OAUTH_SCOPE, 149 150 dpop_bound_access_tokens: true, 150 151 jwks_uri: `${config.domain}/jwks.json`, 151 152 subject_type: 'public',
+10 -12
apps/main-app/src/routes/user.ts
··· 1 1 import { Elysia, t } from 'elysia' 2 2 import { requireAuth } from '../lib/wisp-auth' 3 3 import { NodeOAuthClient } from '@atproto/oauth-client-node' 4 - import { Agent } from '@atproto/api' 5 4 import { getSitesByDid, getDomainByDid, getCustomDomainsByDid, getWispDomainInfo, getDomainsBySite, getAllWispDomains } from '../lib/db' 6 5 import { syncSitesFromPDS } from '../lib/sync-sites' 7 6 import { createLogger } from '@wisp/observability' 7 + import { createDidResolver, extractAtprotoData } from '@atproto-labs/did-resolver' 8 8 9 9 const logger = createLogger('main-app') 10 + const didResolver = createDidResolver({}) 10 11 11 12 export const userRoutes = (client: NodeOAuthClient, cookieSecret: string) => 12 13 new Elysia({ ··· 42 43 }) 43 44 .get('/info', async ({ auth }) => { 44 45 try { 45 - // Get user's handle from AT Protocol 46 - const agent = new Agent(auth.session) 47 - 48 46 let handle = 'unknown' 49 47 try { 50 - console.log('[User] Attempting to fetch profile for DID:', auth.did) 51 - const profile = await agent.getProfile({ actor: auth.did }) 52 - console.log('[User] Profile fetched successfully:', profile.data.handle) 53 - handle = profile.data.handle 48 + const didDoc = await didResolver.resolve(auth.did) 49 + const atprotoData = extractAtprotoData(didDoc) 50 + 51 + if (atprotoData.aka) { 52 + handle = atprotoData.aka 53 + } 54 54 } catch (err) { 55 - console.error('[User] Failed to fetch profile - Full error:', err) 56 - console.error('[User] Error message:', err instanceof Error ? err.message : String(err)) 57 - console.error('[User] Error stack:', err instanceof Error ? err.stack : 'No stack') 58 - logger.error('[User] Failed to fetch profile', err) 55 + 56 + logger.error('[User] Failed to resolve DID', err) 59 57 } 60 58 61 59 return {
+10 -16
apps/main-app/src/routes/wisp.ts
··· 39 39 40 40 const logger = createLogger('main-app') 41 41 42 - function isValidSiteName(siteName: string): boolean { 42 + export function isValidSiteName(siteName: string): boolean { 43 43 if (!siteName || typeof siteName !== 'string') return false; 44 44 45 45 // Length check (AT Protocol rkey limit) ··· 183 183 continue; 184 184 } 185 185 186 - console.log(`Processing file ${i + 1}/${fileArray.length}:`, file.name, file.size, 'bytes'); 186 + // Use webkitRelativePath when available (directory uploads), fallback to name for regular file uploads 187 + const webkitPath = 'webkitRelativePath' in file ? String(file.webkitRelativePath) : ''; 188 + const filePath = webkitPath || file.name; 189 + 187 190 updateJobProgress(jobId, { 188 191 filesProcessed: i + 1, 189 - currentFile: file.name 192 + currentFile: filePath 190 193 }); 191 194 192 195 // Skip files that match ignore patterns 193 - const normalizedPath = file.name.replace(/^[^\/]*\//, ''); 196 + const normalizedPath = filePath.replace(/^[^\/]*\//, ''); 194 197 195 198 if (shouldIgnore(ignoreMatcher, normalizedPath)) { 196 - console.log(`Skipping ignored file: ${file.name}`); 197 199 skippedFiles.push({ 198 - name: file.name, 200 + name: filePath, 199 201 reason: 'matched ignore pattern' 200 202 }); 201 203 continue; ··· 205 207 const maxSize = MAX_FILE_SIZE; 206 208 if (file.size > maxSize) { 207 209 skippedFiles.push({ 208 - name: file.name, 210 + name: filePath, 209 211 reason: `file too large (${(file.size / 1024 / 1024).toFixed(2)}MB, max 100MB)` 210 212 }); 211 213 continue; ··· 238 240 // Text files: compress AND base64 encode 239 241 finalContent = Buffer.from(compressedContent.toString('base64'), 'binary'); 240 242 base64Encoded = true; 241 - const compressionRatio = (compressedContent.length / originalContent.length * 100).toFixed(1); 242 - console.log(`Compressing+base64 ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%), base64: ${finalContent.length} bytes`); 243 - logger.info(`Compressing+base64 ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%), base64: ${finalContent.length} bytes`); 244 243 } else { 245 244 // Audio files: just compress, no base64 246 245 finalContent = compressedContent; 247 - const compressionRatio = (compressedContent.length / originalContent.length * 100).toFixed(1); 248 - console.log(`Compressing ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%)`); 249 - logger.info(`Compressing ${file.name}: ${originalContent.length} -> ${compressedContent.length} bytes (${compressionRatio}%)`); 250 246 } 251 247 } else { 252 248 // Binary files: upload directly 253 249 finalContent = originalContent; 254 - console.log(`Uploading ${file.name} directly: ${originalContent.length} bytes (no compression)`); 255 - logger.info(`Uploading ${file.name} directly: ${originalContent.length} bytes (binary)`); 256 250 } 257 251 258 252 uploadedFiles.push({ 259 - name: file.name, 253 + name: filePath, 260 254 content: finalContent, 261 255 mimeType: originalMimeType, 262 256 size: finalContent.length,
+59
backup.nix
··· 1 + { 2 + inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 3 + inputs.nci.url = "github:90-008/nix-cargo-integration"; 4 + inputs.nci.inputs.nixpkgs.follows = "nixpkgs"; 5 + inputs.parts.url = "github:hercules-ci/flake-parts"; 6 + inputs.parts.inputs.nixpkgs-lib.follows = "nixpkgs"; 7 + inputs.fenix = { 8 + url = "github:nix-community/fenix"; 9 + inputs.nixpkgs.follows = "nixpkgs"; 10 + }; 11 + 12 + outputs = inputs @ { 13 + parts, 14 + nci, 15 + ... 16 + }: 17 + parts.lib.mkFlake {inherit inputs;} { 18 + systems = ["x86_64-linux" "aarch64-darwin"]; 19 + imports = [ 20 + nci.flakeModule 21 + ./crates.nix 22 + ]; 23 + perSystem = { 24 + pkgs, 25 + config, 26 + ... 27 + }: let 28 + crateOutputs = config.nci.outputs."wisp-cli"; 29 + mkRenamedPackage = name: pkg: isWindows: pkgs.runCommand name {} '' 30 + mkdir -p $out/bin 31 + if [ -f ${pkg}/bin/wisp-cli.exe ]; then 32 + cp ${pkg}/bin/wisp-cli.exe $out/bin/${name} 33 + elif [ -f ${pkg}/bin/wisp-cli ]; then 34 + cp ${pkg}/bin/wisp-cli $out/bin/${name} 35 + else 36 + echo "Error: Could not find wisp-cli binary in ${pkg}/bin/" 37 + ls -la ${pkg}/bin/ || true 38 + exit 1 39 + fi 40 + ''; 41 + in { 42 + devShells.default = crateOutputs.devShell; 43 + packages.default = crateOutputs.packages.release; 44 + packages.wisp-cli-x86_64-linux = mkRenamedPackage "wisp-cli-x86_64-linux" crateOutputs.packages.release false; 45 + packages.wisp-cli-aarch64-linux = mkRenamedPackage "wisp-cli-aarch64-linux" crateOutputs.allTargets."aarch64-unknown-linux-gnu".packages.release false; 46 + packages.wisp-cli-x86_64-windows = mkRenamedPackage "wisp-cli-x86_64-windows.exe" crateOutputs.allTargets."x86_64-pc-windows-gnu".packages.release true; 47 + packages.wisp-cli-aarch64-darwin = mkRenamedPackage "wisp-cli-aarch64-darwin" crateOutputs.allTargets."aarch64-apple-darwin".packages.release false; 48 + packages.all = pkgs.symlinkJoin { 49 + name = "wisp-cli-all"; 50 + paths = [ 51 + config.packages.wisp-cli-x86_64-linux 52 + config.packages.wisp-cli-aarch64-linux 53 + config.packages.wisp-cli-x86_64-windows 54 + config.packages.wisp-cli-aarch64-darwin 55 + ]; 56 + }; 57 + }; 58 + }; 59 + }
+773 -405
bun.lock
··· 3 3 "configVersion": 1, 4 4 "workspaces": { 5 5 "": { 6 - "name": "elysia-static", 6 + "name": "@wisp/monorepo", 7 7 "dependencies": { 8 8 "@tailwindcss/cli": "^4.1.17", 9 9 "atproto-ui": "^0.12.0", 10 10 "bun-plugin-tailwind": "^0.1.2", 11 + "elysia": "^1.4.18", 11 12 "tailwindcss": "^4.1.17", 12 13 }, 14 + "devDependencies": { 15 + "@types/bun": "^1.3.5", 16 + }, 13 17 }, 14 18 "apps/hosting-service": { 15 19 "name": "wisp-hosting-service", ··· 32 36 "mime-types": "^2.1.35", 33 37 "multiformats": "^13.4.1", 34 38 "postgres": "^3.4.5", 39 + "tiered-storage": "1.0.3", 35 40 }, 36 41 "devDependencies": { 37 42 "@types/bun": "^1.3.1", ··· 45 50 "name": "@wisp/main-app", 46 51 "version": "1.0.50", 47 52 "dependencies": { 48 - "@atproto/api": "^0.17.3", 49 - "@atproto/common-web": "^0.4.5", 53 + "@atproto-labs/did-resolver": "^0.2.4", 54 + "@atproto/api": "^0.17.7", 55 + "@atproto/common-web": "^0.4.6", 50 56 "@atproto/jwk-jose": "^0.1.11", 51 - "@atproto/lex-cli": "^0.9.5", 52 - "@atproto/oauth-client-node": "^0.3.9", 53 - "@atproto/xrpc-server": "^0.9.5", 57 + "@atproto/lex-cli": "^0.9.7", 58 + "@atproto/oauth-client-node": "^0.3.12", 59 + "@atproto/xrpc-server": "^0.9.6", 54 60 "@elysiajs/cors": "^1.4.0", 55 61 "@elysiajs/eden": "^1.4.3", 56 62 "@elysiajs/openapi": "^1.4.11", ··· 74 80 "bun-plugin-tailwind": "^0.1.2", 75 81 "class-variance-authority": "^0.7.1", 76 82 "clsx": "^2.1.1", 77 - "elysia": "latest", 83 + "elysia": "^1.4.18", 78 84 "ignore": "^7.0.5", 79 85 "iron-session": "^8.0.4", 80 86 "lucide-react": "^0.546.0", ··· 144 150 "packages/@wisp/observability": { 145 151 "name": "@wisp/observability", 146 152 "version": "1.0.0", 153 + "dependencies": { 154 + "@opentelemetry/api": "^1.9.0", 155 + "@opentelemetry/exporter-metrics-otlp-http": "^0.56.0", 156 + "@opentelemetry/resources": "^1.29.0", 157 + "@opentelemetry/sdk-metrics": "^1.29.0", 158 + "@opentelemetry/semantic-conventions": "^1.29.0", 159 + }, 160 + "devDependencies": { 161 + "@hono/node-server": "^1.19.6", 162 + "bun-types": "^1.3.3", 163 + "typescript": "^5.9.3", 164 + }, 147 165 "peerDependencies": { 148 - "hono": "^4.0.0", 166 + "hono": "^4.10.7", 149 167 }, 150 168 "optionalPeers": [ 151 169 "hono", ··· 181 199 182 200 "@atcute/util-fetch": ["@atcute/util-fetch@1.0.4", "", { "dependencies": { "@badrap/valita": "^0.4.6" } }, "sha512-sIU9Qk0dE8PLEXSfhy+gIJV+HpiiknMytCI2SqLlqd0vgZUtEKI/EQfP+23LHWvP+CLCzVDOa6cpH045OlmNBg=="], 183 201 184 - "@atproto-labs/did-resolver": ["@atproto-labs/did-resolver@0.2.2", "", { "dependencies": { "@atproto-labs/fetch": "0.2.3", "@atproto-labs/pipe": "0.1.1", "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.1", "zod": "^3.23.8" } }, ""], 202 + "@atproto-labs/did-resolver": ["@atproto-labs/did-resolver@0.2.4", "", { "dependencies": { "@atproto-labs/fetch": "0.2.3", "@atproto-labs/pipe": "0.1.1", "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.3", "zod": "^3.23.8" } }, "sha512-sbXxBnAJWsKv/FEGG6a/WLz7zQYUr1vA2TXvNnPwwJQJCjPwEJMOh1vM22wBr185Phy7D2GD88PcRokn7eUVyw=="], 185 203 186 - "@atproto-labs/fetch": ["@atproto-labs/fetch@0.2.3", "", { "dependencies": { "@atproto-labs/pipe": "0.1.1" } }, ""], 204 + "@atproto-labs/fetch": ["@atproto-labs/fetch@0.2.3", "", { "dependencies": { "@atproto-labs/pipe": "0.1.1" } }, "sha512-NZtbJOCbxKUFRFKMpamT38PUQMY0hX0p7TG5AEYOPhZKZEP7dHZ1K2s1aB8MdVH0qxmqX7nQleNrrvLf09Zfdw=="], 187 205 188 - "@atproto-labs/fetch-node": ["@atproto-labs/fetch-node@0.2.0", "", { "dependencies": { "@atproto-labs/fetch": "0.2.3", "@atproto-labs/pipe": "0.1.1", "ipaddr.js": "^2.1.0", "undici": "^6.14.1" } }, ""], 206 + "@atproto-labs/fetch-node": ["@atproto-labs/fetch-node@0.2.0", "", { "dependencies": { "@atproto-labs/fetch": "0.2.3", "@atproto-labs/pipe": "0.1.1", "ipaddr.js": "^2.1.0", "undici": "^6.14.1" } }, "sha512-Krq09nH/aeoiU2s9xdHA0FjTEFWG9B5FFenipv1iRixCcPc7V3DhTNDawxG9gI8Ny0k4dBVS9WTRN/IDzBx86Q=="], 189 207 190 208 "@atproto-labs/handle-resolver": ["@atproto-labs/handle-resolver@0.3.4", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.3", "zod": "^3.23.8" } }, "sha512-wsNopfzfgO3uPvfnFDgNeXgDufXxSXhjBjp2WEiSzEiLrMy0Jodnqggw4OzD9MJKf0a4Iu2/ydd537qdy91LrQ=="], 191 209 192 - "@atproto-labs/handle-resolver-node": ["@atproto-labs/handle-resolver-node@0.1.21", "", { "dependencies": { "@atproto-labs/fetch-node": "0.2.0", "@atproto-labs/handle-resolver": "0.3.2", "@atproto/did": "0.2.1" } }, ""], 210 + "@atproto-labs/handle-resolver-node": ["@atproto-labs/handle-resolver-node@0.1.23", "", { "dependencies": { "@atproto-labs/fetch-node": "0.2.0", "@atproto-labs/handle-resolver": "0.3.4", "@atproto/did": "0.2.3" } }, "sha512-tBRr2LCgzn3klk+DL0xrTFv4zg5tEszdeW6vSIFVebBYSb3MLdfhievmSqZdIQ4c9UCC4hN7YXTlZCXj8+2YmQ=="], 193 211 194 - "@atproto-labs/identity-resolver": ["@atproto-labs/identity-resolver@0.3.2", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.2", "@atproto-labs/handle-resolver": "0.3.2" } }, ""], 212 + "@atproto-labs/identity-resolver": ["@atproto-labs/identity-resolver@0.3.4", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.4", "@atproto-labs/handle-resolver": "0.3.4" } }, "sha512-HNUEFQIo2ws6iATxmgHd5D5rAsWYupgxZucgwolVHPiMjE1SY+EmxEsfbEN1wDEzM8/u9AKUg/jrxxPEwsgbew=="], 195 213 196 - "@atproto-labs/pipe": ["@atproto-labs/pipe@0.1.1", "", {}, ""], 214 + "@atproto-labs/pipe": ["@atproto-labs/pipe@0.1.1", "", {}, "sha512-hdNw2oUs2B6BN1lp+32pF7cp8EMKuIN5Qok2Vvv/aOpG/3tNSJ9YkvfI0k6Zd188LeDDYRUpYpxcoFIcGH/FNg=="], 197 215 198 - "@atproto-labs/simple-store": ["@atproto-labs/simple-store@0.3.0", "", {}, ""], 216 + "@atproto-labs/simple-store": ["@atproto-labs/simple-store@0.3.0", "", {}, "sha512-nOb6ONKBRJHRlukW1sVawUkBqReLlLx6hT35VS3imaNPwiXDxLnTK7lxw3Lrl9k5yugSBDQAkZAq3MPTEFSUBQ=="], 199 217 200 - "@atproto-labs/simple-store-memory": ["@atproto-labs/simple-store-memory@0.1.4", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "lru-cache": "^10.2.0" } }, ""], 218 + "@atproto-labs/simple-store-memory": ["@atproto-labs/simple-store-memory@0.1.4", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "lru-cache": "^10.2.0" } }, "sha512-3mKY4dP8I7yKPFj9VKpYyCRzGJOi5CEpOLPlRhoJyLmgs3J4RzDrjn323Oakjz2Aj2JzRU/AIvWRAZVhpYNJHw=="], 201 219 202 220 "@atproto/api": ["@atproto/api@0.14.22", "", { "dependencies": { "@atproto/common-web": "^0.4.1", "@atproto/lexicon": "^0.4.10", "@atproto/syntax": "^0.4.0", "@atproto/xrpc": "^0.6.12", "await-lock": "^2.2.2", "multiformats": "^9.9.0", "tlds": "^1.234.0", "zod": "^3.23.8" } }, "sha512-ziXPau+sUdFovObSnsoN7JbOmUw1C5e5L28/yXf3P8vbEnSS3HVVGD1jYcscBYY34xQqi4bVDpwMYx/4yRsTuQ=="], 203 221 204 - "@atproto/common": ["@atproto/common@0.4.12", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@ipld/dag-cbor": "^7.0.3", "cbor-x": "^1.5.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, ""], 222 + "@atproto/common": ["@atproto/common@0.4.12", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@ipld/dag-cbor": "^7.0.3", "cbor-x": "^1.5.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, "sha512-NC+TULLQiqs6MvNymhQS5WDms3SlbIKGLf4n33tpftRJcalh507rI+snbcUb7TLIkKw7VO17qMqxEXtIdd5auQ=="], 205 223 206 224 "@atproto/common-web": ["@atproto/common-web@0.4.6", "", { "dependencies": { "@atproto/lex-data": "0.0.2", "@atproto/lex-json": "0.0.2", "zod": "^3.23.8" } }, "sha512-+2mG/1oBcB/ZmYIU1ltrFMIiuy9aByKAkb2Fos/0eTdczcLBaH17k0KoxMGvhfsujN2r62XlanOAMzysa7lv1g=="], 207 225 208 - "@atproto/crypto": ["@atproto/crypto@0.4.4", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, ""], 226 + "@atproto/crypto": ["@atproto/crypto@0.4.5", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, "sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw=="], 209 227 210 228 "@atproto/did": ["@atproto/did@0.2.3", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-VI8JJkSizvM2cHYJa37WlbzeCm5tWpojyc1/Zy8q8OOjyoy6X4S4BEfoP941oJcpxpMTObamibQIXQDo7tnIjg=="], 211 229 212 230 "@atproto/identity": ["@atproto/identity@0.4.10", "", { "dependencies": { "@atproto/common-web": "^0.4.4", "@atproto/crypto": "^0.4.4" } }, "sha512-nQbzDLXOhM8p/wo0cTh5DfMSOSHzj6jizpodX37LJ4S1TZzumSxAjHEZa5Rev3JaoD5uSWMVE0MmKEGWkPPvfQ=="], 213 231 214 - "@atproto/jwk": ["@atproto/jwk@0.6.0", "", { "dependencies": { "multiformats": "^9.9.0", "zod": "^3.23.8" } }, ""], 232 + "@atproto/jwk": ["@atproto/jwk@0.6.0", "", { "dependencies": { "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-bDoJPvt7TrQVi/rBfBrSSpGykhtIriKxeYCYQTiPRKFfyRhbgpElF0wPXADjIswnbzZdOwbY63az4E/CFVT3Tw=="], 215 233 216 - "@atproto/jwk-jose": ["@atproto/jwk-jose@0.1.11", "", { "dependencies": { "@atproto/jwk": "0.6.0", "jose": "^5.2.0" } }, ""], 234 + "@atproto/jwk-jose": ["@atproto/jwk-jose@0.1.11", "", { "dependencies": { "@atproto/jwk": "0.6.0", "jose": "^5.2.0" } }, "sha512-i4Fnr2sTBYmMmHXl7NJh8GrCH+tDQEVWrcDMDnV5DjJfkgT17wIqvojIw9SNbSL4Uf0OtfEv6AgG0A+mgh8b5Q=="], 217 235 218 - "@atproto/jwk-webcrypto": ["@atproto/jwk-webcrypto@0.2.0", "", { "dependencies": { "@atproto/jwk": "0.6.0", "@atproto/jwk-jose": "0.1.11", "zod": "^3.23.8" } }, ""], 236 + "@atproto/jwk-webcrypto": ["@atproto/jwk-webcrypto@0.2.0", "", { "dependencies": { "@atproto/jwk": "0.6.0", "@atproto/jwk-jose": "0.1.11", "zod": "^3.23.8" } }, "sha512-UmgRrrEAkWvxwhlwe30UmDOdTEFidlIzBC7C3cCbeJMcBN1x8B3KH+crXrsTqfWQBG58mXgt8wgSK3Kxs2LhFg=="], 219 237 220 238 "@atproto/lex-cbor": ["@atproto/lex-cbor@0.0.2", "", { "dependencies": { "@atproto/lex-data": "0.0.2", "multiformats": "^9.9.0", "tslib": "^2.8.1" } }, "sha512-sTr3UCL2SgxEoYVpzJGgWTnNl4TpngP5tMcRyaOvi21Se4m3oR4RDsoVDPz8AS6XphiteRwzwPstquN7aWWMbA=="], 221 239 222 - "@atproto/lex-cli": ["@atproto/lex-cli@0.9.6", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "@atproto/syntax": "^0.4.1", "chalk": "^4.1.2", "commander": "^9.4.0", "prettier": "^3.2.5", "ts-morph": "^24.0.0", "yesno": "^0.4.0", "zod": "^3.23.8" }, "bin": { "lex": "dist/index.js" } }, ""], 240 + "@atproto/lex-cli": ["@atproto/lex-cli@0.9.7", "", { "dependencies": { "@atproto/lexicon": "^0.5.2", "@atproto/syntax": "^0.4.1", "chalk": "^4.1.2", "commander": "^9.4.0", "prettier": "^3.2.5", "ts-morph": "^24.0.0", "yesno": "^0.4.0", "zod": "^3.23.8" }, "bin": { "lex": "dist/index.js" } }, "sha512-UZVf0pK0mB4qiuwbnrxmV0mC9/Vk2v7W3u9pd4wc4GFojzAyGP76MF2TiwWFya5mgzC7723/r5Jb4ADg0rtfng=="], 223 241 224 242 "@atproto/lex-data": ["@atproto/lex-data@0.0.2", "", { "dependencies": { "@atproto/syntax": "0.4.2", "multiformats": "^9.9.0", "tslib": "^2.8.1", "uint8arrays": "3.0.0", "unicode-segmenter": "^0.14.0" } }, "sha512-euV2rDGi+coH8qvZOU+ieUOEbwPwff9ca6IiXIqjZJ76AvlIpj7vtAyIRCxHUW2BoU6h9yqyJgn9MKD2a7oIwg=="], 225 243 ··· 227 245 228 246 "@atproto/lexicon": ["@atproto/lexicon@0.5.2", "", { "dependencies": { "@atproto/common-web": "^0.4.4", "@atproto/syntax": "^0.4.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-lRmJgMA8f5j7VB5Iu5cp188ald5FuI4FlmZ7nn6EBrk1dgOstWVrI5Ft6K3z2vjyLZRG6nzknlsw+tDP63p7bQ=="], 229 247 230 - "@atproto/oauth-client": ["@atproto/oauth-client@0.5.8", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.2", "@atproto-labs/fetch": "0.2.3", "@atproto-labs/handle-resolver": "0.3.2", "@atproto-labs/identity-resolver": "0.3.2", "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.1", "@atproto/jwk": "0.6.0", "@atproto/oauth-types": "0.5.0", "@atproto/xrpc": "0.7.5", "core-js": "^3", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, ""], 248 + "@atproto/oauth-client": ["@atproto/oauth-client@0.5.10", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.4", "@atproto-labs/fetch": "0.2.3", "@atproto-labs/handle-resolver": "0.3.4", "@atproto-labs/identity-resolver": "0.3.4", "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.3", "@atproto/jwk": "0.6.0", "@atproto/oauth-types": "0.5.2", "@atproto/xrpc": "0.7.6", "core-js": "^3", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-2mdJFyYbaOw3e/1KMBOQ2/J9p+MfWW8kE6FKdExWrJ7JPJpTJw2ZF2EmdGHCVeXw386dQgXbLkr+w4vbgSqfMQ=="], 231 249 232 - "@atproto/oauth-client-node": ["@atproto/oauth-client-node@0.3.10", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.2", "@atproto-labs/handle-resolver-node": "0.1.21", "@atproto-labs/simple-store": "0.3.0", "@atproto/did": "0.2.1", "@atproto/jwk": "0.6.0", "@atproto/jwk-jose": "0.1.11", "@atproto/jwk-webcrypto": "0.2.0", "@atproto/oauth-client": "0.5.8", "@atproto/oauth-types": "0.5.0" } }, ""], 250 + "@atproto/oauth-client-node": ["@atproto/oauth-client-node@0.3.12", "", { "dependencies": { "@atproto-labs/did-resolver": "0.2.4", "@atproto-labs/handle-resolver-node": "0.1.23", "@atproto-labs/simple-store": "0.3.0", "@atproto/did": "0.2.3", "@atproto/jwk": "0.6.0", "@atproto/jwk-jose": "0.1.11", "@atproto/jwk-webcrypto": "0.2.0", "@atproto/oauth-client": "0.5.10", "@atproto/oauth-types": "0.5.2" } }, "sha512-9ejfO1H8qo3EbiAJgxKcdcR5Ay/9hgaC5OdxtTN63bcOrkIhvBN0xpVPGZYLL1iJQyNeK1T5m/LDrv4gUS1B+g=="], 233 251 234 - "@atproto/oauth-types": ["@atproto/oauth-types@0.5.0", "", { "dependencies": { "@atproto/did": "0.2.1", "@atproto/jwk": "0.6.0", "zod": "^3.23.8" } }, ""], 252 + "@atproto/oauth-types": ["@atproto/oauth-types@0.5.2", "", { "dependencies": { "@atproto/did": "0.2.3", "@atproto/jwk": "0.6.0", "zod": "^3.23.8" } }, "sha512-9DCDvtvCanTwAaU5UakYDO0hzcOITS3RutK5zfLytE5Y9unj0REmTDdN8Xd8YCfUJl7T/9pYpf04Uyq7bFTASg=="], 235 253 236 254 "@atproto/repo": ["@atproto/repo@0.8.11", "", { "dependencies": { "@atproto/common": "^0.5.0", "@atproto/common-web": "^0.4.4", "@atproto/crypto": "^0.4.4", "@atproto/lexicon": "^0.5.2", "@ipld/dag-cbor": "^7.0.0", "multiformats": "^9.9.0", "uint8arrays": "3.0.0", "varint": "^6.0.0", "zod": "^3.23.8" } }, "sha512-b/WCu5ITws4ILHoXiZz0XXB5U9C08fUVzkBQDwpnme62GXv8gUaAPL/ttG61OusW09ARwMMQm4vxoP0hTFg+zA=="], 237 255 ··· 239 257 240 258 "@atproto/syntax": ["@atproto/syntax@0.4.2", "", {}, "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA=="], 241 259 242 - "@atproto/ws-client": ["@atproto/ws-client@0.0.3", "", { "dependencies": { "@atproto/common": "^0.5.0", "ws": "^8.12.0" } }, "sha512-eKqkTWBk6zuMY+6gs02eT7mS8Btewm8/qaL/Dp00NDCqpNC+U59MWvQsOWT3xkNGfd9Eip+V6VI4oyPvAfsfTA=="], 260 + "@atproto/ws-client": ["@atproto/ws-client@0.0.2", "", { "dependencies": { "@atproto/common": "^0.4.12", "ws": "^8.12.0" } }, "sha512-yb11WtI9cZfx/00MTgZRabB97Quf/TerMmtzIm2H2YirIq2oW++NPoufXYCuXuQGR4ep4fvCyzz0/GX95jCONQ=="], 243 261 244 262 "@atproto/xrpc": ["@atproto/xrpc@0.7.6", "", { "dependencies": { "@atproto/lexicon": "^0.5.2", "zod": "^3.23.8" } }, "sha512-RvCf4j0JnKYWuz3QzsYCntJi3VuiAAybQsMIUw2wLWcHhchO9F7UaBZINLL2z0qc/cYWPv5NSwcVydMseoCZLA=="], 245 263 246 - "@atproto/xrpc-server": ["@atproto/xrpc-server@0.9.5", "", { "dependencies": { "@atproto/common": "^0.4.12", "@atproto/crypto": "^0.4.4", "@atproto/lexicon": "^0.5.1", "@atproto/xrpc": "^0.7.5", "cbor-x": "^1.5.1", "express": "^4.17.2", "http-errors": "^2.0.0", "mime-types": "^2.1.35", "rate-limiter-flexible": "^2.4.1", "uint8arrays": "3.0.0", "ws": "^8.12.0", "zod": "^3.23.8" } }, ""], 264 + "@atproto/xrpc-server": ["@atproto/xrpc-server@0.9.6", "", { "dependencies": { "@atproto/common": "^0.4.12", "@atproto/crypto": "^0.4.4", "@atproto/lexicon": "^0.5.1", "@atproto/ws-client": "^0.0.2", "@atproto/xrpc": "^0.7.5", "cbor-x": "^1.5.1", "express": "^4.17.2", "http-errors": "^2.0.0", "mime-types": "^2.1.35", "rate-limiter-flexible": "^2.4.1", "uint8arrays": "3.0.0", "ws": "^8.12.0", "zod": "^3.23.8" } }, "sha512-N/wPK0VEk8lZLkVsfG1wlkINQnBLO2fzWT+xclOjYl5lJwDi5xgiiyEQJAyZN49d6cmbsONu0SuOVw9pa5xLCw=="], 265 + 266 + "@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="], 267 + 268 + "@aws-crypto/crc32c": ["@aws-crypto/crc32c@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag=="], 269 + 270 + "@aws-crypto/sha1-browser": ["@aws-crypto/sha1-browser@5.2.0", "", { "dependencies": { "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg=="], 271 + 272 + "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], 273 + 274 + "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], 275 + 276 + "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="], 277 + 278 + "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], 279 + 280 + "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.962.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/credential-provider-node": "3.962.0", "@aws-sdk/middleware-bucket-endpoint": "3.957.0", "@aws-sdk/middleware-expect-continue": "3.957.0", "@aws-sdk/middleware-flexible-checksums": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-location-constraint": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-sdk-s3": "3.957.0", "@aws-sdk/middleware-ssec": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/signature-v4-multi-region": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/eventstream-serde-browser": "^4.2.7", "@smithy/eventstream-serde-config-resolver": "^4.3.7", "@smithy/eventstream-serde-node": "^4.2.7", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-blob-browser": "^4.2.8", "@smithy/hash-node": "^4.2.7", "@smithy/hash-stream-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/md5-js": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-I2/1McBZCcM3PfM4ck8D6gnZR3K7+yl1fGkwTq/3ThEn9tdLjNwcdgTbPfxfX6LoecLrH9Ekoo+D9nmQ0T261w=="], 281 + 282 + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.958.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6qNCIeaMzKzfqasy2nNRuYnMuaMebCcCPP4J2CVGkA8QYMbIVKPlkn9bpB20Vxe6H/r3jtCCLQaOJjVTx/6dXg=="], 283 + 284 + "@aws-sdk/core": ["@aws-sdk/core@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws-sdk/xml-builder": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DrZgDnF1lQZv75a52nFWs6MExihJF2GZB6ETZRqr6jMwhrk2kbJPUtvgbifwcL7AYmVqHQDJBrR/MqkwwFCpiw=="], 285 + 286 + "@aws-sdk/crc64-nvme": ["@aws-sdk/crc64-nvme@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-qSwSfI+qBU9HDsd6/4fM9faCxYJx2yDuHtj+NVOQ6XYDWQzFab/hUdwuKZ77Pi6goLF1pBZhJ2azaC2w7LbnTA=="], 287 + 288 + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-475mkhGaWCr+Z52fOOVb/q2VHuNvqEDixlYIkeaO6xJ6t9qR0wpLt4hOQaR6zR1wfZV0SlE7d8RErdYq/PByog=="], 289 + 290 + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-8dS55QHRxXgJlHkEYaCGZIhieCs9NU1HU1BcqQ4RfUdSsfRdxxktqUKgCnBnOOn0oD3PPA8cQOCAVgIyRb3Rfw=="], 291 + 292 + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.962.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/credential-provider-env": "3.957.0", "@aws-sdk/credential-provider-http": "3.957.0", "@aws-sdk/credential-provider-login": "3.962.0", "@aws-sdk/credential-provider-process": "3.957.0", "@aws-sdk/credential-provider-sso": "3.958.0", "@aws-sdk/credential-provider-web-identity": "3.958.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-h0kVnXLW2d3nxbcrR/Pfg3W/+YoCguasWz7/3nYzVqmdKarGrpJzaFdoZtLgvDSZ8VgWUC4lWOTcsDMV0UNqUQ=="], 293 + 294 + "@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.962.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-kHYH6Av2UifG3mPkpPUNRh/PuX6adaAcpmsclJdHdxlixMCRdh8GNeEihq480DC0GmfqdpoSf1w2CLmLLPIS6w=="], 295 + 296 + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.962.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.957.0", "@aws-sdk/credential-provider-http": "3.957.0", "@aws-sdk/credential-provider-ini": "3.962.0", "@aws-sdk/credential-provider-process": "3.957.0", "@aws-sdk/credential-provider-sso": "3.958.0", "@aws-sdk/credential-provider-web-identity": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-CS78NsWRxLa+nWqeWBEYMZTLacMFIXs1C5WJuM9kD05LLiWL32ksljoPsvNN24Bc7rCSQIIMx/U3KGvkDVZMVg=="], 297 + 298 + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/KIz9kadwbeLy6SKvT79W81Y+hb/8LMDyeloA2zhouE28hmne+hLn0wNCQXAAupFFlYOAtZR2NTBs7HBAReJlg=="], 299 + 300 + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.958.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.958.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/token-providers": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-CBYHJ5ufp8HC4q+o7IJejCUctJXWaksgpmoFpXerbjAso7/Fg7LLUu9inXVOxlHKLlvYekDXjIUBXDJS2WYdgg=="], 301 + 302 + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-dgnvwjMq5Y66WozzUzxNkCFap+umHUtqMMKlr8z/vl9NYMLem/WUbWNpFFOVFWquXikc+ewtpBMR4KEDXfZ+KA=="], 303 + 304 + "@aws-sdk/lib-storage": ["@aws-sdk/lib-storage@3.962.0", "", { "dependencies": { "@smithy/abort-controller": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/smithy-client": "^4.10.2", "buffer": "5.6.0", "events": "3.3.0", "stream-browserify": "3.0.0", "tslib": "^2.6.2" }, "peerDependencies": { "@aws-sdk/client-s3": "^3.962.0" } }, "sha512-Ai5gWRQkzsUMQ6NPoZZoiLXoQ6/yPRcR4oracIVjyWcu48TfBpsRgbqY/5zNOM55ag1wPX9TtJJGOhK3TNk45g=="], 305 + 306 + "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws-sdk/util-arn-parser": "3.957.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iczcn/QRIBSpvsdAS/rbzmoBpleX1JBjXvCynMbDceVLBIcVrwT1hXECrhtIC2cjh4HaLo9ClAbiOiWuqt+6MA=="], 307 + 308 + "@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-AlbK3OeVNwZZil0wlClgeI/ISlOt/SPUxBsIns876IFaVu/Pj3DgImnYhpcJuFRek4r4XM51xzIaGQXM6GDHGg=="], 309 + 310 + "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.957.0", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/crc64-nvme": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/is-array-buffer": "^4.2.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iJpeVR5V8se1hl2pt+k8bF/e9JO4KWgPCMjg8BtRspNtKIUGy7j6msYvbDixaKZaF2Veg9+HoYcOhwnZumjXSA=="], 311 + 312 + "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-BBgKawVyfQZglEkNTuBBdC3azlyqNXsvvN4jPkWAiNYcY0x1BasaJFl+7u/HisfULstryweJq/dAvIZIxzlZaA=="], 313 + 314 + "@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-y8/W7TOQpmDJg/fPYlqAhwA4+I15LrS7TwgUEoxogtkD8gfur9wFMRLT8LCyc9o4NMEcAnK50hSb4+wB0qv6tQ=="], 315 + 316 + "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-w1qfKrSKHf9b5a8O76yQ1t69u6NWuBjr5kBX+jRWFx/5mu6RLpqERXRpVJxfosbep7k3B+DSB5tZMZ82GKcJtQ=="], 317 + 318 + "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-D2H/WoxhAZNYX+IjkKTdOhOkWQaK0jjJrDBj56hKjU5c9ltQiaX/1PqJ4dfjHntEshJfu0w+E6XJ+/6A6ILBBA=="], 319 + 320 + "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-arn-parser": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-5B2qY2nR2LYpxoQP0xUum5A1UNvH2JQpLHDH1nWFNF/XetV7ipFHksMxPNhtJJ6ARaWhQIDXfOUj0jcnkJxXUg=="], 321 + 322 + "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-qwkmrK0lizdjNt5qxl4tHYfASh8DFpHXM1iDVo+qHe+zuslfMqQEGRkzxS8tJq/I+8F0c6v3IKOveKJAfIvfqQ=="], 323 + 324 + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-50vcHu96XakQnIvlKJ1UoltrFODjsq2KvtTgHiPFteUS884lQnK5VC/8xd1Msz/1ONpLMzdCVproCQqhDTtMPQ=="], 325 + 326 + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.958.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw=="], 327 + 328 + "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-V8iY3blh8l2iaOqXWW88HbkY5jDoWjH56jonprG/cpyqqCnprvpMUZWPWYJoI8rHRf2bqzZeql1slxG6EnKI7A=="], 329 + 330 + "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.957.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-t6UfP1xMUigMMzHcb7vaZcjv7dA2DQkk9C/OAP1dKyrE0vb4lFGDaTApi17GN6Km9zFxJthEMUbBc7DL0hq1Bg=="], 331 + 332 + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-UCj7lQXODduD1myNJQkV+LYcGYJ9iiMggR8ow8Hva1g3A/Na5imNXzz6O67k7DAee0TYpy+gkNw+SizC6min8Q=="], 333 + 334 + "@aws-sdk/types": ["@aws-sdk/types@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-wzWC2Nrt859ABk6UCAVY/WYEbAd7FjkdrQL6m24+tfmWYDNRByTJ9uOgU/kw9zqLCAwb//CPvrJdhqjTznWXAg=="], 335 + 336 + "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.957.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Aj6m+AyrhWyg8YQ4LDPg2/gIfGHCEcoQdBt5DeSFogN5k9mmJPOJ+IAmNSWmWRjpOxEy6eY813RNDI6qS97M0g=="], 337 + 338 + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-endpoints": "^3.2.7", "tslib": "^2.6.2" } }, "sha512-xwF9K24mZSxcxKS3UKQFeX/dPYkEps9wF1b+MGON7EvnbcucrJGyQyK1v1xFPn1aqXkBTFi+SZaMRx5E5YCVFw=="], 339 + 340 + "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.957.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nhmgKHnNV9K+i9daumaIz8JTLsIIML9PE/HUks5liyrjUzenjW/aHoc7WJ9/Td/gPZtayxFnXQSJRb/fDlBuJw=="], 341 + 342 + "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-exueuwxef0lUJRnGaVkNSC674eAiWU07ORhxBnevFFZEKisln+09Qrtw823iyv5I1N8T+wKfh95xvtWQrNKNQw=="], 343 + 344 + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.957.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-ycbYCwqXk4gJGp0Oxkzf2KBeeGBdTxz559D41NJP8FlzSej1Gh7Rk40Zo6AyTfsNWkrl/kVi1t937OIzC5t+9Q=="], 345 + 346 + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-Ai5iiQqS8kJ5PjzMhWcLKN0G2yasAkvpnPlq2EnqlIMdB48HsizElt62qcktdxp4neRMyGkFq4NzgmDbXnhRiA=="], 347 + 348 + "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.2", "", {}, "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg=="], 247 349 248 350 "@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="], 249 351 250 - "@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, ""], 352 + "@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="], 251 353 252 - "@cbor-extract/cbor-extract-darwin-arm64": ["@cbor-extract/cbor-extract-darwin-arm64@2.2.0", "", { "os": "darwin", "cpu": "arm64" }, ""], 354 + "@cbor-extract/cbor-extract-darwin-arm64": ["@cbor-extract/cbor-extract-darwin-arm64@2.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w=="], 253 355 254 - "@elysiajs/cors": ["@elysiajs/cors@1.4.0", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, ""], 356 + "@cbor-extract/cbor-extract-darwin-x64": ["@cbor-extract/cbor-extract-darwin-x64@2.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w=="], 255 357 256 - "@elysiajs/eden": ["@elysiajs/eden@1.4.4", "", { "peerDependencies": { "elysia": ">= 1.4.0-exp.0" } }, ""], 358 + "@cbor-extract/cbor-extract-linux-arm": ["@cbor-extract/cbor-extract-linux-arm@2.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q=="], 257 359 258 - "@elysiajs/openapi": ["@elysiajs/openapi@1.4.11", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, ""], 360 + "@cbor-extract/cbor-extract-linux-arm64": ["@cbor-extract/cbor-extract-linux-arm64@2.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ=="], 259 361 260 - "@elysiajs/opentelemetry": ["@elysiajs/opentelemetry@1.4.6", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/instrumentation": "^0.200.0", "@opentelemetry/sdk-node": "^0.200.0" }, "peerDependencies": { "elysia": ">= 1.4.0" } }, ""], 362 + "@cbor-extract/cbor-extract-linux-x64": ["@cbor-extract/cbor-extract-linux-x64@2.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw=="], 261 363 262 - "@elysiajs/static": ["@elysiajs/static@1.4.6", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, ""], 364 + "@cbor-extract/cbor-extract-win32-x64": ["@cbor-extract/cbor-extract-win32-x64@2.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w=="], 365 + 366 + "@elysiajs/cors": ["@elysiajs/cors@1.4.0", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-pb0SCzBfFbFSYA/U40HHO7R+YrcXBJXOWgL20eSViK33ol1e20ru2/KUaZYo5IMUn63yaTJI/bQERuQ+77ND8g=="], 367 + 368 + "@elysiajs/eden": ["@elysiajs/eden@1.4.5", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-hIOeH+S5NU/84A7+t8yB1JjxqjmzRkBF9fnLn6y+AH8EcF39KumOAnciMhIOkhhThVZvXZ3d+GsizRc+Fxoi8g=="], 369 + 370 + "@elysiajs/openapi": ["@elysiajs/openapi@1.4.11", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-d75bMxYJpN6qSDi/z9L1S7SLk1S/8Px+cTb3W2lrYzU8uQ5E0kXdy1oOMJEfTyVsz3OA19NP9KNxE7ztSbLBLg=="], 371 + 372 + "@elysiajs/opentelemetry": ["@elysiajs/opentelemetry@1.4.8", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/instrumentation": "^0.200.0", "@opentelemetry/sdk-node": "^0.200.0" }, "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-c9unbcdXfehExCv1GsiTCfos5SyIAyDwP7apcMeXmUMBaJZiAYMfiEH8RFFFIfIHJHC/xlNJzUPodkcUaaoJJQ=="], 373 + 374 + "@elysiajs/static": ["@elysiajs/static@1.4.7", "", { "peerDependencies": { "elysia": ">= 1.4.0" } }, "sha512-Go4kIXZ0G3iWfkAld07HmLglqIDMVXdyRKBQK/sVEjtpDdjHNb+rUIje73aDTWpZYg4PEVHUpi9v4AlNEwrQug=="], 263 375 264 376 "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.26.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-hj0sKNCQOOo2fgyII3clmJXP28VhgDfU5iy3GNHlWO76KG6N7x4D9ezH5lJtQTG+1J6MFDAJXC1qsI+W+LvZoA=="], 265 377 ··· 313 425 314 426 "@esbuild/win32-x64": ["@esbuild/win32-x64@0.26.0", "", { "os": "win32", "cpu": "x64" }, "sha512-WAckBKaVnmFqbEhbymrPK7M086DQMpL1XoRbpmN0iW8k5JSXjDRQBhcZNa0VweItknLq9eAeCL34jK7/CDcw7A=="], 315 427 316 - "@grpc/grpc-js": ["@grpc/grpc-js@1.14.1", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, ""], 428 + "@grpc/grpc-js": ["@grpc/grpc-js@1.14.2", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-QzVUtEFyu05UNx2xr0fCQmStUO17uVQhGNowtxs00IgTZT6/W2PBLfUkj30s0FKJ29VtTa3ArVNIhNP6akQhqA=="], 317 429 318 - "@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, ""], 430 + "@grpc/proto-loader": ["@grpc/proto-loader@0.8.0", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.3", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ=="], 319 431 320 432 "@hono/node-server": ["@hono/node-server@1.19.6", "", { "peerDependencies": { "hono": "^4" } }, "sha512-Shz/KjlIeAhfiuE93NDKVdZ7HdBVLQAfdbaXEaoAVO3ic9ibRSLGIQGkcBbFyuLr+7/1D5ZCINM8B+6IvXeMtw=="], 321 433 322 - "@ipld/dag-cbor": ["@ipld/dag-cbor@7.0.3", "", { "dependencies": { "cborg": "^1.6.0", "multiformats": "^9.5.4" } }, ""], 434 + "@ipld/dag-cbor": ["@ipld/dag-cbor@7.0.3", "", { "dependencies": { "cborg": "^1.6.0", "multiformats": "^9.5.4" } }, "sha512-1VVh2huHsuohdXC1bGJNE8WR72slZ9XE2T3wbBBq31dm7ZBatmKLLxrB+XAqafxfRFjv08RZmj/W/ZqaM13AuA=="], 323 435 324 436 "@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=="], 325 437 ··· 331 443 332 444 "@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=="], 333 445 334 - "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, ""], 446 + "@js-sdsl/ordered-map": ["@js-sdsl/ordered-map@4.4.2", "", {}, "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw=="], 447 + 448 + "@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw=="], 449 + 450 + "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="], 451 + 452 + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], 453 + 454 + "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.200.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IKJBQxh91qJ+3ssRly5hYEJ8NDHu9oY/B1PXVSCWf7zytmYO9RNLB0Ox9XQ/fJ8m6gY6Q6NtBWlmXfaXt5Uc4Q=="], 455 + 456 + "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.0.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-IEkJGzK1A9v3/EHjXh3s2IiFc6L4jfK+lNgKVgUjeUJQRRhnVFMIO3TAvKwonm9O1HebCuoOt98v8bZW7oVQHA=="], 457 + 458 + "@opentelemetry/core": ["@opentelemetry/core@1.29.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-gmT7vAreXl0DTHD2rVZcw3+l2g84+5XiHIqdBUxXbExymPCvSsGOpiwMmn8nkiJur28STV31wnhIDrzWDPzjfA=="], 459 + 460 + "@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/sdk-logs": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+3MDfa5YQPGM3WXxW9kqGD85Q7s9wlEMVNhXXG7tYFLnIeaseUt9YtCeFhEDFzfEktacdFpOtXmJuNW8cHbU5A=="], 461 + 462 + "@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/sdk-logs": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-KfWw49htbGGp9s8N4KI8EQ9XuqKJ0VG+yVYVYFiCYSjEV32qpQ5qZ9UZBzOZ6xRb+E16SXOSCT3RkqBVSABZ+g=="], 463 + 464 + "@opentelemetry/exporter-logs-otlp-proto": ["@opentelemetry/exporter-logs-otlp-proto@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-GmahpUU/55hxfH4TP77ChOfftADsCq/nuri73I/AVLe2s4NIglvTsaACkFVZAVmnXXyPS00Fk3x27WS3yO07zA=="], 335 465 336 - "@noble/curves": ["@noble/curves@1.9.7", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, ""], 466 + "@opentelemetry/exporter-metrics-otlp-grpc": ["@opentelemetry/exporter-metrics-otlp-grpc@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-uHawPRvKIrhqH09GloTuYeq2BjyieYHIpiklOvxm9zhrCL2eRsnI/6g9v2BZTVtGp8tEgIa7rCQ6Ltxw6NBgew=="], 337 467 338 - "@noble/hashes": ["@noble/hashes@1.8.0", "", {}, ""], 468 + "@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.56.0", "", { "dependencies": { "@opentelemetry/core": "1.29.0", "@opentelemetry/otlp-exporter-base": "0.56.0", "@opentelemetry/otlp-transformer": "0.56.0", "@opentelemetry/resources": "1.29.0", "@opentelemetry/sdk-metrics": "1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-GD5QuCT6js+mDpb5OBO6OSyCH+k2Gy3xPHJV9BnjV8W6kpSuY8y2Samzs5vl23UcGMq6sHLAbs+Eq/VYsLMiVw=="], 339 469 340 - "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, ""], 470 + "@opentelemetry/exporter-metrics-otlp-proto": ["@opentelemetry/exporter-metrics-otlp-proto@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-E+uPj0yyvz81U9pvLZp3oHtFrEzNSqKGVkIViTQY1rH3TOobeJPSpLnTVXACnCwkPR5XeTvPnK3pZ2Kni8AFMg=="], 341 471 342 - "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.200.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 472 + "@opentelemetry/exporter-prometheus": ["@opentelemetry/exporter-prometheus@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-ZYdlU9r0USuuYppiDyU2VFRA0kFl855ylnb3N/2aOlXrbA4PMCznen7gmPbetGQu7pz8Jbaf4fwvrDnVdQQXSw=="], 473 + 474 + "@opentelemetry/exporter-trace-otlp-grpc": ["@opentelemetry/exporter-trace-otlp-grpc@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-hmeZrUkFl1YMsgukSuHCFPYeF9df0hHoKeHUthRKFCxiURs+GwF1VuabuHmBMZnjTbsuvNjOB+JSs37Csem/5Q=="], 475 + 476 + "@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Goi//m/7ZHeUedxTGVmEzH19NgqJY+Bzr6zXo1Rni1+hwqaksEyJ44gdlEMREu6dzX1DlAaH/qSykSVzdrdafA=="], 343 477 344 - "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@2.0.0", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, ""], 478 + "@opentelemetry/exporter-trace-otlp-proto": ["@opentelemetry/exporter-trace-otlp-proto@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-V9TDSD3PjK1OREw2iT9TUTzNYEVWJk4Nhodzhp9eiz4onDMYmPy3LaGbPv81yIR6dUb/hNp/SIhpiCHwFUq2Vg=="], 345 479 346 - "@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, ""], 480 + "@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, "sha512-icxaKZ+jZL/NHXX8Aru4HGsrdhK0MLcuRXkX5G5IRmCgoRLw+Br6I/nMVozX2xjGGwV7hw2g+4Slj8K7s4HbVg=="], 347 481 348 - "@opentelemetry/exporter-logs-otlp-grpc": ["@opentelemetry/exporter-logs-otlp-grpc@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/sdk-logs": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 482 + "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", "shimmer": "^1.2.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-pmPlzfJd+vvgaZd/reMsC8RWgTXn2WY1OWT5RT42m3aOn5532TozwXNDhg1vzqJ+jnvmkREcdLr27ebJEQt0Jg=="], 349 483 350 - "@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/sdk-logs": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 484 + "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.56.0", "", { "dependencies": { "@opentelemetry/core": "1.29.0", "@opentelemetry/otlp-transformer": "0.56.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-eURvv0fcmBE+KE1McUeRo+u0n18ZnUeSc7lDlW/dzlqFYasEbsztTK4v0Qf8C4vEY+aMTjPKUxBG0NX2Te3Pmw=="], 351 485 352 - "@opentelemetry/exporter-logs-otlp-proto": ["@opentelemetry/exporter-logs-otlp-proto@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 486 + "@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-CK2S+bFgOZ66Bsu5hlDeOX6cvW5FVtVjFFbWuaJP0ELxJKBB6HlbLZQ2phqz/uLj1cWap5xJr/PsR3iGoB7Vqw=="], 353 487 354 - "@opentelemetry/exporter-metrics-otlp-grpc": ["@opentelemetry/exporter-metrics-otlp-grpc@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 488 + "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.56.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.56.0", "@opentelemetry/core": "1.29.0", "@opentelemetry/resources": "1.29.0", "@opentelemetry/sdk-logs": "0.56.0", "@opentelemetry/sdk-metrics": "1.29.0", "@opentelemetry/sdk-trace-base": "1.29.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-kVkH/W2W7EpgWWpyU5VnnjIdSD7Y7FljQYObAQSKdRcejiwMj2glypZtUdfq1LTJcv4ht0jyTrw1D3CCxssNtQ=="], 355 489 356 - "@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 490 + "@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-blx9S2EI49Ycuw6VZq+bkpaIoiJFhsDuvFGhBIoH3vJ5oYjJ2U0s3fAM5jYft99xVIAv6HqoPtlP9gpVA2IZtA=="], 357 491 358 - "@opentelemetry/exporter-metrics-otlp-proto": ["@opentelemetry/exporter-metrics-otlp-proto@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 492 + "@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-Mbm/LSFyAtQKP0AQah4AfGgsD+vsZcyreZoQ5okFBk33hU7AquU4TltgyL9dvaO8/Zkoud8/0gEvwfOZ5d7EPA=="], 359 493 360 - "@opentelemetry/exporter-prometheus": ["@opentelemetry/exporter-prometheus@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 494 + "@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], 361 495 362 - "@opentelemetry/exporter-trace-otlp-grpc": ["@opentelemetry/exporter-trace-otlp-grpc@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-grpc-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 496 + "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-VZG870063NLfObmQQNtCVcdXXLzI3vOjjrRENmU37HYiPFa0ZXpXVDsTD02Nh3AT3xYJzQaWKl2X2lQ2l7TWJA=="], 363 497 364 - "@opentelemetry/exporter-trace-otlp-http": ["@opentelemetry/exporter-trace-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 498 + "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog=="], 365 499 366 - "@opentelemetry/exporter-trace-otlp-proto": ["@opentelemetry/exporter-trace-otlp-proto@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 500 + "@opentelemetry/sdk-node": ["@opentelemetry/sdk-node@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-logs-otlp-grpc": "0.200.0", "@opentelemetry/exporter-logs-otlp-http": "0.200.0", "@opentelemetry/exporter-logs-otlp-proto": "0.200.0", "@opentelemetry/exporter-metrics-otlp-grpc": "0.200.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/exporter-metrics-otlp-proto": "0.200.0", "@opentelemetry/exporter-prometheus": "0.200.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.200.0", "@opentelemetry/exporter-trace-otlp-http": "0.200.0", "@opentelemetry/exporter-trace-otlp-proto": "0.200.0", "@opentelemetry/exporter-zipkin": "2.0.0", "@opentelemetry/instrumentation": "0.200.0", "@opentelemetry/propagator-b3": "2.0.0", "@opentelemetry/propagator-jaeger": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "@opentelemetry/sdk-trace-node": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-S/YSy9GIswnhYoDor1RusNkmRughipvTCOQrlF1dzI70yQaf68qgf5WMnzUxdlCl3/et/pvaO75xfPfuEmCK5A=="], 367 501 368 - "@opentelemetry/exporter-zipkin": ["@opentelemetry/exporter-zipkin@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0" } }, ""], 502 + "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-qQnYdX+ZCkonM7tA5iU4fSRsVxbFGml8jbxOgipRGMFHKaXKHQ30js03rTobYjKjIfnOsZSbHKWF0/0v0OQGfw=="], 369 503 370 - "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", "shimmer": "^1.2.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 504 + "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.0.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.0.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-omdilCZozUjQwY3uZRBwbaRMJ3p09l4t187Lsdf0dGMye9WKD4NGcpgZRvqhI1dwcH6og+YXQEtoO9Wx3ykilg=="], 371 505 372 - "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 506 + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.38.0", "", {}, "sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg=="], 373 507 374 - "@opentelemetry/otlp-grpc-exporter-base": ["@opentelemetry/otlp-grpc-exporter-base@0.200.0", "", { "dependencies": { "@grpc/grpc-js": "^1.7.1", "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 508 + "@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eJopQrUk0WR7jViYDC29+Rp50xGvs4GtWOXBeqCoFMzutkkO3CZvHehA4JqnjfWMTSS8toqvRhCSOpOz62Wf9w=="], 375 509 376 - "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, ""], 510 + "@oven/bun-darwin-x64": ["@oven/bun-darwin-x64@1.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-xGDePueVFrNgkS+iN0QdEFeRrx2MQ5hQ9ipRFu7N73rgoSSJsFlOKKt2uGZzunczedViIfjYl0ii0K4E9aZ0Ow=="], 377 511 378 - "@opentelemetry/propagator-b3": ["@opentelemetry/propagator-b3@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, ""], 512 + "@oven/bun-darwin-x64-baseline": ["@oven/bun-darwin-x64-baseline@1.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ij4wQ9ECLFf1XFry+IFUN+28if40ozDqq6+QtuyOhIwraKzXOlAUbILhRMGvM3ED3yBex2mTwlKpA4Vja/V2g=="], 379 513 380 - "@opentelemetry/propagator-jaeger": ["@opentelemetry/propagator-jaeger@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, ""], 514 + "@oven/bun-linux-aarch64": ["@oven/bun-linux-aarch64@1.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-DabZ3Mt1XcJneWdEEug8l7bCPVvDBRBpjUIpNnRnMFWFnzr8KBEpMcaWTwYOghjXyJdhB4MPKb19MwqyQ+FHAw=="], 381 515 382 - "@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, ""], 516 + "@oven/bun-linux-aarch64-musl": ["@oven/bun-linux-aarch64-musl@1.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-XWQ3tV/gtZj0wn2AdSUq/tEOKWT4OY+Uww70EbODgrrq00jxuTfq5nnYP6rkLD0M/T5BHJdQRSfQYdIni9vldw=="], 383 517 384 - "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, ""], 518 + "@oven/bun-linux-x64": ["@oven/bun-linux-x64@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-7eIARtKZKZDtah1aCpQUj/1/zT/zHRR063J6oAxZP9AuA547j5B9OM2D/vi/F4En7Gjk9FPjgPGTSYeqpQDzJw=="], 385 519 386 - "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, ""], 520 + "@oven/bun-linux-x64-baseline": ["@oven/bun-linux-x64-baseline@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-IU8pxhIf845psOv55LqJyL+tSUc6HHMfs6FGhuJcAnyi92j+B1HjOhnFQh9MW4vjoo7do5F8AerXlvk59RGH2w=="], 387 521 388 - "@opentelemetry/sdk-node": ["@opentelemetry/sdk-node@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/exporter-logs-otlp-grpc": "0.200.0", "@opentelemetry/exporter-logs-otlp-http": "0.200.0", "@opentelemetry/exporter-logs-otlp-proto": "0.200.0", "@opentelemetry/exporter-metrics-otlp-grpc": "0.200.0", "@opentelemetry/exporter-metrics-otlp-http": "0.200.0", "@opentelemetry/exporter-metrics-otlp-proto": "0.200.0", "@opentelemetry/exporter-prometheus": "0.200.0", "@opentelemetry/exporter-trace-otlp-grpc": "0.200.0", "@opentelemetry/exporter-trace-otlp-http": "0.200.0", "@opentelemetry/exporter-trace-otlp-proto": "0.200.0", "@opentelemetry/exporter-zipkin": "2.0.0", "@opentelemetry/instrumentation": "0.200.0", "@opentelemetry/propagator-b3": "2.0.0", "@opentelemetry/propagator-jaeger": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "@opentelemetry/sdk-trace-node": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, ""], 522 + "@oven/bun-linux-x64-musl": ["@oven/bun-linux-x64-musl@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-xNSDRPn1yyObKteS8fyQogwsS4eCECswHHgaKM+/d4wy/omZQrXn8ZyGm/ZF9B73UfQytUfbhE7nEnrFq03f0w=="], 389 523 390 - "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, ""], 524 + "@oven/bun-linux-x64-musl-baseline": ["@oven/bun-linux-x64-musl-baseline@1.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-JoRTPdAXRkNYouUlJqEncMWUKn/3DiWP03A7weBbtbsKr787gcdNna2YeyQKCb1lIXE4v1k18RM3gaOpQobGIQ=="], 391 525 392 - "@opentelemetry/sdk-trace-node": ["@opentelemetry/sdk-trace-node@2.0.0", "", { "dependencies": { "@opentelemetry/context-async-hooks": "2.0.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, ""], 526 + "@oven/bun-windows-x64": ["@oven/bun-windows-x64@1.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-kWqa1LKvDdAIzyfHxo3zGz3HFWbFHDlrNK77hKjUN42ycikvZJ+SHSX76+1OW4G8wmLETX4Jj+4BM1y01DQRIQ=="], 393 527 394 - "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.38.0", "", {}, ""], 528 + "@oven/bun-windows-x64-baseline": ["@oven/bun-windows-x64-baseline@1.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-u5eZHKq6TPJSE282KyBOicGQ2trkFml0RoUfqkPOJVo7TXGrsGYYzdsugZRnVQY/WEmnxGtBy4T3PAaPqgQViA=="], 395 529 396 530 "@parcel/watcher": ["@parcel/watcher@2.5.1", "", { "dependencies": { "detect-libc": "^1.0.3", "is-glob": "^4.0.3", "micromatch": "^4.0.5", "node-addon-api": "^7.0.0" }, "optionalDependencies": { "@parcel/watcher-android-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-freebsd-x64": "2.5.1", "@parcel/watcher-linux-arm-glibc": "2.5.1", "@parcel/watcher-linux-arm-musl": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-musl": "2.5.1", "@parcel/watcher-linux-x64-glibc": "2.5.1", "@parcel/watcher-linux-x64-musl": "2.5.1", "@parcel/watcher-win32-arm64": "2.5.1", "@parcel/watcher-win32-ia32": "2.5.1", "@parcel/watcher-win32-x64": "2.5.1" } }, "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg=="], 397 531 ··· 421 555 422 556 "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], 423 557 424 - "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, ""], 558 + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], 559 + 560 + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], 561 + 562 + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], 563 + 564 + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], 565 + 566 + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], 567 + 568 + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], 569 + 570 + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], 571 + 572 + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], 573 + 574 + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], 575 + 576 + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], 577 + 578 + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], 579 + 580 + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw=="], 581 + 582 + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], 583 + 584 + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], 585 + 586 + "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], 587 + 588 + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], 589 + 590 + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], 591 + 592 + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], 593 + 594 + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], 595 + 596 + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], 597 + 598 + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], 599 + 600 + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="], 601 + 602 + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ=="], 603 + 604 + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], 605 + 606 + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], 607 + 608 + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ=="], 609 + 610 + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], 611 + 612 + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], 613 + 614 + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], 615 + 616 + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg=="], 617 + 618 + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg=="], 619 + 620 + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA=="], 621 + 622 + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g=="], 623 + 624 + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ=="], 625 + 626 + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ=="], 627 + 628 + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ=="], 629 + 630 + "@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="], 631 + 632 + "@smithy/abort-controller": ["@smithy/abort-controller@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw=="], 633 + 634 + "@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA=="], 635 + 636 + "@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.2.1", "", { "dependencies": { "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ=="], 637 + 638 + "@smithy/config-resolver": ["@smithy/config-resolver@4.4.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg=="], 639 + 640 + "@smithy/core": ["@smithy/core@3.20.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.8", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ=="], 641 + 642 + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA=="], 643 + 644 + "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.7", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ=="], 645 + 646 + "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.7", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-ujzPk8seYoDBmABDE5YqlhQZAXLOrtxtJLrbhHMKjBoG5b4dK4i6/mEU+6/7yXIAkqOO8sJ6YxZl+h0QQ1IJ7g=="], 647 + 648 + "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-x7BtAiIPSaNaWuzm24Q/mtSkv+BrISO/fmheiJ39PKRNH3RmH2Hph/bUKSOBOBC9unqfIYDhKTHwpyZycLGPVQ=="], 649 + 650 + "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.7", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-roySCtHC5+pQq5lK4be1fZ/WR6s/AxnPaLfCODIPArtN2du8s5Ot4mKVK3pPtijL/L654ws592JHJ1PbZFF6+A=="], 651 + 652 + "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.7", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-QVD+g3+icFkThoy4r8wVFZMsIP08taHVKjE6Jpmz8h5CgX/kk6pTODq5cht0OMtcapUx+xrPzUTQdA+TmO0m1g=="], 653 + 654 + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg=="], 655 + 656 + "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.2.8", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.2.0", "@smithy/chunked-blob-reader-native": "^4.2.1", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-07InZontqsM1ggTCPSRgI7d8DirqRrnpL7nIACT4PW0AWrgDiHhjGZzbAE5UtRSiU0NISGUYe7/rri9ZeWyDpw=="], 425 657 426 - "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, ""], 658 + "@smithy/hash-node": ["@smithy/hash-node@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw=="], 427 659 428 - "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, ""], 660 + "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZQVoAwNYnFMIbd4DUc517HuwNelJUY6YOzwqrbcAgCnVn+79/OK7UjwA93SPpdTOpKDVkLIzavWm/Ck7SmnDPQ=="], 429 661 430 - "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, ""], 662 + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ=="], 431 663 432 - "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, ""], 664 + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="], 433 665 434 - "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, ""], 666 + "@smithy/md5-js": ["@smithy/md5-js@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Wv6JcUxtOLTnxvNjDnAiATUsk8gvA6EeS8zzHig07dotpByYsLot+m0AaQEniUBjx97AC41MQR4hW0baraD1Xw=="], 435 667 436 - "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, ""], 668 + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg=="], 437 669 438 - "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, ""], 670 + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.1", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-serde": "^4.2.8", "@smithy/node-config-provider": "^4.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg=="], 439 671 440 - "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, ""], 672 + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.17", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/service-error-classification": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg=="], 441 673 442 - "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, ""], 674 + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w=="], 443 675 444 - "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, ""], 676 + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw=="], 445 677 446 - "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.3.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 678 + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.7", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw=="], 447 679 448 - "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 680 + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.7", "", { "dependencies": { "@smithy/abort-controller": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ=="], 449 681 450 - "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 682 + "@smithy/property-provider": ["@smithy/property-provider@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="], 451 683 452 - "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 684 + "@smithy/protocol-http": ["@smithy/protocol-http@5.3.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA=="], 453 685 454 - "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 686 + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg=="], 455 687 456 - "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 688 + "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w=="], 457 689 458 - "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 690 + "@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0" } }, "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA=="], 459 691 460 - "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 692 + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.2", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg=="], 461 693 462 - "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 694 + "@smithy/signature-v4": ["@smithy/signature-v4@5.3.7", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg=="], 463 695 464 - "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 696 + "@smithy/smithy-client": ["@smithy/smithy-client@4.10.2", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-stack": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g=="], 465 697 466 - "@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 698 + "@smithy/types": ["@smithy/types@4.11.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA=="], 467 699 468 - "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.9", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 700 + "@smithy/url-parser": ["@smithy/url-parser@4.2.7", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg=="], 469 701 470 - "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 702 + "@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="], 471 703 472 - "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 704 + "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="], 473 705 474 - "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.3.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 706 + "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA=="], 475 707 476 - "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 708 + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="], 477 709 478 - "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 710 + "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="], 479 711 480 - "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 712 + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.16", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ=="], 481 713 482 - "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 714 + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.19", "", { "dependencies": { "@smithy/config-resolver": "^4.4.5", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA=="], 483 715 484 - "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.2.2", "", { "dependencies": { "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 716 + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg=="], 485 717 486 - "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.2", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 718 + "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="], 487 719 488 - "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.1", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 720 + "@smithy/util-middleware": ["@smithy/util-middleware@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w=="], 489 721 490 - "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 722 + "@smithy/util-retry": ["@smithy/util-retry@4.2.7", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg=="], 491 723 492 - "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 724 + "@smithy/util-stream": ["@smithy/util-stream@4.5.8", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="], 493 725 494 - "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 726 + "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="], 495 727 496 - "@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, ""], 728 + "@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="], 729 + 730 + "@smithy/util-waiter": ["@smithy/util-waiter@4.2.7", "", { "dependencies": { "@smithy/abort-controller": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw=="], 731 + 732 + "@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="], 497 733 498 734 "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], 499 735 ··· 527 763 528 764 "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.17", "", { "os": "win32", "cpu": "x64" }, "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw=="], 529 765 530 - "@tanstack/query-core": ["@tanstack/query-core@5.90.7", "", {}, ""], 766 + "@tanstack/query-core": ["@tanstack/query-core@5.90.12", "", {}, "sha512-T1/8t5DhV/SisWjDnaiU2drl6ySvsHj1bHBCWNXd+/T+Hh1cf6JodyEYMd5sgwm+b/mETT4EV3H+zCVczCU5hg=="], 531 767 532 - "@tanstack/react-query": ["@tanstack/react-query@5.90.7", "", { "dependencies": { "@tanstack/query-core": "5.90.7" }, "peerDependencies": { "react": "^18 || ^19" } }, ""], 768 + "@tanstack/react-query": ["@tanstack/react-query@5.90.12", "", { "dependencies": { "@tanstack/query-core": "5.90.12" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-graRZspg7EoEaw0a8faiUASCyJrqjKPdqJ9EwuDRUF9mEYJ1YPczI9H+/agJ0mOJkPCJDk0lsz5QTrLZ/jQ2rg=="], 533 769 534 - "@tokenizer/inflate": ["@tokenizer/inflate@0.2.7", "", { "dependencies": { "debug": "^4.4.0", "fflate": "^0.8.2", "token-types": "^6.0.0" } }, ""], 770 + "@tokenizer/inflate": ["@tokenizer/inflate@0.4.1", "", { "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" } }, "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA=="], 535 771 536 - "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, ""], 772 + "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="], 537 773 538 - "@ts-morph/common": ["@ts-morph/common@0.25.0", "", { "dependencies": { "minimatch": "^9.0.4", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.9" } }, ""], 774 + "@ts-morph/common": ["@ts-morph/common@0.25.0", "", { "dependencies": { "minimatch": "^9.0.4", "path-browserify": "^1.0.1", "tinyglobby": "^0.2.9" } }, "sha512-kMnZz+vGGHi4GoHnLmMhGNjm44kGtKUXGnOvrKmMwAuvNjM/PgKVGfUnL7IDvK7Jb2QQ82jq3Zmp04Gy+r3Dkg=="], 539 775 540 - "@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="], 776 + "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="], 541 777 542 778 "@types/mime-types": ["@types/mime-types@2.1.4", "", {}, "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w=="], 543 779 544 780 "@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], 545 781 546 - "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, ""], 782 + "@types/react": ["@types/react@19.2.7", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg=="], 547 783 548 - "@types/react-dom": ["@types/react-dom@19.2.2", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, ""], 784 + "@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="], 549 785 550 - "@types/shimmer": ["@types/shimmer@1.2.0", "", {}, ""], 786 + "@types/shimmer": ["@types/shimmer@1.2.0", "", {}, "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg=="], 551 787 552 788 "@wisp/atproto-utils": ["@wisp/atproto-utils@workspace:packages/@wisp/atproto-utils"], 553 789 ··· 565 801 566 802 "@wisp/safe-fetch": ["@wisp/safe-fetch@workspace:packages/@wisp/safe-fetch"], 567 803 568 - "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, ""], 804 + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], 569 805 570 - "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, ""], 806 + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], 571 807 572 - "acorn": ["acorn@8.15.0", "", { "bin": "bin/acorn" }, ""], 808 + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], 573 809 574 - "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, ""], 810 + "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="], 575 811 576 812 "actor-typeahead": ["actor-typeahead@0.1.2", "", {}, "sha512-I97YqqNl7Kar0J/bIJvgY/KmHpssHcDElhfwVTLP7wRFlkxso2ZLBqiS2zol5A8UVUJbQK2JXYaqNpZXz8Uk2A=="], 577 813 578 - "ansi-regex": ["ansi-regex@5.0.1", "", {}, ""], 814 + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], 579 815 580 - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, ""], 816 + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], 581 817 582 - "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, ""], 818 + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], 583 819 584 - "array-flatten": ["array-flatten@1.1.1", "", {}, ""], 820 + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], 585 821 586 - "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, ""], 822 + "atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="], 587 823 588 824 "atproto-ui": ["atproto-ui@0.12.0", "", { "dependencies": { "@atcute/atproto": "^3.1.7", "@atcute/bluesky": "^3.2.3", "@atcute/client": "^4.0.3", "@atcute/identity-resolver": "^1.1.3", "@atcute/tangled": "^1.0.10" }, "peerDependencies": { "react": "^18.2.0 || ^19.0.0", "react-dom": "^18.2.0 || ^19.0.0" }, "optionalPeers": ["react-dom"] }, "sha512-vdJmKNyuGWspuIIvySD601dL8wLJafgxfS/6NGBvbBFectoiaZ92Cua2JdDuSD/uRxUnRJ3AvMg7eL0M39DZ3Q=="], 589 825 590 - "await-lock": ["await-lock@2.2.2", "", {}, ""], 826 + "await-lock": ["await-lock@2.2.2", "", {}, "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw=="], 591 827 592 - "balanced-match": ["balanced-match@1.0.2", "", {}, ""], 828 + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], 829 + 830 + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], 593 831 594 - "base64-js": ["base64-js@1.5.1", "", {}, ""], 832 + "body-parser": ["body-parser@1.20.4", "", { "dependencies": { "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "~1.2.0", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "on-finished": "~2.4.1", "qs": "~6.14.0", "raw-body": "~2.5.3", "type-is": "~1.6.18", "unpipe": "~1.0.0" } }, "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA=="], 595 833 596 - "body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, ""], 834 + "bowser": ["bowser@2.13.1", "", {}, "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw=="], 597 835 598 - "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, ""], 836 + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], 599 837 600 838 "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], 601 839 602 - "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, ""], 840 + "buffer": ["buffer@5.6.0", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" } }, "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw=="], 603 841 604 - "bun": ["bun@1.3.2", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bunx.exe" } }, ""], 842 + "bun": ["bun@1.3.3", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.3.3", "@oven/bun-darwin-x64": "1.3.3", "@oven/bun-darwin-x64-baseline": "1.3.3", "@oven/bun-linux-aarch64": "1.3.3", "@oven/bun-linux-aarch64-musl": "1.3.3", "@oven/bun-linux-x64": "1.3.3", "@oven/bun-linux-x64-baseline": "1.3.3", "@oven/bun-linux-x64-musl": "1.3.3", "@oven/bun-linux-x64-musl-baseline": "1.3.3", "@oven/bun-windows-x64": "1.3.3", "@oven/bun-windows-x64-baseline": "1.3.3" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bunx.exe" } }, "sha512-2hJ4ocTZ634/Ptph4lysvO+LbbRZq8fzRvMwX0/CqaLBxrF2UB5D1LdMB8qGcdtCer4/VR9Bx5ORub0yn+yzmw=="], 605 843 606 - "bun-plugin-tailwind": ["bun-plugin-tailwind@0.1.2", "", { "peerDependencies": { "bun": ">=1.0.0" } }, ""], 844 + "bun-plugin-tailwind": ["bun-plugin-tailwind@0.1.2", "", { "peerDependencies": { "bun": ">=1.0.0" } }, "sha512-41jNC1tZRSK3s1o7pTNrLuQG8kL/0vR/JgiTmZAJ1eHwe0w5j6HFPKeqEk0WAD13jfrUC7+ULuewFBBCoADPpg=="], 607 845 608 - "bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="], 846 + "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], 609 847 610 - "bytes": ["bytes@3.1.2", "", {}, ""], 848 + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], 611 849 612 - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, ""], 850 + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], 613 851 614 - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, ""], 852 + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], 615 853 616 - "cbor-extract": ["cbor-extract@2.2.0", "", { "dependencies": { "node-gyp-build-optional-packages": "5.1.1" }, "optionalDependencies": { "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0" }, "bin": { "download-cbor-prebuilds": "bin/download-prebuilds.js" } }, ""], 854 + "cbor-extract": ["cbor-extract@2.2.0", "", { "dependencies": { "node-gyp-build-optional-packages": "5.1.1" }, "optionalDependencies": { "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0", "@cbor-extract/cbor-extract-darwin-x64": "2.2.0", "@cbor-extract/cbor-extract-linux-arm": "2.2.0", "@cbor-extract/cbor-extract-linux-arm64": "2.2.0", "@cbor-extract/cbor-extract-linux-x64": "2.2.0", "@cbor-extract/cbor-extract-win32-x64": "2.2.0" }, "bin": { "download-cbor-prebuilds": "bin/download-prebuilds.js" } }, "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA=="], 617 855 618 - "cbor-x": ["cbor-x@1.6.0", "", { "optionalDependencies": { "cbor-extract": "^2.2.0" } }, ""], 856 + "cbor-x": ["cbor-x@1.6.0", "", { "optionalDependencies": { "cbor-extract": "^2.2.0" } }, "sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg=="], 619 857 620 - "cborg": ["cborg@1.10.2", "", { "bin": "cli.js" }, ""], 858 + "cborg": ["cborg@1.10.2", "", { "bin": { "cborg": "cli.js" } }, "sha512-b3tFPA9pUr2zCUiCfRd2+wok2/LBSNUMKOuRRok+WlvvAgEt/PlbgPTsZUcwCOs53IJvLgTp0eotwtosE6njug=="], 621 859 622 - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, ""], 860 + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], 623 861 624 - "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, ""], 862 + "cjs-module-lexer": ["cjs-module-lexer@1.4.3", "", {}, "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q=="], 625 863 626 - "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, ""], 864 + "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], 627 865 628 - "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, ""], 866 + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], 629 867 630 - "clsx": ["clsx@2.1.1", "", {}, ""], 868 + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], 631 869 632 - "code-block-writer": ["code-block-writer@13.0.3", "", {}, ""], 870 + "code-block-writer": ["code-block-writer@13.0.3", "", {}, "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg=="], 633 871 634 - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, ""], 872 + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], 635 873 636 - "color-name": ["color-name@1.1.4", "", {}, ""], 874 + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], 637 875 638 - "commander": ["commander@9.5.0", "", {}, ""], 876 + "commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="], 639 877 640 - "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, ""], 878 + "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], 641 879 642 - "content-type": ["content-type@1.0.5", "", {}, ""], 880 + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], 643 881 644 882 "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], 645 883 646 - "cookie-signature": ["cookie-signature@1.0.6", "", {}, ""], 884 + "cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="], 647 885 648 - "core-js": ["core-js@3.46.0", "", {}, ""], 886 + "core-js": ["core-js@3.47.0", "", {}, "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg=="], 649 887 650 - "csstype": ["csstype@3.1.3", "", {}, ""], 888 + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], 651 889 652 - "debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, ""], 890 + "debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 653 891 654 - "depd": ["depd@2.0.0", "", {}, ""], 892 + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], 655 893 656 - "destroy": ["destroy@1.2.0", "", {}, ""], 894 + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], 657 895 658 896 "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], 659 897 660 - "detect-node-es": ["detect-node-es@1.1.0", "", {}, ""], 898 + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], 661 899 662 - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, ""], 900 + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], 663 901 664 - "ee-first": ["ee-first@1.1.1", "", {}, ""], 902 + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], 665 903 666 904 "elysia": ["elysia@1.4.18", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "0.2.5", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-A6BhlipmSvgCy69SBgWADYZSdDIj3fT2gk8/9iMAC8iD+aGcnCr0fitziX0xr36MFDs/fsvVp8dWqxeq1VCgKg=="], 667 905 668 - "emoji-regex": ["emoji-regex@8.0.0", "", {}, ""], 906 + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], 669 907 670 - "encodeurl": ["encodeurl@2.0.0", "", {}, ""], 908 + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], 671 909 672 910 "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], 673 911 674 - "es-define-property": ["es-define-property@1.0.1", "", {}, ""], 912 + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], 675 913 676 - "es-errors": ["es-errors@1.3.0", "", {}, ""], 914 + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], 677 915 678 - "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, ""], 916 + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], 679 917 680 - "esbuild": ["esbuild@0.26.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.26.0", "@esbuild/android-arm": "0.26.0", "@esbuild/android-arm64": "0.26.0", "@esbuild/android-x64": "0.26.0", "@esbuild/darwin-arm64": "0.26.0", "@esbuild/darwin-x64": "0.26.0", "@esbuild/freebsd-arm64": "0.26.0", "@esbuild/freebsd-x64": "0.26.0", "@esbuild/linux-arm": "0.26.0", "@esbuild/linux-arm64": "0.26.0", "@esbuild/linux-ia32": "0.26.0", "@esbuild/linux-loong64": "0.26.0", "@esbuild/linux-mips64el": "0.26.0", "@esbuild/linux-ppc64": "0.26.0", "@esbuild/linux-riscv64": "0.26.0", "@esbuild/linux-s390x": "0.26.0", "@esbuild/linux-x64": "0.26.0", "@esbuild/netbsd-arm64": "0.26.0", "@esbuild/netbsd-x64": "0.26.0", "@esbuild/openbsd-arm64": "0.26.0", "@esbuild/openbsd-x64": "0.26.0", "@esbuild/openharmony-arm64": "0.26.0", "@esbuild/sunos-x64": "0.26.0", "@esbuild/win32-arm64": "0.26.0", "@esbuild/win32-ia32": "0.26.0", "@esbuild/win32-x64": "0.26.0" }, "bin": "bin/esbuild" }, "sha512-3Hq7jri+tRrVWha+ZeIVhl4qJRha/XjRNSopvTsOaCvfPHrflTYTcUFcEjMKdxofsXXsdc4zjg5NOTnL4Gl57Q=="], 918 + "esbuild": ["esbuild@0.26.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.26.0", "@esbuild/android-arm": "0.26.0", "@esbuild/android-arm64": "0.26.0", "@esbuild/android-x64": "0.26.0", "@esbuild/darwin-arm64": "0.26.0", "@esbuild/darwin-x64": "0.26.0", "@esbuild/freebsd-arm64": "0.26.0", "@esbuild/freebsd-x64": "0.26.0", "@esbuild/linux-arm": "0.26.0", "@esbuild/linux-arm64": "0.26.0", "@esbuild/linux-ia32": "0.26.0", "@esbuild/linux-loong64": "0.26.0", "@esbuild/linux-mips64el": "0.26.0", "@esbuild/linux-ppc64": "0.26.0", "@esbuild/linux-riscv64": "0.26.0", "@esbuild/linux-s390x": "0.26.0", "@esbuild/linux-x64": "0.26.0", "@esbuild/netbsd-arm64": "0.26.0", "@esbuild/netbsd-x64": "0.26.0", "@esbuild/openbsd-arm64": "0.26.0", "@esbuild/openbsd-x64": "0.26.0", "@esbuild/openharmony-arm64": "0.26.0", "@esbuild/sunos-x64": "0.26.0", "@esbuild/win32-arm64": "0.26.0", "@esbuild/win32-ia32": "0.26.0", "@esbuild/win32-x64": "0.26.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-3Hq7jri+tRrVWha+ZeIVhl4qJRha/XjRNSopvTsOaCvfPHrflTYTcUFcEjMKdxofsXXsdc4zjg5NOTnL4Gl57Q=="], 681 919 682 - "escalade": ["escalade@3.2.0", "", {}, ""], 920 + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], 683 921 684 - "escape-html": ["escape-html@1.0.3", "", {}, ""], 922 + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], 685 923 686 924 "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], 687 925 688 - "etag": ["etag@1.8.1", "", {}, ""], 926 + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], 689 927 690 - "event-target-shim": ["event-target-shim@5.0.1", "", {}, ""], 928 + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], 691 929 692 930 "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], 693 931 694 - "events": ["events@3.3.0", "", {}, ""], 932 + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], 695 933 696 934 "exact-mirror": ["exact-mirror@0.2.5", "", { "peerDependencies": { "@sinclair/typebox": "^0.34.15" }, "optionalPeers": ["@sinclair/typebox"] }, "sha512-u8Wu2lO8nio5lKSJubOydsdNtQmH8ENba5m0nbQYmTvsjksXKYIS1nSShdDlO8Uem+kbo+N6eD5I03cpZ+QsRQ=="], 697 935 698 - "express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, ""], 936 + "express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="], 699 937 700 - "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, ""], 938 + "fast-decode-uri-component": ["fast-decode-uri-component@1.0.1", "", {}, "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg=="], 701 939 702 - "fast-redact": ["fast-redact@3.5.0", "", {}, ""], 940 + "fast-redact": ["fast-redact@3.5.0", "", {}, "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="], 703 941 704 - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, ""], 942 + "fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], 705 943 706 - "fflate": ["fflate@0.8.2", "", {}, ""], 944 + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], 707 945 708 - "file-type": ["file-type@21.0.0", "", { "dependencies": { "@tokenizer/inflate": "^0.2.7", "strtok3": "^10.2.2", "token-types": "^6.0.0", "uint8array-extras": "^1.4.0" } }, ""], 946 + "file-type": ["file-type@21.1.1", "", { "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" } }, "sha512-ifJXo8zUqbQ/bLbl9sFoqHNTNWbnPY1COImFfM6CCy7z+E+jC1eY9YfOKkx0fckIg+VljAy2/87T61fp0+eEkg=="], 709 947 710 948 "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], 711 949 712 - "finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, ""], 950 + "finalhandler": ["finalhandler@1.3.2", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "statuses": "~2.0.2", "unpipe": "~1.0.0" } }, "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg=="], 713 951 714 - "forwarded": ["forwarded@0.2.0", "", {}, ""], 952 + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], 715 953 716 - "fresh": ["fresh@0.5.2", "", {}, ""], 954 + "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], 717 955 718 956 "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], 719 957 720 - "function-bind": ["function-bind@1.1.2", "", {}, ""], 958 + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], 721 959 722 - "get-caller-file": ["get-caller-file@2.0.5", "", {}, ""], 960 + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], 723 961 724 - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, ""], 962 + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], 725 963 726 - "get-nonce": ["get-nonce@1.0.1", "", {}, ""], 964 + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], 727 965 728 - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, ""], 966 + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], 729 967 730 968 "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], 731 969 732 - "gopd": ["gopd@1.2.0", "", {}, ""], 970 + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], 733 971 734 972 "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], 735 973 736 - "graphemer": ["graphemer@1.4.0", "", {}, ""], 974 + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], 737 975 738 - "has-flag": ["has-flag@4.0.0", "", {}, ""], 976 + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], 739 977 740 - "has-symbols": ["has-symbols@1.1.0", "", {}, ""], 741 - 742 - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, ""], 978 + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 743 979 744 980 "hono": ["hono@4.10.7", "", {}, "sha512-icXIITfw/07Q88nLSkB9aiUrd8rYzSweK681Kjo/TSggaGbOX4RRyxxm71v+3PC8C/j+4rlxGeoTRxQDkaJkUw=="], 745 981 746 - "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, ""], 982 + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], 747 983 748 - "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, ""], 984 + "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], 749 985 750 - "ieee754": ["ieee754@1.2.1", "", {}, ""], 986 + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], 751 987 752 988 "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], 753 989 754 - "import-in-the-middle": ["import-in-the-middle@1.15.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } }, ""], 990 + "import-in-the-middle": ["import-in-the-middle@1.15.0", "", { "dependencies": { "acorn": "^8.14.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } }, "sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA=="], 755 991 756 - "inherits": ["inherits@2.0.4", "", {}, ""], 992 + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], 757 993 758 - "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, ""], 994 + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], 759 995 760 - "iron-session": ["iron-session@8.0.4", "", { "dependencies": { "cookie": "^0.7.2", "iron-webcrypto": "^1.2.1", "uncrypto": "^0.1.3" } }, ""], 996 + "iron-session": ["iron-session@8.0.4", "", { "dependencies": { "cookie": "^0.7.2", "iron-webcrypto": "^1.2.1", "uncrypto": "^0.1.3" } }, "sha512-9ivNnaKOd08osD0lJ3i6If23GFS2LsxyMU8Gf/uBUEgm8/8CC1hrrCHFDpMo3IFbpBgwoo/eairRsaD3c5itxA=="], 761 997 762 - "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, ""], 998 + "iron-webcrypto": ["iron-webcrypto@1.2.1", "", {}, "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg=="], 763 999 764 - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, ""], 1000 + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], 765 1001 766 1002 "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], 767 1003 768 - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, ""], 1004 + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], 769 1005 770 1006 "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], 771 1007 772 1008 "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], 773 1009 774 - "iso-datestring-validator": ["iso-datestring-validator@2.2.2", "", {}, ""], 1010 + "iso-datestring-validator": ["iso-datestring-validator@2.2.2", "", {}, "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA=="], 775 1011 776 1012 "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], 777 1013 778 - "jose": ["jose@5.10.0", "", {}, ""], 1014 + "jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], 779 1015 780 1016 "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], 781 1017 ··· 801 1037 802 1038 "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.2", "", { "os": "win32", "cpu": "x64" }, "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw=="], 803 1039 804 - "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, ""], 1040 + "lodash.camelcase": ["lodash.camelcase@4.3.0", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], 805 1041 806 - "long": ["long@5.3.2", "", {}, ""], 1042 + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], 807 1043 808 - "lru-cache": ["lru-cache@10.4.3", "", {}, ""], 1044 + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], 809 1045 810 - "lucide-react": ["lucide-react@0.546.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, ""], 1046 + "lucide-react": ["lucide-react@0.546.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ=="], 811 1047 812 1048 "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], 813 1049 814 - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, ""], 1050 + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], 815 1051 816 - "media-typer": ["media-typer@0.3.0", "", {}, ""], 1052 + "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], 817 1053 818 - "memoirist": ["memoirist@0.4.0", "", {}, ""], 1054 + "memoirist": ["memoirist@0.4.0", "", {}, "sha512-zxTgA0mSYELa66DimuNQDvyLq36AwDlTuVRbnQtB+VuTcKWm5Qc4z3WkSpgsFWHNhexqkIooqpv4hdcqrX5Nmg=="], 819 1055 820 - "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, ""], 1056 + "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], 821 1057 822 - "methods": ["methods@1.1.2", "", {}, ""], 1058 + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], 823 1059 824 1060 "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], 825 1061 826 - "mime": ["mime@1.6.0", "", { "bin": "cli.js" }, ""], 1062 + "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], 827 1063 828 - "mime-db": ["mime-db@1.52.0", "", {}, ""], 1064 + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], 829 1065 830 - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, ""], 1066 + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], 831 1067 832 - "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, ""], 1068 + "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], 833 1069 834 - "module-details-from-path": ["module-details-from-path@1.0.4", "", {}, ""], 1070 + "module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="], 835 1071 836 1072 "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], 837 1073 838 - "ms": ["ms@2.0.0", "", {}, ""], 1074 + "ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 839 1075 840 - "multiformats": ["multiformats@13.4.1", "", {}, ""], 1076 + "multiformats": ["multiformats@13.4.1", "", {}, "sha512-VqO6OSvLrFVAYYjgsr8tyv62/rCQhPgsZUXLTqoFLSgdkgiUYKYeArbt1uWLlEpkjxQe+P0+sHlbPEte1Bi06Q=="], 841 1077 842 - "negotiator": ["negotiator@0.6.3", "", {}, ""], 1078 + "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], 843 1079 844 1080 "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], 845 1081 846 - "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.1.1", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, ""], 1082 + "node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.1.1", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-test": "build-test.js", "node-gyp-build-optional-packages-optional": "optional.js" } }, "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw=="], 847 1083 848 - "object-inspect": ["object-inspect@1.13.4", "", {}, ""], 1084 + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], 849 1085 850 - "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, ""], 1086 + "on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="], 851 1087 852 - "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, ""], 1088 + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], 853 1089 854 - "openapi-types": ["openapi-types@12.1.3", "", {}, ""], 1090 + "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], 855 1091 856 1092 "p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="], 857 1093 ··· 859 1095 860 1096 "p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="], 861 1097 862 - "parseurl": ["parseurl@1.3.3", "", {}, ""], 1098 + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], 863 1099 864 - "path-browserify": ["path-browserify@1.0.1", "", {}, ""], 1100 + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], 865 1101 866 - "path-parse": ["path-parse@1.0.7", "", {}, ""], 1102 + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], 867 1103 868 - "path-to-regexp": ["path-to-regexp@0.1.12", "", {}, ""], 1104 + "path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="], 869 1105 870 1106 "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], 871 1107 872 1108 "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], 873 1109 874 - "pino": ["pino@8.21.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^1.2.0", "pino-std-serializers": "^6.0.0", "process-warning": "^3.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^3.7.0", "thread-stream": "^2.6.0" }, "bin": "bin.js" }, ""], 1110 + "pino": ["pino@8.21.0", "", { "dependencies": { "atomic-sleep": "^1.0.0", "fast-redact": "^3.1.1", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^1.2.0", "pino-std-serializers": "^6.0.0", "process-warning": "^3.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^3.7.0", "thread-stream": "^2.6.0" }, "bin": { "pino": "bin.js" } }, "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q=="], 875 1111 876 - "pino-abstract-transport": ["pino-abstract-transport@1.2.0", "", { "dependencies": { "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, ""], 1112 + "pino-abstract-transport": ["pino-abstract-transport@1.2.0", "", { "dependencies": { "readable-stream": "^4.0.0", "split2": "^4.0.0" } }, "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q=="], 877 1113 878 - "pino-std-serializers": ["pino-std-serializers@6.2.2", "", {}, ""], 1114 + "pino-std-serializers": ["pino-std-serializers@6.2.2", "", {}, "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA=="], 879 1115 880 1116 "playwright": ["playwright@1.57.0", "", { "dependencies": { "playwright-core": "1.57.0" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw=="], 881 1117 ··· 883 1119 884 1120 "postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="], 885 1121 886 - "prettier": ["prettier@3.6.2", "", { "bin": "bin/prettier.cjs" }, ""], 1122 + "prettier": ["prettier@3.7.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA=="], 887 1123 888 1124 "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], 889 1125 890 - "process": ["process@0.11.10", "", {}, ""], 1126 + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], 891 1127 892 - "process-warning": ["process-warning@3.0.0", "", {}, ""], 1128 + "process-warning": ["process-warning@3.0.0", "", {}, "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ=="], 893 1129 894 - "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, ""], 1130 + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], 895 1131 896 - "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, ""], 1132 + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], 897 1133 898 - "qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, ""], 1134 + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], 899 1135 900 - "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, ""], 1136 + "quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="], 901 1137 902 - "range-parser": ["range-parser@1.2.1", "", {}, ""], 1138 + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], 903 1139 904 - "rate-limiter-flexible": ["rate-limiter-flexible@2.4.2", "", {}, ""], 1140 + "rate-limiter-flexible": ["rate-limiter-flexible@2.4.2", "", {}, "sha512-rMATGGOdO1suFyf/mI5LYhts71g1sbdhmd6YvdiXO2gJnd42Tt6QS4JUKJKSWVVkMtBacm6l40FR7Trjo6Iruw=="], 905 1141 906 - "raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, ""], 1142 + "raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="], 907 1143 908 - "react": ["react@19.2.0", "", {}, ""], 1144 + "react": ["react@19.2.1", "", {}, "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw=="], 909 1145 910 - "react-dom": ["react-dom@19.2.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, ""], 1146 + "react-dom": ["react-dom@19.2.1", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.1" } }, "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg=="], 911 1147 912 - "react-remove-scroll": ["react-remove-scroll@2.7.1", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, ""], 1148 + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], 913 1149 914 - "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, ""], 1150 + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], 915 1151 916 - "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, ""], 1152 + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], 917 1153 918 - "readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, ""], 1154 + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], 919 1155 920 - "real-require": ["real-require@0.2.0", "", {}, ""], 1156 + "real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="], 921 1157 922 - "require-directory": ["require-directory@2.1.1", "", {}, ""], 1158 + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], 923 1159 924 - "require-in-the-middle": ["require-in-the-middle@7.5.2", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3", "resolve": "^1.22.8" } }, ""], 1160 + "require-in-the-middle": ["require-in-the-middle@7.5.2", "", { "dependencies": { "debug": "^4.3.5", "module-details-from-path": "^1.0.3", "resolve": "^1.22.8" } }, "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ=="], 925 1161 926 - "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": "bin/resolve" }, ""], 1162 + "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=="], 927 1163 928 1164 "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], 929 1165 930 - "safe-buffer": ["safe-buffer@5.2.1", "", {}, ""], 1166 + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], 931 1167 932 - "safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, ""], 1168 + "safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="], 933 1169 934 - "safer-buffer": ["safer-buffer@2.1.2", "", {}, ""], 1170 + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], 935 1171 936 - "scheduler": ["scheduler@0.27.0", "", {}, ""], 1172 + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], 937 1173 938 - "send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, ""], 1174 + "send": ["send@0.19.1", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg=="], 939 1175 940 - "serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, ""], 1176 + "serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="], 941 1177 942 - "setprototypeof": ["setprototypeof@1.2.0", "", {}, ""], 1178 + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], 943 1179 944 - "shimmer": ["shimmer@1.2.1", "", {}, ""], 1180 + "shimmer": ["shimmer@1.2.1", "", {}, "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw=="], 945 1181 946 - "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, ""], 1182 + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], 947 1183 948 - "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, ""], 1184 + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], 949 1185 950 - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, ""], 1186 + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], 951 1187 952 - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, ""], 1188 + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], 953 1189 954 - "sonic-boom": ["sonic-boom@3.8.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, ""], 1190 + "sonic-boom": ["sonic-boom@3.8.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg=="], 955 1191 956 1192 "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], 957 1193 958 - "split2": ["split2@4.2.0", "", {}, ""], 1194 + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], 959 1195 960 - "statuses": ["statuses@2.0.1", "", {}, ""], 1196 + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], 961 1197 962 - "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, ""], 1198 + "stream-browserify": ["stream-browserify@3.0.0", "", { "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" } }, "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA=="], 963 1199 964 - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, ""], 1200 + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], 965 1201 966 - "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, ""], 1202 + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], 967 1203 968 - "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, ""], 1204 + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], 969 1205 970 - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, ""], 1206 + "strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="], 971 1207 972 - "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, ""], 1208 + "strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="], 973 1209 974 - "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, ""], 1210 + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], 1211 + 1212 + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], 1213 + 1214 + "tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], 975 1215 976 - "tailwindcss": ["tailwindcss@4.1.17", "", {}, ""], 1216 + "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], 977 1217 978 1218 "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], 979 1219 980 - "thread-stream": ["thread-stream@2.7.0", "", { "dependencies": { "real-require": "^0.2.0" } }, ""], 1220 + "thread-stream": ["thread-stream@2.7.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw=="], 1221 + 1222 + "tiered-storage": ["tiered-storage@1.0.3", "", { "dependencies": { "@aws-sdk/client-s3": "^3.500.0", "@aws-sdk/lib-storage": "^3.500.0", "hono": "^4.10.7", "mime-types": "^3.0.2", "tiny-lru": "^11.0.0" } }, "sha512-ntJCsWWYHSQWIDbObMfnJ8xw7cRSJCPzfbrjoY4hu0YIRizCthRWg8kCvJOgsgC+Upt259YHmfNwEr1wLv6Rxw=="], 981 1223 982 - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, ""], 1224 + "tiny-lru": ["tiny-lru@11.4.5", "", {}, "sha512-hkcz3FjNJfKXjV4mjQ1OrXSLAehg8Hw+cEZclOVT+5c/cWQWImQ9wolzTjth+dmmDe++p3bme3fTxz6Q4Etsqw=="], 1225 + 1226 + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], 983 1227 984 - "tlds": ["tlds@1.261.0", "", { "bin": "bin.js" }, ""], 1228 + "tlds": ["tlds@1.261.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA=="], 985 1229 986 1230 "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], 987 1231 988 - "toidentifier": ["toidentifier@1.0.1", "", {}, ""], 1232 + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], 989 1233 990 - "token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, ""], 1234 + "token-types": ["token-types@6.1.1", "", { "dependencies": { "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ=="], 991 1235 992 - "ts-morph": ["ts-morph@24.0.0", "", { "dependencies": { "@ts-morph/common": "~0.25.0", "code-block-writer": "^13.0.3" } }, ""], 1236 + "ts-morph": ["ts-morph@24.0.0", "", { "dependencies": { "@ts-morph/common": "~0.25.0", "code-block-writer": "^13.0.3" } }, "sha512-2OAOg/Ob5yx9Et7ZX4CvTCc0UFoZHwLEJ+dpDPSUi5TgwwlTlX47w+iFRrEwzUZwYACjq83cgjS/Da50Ga37uw=="], 993 1237 994 - "tslib": ["tslib@2.8.1", "", {}, ""], 1238 + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 995 1239 996 1240 "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], 997 1241 998 - "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, ""], 1242 + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], 999 1243 1000 - "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, ""], 1244 + "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], 1001 1245 1002 - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, ""], 1246 + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], 1003 1247 1004 - "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, ""], 1248 + "uint8array-extras": ["uint8array-extras@1.5.0", "", {}, "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A=="], 1005 1249 1006 - "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, ""], 1250 + "uint8arrays": ["uint8arrays@3.0.0", "", { "dependencies": { "multiformats": "^9.4.2" } }, "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA=="], 1007 1251 1008 - "uncrypto": ["uncrypto@0.1.3", "", {}, ""], 1252 + "uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="], 1009 1253 1010 - "undici": ["undici@6.22.0", "", {}, ""], 1254 + "undici": ["undici@6.22.0", "", {}, "sha512-hU/10obOIu62MGYjdskASR3CUAiYaFTtC9Pa6vHyf//mAipSvSQg6od2CnJswq7fvzNS3zJhxoRkgNVaHurWKw=="], 1011 1255 1012 1256 "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], 1013 1257 1014 1258 "unicode-segmenter": ["unicode-segmenter@0.14.0", "", {}, "sha512-AH4lhPCJANUnSLEKnM4byboctePJzltF4xj8b+NbNiYeAkAXGh7px2K/4NANFp7dnr6+zB3e6HLu8Jj8SKyvYg=="], 1015 1259 1016 - "unpipe": ["unpipe@1.0.0", "", {}, ""], 1260 + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], 1261 + 1262 + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], 1017 1263 1018 - "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, ""], 1264 + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], 1019 1265 1020 - "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, ""], 1266 + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], 1021 1267 1022 - "utils-merge": ["utils-merge@1.0.1", "", {}, ""], 1268 + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], 1023 1269 1024 1270 "varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="], 1025 1271 1026 - "vary": ["vary@1.1.2", "", {}, ""], 1272 + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], 1027 1273 1028 1274 "wisp-hosting-service": ["wisp-hosting-service@workspace:apps/hosting-service"], 1029 1275 1030 - "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, ""], 1276 + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], 1277 + 1278 + "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], 1279 + 1280 + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], 1281 + 1282 + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], 1283 + 1284 + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], 1285 + 1286 + "yesno": ["yesno@0.4.0", "", {}, "sha512-tdBxmHvbXPBKYIg81bMCB7bVeDmHkRzk5rVJyYYXurwKkHq/MCd8rz4HSJUP7hW0H2NlXiq8IFiWvYKEHhlotA=="], 1287 + 1288 + "zlib": ["zlib@1.0.5", "", {}, "sha512-40fpE2II+Cd3k8HWTWONfeKE2jL+P42iWJ1zzps5W51qcTsOUKM5Q5m2PFb0CLxlmFAaUuUdJGc3OfZy947v0w=="], 1289 + 1290 + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], 1291 + 1292 + "@atproto-labs/fetch-node/ipaddr.js": ["ipaddr.js@2.3.0", "", {}, "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg=="], 1293 + 1294 + "@atproto/api/@atproto/lexicon": ["@atproto/lexicon@0.4.14", "", { "dependencies": { "@atproto/common-web": "^0.4.2", "@atproto/syntax": "^0.4.0", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-jiKpmH1QER3Gvc7JVY5brwrfo+etFoe57tKPQX/SmPwjvUsFnJAow5xLIryuBaJgFAhnTZViXKs41t//pahGHQ=="], 1295 + 1296 + "@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.6.12", "", { "dependencies": { "@atproto/lexicon": "^0.4.10", "zod": "^3.23.8" } }, "sha512-Ut3iISNLujlmY9Gu8sNU+SPDJDvqlVzWddU8qUr0Yae5oD4SguaUFjjhireMGhQ3M5E0KljQgDbTmnBo1kIZ3w=="], 1297 + 1298 + "@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1299 + 1300 + "@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1301 + 1302 + "@atproto/jwk/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1303 + 1304 + "@atproto/lex-cbor/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1305 + 1306 + "@atproto/lex-data/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1307 + 1308 + "@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1309 + 1310 + "@atproto/oauth-client/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1311 + 1312 + "@atproto/repo/@atproto/common": ["@atproto/common@0.5.2", "", { "dependencies": { "@atproto/common-web": "^0.4.6", "@atproto/lex-cbor": "0.0.2", "@atproto/lex-data": "0.0.2", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, "sha512-7KdU8FcIfnwS2kmv7M86pKxtw/fLvPY2bSI1rXpG+AmA8O++IUGlSCujBGzbrPwnQvY/z++f6Le4rdBzu8bFaA=="], 1313 + 1314 + "@atproto/repo/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1315 + 1316 + "@atproto/sync/@atproto/common": ["@atproto/common@0.5.2", "", { "dependencies": { "@atproto/common-web": "^0.4.6", "@atproto/lex-cbor": "0.0.2", "@atproto/lex-data": "0.0.2", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, "sha512-7KdU8FcIfnwS2kmv7M86pKxtw/fLvPY2bSI1rXpG+AmA8O++IUGlSCujBGzbrPwnQvY/z++f6Le4rdBzu8bFaA=="], 1317 + 1318 + "@atproto/sync/@atproto/xrpc-server": ["@atproto/xrpc-server@0.10.2", "", { "dependencies": { "@atproto/common": "^0.5.2", "@atproto/crypto": "^0.4.5", "@atproto/lex-cbor": "0.0.2", "@atproto/lex-data": "0.0.2", "@atproto/lexicon": "^0.5.2", "@atproto/ws-client": "^0.0.3", "@atproto/xrpc": "^0.7.6", "express": "^4.17.2", "http-errors": "^2.0.0", "mime-types": "^2.1.35", "rate-limiter-flexible": "^2.4.1", "ws": "^8.12.0", "zod": "^3.23.8" } }, "sha512-5AzN8xoV8K1Omn45z6qKH414+B3Z35D536rrScwF3aQGDEdpObAS+vya9UoSg+Gvm2+oOtVEbVri7riLTBW3Vg=="], 1319 + 1320 + "@atproto/sync/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1321 + 1322 + "@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], 1323 + 1324 + "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], 1325 + 1326 + "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], 1327 + 1328 + "@ipld/dag-cbor/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1329 + 1330 + "@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], 1331 + 1332 + "@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1333 + 1334 + "@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1335 + 1336 + "@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1337 + 1338 + "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1339 + 1340 + "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1031 1341 1032 - "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, ""], 1342 + "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1033 1343 1034 - "y18n": ["y18n@5.0.8", "", {}, ""], 1344 + "@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1035 1345 1036 - "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, ""], 1346 + "@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1037 1347 1038 - "yargs-parser": ["yargs-parser@21.1.1", "", {}, ""], 1348 + "@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1039 1349 1040 - "yesno": ["yesno@0.4.0", "", {}, ""], 1350 + "@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1041 1351 1042 - "zlib": ["zlib@1.0.5", "", {}, ""], 1352 + "@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1043 1353 1044 - "zod": ["zod@3.25.76", "", {}, ""], 1354 + "@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-5BiR6i8yHc9+qW7F6LqkuUnIzVNA7lt0qRxIKcKT+gq3eGUPHZ3DY29sfxI3tkvnwMgtnHDMNze5DdxW39HsAw=="], 1045 1355 1046 - "@atproto-labs/did-resolver/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1356 + "@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1047 1357 1048 - "@atproto-labs/fetch-node/ipaddr.js": ["ipaddr.js@2.2.0", "", {}, ""], 1358 + "@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1049 1359 1050 - "@atproto-labs/handle-resolver-node/@atproto-labs/handle-resolver": ["@atproto-labs/handle-resolver@0.3.2", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.1", "zod": "^3.23.8" } }, ""], 1360 + "@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1051 1361 1052 - "@atproto-labs/handle-resolver-node/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1362 + "@opentelemetry/exporter-metrics-otlp-grpc/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1053 1363 1054 - "@atproto-labs/identity-resolver/@atproto-labs/handle-resolver": ["@atproto-labs/handle-resolver@0.3.2", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.1", "zod": "^3.23.8" } }, ""], 1364 + "@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/resources": ["@opentelemetry/resources@1.29.0", "", { "dependencies": { "@opentelemetry/core": "1.29.0", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-s7mLXuHZE7RQr1wwweGcaRp3Q4UJJ0wazeGlc/N5/XSe6UyXfsh1UQGMADYeg7YwD+cEdMtU1yJAUXdnFzYzyQ=="], 1055 1365 1056 - "@atproto/api/@atproto/lexicon": ["@atproto/lexicon@0.4.14", "", { "dependencies": { "@atproto/common-web": "^0.4.2", "@atproto/syntax": "^0.4.0", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-jiKpmH1QER3Gvc7JVY5brwrfo+etFoe57tKPQX/SmPwjvUsFnJAow5xLIryuBaJgFAhnTZViXKs41t//pahGHQ=="], 1366 + "@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@1.29.0", "", { "dependencies": { "@opentelemetry/core": "1.29.0", "@opentelemetry/resources": "1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-MkVtuzDjXZaUJSuJlHn6BSXjcQlMvHcsDV7LjY4P6AJeffMa4+kIGDjzsCf6DkAh6Vqlwag5EWEam3KZOX5Drw=="], 1367 + 1368 + "@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1369 + 1370 + "@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-5BiR6i8yHc9+qW7F6LqkuUnIzVNA7lt0qRxIKcKT+gq3eGUPHZ3DY29sfxI3tkvnwMgtnHDMNze5DdxW39HsAw=="], 1371 + 1372 + "@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1373 + 1374 + "@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1375 + 1376 + "@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1377 + 1378 + "@opentelemetry/exporter-metrics-otlp-proto/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1379 + 1380 + "@opentelemetry/exporter-prometheus/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1381 + 1382 + "@opentelemetry/exporter-prometheus/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1383 + 1384 + "@opentelemetry/exporter-prometheus/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1385 + 1386 + "@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1387 + 1388 + "@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1389 + 1390 + "@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1391 + 1392 + "@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1393 + 1394 + "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1395 + 1396 + "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1397 + 1398 + "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1399 + 1400 + "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1401 + 1402 + "@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1057 1403 1058 - "@atproto/api/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1404 + "@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1059 1405 1060 - "@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.6.12", "", { "dependencies": { "@atproto/lexicon": "^0.4.10", "zod": "^3.23.8" } }, "sha512-Ut3iISNLujlmY9Gu8sNU+SPDJDvqlVzWddU8qUr0Yae5oD4SguaUFjjhireMGhQ3M5E0KljQgDbTmnBo1kIZ3w=="], 1406 + "@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1061 1407 1062 - "@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, ""], 1408 + "@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1063 1409 1064 - "@atproto/common/@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" } }, ""], 1410 + "@opentelemetry/exporter-zipkin/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1065 1411 1066 - "@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, ""], 1412 + "@opentelemetry/exporter-zipkin/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1067 1413 1068 - "@atproto/jwk/multiformats": ["multiformats@9.9.0", "", {}, ""], 1414 + "@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1069 1415 1070 - "@atproto/lex-cbor/multiformats": ["multiformats@9.9.0", "", {}, ""], 1416 + "@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1071 1417 1072 - "@atproto/lex-cli/@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" } }, ""], 1418 + "@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1073 1419 1074 - "@atproto/lex-cli/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1420 + "@opentelemetry/otlp-transformer/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.56.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-Wr39+94UNNG3Ei9nv3pHd4AJ63gq5nSemMRpCd8fPwDL9rN3vK26lzxfH27mw16XzOSO+TpyQwBAMaLxaPWG0g=="], 1075 1421 1076 - "@atproto/lex-data/multiformats": ["multiformats@9.9.0", "", {}, ""], 1422 + "@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@1.29.0", "", { "dependencies": { "@opentelemetry/core": "1.29.0", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-s7mLXuHZE7RQr1wwweGcaRp3Q4UJJ0wazeGlc/N5/XSe6UyXfsh1UQGMADYeg7YwD+cEdMtU1yJAUXdnFzYzyQ=="], 1077 1423 1078 - "@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1424 + "@opentelemetry/otlp-transformer/@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.56.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.56.0", "@opentelemetry/core": "1.29.0", "@opentelemetry/resources": "1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-OS0WPBJF++R/cSl+terUjQH5PebloidB1Jbbecgg2rnCmQbTST9xsRes23bLfDQVRvmegmHqDh884h0aRdJyLw=="], 1079 1425 1080 - "@atproto/oauth-client/@atproto-labs/handle-resolver": ["@atproto-labs/handle-resolver@0.3.2", "", { "dependencies": { "@atproto-labs/simple-store": "0.3.0", "@atproto-labs/simple-store-memory": "0.1.4", "@atproto/did": "0.2.1", "zod": "^3.23.8" } }, ""], 1426 + "@opentelemetry/otlp-transformer/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@1.29.0", "", { "dependencies": { "@opentelemetry/core": "1.29.0", "@opentelemetry/resources": "1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-MkVtuzDjXZaUJSuJlHn6BSXjcQlMvHcsDV7LjY4P6AJeffMa4+kIGDjzsCf6DkAh6Vqlwag5EWEam3KZOX5Drw=="], 1081 1427 1082 - "@atproto/oauth-client/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1428 + "@opentelemetry/otlp-transformer/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@1.29.0", "", { "dependencies": { "@opentelemetry/core": "1.29.0", "@opentelemetry/resources": "1.29.0", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-hEOpAYLKXF3wGJpXOtWsxEtqBgde0SCv+w+jvr3/UusR4ll3QrENEGnSl1WDCyRrpqOQ5NCNOvZch9UFVa7MnQ=="], 1083 1429 1084 - "@atproto/oauth-client/@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""], 1430 + "@opentelemetry/propagator-b3/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1085 1431 1086 - "@atproto/oauth-client/multiformats": ["multiformats@9.9.0", "", {}, ""], 1432 + "@opentelemetry/propagator-jaeger/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1087 1433 1088 - "@atproto/oauth-client-node/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1434 + "@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], 1089 1435 1090 - "@atproto/oauth-types/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1436 + "@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], 1091 1437 1092 - "@atproto/repo/@atproto/common": ["@atproto/common@0.5.2", "", { "dependencies": { "@atproto/common-web": "^0.4.6", "@atproto/lex-cbor": "0.0.2", "@atproto/lex-data": "0.0.2", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, "sha512-7KdU8FcIfnwS2kmv7M86pKxtw/fLvPY2bSI1rXpG+AmA8O++IUGlSCujBGzbrPwnQvY/z++f6Le4rdBzu8bFaA=="], 1438 + "@opentelemetry/sdk-logs/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1093 1439 1094 - "@atproto/repo/@atproto/crypto": ["@atproto/crypto@0.4.5", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, "sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw=="], 1440 + "@opentelemetry/sdk-logs/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1095 1441 1096 - "@atproto/repo/multiformats": ["multiformats@9.9.0", "", {}, ""], 1442 + "@opentelemetry/sdk-metrics/@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], 1097 1443 1098 - "@atproto/sync/@atproto/common": ["@atproto/common@0.5.2", "", { "dependencies": { "@atproto/common-web": "^0.4.6", "@atproto/lex-cbor": "0.0.2", "@atproto/lex-data": "0.0.2", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, "sha512-7KdU8FcIfnwS2kmv7M86pKxtw/fLvPY2bSI1rXpG+AmA8O++IUGlSCujBGzbrPwnQvY/z++f6Le4rdBzu8bFaA=="], 1444 + "@opentelemetry/sdk-node/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1099 1445 1100 - "@atproto/sync/@atproto/xrpc-server": ["@atproto/xrpc-server@0.10.2", "", { "dependencies": { "@atproto/common": "^0.5.2", "@atproto/crypto": "^0.4.5", "@atproto/lex-cbor": "0.0.2", "@atproto/lex-data": "0.0.2", "@atproto/lexicon": "^0.5.2", "@atproto/ws-client": "^0.0.3", "@atproto/xrpc": "^0.7.6", "express": "^4.17.2", "http-errors": "^2.0.0", "mime-types": "^2.1.35", "rate-limiter-flexible": "^2.4.1", "ws": "^8.12.0", "zod": "^3.23.8" } }, "sha512-5AzN8xoV8K1Omn45z6qKH414+B3Z35D536rrScwF3aQGDEdpObAS+vya9UoSg+Gvm2+oOtVEbVri7riLTBW3Vg=="], 1446 + "@opentelemetry/sdk-node/@opentelemetry/exporter-metrics-otlp-http": ["@opentelemetry/exporter-metrics-otlp-http@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-exporter-base": "0.200.0", "@opentelemetry/otlp-transformer": "0.200.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-metrics": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-5BiR6i8yHc9+qW7F6LqkuUnIzVNA7lt0qRxIKcKT+gq3eGUPHZ3DY29sfxI3tkvnwMgtnHDMNze5DdxW39HsAw=="], 1101 1447 1102 - "@atproto/sync/multiformats": ["multiformats@9.9.0", "", {}, ""], 1448 + "@opentelemetry/sdk-node/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1103 1449 1104 - "@atproto/ws-client/@atproto/common": ["@atproto/common@0.5.2", "", { "dependencies": { "@atproto/common-web": "^0.4.6", "@atproto/lex-cbor": "0.0.2", "@atproto/lex-data": "0.0.2", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, "sha512-7KdU8FcIfnwS2kmv7M86pKxtw/fLvPY2bSI1rXpG+AmA8O++IUGlSCujBGzbrPwnQvY/z++f6Le4rdBzu8bFaA=="], 1450 + "@opentelemetry/sdk-node/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1105 1451 1106 - "@atproto/xrpc-server/@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" } }, ""], 1452 + "@opentelemetry/sdk-trace-base/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1107 1453 1108 - "@atproto/xrpc-server/@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""], 1454 + "@opentelemetry/sdk-trace-base/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1109 1455 1110 - "@ipld/dag-cbor/multiformats": ["multiformats@9.9.0", "", {}, ""], 1456 + "@opentelemetry/sdk-trace-node/@opentelemetry/core": ["@opentelemetry/core@2.0.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-SLX36allrcnVaPYG3R78F/UZZsBsvbc7lMCLx37LyH5MJ1KAAZ2E3mW9OAD3zGz0G8q/BtoS5VUrjzDydhD6LQ=="], 1111 1457 1112 - "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 1458 + "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], 1113 1459 1114 - "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 1460 + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], 1115 1461 1116 - "@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 1462 + "@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], 1117 1463 1118 - "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" } }, ""], 1464 + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], 1119 1465 1120 1466 "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], 1121 1467 ··· 1127 1473 1128 1474 "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], 1129 1475 1130 - "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, ""], 1476 + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], 1131 1477 1132 - "@tokenizer/inflate/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, ""], 1478 + "@tokenizer/inflate/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], 1133 1479 1134 - "@wisp/main-app/@atproto/api": ["@atproto/api@0.17.7", "", { "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" } }, ""], 1480 + "@types/bun/bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], 1135 1481 1136 - "bun-types/@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, ""], 1482 + "@wisp/main-app/@atproto/api": ["@atproto/api@0.17.7", "", { "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-V+OJBZq9chcrD21xk1bUa6oc5DSKfQj5DmUPf5rmZncqL1w9ZEbS38H5cMyqqdhfgo2LWeDRdZHD0rvNyJsIaw=="], 1137 1483 1138 - "express/cookie": ["cookie@0.7.1", "", {}, ""], 1484 + "@wisp/observability/bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="], 1139 1485 1140 - "fdir/picomatch": ["picomatch@4.0.3", "", {}, ""], 1486 + "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], 1487 + 1488 + "iron-session/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], 1141 1489 1142 - "iron-session/cookie": ["cookie@0.7.2", "", {}, ""], 1490 + "lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 1143 1491 1144 - "lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, ""], 1492 + "node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], 1493 + 1494 + "pino-abstract-transport/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], 1495 + 1496 + "require-in-the-middle/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], 1145 1497 1146 - "node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, ""], 1498 + "send/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], 1147 1499 1148 - "protobufjs/@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, ""], 1500 + "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 1149 1501 1150 - "require-in-the-middle/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, ""], 1502 + "send/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], 1151 1503 1152 - "send/encodeurl": ["encodeurl@1.0.2", "", {}, ""], 1504 + "serve-static/send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="], 1153 1505 1154 - "send/ms": ["ms@2.1.3", "", {}, ""], 1506 + "tiered-storage/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], 1155 1507 1156 - "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, ""], 1508 + "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], 1157 1509 1158 - "tsx/esbuild": ["esbuild@0.27.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.0", "@esbuild/android-arm": "0.27.0", "@esbuild/android-arm64": "0.27.0", "@esbuild/android-x64": "0.27.0", "@esbuild/darwin-arm64": "0.27.0", "@esbuild/darwin-x64": "0.27.0", "@esbuild/freebsd-arm64": "0.27.0", "@esbuild/freebsd-x64": "0.27.0", "@esbuild/linux-arm": "0.27.0", "@esbuild/linux-arm64": "0.27.0", "@esbuild/linux-ia32": "0.27.0", "@esbuild/linux-loong64": "0.27.0", "@esbuild/linux-mips64el": "0.27.0", "@esbuild/linux-ppc64": "0.27.0", "@esbuild/linux-riscv64": "0.27.0", "@esbuild/linux-s390x": "0.27.0", "@esbuild/linux-x64": "0.27.0", "@esbuild/netbsd-arm64": "0.27.0", "@esbuild/netbsd-x64": "0.27.0", "@esbuild/openbsd-arm64": "0.27.0", "@esbuild/openbsd-x64": "0.27.0", "@esbuild/openharmony-arm64": "0.27.0", "@esbuild/sunos-x64": "0.27.0", "@esbuild/win32-arm64": "0.27.0", "@esbuild/win32-ia32": "0.27.0", "@esbuild/win32-x64": "0.27.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA=="], 1510 + "tsx/esbuild": ["esbuild@0.27.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.1", "@esbuild/android-arm": "0.27.1", "@esbuild/android-arm64": "0.27.1", "@esbuild/android-x64": "0.27.1", "@esbuild/darwin-arm64": "0.27.1", "@esbuild/darwin-x64": "0.27.1", "@esbuild/freebsd-arm64": "0.27.1", "@esbuild/freebsd-x64": "0.27.1", "@esbuild/linux-arm": "0.27.1", "@esbuild/linux-arm64": "0.27.1", "@esbuild/linux-ia32": "0.27.1", "@esbuild/linux-loong64": "0.27.1", "@esbuild/linux-mips64el": "0.27.1", "@esbuild/linux-ppc64": "0.27.1", "@esbuild/linux-riscv64": "0.27.1", "@esbuild/linux-s390x": "0.27.1", "@esbuild/linux-x64": "0.27.1", "@esbuild/netbsd-arm64": "0.27.1", "@esbuild/netbsd-x64": "0.27.1", "@esbuild/openbsd-arm64": "0.27.1", "@esbuild/openbsd-x64": "0.27.1", "@esbuild/openharmony-arm64": "0.27.1", "@esbuild/sunos-x64": "0.27.1", "@esbuild/win32-arm64": "0.27.1", "@esbuild/win32-ia32": "0.27.1", "@esbuild/win32-x64": "0.27.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA=="], 1159 1511 1160 1512 "tsx/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], 1161 1513 1162 - "uint8arrays/multiformats": ["multiformats@9.9.0", "", {}, ""], 1514 + "uint8arrays/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1163 1515 1164 - "wisp-hosting-service/@atproto/api": ["@atproto/api@0.17.7", "", { "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" } }, ""], 1516 + "wisp-hosting-service/@atproto/api": ["@atproto/api@0.17.7", "", { "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-V+OJBZq9chcrD21xk1bUa6oc5DSKfQj5DmUPf5rmZncqL1w9ZEbS38H5cMyqqdhfgo2LWeDRdZHD0rvNyJsIaw=="], 1165 1517 1166 - "@atproto-labs/identity-resolver/@atproto-labs/handle-resolver/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1518 + "@atproto/sync/@atproto/xrpc-server/@atproto/ws-client": ["@atproto/ws-client@0.0.3", "", { "dependencies": { "@atproto/common": "^0.5.0", "ws": "^8.12.0" } }, "sha512-eKqkTWBk6zuMY+6gs02eT7mS8Btewm8/qaL/Dp00NDCqpNC+U59MWvQsOWT3xkNGfd9Eip+V6VI4oyPvAfsfTA=="], 1167 1519 1168 - "@atproto/lex-cli/@atproto/lexicon/@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" } }, ""], 1520 + "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], 1169 1521 1170 - "@atproto/lex-cli/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1522 + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], 1171 1523 1172 - "@atproto/oauth-client/@atproto/xrpc/@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" } }, ""], 1524 + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], 1173 1525 1174 - "@atproto/sync/@atproto/xrpc-server/@atproto/crypto": ["@atproto/crypto@0.4.5", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, "sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw=="], 1526 + "@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1527 + 1528 + "@opentelemetry/exporter-logs-otlp-grpc/@opentelemetry/otlp-transformer/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1529 + 1530 + "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1531 + 1532 + "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/otlp-transformer/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1533 + 1534 + "@opentelemetry/exporter-logs-otlp-proto/@opentelemetry/otlp-transformer/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1535 + 1536 + "@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], 1537 + 1538 + "@opentelemetry/exporter-trace-otlp-grpc/@opentelemetry/otlp-transformer/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1539 + 1540 + "@opentelemetry/exporter-trace-otlp-http/@opentelemetry/otlp-transformer/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1175 1541 1176 - "@atproto/ws-client/@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, ""], 1542 + "@opentelemetry/exporter-trace-otlp-proto/@opentelemetry/otlp-transformer/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1543 + 1544 + "@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-rnZr6dML2z4IARI4zPGQV4arDikF/9OXZQzrC01dLmn0CZxU5U5OLd/m1T7YkGRj5UitjeoCtg/zorlgMQcdTg=="], 1177 1545 1178 - "@atproto/xrpc-server/@atproto/lexicon/@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" } }, ""], 1546 + "@opentelemetry/otlp-grpc-exporter-base/@opentelemetry/otlp-transformer/@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-Bvy8QDjO05umd0+j+gDeWcTaVa1/R2lDj/eOvjzpm8VQj1K1vVZJuyjThpV5/lSHyYW2JaHF2IQ7Z8twJFAhjA=="], 1179 1547 1180 - "@atproto/xrpc-server/@atproto/lexicon/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1548 + "@opentelemetry/otlp-transformer/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], 1181 1549 1182 - "@atproto/xrpc-server/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1550 + "@opentelemetry/otlp-transformer/@opentelemetry/sdk-trace-base/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], 1183 1551 1184 - "@tokenizer/inflate/debug/ms": ["ms@2.1.3", "", {}, ""], 1552 + "@opentelemetry/sdk-metrics/@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], 1185 1553 1186 - "@wisp/main-app/@atproto/api/@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" } }, ""], 1554 + "@opentelemetry/sdk-node/@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.200.0", "", { "dependencies": { "@opentelemetry/core": "2.0.0", "@opentelemetry/otlp-transformer": "0.200.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-IxJgA3FD7q4V6gGq4bnmQM5nTIyMDkoGFGrBrrDjB6onEiq1pafma55V+bHvGYLWvcqbBbRfezr1GED88lacEQ=="], 1187 1555 1188 - "@wisp/main-app/@atproto/api/@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" } }, ""], 1556 + "@opentelemetry/sdk-node/@opentelemetry/exporter-metrics-otlp-http/@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.200.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.200.0", "@opentelemetry/core": "2.0.0", "@opentelemetry/resources": "2.0.0", "@opentelemetry/sdk-logs": "0.200.0", "@opentelemetry/sdk-metrics": "2.0.0", "@opentelemetry/sdk-trace-base": "2.0.0", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-+9YDZbYybOnv7sWzebWOeK6gKyt2XE7iarSyBFkwwnP559pEevKOUD8NyDHhRjCSp13ybh9iVXlMfcj/DwF/yw=="], 1189 1557 1190 - "@wisp/main-app/@atproto/api/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1558 + "@tokenizer/inflate/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 1191 1559 1192 - "@wisp/main-app/@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""], 1560 + "@wisp/main-app/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1193 1561 1194 - "@wisp/main-app/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, ""], 1562 + "pino-abstract-transport/readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], 1195 1563 1196 - "bun-types/@types/node/undici-types": ["undici-types@7.16.0", "", {}, ""], 1564 + "require-in-the-middle/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 1197 1565 1198 - "protobufjs/@types/node/undici-types": ["undici-types@7.16.0", "", {}, ""], 1566 + "serve-static/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], 1199 1567 1200 - "require-in-the-middle/debug/ms": ["ms@2.1.3", "", {}, ""], 1568 + "serve-static/send/http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], 1201 1569 1202 - "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A=="], 1570 + "serve-static/send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 1203 1571 1204 - "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.0", "", { "os": "android", "cpu": "arm" }, "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ=="], 1572 + "serve-static/send/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], 1205 1573 1206 - "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.0", "", { "os": "android", "cpu": "arm64" }, "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ=="], 1574 + "tiered-storage/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], 1207 1575 1208 - "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.0", "", { "os": "android", "cpu": "x64" }, "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q=="], 1576 + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="], 1209 1577 1210 - "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg=="], 1578 + "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.1", "", { "os": "android", "cpu": "arm" }, "sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg=="], 1211 1579 1212 - "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g=="], 1580 + "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.1", "", { "os": "android", "cpu": "arm64" }, "sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ=="], 1213 1581 1214 - "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw=="], 1582 + "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.27.1", "", { "os": "android", "cpu": "x64" }, "sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ=="], 1215 1583 1216 - "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g=="], 1584 + "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ=="], 1217 1585 1218 - "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ=="], 1586 + "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ=="], 1219 1587 1220 - "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ=="], 1588 + "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg=="], 1221 1589 1222 - "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw=="], 1590 + "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ=="], 1223 1591 1224 - "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg=="], 1592 + "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA=="], 1225 1593 1226 - "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg=="], 1594 + "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q=="], 1227 1595 1228 - "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA=="], 1596 + "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw=="], 1229 1597 1230 - "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.0", "", { "os": "linux", "cpu": "none" }, "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ=="], 1598 + "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg=="], 1231 1599 1232 - "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w=="], 1600 + "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA=="], 1233 1601 1234 - "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw=="], 1602 + "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ=="], 1235 1603 1236 - "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w=="], 1604 + "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.1", "", { "os": "linux", "cpu": "none" }, "sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ=="], 1237 1605 1238 - "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.0", "", { "os": "none", "cpu": "x64" }, "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA=="], 1606 + "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw=="], 1239 1607 1240 - "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ=="], 1608 + "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.1", "", { "os": "linux", "cpu": "x64" }, "sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA=="], 1241 1609 1242 - "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A=="], 1610 + "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ=="], 1243 1611 1244 - "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.0", "", { "os": "none", "cpu": "arm64" }, "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA=="], 1612 + "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.1", "", { "os": "none", "cpu": "x64" }, "sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg=="], 1245 1613 1246 - "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA=="], 1614 + "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g=="], 1247 1615 1248 - "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg=="], 1616 + "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg=="], 1249 1617 1250 - "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ=="], 1618 + "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg=="], 1251 1619 1252 - "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="], 1620 + "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA=="], 1253 1621 1254 - "wisp-hosting-service/@atproto/api/@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" } }, ""], 1622 + "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg=="], 1255 1623 1256 - "wisp-hosting-service/@atproto/api/@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" } }, ""], 1624 + "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ=="], 1257 1625 1258 - "wisp-hosting-service/@atproto/api/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1626 + "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.1", "", { "os": "win32", "cpu": "x64" }, "sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw=="], 1259 1627 1260 - "wisp-hosting-service/@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""], 1628 + "wisp-hosting-service/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1261 1629 1262 - "wisp-hosting-service/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, ""], 1630 + "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], 1263 1631 1264 - "@atproto/oauth-client/@atproto/xrpc/@atproto/lexicon/@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" } }, ""], 1632 + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], 1265 1633 1266 - "@atproto/oauth-client/@atproto/xrpc/@atproto/lexicon/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1634 + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], 1267 1635 } 1268 1636 }
+370 -503
cli/Cargo.lock
··· 3 3 version = 4 4 4 5 5 [[package]] 6 - name = "abnf" 7 - version = "0.13.0" 8 - source = "registry+https://github.com/rust-lang/crates.io-index" 9 - checksum = "087113bd50d9adce24850eed5d0476c7d199d532fce8fab5173650331e09033a" 10 - dependencies = [ 11 - "abnf-core", 12 - "nom", 13 - ] 14 - 15 - [[package]] 16 - name = "abnf-core" 17 - version = "0.5.0" 18 - source = "registry+https://github.com/rust-lang/crates.io-index" 19 - checksum = "c44e09c43ae1c368fb91a03a566472d0087c26cf7e1b9e8e289c14ede681dd7d" 20 - dependencies = [ 21 - "nom", 22 - ] 23 - 24 - [[package]] 25 6 name = "addr2line" 26 7 version = "0.25.1" 27 8 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 71 52 dependencies = [ 72 53 "alloc-no-stdlib", 73 54 ] 55 + 56 + [[package]] 57 + name = "allocator-api2" 58 + version = "0.2.21" 59 + source = "registry+https://github.com/rust-lang/crates.io-index" 60 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 74 61 75 62 [[package]] 76 63 name = "android_system_properties" ··· 139 126 140 127 [[package]] 141 128 name = "async-compression" 142 - version = "0.4.34" 129 + version = "0.4.36" 143 130 source = "registry+https://github.com/rust-lang/crates.io-index" 144 - checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473" 131 + checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" 145 132 dependencies = [ 146 133 "compression-codecs", 147 134 "compression-core", ··· 158 145 dependencies = [ 159 146 "proc-macro2", 160 147 "quote", 161 - "syn 2.0.111", 148 + "syn 2.0.113", 149 + ] 150 + 151 + [[package]] 152 + name = "atomic-polyfill" 153 + version = "1.0.3" 154 + source = "registry+https://github.com/rust-lang/crates.io-index" 155 + checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" 156 + dependencies = [ 157 + "critical-section", 162 158 ] 163 159 164 160 [[package]] ··· 175 171 176 172 [[package]] 177 173 name = "axum" 178 - version = "0.8.7" 174 + version = "0.8.8" 179 175 source = "registry+https://github.com/rust-lang/crates.io-index" 180 - checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425" 176 + checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" 181 177 dependencies = [ 182 178 "axum-core", 183 179 "bytes", ··· 208 204 209 205 [[package]] 210 206 name = "axum-core" 211 - version = "0.5.5" 207 + version = "0.5.6" 212 208 source = "registry+https://github.com/rust-lang/crates.io-index" 213 - checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" 209 + checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" 214 210 dependencies = [ 215 211 "bytes", 216 212 "futures-core", ··· 237 233 "miniz_oxide", 238 234 "object", 239 235 "rustc-demangle", 240 - "windows-link 0.2.1", 236 + "windows-link", 241 237 ] 242 238 243 239 [[package]] ··· 285 281 286 282 [[package]] 287 283 name = "base64ct" 288 - version = "1.8.0" 284 + version = "1.8.2" 289 285 source = "registry+https://github.com/rust-lang/crates.io-index" 290 - checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" 286 + checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82" 291 287 292 288 [[package]] 293 289 name = "bitflags" ··· 326 322 "proc-macro2", 327 323 "quote", 328 324 "rustversion", 329 - "syn 2.0.111", 325 + "syn 2.0.113", 330 326 ] 331 327 332 328 [[package]] 333 329 name = "borsh" 334 - version = "1.5.7" 330 + version = "1.6.0" 335 331 source = "registry+https://github.com/rust-lang/crates.io-index" 336 - checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" 332 + checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" 337 333 dependencies = [ 338 334 "cfg_aliases", 339 335 ] ··· 370 366 ] 371 367 372 368 [[package]] 373 - name = "btree-range-map" 374 - version = "0.7.2" 375 - source = "registry+https://github.com/rust-lang/crates.io-index" 376 - checksum = "1be5c9672446d3800bcbcaabaeba121fe22f1fb25700c4562b22faf76d377c33" 377 - dependencies = [ 378 - "btree-slab", 379 - "cc-traits", 380 - "range-traits", 381 - "serde", 382 - "slab", 383 - ] 384 - 385 - [[package]] 386 - name = "btree-slab" 387 - version = "0.6.1" 388 - source = "registry+https://github.com/rust-lang/crates.io-index" 389 - checksum = "7a2b56d3029f075c4fa892428a098425b86cef5c89ae54073137ece416aef13c" 390 - dependencies = [ 391 - "cc-traits", 392 - "slab", 393 - "smallvec", 394 - ] 395 - 396 - [[package]] 397 369 name = "buf_redux" 398 370 version = "0.8.4" 399 371 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 405 377 406 378 [[package]] 407 379 name = "bumpalo" 408 - version = "3.19.0" 380 + version = "3.19.1" 409 381 source = "registry+https://github.com/rust-lang/crates.io-index" 410 - checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 382 + checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" 411 383 412 384 [[package]] 413 385 name = "byteorder" ··· 435 407 436 408 [[package]] 437 409 name = "cc" 438 - version = "1.2.47" 410 + version = "1.2.51" 439 411 source = "registry+https://github.com/rust-lang/crates.io-index" 440 - checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07" 412 + checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" 441 413 dependencies = [ 442 414 "find-msvc-tools", 443 415 "shlex", 444 416 ] 445 417 446 418 [[package]] 447 - name = "cc-traits" 448 - version = "2.0.0" 449 - source = "registry+https://github.com/rust-lang/crates.io-index" 450 - checksum = "060303ef31ef4a522737e1b1ab68c67916f2a787bb2f4f54f383279adba962b5" 451 - dependencies = [ 452 - "slab", 453 - ] 454 - 455 - [[package]] 456 419 name = "cesu8" 457 420 version = "1.1.0" 458 421 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 481 444 "num-traits", 482 445 "serde", 483 446 "wasm-bindgen", 484 - "windows-link 0.2.1", 447 + "windows-link", 485 448 ] 486 449 487 450 [[package]] ··· 533 496 534 497 [[package]] 535 498 name = "clap" 536 - version = "4.5.53" 499 + version = "4.5.54" 537 500 source = "registry+https://github.com/rust-lang/crates.io-index" 538 - checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" 501 + checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" 539 502 dependencies = [ 540 503 "clap_builder", 541 504 "clap_derive", ··· 543 506 544 507 [[package]] 545 508 name = "clap_builder" 546 - version = "4.5.53" 509 + version = "4.5.54" 547 510 source = "registry+https://github.com/rust-lang/crates.io-index" 548 - checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" 511 + checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" 549 512 dependencies = [ 550 513 "anstream", 551 514 "anstyle", ··· 562 525 "heck 0.5.0", 563 526 "proc-macro2", 564 527 "quote", 565 - "syn 2.0.111", 528 + "syn 2.0.113", 566 529 ] 567 530 568 531 [[package]] ··· 572 535 checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" 573 536 574 537 [[package]] 538 + name = "cobs" 539 + version = "0.3.0" 540 + source = "registry+https://github.com/rust-lang/crates.io-index" 541 + checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" 542 + dependencies = [ 543 + "thiserror 2.0.17", 544 + ] 545 + 546 + [[package]] 575 547 name = "colorchoice" 576 548 version = "1.0.4" 577 549 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 589 561 590 562 [[package]] 591 563 name = "compression-codecs" 592 - version = "0.4.33" 564 + version = "0.4.35" 593 565 source = "registry+https://github.com/rust-lang/crates.io-index" 594 - checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad" 566 + checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" 595 567 dependencies = [ 596 568 "compression-core", 597 569 "flate2", ··· 693 665 ] 694 666 695 667 [[package]] 668 + name = "critical-section" 669 + version = "1.2.0" 670 + source = "registry+https://github.com/rust-lang/crates.io-index" 671 + checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 672 + 673 + [[package]] 696 674 name = "crossbeam-channel" 697 675 version = "0.5.15" 698 676 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 775 753 "proc-macro2", 776 754 "quote", 777 755 "strsim", 778 - "syn 2.0.111", 756 + "syn 2.0.113", 779 757 ] 780 758 781 759 [[package]] ··· 786 764 dependencies = [ 787 765 "darling_core", 788 766 "quote", 789 - "syn 2.0.111", 767 + "syn 2.0.113", 790 768 ] 791 769 792 770 [[package]] ··· 826 804 checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 827 805 dependencies = [ 828 806 "data-encoding", 829 - "syn 2.0.111", 807 + "syn 2.0.113", 830 808 ] 831 809 832 810 [[package]] ··· 857 835 checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" 858 836 dependencies = [ 859 837 "powerfmt", 860 - "serde_core", 861 838 ] 862 839 863 840 [[package]] ··· 877 854 dependencies = [ 878 855 "proc-macro2", 879 856 "quote", 880 - "syn 2.0.111", 857 + "syn 2.0.113", 881 858 "unicode-xid", 882 859 ] 883 860 ··· 928 905 dependencies = [ 929 906 "proc-macro2", 930 907 "quote", 931 - "syn 2.0.111", 908 + "syn 2.0.113", 932 909 ] 933 910 934 911 [[package]] 935 - name = "dyn-clone" 936 - version = "1.0.20" 937 - source = "registry+https://github.com/rust-lang/crates.io-index" 938 - checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" 939 - 940 - [[package]] 941 912 name = "ecdsa" 942 913 version = "0.16.9" 943 914 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 972 943 ] 973 944 974 945 [[package]] 946 + name = "embedded-io" 947 + version = "0.4.0" 948 + source = "registry+https://github.com/rust-lang/crates.io-index" 949 + checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" 950 + 951 + [[package]] 952 + name = "embedded-io" 953 + version = "0.6.1" 954 + source = "registry+https://github.com/rust-lang/crates.io-index" 955 + checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 956 + 957 + [[package]] 975 958 name = "encode_unicode" 976 959 version = "1.0.0" 977 960 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 995 978 "heck 0.5.0", 996 979 "proc-macro2", 997 980 "quote", 998 - "syn 2.0.111", 981 + "syn 2.0.113", 999 982 ] 1000 983 1001 984 [[package]] ··· 1044 1027 1045 1028 [[package]] 1046 1029 name = "find-msvc-tools" 1047 - version = "0.1.5" 1030 + version = "0.1.6" 1048 1031 source = "registry+https://github.com/rust-lang/crates.io-index" 1049 - checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" 1032 + checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" 1050 1033 1051 1034 [[package]] 1052 1035 name = "flate2" ··· 1063 1046 version = "1.0.7" 1064 1047 source = "registry+https://github.com/rust-lang/crates.io-index" 1065 1048 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 1049 + 1050 + [[package]] 1051 + name = "foldhash" 1052 + version = "0.1.5" 1053 + source = "registry+https://github.com/rust-lang/crates.io-index" 1054 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 1066 1055 1067 1056 [[package]] 1068 1057 name = "form_urlencoded" ··· 1165 1154 dependencies = [ 1166 1155 "proc-macro2", 1167 1156 "quote", 1168 - "syn 2.0.111", 1157 + "syn 2.0.113", 1169 1158 ] 1170 1159 1171 1160 [[package]] ··· 1200 1189 1201 1190 [[package]] 1202 1191 name = "generator" 1203 - version = "0.8.7" 1192 + version = "0.8.8" 1204 1193 source = "registry+https://github.com/rust-lang/crates.io-index" 1205 - checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" 1194 + checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" 1206 1195 dependencies = [ 1207 1196 "cc", 1208 1197 "cfg-if", 1209 1198 "libc", 1210 1199 "log", 1211 1200 "rustversion", 1212 - "windows", 1201 + "windows-link", 1202 + "windows-result", 1213 1203 ] 1214 1204 1215 1205 [[package]] ··· 1319 1309 1320 1310 [[package]] 1321 1311 name = "h2" 1322 - version = "0.4.12" 1312 + version = "0.4.13" 1323 1313 source = "registry+https://github.com/rust-lang/crates.io-index" 1324 - checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" 1314 + checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" 1325 1315 dependencies = [ 1326 1316 "atomic-waker", 1327 1317 "bytes", ··· 1329 1319 "futures-core", 1330 1320 "futures-sink", 1331 1321 "http", 1332 - "indexmap 2.12.1", 1322 + "indexmap", 1333 1323 "slab", 1334 1324 "tokio", 1335 1325 "tokio-util", ··· 1348 1338 ] 1349 1339 1350 1340 [[package]] 1351 - name = "hashbrown" 1352 - version = "0.12.3" 1341 + name = "hash32" 1342 + version = "0.2.1" 1353 1343 source = "registry+https://github.com/rust-lang/crates.io-index" 1354 - checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 1344 + checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 1345 + dependencies = [ 1346 + "byteorder", 1347 + ] 1355 1348 1356 1349 [[package]] 1357 1350 name = "hashbrown" ··· 1361 1354 1362 1355 [[package]] 1363 1356 name = "hashbrown" 1357 + version = "0.15.5" 1358 + source = "registry+https://github.com/rust-lang/crates.io-index" 1359 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 1360 + dependencies = [ 1361 + "allocator-api2", 1362 + "equivalent", 1363 + "foldhash", 1364 + ] 1365 + 1366 + [[package]] 1367 + name = "hashbrown" 1364 1368 version = "0.16.1" 1365 1369 source = "registry+https://github.com/rust-lang/crates.io-index" 1366 1370 checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 1367 1371 1368 1372 [[package]] 1373 + name = "heapless" 1374 + version = "0.7.17" 1375 + source = "registry+https://github.com/rust-lang/crates.io-index" 1376 + checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" 1377 + dependencies = [ 1378 + "atomic-polyfill", 1379 + "hash32", 1380 + "rustc_version", 1381 + "serde", 1382 + "spin 0.9.8", 1383 + "stable_deref_trait", 1384 + ] 1385 + 1386 + [[package]] 1369 1387 name = "heck" 1370 1388 version = "0.4.1" 1371 1389 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1390 1408 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 1391 1409 1392 1410 [[package]] 1393 - name = "hex_fmt" 1394 - version = "0.3.0" 1395 - source = "registry+https://github.com/rust-lang/crates.io-index" 1396 - checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" 1397 - 1398 - [[package]] 1399 1411 name = "hickory-proto" 1400 1412 version = "0.24.4" 1401 1413 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1460 1472 "markup5ever", 1461 1473 "proc-macro2", 1462 1474 "quote", 1463 - "syn 2.0.111", 1475 + "syn 2.0.113", 1464 1476 ] 1465 1477 1466 1478 [[package]] ··· 1556 1568 1557 1569 [[package]] 1558 1570 name = "hyper-util" 1559 - version = "0.1.18" 1571 + version = "0.1.19" 1560 1572 source = "registry+https://github.com/rust-lang/crates.io-index" 1561 - checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" 1573 + checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" 1562 1574 dependencies = [ 1563 1575 "base64 0.22.1", 1564 1576 "bytes", ··· 1592 1604 "js-sys", 1593 1605 "log", 1594 1606 "wasm-bindgen", 1595 - "windows-core 0.62.2", 1607 + "windows-core", 1596 1608 ] 1597 1609 1598 1610 [[package]] ··· 1652 1664 1653 1665 [[package]] 1654 1666 name = "icu_properties" 1655 - version = "2.1.1" 1667 + version = "2.1.2" 1656 1668 source = "registry+https://github.com/rust-lang/crates.io-index" 1657 - checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" 1669 + checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" 1658 1670 dependencies = [ 1659 1671 "icu_collections", 1660 1672 "icu_locale_core", ··· 1666 1678 1667 1679 [[package]] 1668 1680 name = "icu_properties_data" 1669 - version = "2.1.1" 1681 + version = "2.1.2" 1670 1682 source = "registry+https://github.com/rust-lang/crates.io-index" 1671 - checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" 1683 + checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" 1672 1684 1673 1685 [[package]] 1674 1686 name = "icu_provider" ··· 1730 1742 1731 1743 [[package]] 1732 1744 name = "indexmap" 1733 - version = "1.9.3" 1734 - source = "registry+https://github.com/rust-lang/crates.io-index" 1735 - checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 1736 - dependencies = [ 1737 - "autocfg", 1738 - "hashbrown 0.12.3", 1739 - "serde", 1740 - ] 1741 - 1742 - [[package]] 1743 - name = "indexmap" 1744 1745 version = "2.12.1" 1745 1746 source = "registry+https://github.com/rust-lang/crates.io-index" 1746 1747 checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" 1747 1748 dependencies = [ 1748 1749 "equivalent", 1749 1750 "hashbrown 0.16.1", 1750 - "serde", 1751 - "serde_core", 1752 1751 ] 1753 1752 1754 1753 [[package]] ··· 1765 1764 ] 1766 1765 1767 1766 [[package]] 1768 - name = "indoc" 1769 - version = "2.0.7" 1770 - source = "registry+https://github.com/rust-lang/crates.io-index" 1771 - checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" 1772 - dependencies = [ 1773 - "rustversion", 1774 - ] 1775 - 1776 - [[package]] 1777 1767 name = "inventory" 1778 1768 version = "0.3.21" 1779 1769 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1813 1803 1814 1804 [[package]] 1815 1805 name = "iri-string" 1816 - version = "0.7.9" 1806 + version = "0.7.10" 1817 1807 source = "registry+https://github.com/rust-lang/crates.io-index" 1818 - checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" 1808 + checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" 1819 1809 dependencies = [ 1820 1810 "memchr", 1821 1811 "serde", ··· 1835 1825 1836 1826 [[package]] 1837 1827 name = "itoa" 1838 - version = "1.0.15" 1828 + version = "1.0.17" 1839 1829 source = "registry+https://github.com/rust-lang/crates.io-index" 1840 - checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1830 + checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" 1841 1831 1842 1832 [[package]] 1843 1833 name = "jacquard" 1844 - version = "0.9.3" 1834 + version = "0.9.5" 1835 + source = "git+https://tangled.org/nonbinary.computer/jacquard#5bcf7f8e87324b8e67fc273c678d0490c9c6d15b" 1845 1836 dependencies = [ 1846 1837 "bytes", 1847 1838 "getrandom 0.2.16", ··· 1870 1861 1871 1862 [[package]] 1872 1863 name = "jacquard-api" 1873 - version = "0.9.2" 1864 + version = "0.9.5" 1865 + source = "git+https://tangled.org/nonbinary.computer/jacquard#5bcf7f8e87324b8e67fc273c678d0490c9c6d15b" 1874 1866 dependencies = [ 1875 1867 "bon", 1876 1868 "bytes", ··· 1880 1872 "miette", 1881 1873 "rustversion", 1882 1874 "serde", 1875 + "serde_bytes", 1883 1876 "serde_ipld_dagcbor", 1884 1877 "thiserror 2.0.17", 1885 1878 "unicode-segmentation", ··· 1887 1880 1888 1881 [[package]] 1889 1882 name = "jacquard-common" 1890 - version = "0.9.2" 1883 + version = "0.9.5" 1884 + source = "git+https://tangled.org/nonbinary.computer/jacquard#5bcf7f8e87324b8e67fc273c678d0490c9c6d15b" 1891 1885 dependencies = [ 1892 1886 "base64 0.22.1", 1893 1887 "bon", 1894 1888 "bytes", 1895 1889 "chrono", 1896 1890 "ciborium", 1891 + "ciborium-io", 1897 1892 "cid", 1898 1893 "futures", 1899 1894 "getrandom 0.2.16", 1900 1895 "getrandom 0.3.4", 1896 + "hashbrown 0.15.5", 1901 1897 "http", 1902 1898 "ipld-core", 1903 1899 "k256", 1904 - "langtag", 1900 + "maitake-sync", 1905 1901 "miette", 1906 1902 "multibase", 1907 1903 "multihash", 1908 1904 "n0-future 0.1.3", 1909 1905 "ouroboros", 1906 + "oxilangtag", 1910 1907 "p256", 1908 + "postcard", 1911 1909 "rand 0.9.2", 1912 1910 "regex", 1911 + "regex-automata", 1913 1912 "regex-lite", 1914 1913 "reqwest", 1915 1914 "serde", 1915 + "serde_bytes", 1916 1916 "serde_html_form", 1917 1917 "serde_ipld_dagcbor", 1918 1918 "serde_json", 1919 1919 "signature", 1920 1920 "smol_str", 1921 + "spin 0.10.0", 1921 1922 "thiserror 2.0.17", 1922 1923 "tokio", 1923 1924 "tokio-tungstenite-wasm", ··· 1928 1929 1929 1930 [[package]] 1930 1931 name = "jacquard-derive" 1931 - version = "0.9.3" 1932 + version = "0.9.5" 1933 + source = "git+https://tangled.org/nonbinary.computer/jacquard#5bcf7f8e87324b8e67fc273c678d0490c9c6d15b" 1932 1934 dependencies = [ 1933 1935 "heck 0.5.0", 1934 1936 "jacquard-lexicon", 1935 1937 "proc-macro2", 1936 1938 "quote", 1937 - "syn 2.0.111", 1939 + "syn 2.0.113", 1938 1940 ] 1939 1941 1940 1942 [[package]] 1941 1943 name = "jacquard-identity" 1942 - version = "0.9.2" 1944 + version = "0.9.5" 1945 + source = "git+https://tangled.org/nonbinary.computer/jacquard#5bcf7f8e87324b8e67fc273c678d0490c9c6d15b" 1943 1946 dependencies = [ 1944 1947 "bon", 1945 1948 "bytes", ··· 1949 1952 "jacquard-common", 1950 1953 "jacquard-lexicon", 1951 1954 "miette", 1952 - "mini-moka", 1955 + "mini-moka-wasm", 1956 + "n0-future 0.1.3", 1953 1957 "percent-encoding", 1954 1958 "reqwest", 1955 1959 "serde", ··· 1964 1968 1965 1969 [[package]] 1966 1970 name = "jacquard-lexicon" 1967 - version = "0.9.2" 1971 + version = "0.9.5" 1972 + source = "git+https://tangled.org/nonbinary.computer/jacquard#5bcf7f8e87324b8e67fc273c678d0490c9c6d15b" 1968 1973 dependencies = [ 1969 1974 "cid", 1970 1975 "dashmap", ··· 1982 1987 "serde_repr", 1983 1988 "serde_with", 1984 1989 "sha2", 1985 - "syn 2.0.111", 1990 + "syn 2.0.113", 1986 1991 "thiserror 2.0.17", 1987 1992 "unicode-segmentation", 1988 1993 ] 1989 1994 1990 1995 [[package]] 1991 1996 name = "jacquard-oauth" 1992 - version = "0.9.2" 1997 + version = "0.9.6" 1998 + source = "git+https://tangled.org/nonbinary.computer/jacquard#5bcf7f8e87324b8e67fc273c678d0490c9c6d15b" 1993 1999 dependencies = [ 1994 2000 "base64 0.22.1", 1995 2001 "bytes", ··· 2077 2083 2078 2084 [[package]] 2079 2085 name = "js-sys" 2080 - version = "0.3.82" 2086 + version = "0.3.83" 2081 2087 source = "registry+https://github.com/rust-lang/crates.io-index" 2082 - checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" 2088 + checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" 2083 2089 dependencies = [ 2084 2090 "once_cell", 2085 2091 "wasm-bindgen", ··· 2098 2104 ] 2099 2105 2100 2106 [[package]] 2101 - name = "langtag" 2102 - version = "0.4.0" 2103 - source = "registry+https://github.com/rust-lang/crates.io-index" 2104 - checksum = "9ecb4c689a30e48ebeaa14237f34037e300dd072e6ad21a9ec72e810ff3c6600" 2105 - dependencies = [ 2106 - "serde", 2107 - "static-regular-grammar", 2108 - "thiserror 1.0.69", 2109 - ] 2110 - 2111 - [[package]] 2112 2107 name = "lazy_static" 2113 2108 version = "1.5.0" 2114 2109 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2119 2114 2120 2115 [[package]] 2121 2116 name = "libc" 2122 - version = "0.2.177" 2117 + version = "0.2.179" 2123 2118 source = "registry+https://github.com/rust-lang/crates.io-index" 2124 - checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" 2119 + checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f" 2125 2120 2126 2121 [[package]] 2127 2122 name = "libm" ··· 2131 2126 2132 2127 [[package]] 2133 2128 name = "libredox" 2134 - version = "0.1.10" 2129 + version = "0.1.12" 2135 2130 source = "registry+https://github.com/rust-lang/crates.io-index" 2136 - checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" 2131 + checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" 2137 2132 dependencies = [ 2138 2133 "bitflags", 2139 2134 "libc", 2140 - "redox_syscall", 2135 + "redox_syscall 0.7.0", 2141 2136 ] 2142 2137 2143 2138 [[package]] ··· 2169 2164 2170 2165 [[package]] 2171 2166 name = "log" 2172 - version = "0.4.28" 2167 + version = "0.4.29" 2173 2168 source = "registry+https://github.com/rust-lang/crates.io-index" 2174 - checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 2169 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 2175 2170 2176 2171 [[package]] 2177 2172 name = "loom" ··· 2208 2203 checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" 2209 2204 2210 2205 [[package]] 2206 + name = "maitake-sync" 2207 + version = "0.1.2" 2208 + source = "registry+https://github.com/rust-lang/crates.io-index" 2209 + checksum = "6816ab14147f80234c675b80ed6dc4f440d8a1cefc158e766067aedb84c0bcd5" 2210 + dependencies = [ 2211 + "cordyceps", 2212 + "loom", 2213 + "mycelium-bitfield", 2214 + "pin-project", 2215 + "portable-atomic", 2216 + ] 2217 + 2218 + [[package]] 2211 2219 name = "markup5ever" 2212 2220 version = "0.12.1" 2213 2221 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2292 2300 dependencies = [ 2293 2301 "proc-macro2", 2294 2302 "quote", 2295 - "syn 2.0.111", 2303 + "syn 2.0.113", 2296 2304 ] 2297 2305 2298 2306 [[package]] ··· 2312 2320 ] 2313 2321 2314 2322 [[package]] 2315 - name = "mini-moka" 2323 + name = "mini-moka-wasm" 2316 2324 version = "0.10.99" 2325 + source = "git+https://tangled.org/nonbinary.computer/jacquard#5bcf7f8e87324b8e67fc273c678d0490c9c6d15b" 2317 2326 dependencies = [ 2318 2327 "crossbeam-channel", 2319 2328 "crossbeam-utils", ··· 2325 2334 ] 2326 2335 2327 2336 [[package]] 2328 - name = "minimal-lexical" 2329 - version = "0.2.1" 2330 - source = "registry+https://github.com/rust-lang/crates.io-index" 2331 - checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 2332 - 2333 - [[package]] 2334 2337 name = "miniz_oxide" 2335 2338 version = "0.8.9" 2336 2339 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2342 2345 2343 2346 [[package]] 2344 2347 name = "mio" 2345 - version = "1.1.0" 2348 + version = "1.1.1" 2346 2349 source = "registry+https://github.com/rust-lang/crates.io-index" 2347 - checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" 2350 + checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" 2348 2351 dependencies = [ 2349 2352 "libc", 2350 2353 "wasi", ··· 2393 2396 ] 2394 2397 2395 2398 [[package]] 2399 + name = "mycelium-bitfield" 2400 + version = "0.1.5" 2401 + source = "registry+https://github.com/rust-lang/crates.io-index" 2402 + checksum = "24e0cc5e2c585acbd15c5ce911dff71e1f4d5313f43345873311c4f5efd741cc" 2403 + 2404 + [[package]] 2396 2405 name = "n0-future" 2397 2406 version = "0.1.3" 2398 2407 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2447 2456 checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" 2448 2457 2449 2458 [[package]] 2450 - name = "nom" 2451 - version = "7.1.3" 2452 - source = "registry+https://github.com/rust-lang/crates.io-index" 2453 - checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 2454 - dependencies = [ 2455 - "memchr", 2456 - "minimal-lexical", 2457 - ] 2458 - 2459 - [[package]] 2460 2459 name = "nu-ansi-term" 2461 2460 version = "0.50.3" 2462 2461 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2590 2589 2591 2590 [[package]] 2592 2591 name = "openssl-probe" 2593 - version = "0.1.6" 2592 + version = "0.2.0" 2594 2593 source = "registry+https://github.com/rust-lang/crates.io-index" 2595 - checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 2594 + checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" 2596 2595 2597 2596 [[package]] 2598 2597 name = "option-ext" ··· 2621 2620 "proc-macro2", 2622 2621 "proc-macro2-diagnostics", 2623 2622 "quote", 2624 - "syn 2.0.111", 2623 + "syn 2.0.113", 2625 2624 ] 2626 2625 2627 2626 [[package]] ··· 2631 2630 checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" 2632 2631 2633 2632 [[package]] 2633 + name = "oxilangtag" 2634 + version = "0.1.5" 2635 + source = "registry+https://github.com/rust-lang/crates.io-index" 2636 + checksum = "23f3f87617a86af77fa3691e6350483e7154c2ead9f1261b75130e21ca0f8acb" 2637 + dependencies = [ 2638 + "serde", 2639 + ] 2640 + 2641 + [[package]] 2634 2642 name = "p256" 2635 2643 version = "0.13.2" 2636 2644 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2676 2684 dependencies = [ 2677 2685 "cfg-if", 2678 2686 "libc", 2679 - "redox_syscall", 2687 + "redox_syscall 0.5.18", 2680 2688 "smallvec", 2681 - "windows-link 0.2.1", 2689 + "windows-link", 2682 2690 ] 2683 2691 2684 2692 [[package]] ··· 2751 2759 dependencies = [ 2752 2760 "proc-macro2", 2753 2761 "quote", 2754 - "syn 2.0.111", 2762 + "syn 2.0.113", 2755 2763 ] 2756 2764 2757 2765 [[package]] ··· 2789 2797 2790 2798 [[package]] 2791 2799 name = "portable-atomic" 2792 - version = "1.11.1" 2800 + version = "1.13.0" 2793 2801 source = "registry+https://github.com/rust-lang/crates.io-index" 2794 - checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 2802 + checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" 2803 + 2804 + [[package]] 2805 + name = "postcard" 2806 + version = "1.1.3" 2807 + source = "registry+https://github.com/rust-lang/crates.io-index" 2808 + checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" 2809 + dependencies = [ 2810 + "cobs", 2811 + "embedded-io 0.4.0", 2812 + "embedded-io 0.6.1", 2813 + "heapless", 2814 + "serde", 2815 + ] 2795 2816 2796 2817 [[package]] 2797 2818 name = "potential_utf" ··· 2830 2851 checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 2831 2852 dependencies = [ 2832 2853 "proc-macro2", 2833 - "syn 2.0.111", 2854 + "syn 2.0.113", 2834 2855 ] 2835 2856 2836 2857 [[package]] ··· 2843 2864 ] 2844 2865 2845 2866 [[package]] 2846 - name = "proc-macro-error" 2847 - version = "1.0.4" 2848 - source = "registry+https://github.com/rust-lang/crates.io-index" 2849 - checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 2850 - dependencies = [ 2851 - "proc-macro-error-attr", 2852 - "proc-macro2", 2853 - "quote", 2854 - "syn 1.0.109", 2855 - "version_check", 2856 - ] 2857 - 2858 - [[package]] 2859 - name = "proc-macro-error-attr" 2860 - version = "1.0.4" 2861 - source = "registry+https://github.com/rust-lang/crates.io-index" 2862 - checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 2863 - dependencies = [ 2864 - "proc-macro2", 2865 - "quote", 2866 - "version_check", 2867 - ] 2868 - 2869 - [[package]] 2870 2867 name = "proc-macro2" 2871 - version = "1.0.103" 2868 + version = "1.0.105" 2872 2869 source = "registry+https://github.com/rust-lang/crates.io-index" 2873 - checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" 2870 + checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" 2874 2871 dependencies = [ 2875 2872 "unicode-ident", 2876 2873 ] ··· 2883 2880 dependencies = [ 2884 2881 "proc-macro2", 2885 2882 "quote", 2886 - "syn 2.0.111", 2883 + "syn 2.0.113", 2887 2884 "version_check", 2888 2885 "yansi", 2889 2886 ] ··· 2951 2948 2952 2949 [[package]] 2953 2950 name = "quote" 2954 - version = "1.0.42" 2951 + version = "1.0.43" 2955 2952 source = "registry+https://github.com/rust-lang/crates.io-index" 2956 - checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 2953 + checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" 2957 2954 dependencies = [ 2958 2955 "proc-macro2", 2959 2956 ] ··· 3024 3021 ] 3025 3022 3026 3023 [[package]] 3027 - name = "range-traits" 3028 - version = "0.3.2" 3024 + name = "redox_syscall" 3025 + version = "0.5.18" 3029 3026 source = "registry+https://github.com/rust-lang/crates.io-index" 3030 - checksum = "d20581732dd76fa913c7dff1a2412b714afe3573e94d41c34719de73337cc8ab" 3027 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 3028 + dependencies = [ 3029 + "bitflags", 3030 + ] 3031 3031 3032 3032 [[package]] 3033 3033 name = "redox_syscall" 3034 - version = "0.5.18" 3034 + version = "0.7.0" 3035 3035 source = "registry+https://github.com/rust-lang/crates.io-index" 3036 - checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 3036 + checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" 3037 3037 dependencies = [ 3038 3038 "bitflags", 3039 3039 ] ··· 3047 3047 "getrandom 0.2.16", 3048 3048 "libredox", 3049 3049 "thiserror 2.0.17", 3050 - ] 3051 - 3052 - [[package]] 3053 - name = "ref-cast" 3054 - version = "1.0.25" 3055 - source = "registry+https://github.com/rust-lang/crates.io-index" 3056 - checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" 3057 - dependencies = [ 3058 - "ref-cast-impl", 3059 - ] 3060 - 3061 - [[package]] 3062 - name = "ref-cast-impl" 3063 - version = "1.0.25" 3064 - source = "registry+https://github.com/rust-lang/crates.io-index" 3065 - checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" 3066 - dependencies = [ 3067 - "proc-macro2", 3068 - "quote", 3069 - "syn 2.0.111", 3070 3050 ] 3071 3051 3072 3052 [[package]] ··· 3106 3086 3107 3087 [[package]] 3108 3088 name = "reqwest" 3109 - version = "0.12.24" 3089 + version = "0.12.28" 3110 3090 source = "registry+https://github.com/rust-lang/crates.io-index" 3111 - checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" 3091 + checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" 3112 3092 dependencies = [ 3113 - "async-compression", 3114 3093 "base64 0.22.1", 3115 3094 "bytes", 3116 3095 "encoding_rs", ··· 3205 3184 3206 3185 [[package]] 3207 3186 name = "rsa" 3208 - version = "0.9.9" 3187 + version = "0.9.10" 3209 3188 source = "registry+https://github.com/rust-lang/crates.io-index" 3210 - checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88" 3189 + checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" 3211 3190 dependencies = [ 3212 3191 "const-oid", 3213 3192 "digest", ··· 3236 3215 checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 3237 3216 3238 3217 [[package]] 3218 + name = "rustc_version" 3219 + version = "0.4.1" 3220 + source = "registry+https://github.com/rust-lang/crates.io-index" 3221 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 3222 + dependencies = [ 3223 + "semver", 3224 + ] 3225 + 3226 + [[package]] 3239 3227 name = "rustix" 3240 - version = "1.1.2" 3228 + version = "1.1.3" 3241 3229 source = "registry+https://github.com/rust-lang/crates.io-index" 3242 - checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" 3230 + checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" 3243 3231 dependencies = [ 3244 3232 "bitflags", 3245 3233 "errno", ··· 3250 3238 3251 3239 [[package]] 3252 3240 name = "rustls" 3253 - version = "0.23.35" 3241 + version = "0.23.36" 3254 3242 source = "registry+https://github.com/rust-lang/crates.io-index" 3255 - checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" 3243 + checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" 3256 3244 dependencies = [ 3257 3245 "once_cell", 3258 3246 "ring", ··· 3264 3252 3265 3253 [[package]] 3266 3254 name = "rustls-native-certs" 3267 - version = "0.8.2" 3255 + version = "0.8.3" 3268 3256 source = "registry+https://github.com/rust-lang/crates.io-index" 3269 - checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" 3257 + checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" 3270 3258 dependencies = [ 3271 3259 "openssl-probe", 3272 3260 "rustls-pki-types", ··· 3276 3264 3277 3265 [[package]] 3278 3266 name = "rustls-pki-types" 3279 - version = "1.13.0" 3267 + version = "1.13.2" 3280 3268 source = "registry+https://github.com/rust-lang/crates.io-index" 3281 - checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" 3269 + checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" 3282 3270 dependencies = [ 3283 3271 "web-time", 3284 3272 "zeroize", ··· 3303 3291 3304 3292 [[package]] 3305 3293 name = "ryu" 3306 - version = "1.0.20" 3294 + version = "1.0.22" 3307 3295 source = "registry+https://github.com/rust-lang/crates.io-index" 3308 - checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 3296 + checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" 3309 3297 3310 3298 [[package]] 3311 3299 name = "safemem" ··· 3332 3320 ] 3333 3321 3334 3322 [[package]] 3335 - name = "schemars" 3336 - version = "0.9.0" 3337 - source = "registry+https://github.com/rust-lang/crates.io-index" 3338 - checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" 3339 - dependencies = [ 3340 - "dyn-clone", 3341 - "ref-cast", 3342 - "serde", 3343 - "serde_json", 3344 - ] 3345 - 3346 - [[package]] 3347 - name = "schemars" 3348 - version = "1.1.0" 3349 - source = "registry+https://github.com/rust-lang/crates.io-index" 3350 - checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" 3351 - dependencies = [ 3352 - "dyn-clone", 3353 - "ref-cast", 3354 - "serde", 3355 - "serde_json", 3356 - ] 3357 - 3358 - [[package]] 3359 3323 name = "scoped-tls" 3360 3324 version = "1.0.1" 3361 3325 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3403 3367 "core-foundation-sys", 3404 3368 "libc", 3405 3369 ] 3370 + 3371 + [[package]] 3372 + name = "semver" 3373 + version = "1.0.27" 3374 + source = "registry+https://github.com/rust-lang/crates.io-index" 3375 + checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 3406 3376 3407 3377 [[package]] 3408 3378 name = "send_wrapper" ··· 3447 3417 dependencies = [ 3448 3418 "proc-macro2", 3449 3419 "quote", 3450 - "syn 2.0.111", 3420 + "syn 2.0.113", 3451 3421 ] 3452 3422 3453 3423 [[package]] 3454 3424 name = "serde_html_form" 3455 - version = "0.2.8" 3425 + version = "0.3.2" 3456 3426 source = "registry+https://github.com/rust-lang/crates.io-index" 3457 - checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 3427 + checksum = "2acf96b1d9364968fce46ebb548f1c0e1d7eceae27bdff73865d42e6c7369d94" 3458 3428 dependencies = [ 3459 3429 "form_urlencoded", 3460 - "indexmap 2.12.1", 3430 + "indexmap", 3461 3431 "itoa", 3462 - "ryu", 3463 3432 "serde_core", 3464 3433 ] 3465 3434 ··· 3477 3446 3478 3447 [[package]] 3479 3448 name = "serde_json" 3480 - version = "1.0.145" 3449 + version = "1.0.149" 3481 3450 source = "registry+https://github.com/rust-lang/crates.io-index" 3482 - checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 3451 + checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" 3483 3452 dependencies = [ 3484 3453 "itoa", 3485 3454 "memchr", 3486 - "ryu", 3487 3455 "serde", 3488 3456 "serde_core", 3457 + "zmij", 3489 3458 ] 3490 3459 3491 3460 [[package]] ··· 3507 3476 dependencies = [ 3508 3477 "proc-macro2", 3509 3478 "quote", 3510 - "syn 2.0.111", 3479 + "syn 2.0.113", 3511 3480 ] 3512 3481 3513 3482 [[package]] ··· 3524 3493 3525 3494 [[package]] 3526 3495 name = "serde_with" 3527 - version = "3.16.0" 3496 + version = "3.16.1" 3528 3497 source = "registry+https://github.com/rust-lang/crates.io-index" 3529 - checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" 3498 + checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" 3530 3499 dependencies = [ 3531 3500 "base64 0.22.1", 3532 3501 "chrono", 3533 3502 "hex", 3534 - "indexmap 1.9.3", 3535 - "indexmap 2.12.1", 3536 - "schemars 0.9.0", 3537 - "schemars 1.1.0", 3538 3503 "serde_core", 3539 3504 "serde_json", 3540 3505 "serde_with_macros", ··· 3543 3508 3544 3509 [[package]] 3545 3510 name = "serde_with_macros" 3546 - version = "3.16.0" 3511 + version = "3.16.1" 3547 3512 source = "registry+https://github.com/rust-lang/crates.io-index" 3548 - checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" 3513 + checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" 3549 3514 dependencies = [ 3550 3515 "darling", 3551 3516 "proc-macro2", 3552 3517 "quote", 3553 - "syn 2.0.111", 3518 + "syn 2.0.113", 3554 3519 ] 3555 3520 3556 3521 [[package]] ··· 3607 3572 3608 3573 [[package]] 3609 3574 name = "signal-hook-registry" 3610 - version = "1.4.7" 3575 + version = "1.4.8" 3611 3576 source = "registry+https://github.com/rust-lang/crates.io-index" 3612 - checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" 3577 + checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" 3613 3578 dependencies = [ 3579 + "errno", 3614 3580 "libc", 3615 3581 ] 3616 3582 ··· 3626 3592 3627 3593 [[package]] 3628 3594 name = "simd-adler32" 3629 - version = "0.3.7" 3595 + version = "0.3.8" 3630 3596 source = "registry+https://github.com/rust-lang/crates.io-index" 3631 - checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" 3597 + checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" 3632 3598 3633 3599 [[package]] 3634 3600 name = "siphasher" ··· 3683 3649 version = "0.9.8" 3684 3650 source = "registry+https://github.com/rust-lang/crates.io-index" 3685 3651 checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 3652 + dependencies = [ 3653 + "lock_api", 3654 + ] 3686 3655 3687 3656 [[package]] 3688 3657 name = "spin" ··· 3707 3676 checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 3708 3677 3709 3678 [[package]] 3710 - name = "static-regular-grammar" 3711 - version = "2.0.2" 3712 - source = "registry+https://github.com/rust-lang/crates.io-index" 3713 - checksum = "4f4a6c40247579acfbb138c3cd7de3dab113ab4ac6227f1b7de7d626ee667957" 3714 - dependencies = [ 3715 - "abnf", 3716 - "btree-range-map", 3717 - "ciborium", 3718 - "hex_fmt", 3719 - "indoc", 3720 - "proc-macro-error", 3721 - "proc-macro2", 3722 - "quote", 3723 - "serde", 3724 - "sha2", 3725 - "syn 2.0.111", 3726 - "thiserror 1.0.69", 3727 - ] 3728 - 3729 - [[package]] 3730 3679 name = "static_assertions" 3731 3680 version = "1.1.0" 3732 3681 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3780 3729 3781 3730 [[package]] 3782 3731 name = "supports-hyperlinks" 3783 - version = "3.1.0" 3732 + version = "3.2.0" 3784 3733 source = "registry+https://github.com/rust-lang/crates.io-index" 3785 - checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" 3734 + checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91" 3786 3735 3787 3736 [[package]] 3788 3737 name = "supports-unicode" ··· 3803 3752 3804 3753 [[package]] 3805 3754 name = "syn" 3806 - version = "2.0.111" 3755 + version = "2.0.113" 3807 3756 source = "registry+https://github.com/rust-lang/crates.io-index" 3808 - checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" 3757 + checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4" 3809 3758 dependencies = [ 3810 3759 "proc-macro2", 3811 3760 "quote", ··· 3829 3778 dependencies = [ 3830 3779 "proc-macro2", 3831 3780 "quote", 3832 - "syn 2.0.111", 3781 + "syn 2.0.113", 3833 3782 ] 3834 3783 3835 3784 [[package]] ··· 3861 3810 3862 3811 [[package]] 3863 3812 name = "tempfile" 3864 - version = "3.23.0" 3813 + version = "3.24.0" 3865 3814 source = "registry+https://github.com/rust-lang/crates.io-index" 3866 - checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" 3815 + checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" 3867 3816 dependencies = [ 3868 3817 "fastrand", 3869 3818 "getrandom 0.3.4", ··· 3929 3878 dependencies = [ 3930 3879 "proc-macro2", 3931 3880 "quote", 3932 - "syn 2.0.111", 3881 + "syn 2.0.113", 3933 3882 ] 3934 3883 3935 3884 [[package]] ··· 3940 3889 dependencies = [ 3941 3890 "proc-macro2", 3942 3891 "quote", 3943 - "syn 2.0.111", 3892 + "syn 2.0.113", 3944 3893 ] 3945 3894 3946 3895 [[package]] ··· 3968 3917 checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" 3969 3918 dependencies = [ 3970 3919 "deranged", 3971 - "itoa", 3972 3920 "libc", 3973 3921 "num-conv", 3974 3922 "num_threads", 3975 3923 "powerfmt", 3976 3924 "serde", 3977 3925 "time-core", 3978 - "time-macros", 3979 3926 ] 3980 3927 3981 3928 [[package]] ··· 3983 3930 version = "0.1.6" 3984 3931 source = "registry+https://github.com/rust-lang/crates.io-index" 3985 3932 checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" 3986 - 3987 - [[package]] 3988 - name = "time-macros" 3989 - version = "0.2.24" 3990 - source = "registry+https://github.com/rust-lang/crates.io-index" 3991 - checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" 3992 - dependencies = [ 3993 - "num-conv", 3994 - "time-core", 3995 - ] 3996 3933 3997 3934 [[package]] 3998 3935 name = "tiny_http" ··· 4033 3970 4034 3971 [[package]] 4035 3972 name = "tokio" 4036 - version = "1.48.0" 3973 + version = "1.49.0" 4037 3974 source = "registry+https://github.com/rust-lang/crates.io-index" 4038 - checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" 3975 + checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" 4039 3976 dependencies = [ 4040 3977 "bytes", 4041 3978 "libc", ··· 4056 3993 dependencies = [ 4057 3994 "proc-macro2", 4058 3995 "quote", 4059 - "syn 2.0.111", 3996 + "syn 2.0.113", 4060 3997 ] 4061 3998 4062 3999 [[package]] ··· 4106 4043 4107 4044 [[package]] 4108 4045 name = "tokio-util" 4109 - version = "0.7.17" 4046 + version = "0.7.18" 4110 4047 source = "registry+https://github.com/rust-lang/crates.io-index" 4111 - checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" 4048 + checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" 4112 4049 dependencies = [ 4113 4050 "bytes", 4114 4051 "futures-core", ··· 4136 4073 4137 4074 [[package]] 4138 4075 name = "tower-http" 4139 - version = "0.6.7" 4076 + version = "0.6.8" 4140 4077 source = "registry+https://github.com/rust-lang/crates.io-index" 4141 - checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" 4078 + checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 4142 4079 dependencies = [ 4143 4080 "async-compression", 4144 4081 "bitflags", ··· 4177 4114 4178 4115 [[package]] 4179 4116 name = "tracing" 4180 - version = "0.1.41" 4117 + version = "0.1.44" 4181 4118 source = "registry+https://github.com/rust-lang/crates.io-index" 4182 - checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 4119 + checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" 4183 4120 dependencies = [ 4184 4121 "log", 4185 4122 "pin-project-lite", ··· 4189 4126 4190 4127 [[package]] 4191 4128 name = "tracing-attributes" 4192 - version = "0.1.30" 4129 + version = "0.1.31" 4193 4130 source = "registry+https://github.com/rust-lang/crates.io-index" 4194 - checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" 4131 + checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" 4195 4132 dependencies = [ 4196 4133 "proc-macro2", 4197 4134 "quote", 4198 - "syn 2.0.111", 4135 + "syn 2.0.113", 4199 4136 ] 4200 4137 4201 4138 [[package]] 4202 4139 name = "tracing-core" 4203 - version = "0.1.34" 4140 + version = "0.1.36" 4204 4141 source = "registry+https://github.com/rust-lang/crates.io-index" 4205 - checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 4142 + checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" 4206 4143 dependencies = [ 4207 4144 "once_cell", 4208 4145 "valuable", ··· 4221 4158 4222 4159 [[package]] 4223 4160 name = "tracing-subscriber" 4224 - version = "0.3.20" 4161 + version = "0.3.22" 4225 4162 source = "registry+https://github.com/rust-lang/crates.io-index" 4226 - checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" 4163 + checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" 4227 4164 dependencies = [ 4228 4165 "matchers", 4229 4166 "nu-ansi-term", ··· 4245 4182 dependencies = [ 4246 4183 "proc-macro2", 4247 4184 "quote", 4248 - "syn 2.0.111", 4185 + "syn 2.0.113", 4249 4186 ] 4250 4187 4251 4188 [[package]] ··· 4297 4234 4298 4235 [[package]] 4299 4236 name = "unicase" 4300 - version = "2.8.1" 4237 + version = "2.9.0" 4301 4238 source = "registry+https://github.com/rust-lang/crates.io-index" 4302 - checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" 4239 + checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" 4303 4240 4304 4241 [[package]] 4305 4242 name = "unicode-ident" ··· 4351 4288 4352 4289 [[package]] 4353 4290 name = "url" 4354 - version = "2.5.7" 4291 + version = "2.5.8" 4355 4292 source = "registry+https://github.com/rust-lang/crates.io-index" 4356 - checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" 4293 + checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" 4357 4294 dependencies = [ 4358 4295 "form_urlencoded", 4359 4296 "idna", 4360 4297 "percent-encoding", 4361 4298 "serde", 4299 + "serde_derive", 4362 4300 ] 4363 4301 4364 4302 [[package]] ··· 4433 4371 4434 4372 [[package]] 4435 4373 name = "wasm-bindgen" 4436 - version = "0.2.105" 4374 + version = "0.2.106" 4437 4375 source = "registry+https://github.com/rust-lang/crates.io-index" 4438 - checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" 4376 + checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" 4439 4377 dependencies = [ 4440 4378 "cfg-if", 4441 4379 "once_cell", ··· 4446 4384 4447 4385 [[package]] 4448 4386 name = "wasm-bindgen-futures" 4449 - version = "0.4.55" 4387 + version = "0.4.56" 4450 4388 source = "registry+https://github.com/rust-lang/crates.io-index" 4451 - checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" 4389 + checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" 4452 4390 dependencies = [ 4453 4391 "cfg-if", 4454 4392 "js-sys", ··· 4459 4397 4460 4398 [[package]] 4461 4399 name = "wasm-bindgen-macro" 4462 - version = "0.2.105" 4400 + version = "0.2.106" 4463 4401 source = "registry+https://github.com/rust-lang/crates.io-index" 4464 - checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" 4402 + checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" 4465 4403 dependencies = [ 4466 4404 "quote", 4467 4405 "wasm-bindgen-macro-support", ··· 4469 4407 4470 4408 [[package]] 4471 4409 name = "wasm-bindgen-macro-support" 4472 - version = "0.2.105" 4410 + version = "0.2.106" 4473 4411 source = "registry+https://github.com/rust-lang/crates.io-index" 4474 - checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" 4412 + checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" 4475 4413 dependencies = [ 4476 4414 "bumpalo", 4477 4415 "proc-macro2", 4478 4416 "quote", 4479 - "syn 2.0.111", 4417 + "syn 2.0.113", 4480 4418 "wasm-bindgen-shared", 4481 4419 ] 4482 4420 4483 4421 [[package]] 4484 4422 name = "wasm-bindgen-shared" 4485 - version = "0.2.105" 4423 + version = "0.2.106" 4486 4424 source = "registry+https://github.com/rust-lang/crates.io-index" 4487 - checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" 4425 + checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" 4488 4426 dependencies = [ 4489 4427 "unicode-ident", 4490 4428 ] ··· 4504 4442 4505 4443 [[package]] 4506 4444 name = "web-sys" 4507 - version = "0.3.82" 4445 + version = "0.3.83" 4508 4446 source = "registry+https://github.com/rust-lang/crates.io-index" 4509 - checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" 4447 + checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" 4510 4448 dependencies = [ 4511 4449 "js-sys", 4512 4450 "wasm-bindgen", ··· 4552 4490 4553 4491 [[package]] 4554 4492 name = "webpki-roots" 4555 - version = "1.0.4" 4493 + version = "1.0.5" 4556 4494 source = "registry+https://github.com/rust-lang/crates.io-index" 4557 - checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" 4495 + checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" 4558 4496 dependencies = [ 4559 4497 "rustls-pki-types", 4560 4498 ] ··· 4575 4513 ] 4576 4514 4577 4515 [[package]] 4578 - name = "windows" 4579 - version = "0.61.3" 4580 - source = "registry+https://github.com/rust-lang/crates.io-index" 4581 - checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" 4582 - dependencies = [ 4583 - "windows-collections", 4584 - "windows-core 0.61.2", 4585 - "windows-future", 4586 - "windows-link 0.1.3", 4587 - "windows-numerics", 4588 - ] 4589 - 4590 - [[package]] 4591 - name = "windows-collections" 4592 - version = "0.2.0" 4593 - source = "registry+https://github.com/rust-lang/crates.io-index" 4594 - checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" 4595 - dependencies = [ 4596 - "windows-core 0.61.2", 4597 - ] 4598 - 4599 - [[package]] 4600 - name = "windows-core" 4601 - version = "0.61.2" 4602 - source = "registry+https://github.com/rust-lang/crates.io-index" 4603 - checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 4604 - dependencies = [ 4605 - "windows-implement", 4606 - "windows-interface", 4607 - "windows-link 0.1.3", 4608 - "windows-result 0.3.4", 4609 - "windows-strings 0.4.2", 4610 - ] 4611 - 4612 - [[package]] 4613 4516 name = "windows-core" 4614 4517 version = "0.62.2" 4615 4518 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4617 4520 dependencies = [ 4618 4521 "windows-implement", 4619 4522 "windows-interface", 4620 - "windows-link 0.2.1", 4621 - "windows-result 0.4.1", 4622 - "windows-strings 0.5.1", 4623 - ] 4624 - 4625 - [[package]] 4626 - name = "windows-future" 4627 - version = "0.2.1" 4628 - source = "registry+https://github.com/rust-lang/crates.io-index" 4629 - checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" 4630 - dependencies = [ 4631 - "windows-core 0.61.2", 4632 - "windows-link 0.1.3", 4633 - "windows-threading", 4523 + "windows-link", 4524 + "windows-result", 4525 + "windows-strings", 4634 4526 ] 4635 4527 4636 4528 [[package]] ··· 4641 4533 dependencies = [ 4642 4534 "proc-macro2", 4643 4535 "quote", 4644 - "syn 2.0.111", 4536 + "syn 2.0.113", 4645 4537 ] 4646 4538 4647 4539 [[package]] ··· 4652 4544 dependencies = [ 4653 4545 "proc-macro2", 4654 4546 "quote", 4655 - "syn 2.0.111", 4547 + "syn 2.0.113", 4656 4548 ] 4657 4549 4658 4550 [[package]] 4659 4551 name = "windows-link" 4660 - version = "0.1.3" 4661 - source = "registry+https://github.com/rust-lang/crates.io-index" 4662 - checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 4663 - 4664 - [[package]] 4665 - name = "windows-link" 4666 4552 version = "0.2.1" 4667 4553 source = "registry+https://github.com/rust-lang/crates.io-index" 4668 4554 checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 4669 4555 4670 4556 [[package]] 4671 - name = "windows-numerics" 4672 - version = "0.2.0" 4673 - source = "registry+https://github.com/rust-lang/crates.io-index" 4674 - checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" 4675 - dependencies = [ 4676 - "windows-core 0.61.2", 4677 - "windows-link 0.1.3", 4678 - ] 4679 - 4680 - [[package]] 4681 4557 name = "windows-registry" 4682 4558 version = "0.6.1" 4683 4559 source = "registry+https://github.com/rust-lang/crates.io-index" 4684 4560 checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" 4685 4561 dependencies = [ 4686 - "windows-link 0.2.1", 4687 - "windows-result 0.4.1", 4688 - "windows-strings 0.5.1", 4689 - ] 4690 - 4691 - [[package]] 4692 - name = "windows-result" 4693 - version = "0.3.4" 4694 - source = "registry+https://github.com/rust-lang/crates.io-index" 4695 - checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 4696 - dependencies = [ 4697 - "windows-link 0.1.3", 4562 + "windows-link", 4563 + "windows-result", 4564 + "windows-strings", 4698 4565 ] 4699 4566 4700 4567 [[package]] ··· 4703 4570 source = "registry+https://github.com/rust-lang/crates.io-index" 4704 4571 checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 4705 4572 dependencies = [ 4706 - "windows-link 0.2.1", 4707 - ] 4708 - 4709 - [[package]] 4710 - name = "windows-strings" 4711 - version = "0.4.2" 4712 - source = "registry+https://github.com/rust-lang/crates.io-index" 4713 - checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 4714 - dependencies = [ 4715 - "windows-link 0.1.3", 4573 + "windows-link", 4716 4574 ] 4717 4575 4718 4576 [[package]] ··· 4721 4579 source = "registry+https://github.com/rust-lang/crates.io-index" 4722 4580 checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 4723 4581 dependencies = [ 4724 - "windows-link 0.2.1", 4582 + "windows-link", 4725 4583 ] 4726 4584 4727 4585 [[package]] ··· 4775 4633 source = "registry+https://github.com/rust-lang/crates.io-index" 4776 4634 checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 4777 4635 dependencies = [ 4778 - "windows-link 0.2.1", 4636 + "windows-link", 4779 4637 ] 4780 4638 4781 4639 [[package]] ··· 4830 4688 source = "registry+https://github.com/rust-lang/crates.io-index" 4831 4689 checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" 4832 4690 dependencies = [ 4833 - "windows-link 0.2.1", 4691 + "windows-link", 4834 4692 "windows_aarch64_gnullvm 0.53.1", 4835 4693 "windows_aarch64_msvc 0.53.1", 4836 4694 "windows_i686_gnu 0.53.1", ··· 4839 4697 "windows_x86_64_gnu 0.53.1", 4840 4698 "windows_x86_64_gnullvm 0.53.1", 4841 4699 "windows_x86_64_msvc 0.53.1", 4842 - ] 4843 - 4844 - [[package]] 4845 - name = "windows-threading" 4846 - version = "0.1.0" 4847 - source = "registry+https://github.com/rust-lang/crates.io-index" 4848 - checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" 4849 - dependencies = [ 4850 - "windows-link 0.1.3", 4851 4700 ] 4852 4701 4853 4702 [[package]] ··· 5078 4927 "tower-http", 5079 4928 "url", 5080 4929 "walkdir", 4930 + "wisp-lexicons", 4931 + ] 4932 + 4933 + [[package]] 4934 + name = "wisp-lexicons" 4935 + version = "0.1.0" 4936 + dependencies = [ 4937 + "jacquard-common", 4938 + "jacquard-derive", 4939 + "jacquard-lexicon", 4940 + "rustversion", 4941 + "serde", 5081 4942 ] 5082 4943 5083 4944 [[package]] ··· 5128 4989 dependencies = [ 5129 4990 "proc-macro2", 5130 4991 "quote", 5131 - "syn 2.0.111", 4992 + "syn 2.0.113", 5132 4993 "synstructure", 5133 4994 ] 5134 4995 5135 4996 [[package]] 5136 4997 name = "zerocopy" 5137 - version = "0.8.30" 4998 + version = "0.8.32" 5138 4999 source = "registry+https://github.com/rust-lang/crates.io-index" 5139 - checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" 5000 + checksum = "1fabae64378cb18147bb18bca364e63bdbe72a0ffe4adf0addfec8aa166b2c56" 5140 5001 dependencies = [ 5141 5002 "zerocopy-derive", 5142 5003 ] 5143 5004 5144 5005 [[package]] 5145 5006 name = "zerocopy-derive" 5146 - version = "0.8.30" 5007 + version = "0.8.32" 5147 5008 source = "registry+https://github.com/rust-lang/crates.io-index" 5148 - checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" 5009 + checksum = "c9c2d862265a8bb4471d87e033e730f536e2a285cc7cb05dbce09a2a97075f90" 5149 5010 dependencies = [ 5150 5011 "proc-macro2", 5151 5012 "quote", 5152 - "syn 2.0.111", 5013 + "syn 2.0.113", 5153 5014 ] 5154 5015 5155 5016 [[package]] ··· 5169 5030 dependencies = [ 5170 5031 "proc-macro2", 5171 5032 "quote", 5172 - "syn 2.0.111", 5033 + "syn 2.0.113", 5173 5034 "synstructure", 5174 5035 ] 5175 5036 ··· 5212 5073 dependencies = [ 5213 5074 "proc-macro2", 5214 5075 "quote", 5215 - "syn 2.0.111", 5076 + "syn 2.0.113", 5216 5077 ] 5078 + 5079 + [[package]] 5080 + name = "zmij" 5081 + version = "1.0.12" 5082 + source = "registry+https://github.com/rust-lang/crates.io-index" 5083 + checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8"
+15 -14
cli/Cargo.toml
··· 8 8 place_wisp = [] 9 9 10 10 [dependencies] 11 - # jacquard = { git = "https://tangled.org/nekomimi.pet/jacquard", features = ["loopback"] } 12 - # jacquard-oauth = { git = "https://tangled.org/nekomimi.pet/jacquard" } 13 - # jacquard-api = { git = "https://tangled.org/nekomimi.pet/jacquard", features = ["streaming"] } 14 - # jacquard-common = { git = "https://tangled.org/nekomimi.pet/jacquard", features = ["websocket"] } 15 - # jacquard-identity = { git = "https://tangled.org/nekomimi.pet/jacquard", features = ["dns"] } 16 - # jacquard-derive = { git = "https://tangled.org/nekomimi.pet/jacquard" } 17 - # jacquard-lexicon = { git = "https://tangled.org/nekomimi.pet/jacquard" } 18 - jacquard = { path = "../../jacquard/crates/jacquard", features = ["loopback"] } 19 - jacquard-oauth = { path = "../../jacquard/crates/jacquard-oauth" } 20 - jacquard-api = { path = "../../jacquard/crates/jacquard-api", features = ["streaming"] } 21 - jacquard-common = { path = "../../jacquard/crates/jacquard-common", features = ["websocket"] } 22 - jacquard-identity = { path = "../../jacquard/crates/jacquard-identity", features = ["dns"] } 23 - jacquard-derive = { path = "../../jacquard/crates/jacquard-derive" } 24 - jacquard-lexicon = { path = "../../jacquard/crates/jacquard-lexicon" } 11 + jacquard = { git = "https://tangled.org/nonbinary.computer/jacquard", features = ["loopback"] } 12 + jacquard-oauth = { git = "https://tangled.org/nonbinary.computer/jacquard" } 13 + jacquard-api = { git = "https://tangled.org/nonbinary.computer/jacquard", features = ["streaming"] } 14 + jacquard-common = { git = "https://tangled.org/nonbinary.computer/jacquard", features = ["websocket"] } 15 + jacquard-identity = { git = "https://tangled.org/nonbinary.computer/jacquard", features = ["dns"] } 16 + jacquard-derive = { git = "https://tangled.org/nonbinary.computer/jacquard" } 17 + jacquard-lexicon = { git = "https://tangled.org/nonbinary.computer/jacquard" } 18 + wisp-lexicons = { path = "crates/lexicons" } 19 + #jacquard = { path = "../../jacquard/crates/jacquard", features = ["loopback"] } 20 + #jacquard-oauth = { path = "../../jacquard/crates/jacquard-oauth" } 21 + #jacquard-api = { path = "../../jacquard/crates/jacquard-api", features = ["streaming"] } 22 + #jacquard-common = { path = "../../jacquard/crates/jacquard-common", features = ["websocket"] } 23 + #jacquard-identity = { path = "../../jacquard/crates/jacquard-identity", features = ["dns"] } 24 + #jacquard-derive = { path = "../../jacquard/crates/jacquard-derive" } 25 + #jacquard-lexicon = { path = "../../jacquard/crates/jacquard-lexicon" } 25 26 clap = { version = "4.5.51", features = ["derive"] } 26 27 tokio = { version = "1.48", features = ["full"] } 27 28 miette = { version = "7.6.0", features = ["fancy"] }
+72 -2
cli/README.md
··· 32 32 33 33 ## Usage 34 34 35 + ### Commands 36 + 37 + The CLI supports three main commands: 38 + - **deploy**: Upload a site to your PDS (default command) 39 + - **pull**: Download a site from a PDS to a local directory 40 + - **serve**: Serve a site locally with real-time firehose updates 41 + 35 42 ### Basic Deployment 36 43 37 44 Deploy the current directory: 38 45 39 46 ```bash 40 - wisp-cli nekomimi.ppet --path . --site my-site 47 + wisp-cli nekomimi.pet --path . --site my-site 41 48 ``` 42 49 43 50 Deploy a specific directory: ··· 46 53 wisp-cli alice.bsky.social --path ./dist/ --site my-site 47 54 ``` 48 55 56 + Or use the explicit `deploy` subcommand: 57 + 58 + ```bash 59 + wisp-cli deploy alice.bsky.social --path ./dist/ --site my-site 60 + ``` 61 + 62 + ### Pull a Site 63 + 64 + Download a site from a PDS to a local directory: 65 + 66 + ```bash 67 + wisp-cli pull alice.bsky.social --site my-site --path ./downloaded-site 68 + ``` 69 + 70 + This will download all files from the site to the specified directory. 71 + 72 + ### Serve a Site Locally 73 + 74 + Serve a site locally with real-time updates from the firehose: 75 + 76 + ```bash 77 + wisp-cli serve alice.bsky.social --site my-site --path ./site --port 8080 78 + ``` 79 + 80 + This will: 81 + 1. Download the site to the specified path 82 + 2. Start a local server on the specified port (default: 8080) 83 + 3. Watch the firehose for updates and automatically reload files when changed 84 + 49 85 ### Authentication Methods 50 86 51 87 #### OAuth (Recommended) ··· 79 115 80 116 ## Command-Line Options 81 117 118 + ### Deploy Command 119 + 82 120 ``` 83 - wisp-cli [OPTIONS] <INPUT> 121 + wisp-cli [deploy] [OPTIONS] <INPUT> 84 122 85 123 Arguments: 86 124 <INPUT> Handle (e.g., alice.bsky.social), DID, or PDS URL ··· 90 128 -s, --site <SITE> Site name (defaults to directory name) 91 129 --store <STORE> Path to auth store file (only used with OAuth) [default: /tmp/wisp-oauth-session.json] 92 130 --password <PASSWORD> App Password for authentication (alternative to OAuth) 131 + --directory Enable directory listing mode for paths without index files 132 + --spa Enable SPA mode (serve index.html for all routes) 133 + -y, --yes Skip confirmation prompts (automatically accept warnings) 93 134 -h, --help Print help 94 135 -V, --version Print version 136 + ``` 137 + 138 + ### Pull Command 139 + 140 + ``` 141 + wisp-cli pull [OPTIONS] --site <SITE> <INPUT> 142 + 143 + Arguments: 144 + <INPUT> Handle (e.g., alice.bsky.social) or DID 145 + 146 + Options: 147 + -s, --site <SITE> Site name (record key) 148 + -p, --path <PATH> Output directory for the downloaded site [default: .] 149 + -h, --help Print help 150 + ``` 151 + 152 + ### Serve Command 153 + 154 + ``` 155 + wisp-cli serve [OPTIONS] --site <SITE> <INPUT> 156 + 157 + Arguments: 158 + <INPUT> Handle (e.g., alice.bsky.social) or DID 159 + 160 + Options: 161 + -s, --site <SITE> Site name (record key) 162 + -p, --path <PATH> Output directory for the site files [default: .] 163 + -P, --port <PORT> Port to serve on [default: 8080] 164 + -h, --help Print help 95 165 ``` 96 166 97 167 ## How It Works
+15
cli/crates/lexicons/Cargo.toml
··· 1 + [package] 2 + name = "wisp-lexicons" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [features] 7 + default = ["place_wisp"] 8 + place_wisp = [] 9 + 10 + [dependencies] 11 + jacquard-common = { git = "https://tangled.org/nonbinary.computer/jacquard" } 12 + jacquard-derive = { git = "https://tangled.org/nonbinary.computer/jacquard" } 13 + jacquard-lexicon = { git = "https://tangled.org/nonbinary.computer/jacquard" } 14 + serde = { version = "1.0", features = ["derive"] } 15 + rustversion = "1.0"
+43
cli/crates/lexicons/src/builder_types.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + /// Marker type indicating a builder field has been set 7 + pub struct Set<T>(pub T); 8 + impl<T> Set<T> { 9 + /// Extract the inner value 10 + #[inline] 11 + pub fn into_inner(self) -> T { 12 + self.0 13 + } 14 + } 15 + 16 + /// Marker type indicating a builder field has not been set 17 + pub struct Unset; 18 + /// Trait indicating a builder field is set (has a value) 19 + #[rustversion::attr( 20 + since(1.78.0), 21 + diagnostic::on_unimplemented( 22 + message = "the field `{Self}` was not set, but this method requires it to be set", 23 + label = "the field `{Self}` was not set" 24 + ) 25 + )] 26 + pub trait IsSet: private::Sealed {} 27 + /// Trait indicating a builder field is unset (no value yet) 28 + #[rustversion::attr( 29 + since(1.78.0), 30 + diagnostic::on_unimplemented( 31 + message = "the field `{Self}` was already set, but this method requires it to be unset", 32 + label = "the field `{Self}` was already set" 33 + ) 34 + )] 35 + pub trait IsUnset: private::Sealed {} 36 + impl<T> IsSet for Set<T> {} 37 + impl IsUnset for Unset {} 38 + mod private { 39 + /// Sealed trait to prevent external implementations 40 + pub trait Sealed {} 41 + impl<T> Sealed for super::Set<T> {} 42 + impl Sealed for super::Unset {} 43 + }
+11
cli/crates/lexicons/src/lib.rs
··· 1 + extern crate alloc; 2 + 3 + // @generated by jacquard-lexicon. DO NOT EDIT. 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + pub mod builder_types; 9 + 10 + #[cfg(feature = "place_wisp")] 11 + pub mod place_wisp;
+1490
cli/crates/lexicons/src/place_wisp/fs.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: place.wisp.fs 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[jacquard_derive::lexicon] 9 + #[derive( 10 + serde::Serialize, 11 + serde::Deserialize, 12 + Debug, 13 + Clone, 14 + PartialEq, 15 + Eq, 16 + jacquard_derive::IntoStatic 17 + )] 18 + #[serde(rename_all = "camelCase")] 19 + pub struct Directory<'a> { 20 + #[serde(borrow)] 21 + pub entries: Vec<crate::place_wisp::fs::Entry<'a>>, 22 + #[serde(borrow)] 23 + pub r#type: jacquard_common::CowStr<'a>, 24 + } 25 + 26 + pub mod directory_state { 27 + 28 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 29 + #[allow(unused)] 30 + use ::core::marker::PhantomData; 31 + mod sealed { 32 + pub trait Sealed {} 33 + } 34 + /// State trait tracking which required fields have been set 35 + pub trait State: sealed::Sealed { 36 + type Type; 37 + type Entries; 38 + } 39 + /// Empty state - all required fields are unset 40 + pub struct Empty(()); 41 + impl sealed::Sealed for Empty {} 42 + impl State for Empty { 43 + type Type = Unset; 44 + type Entries = Unset; 45 + } 46 + ///State transition - sets the `type` field to Set 47 + pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 48 + impl<S: State> sealed::Sealed for SetType<S> {} 49 + impl<S: State> State for SetType<S> { 50 + type Type = Set<members::r#type>; 51 + type Entries = S::Entries; 52 + } 53 + ///State transition - sets the `entries` field to Set 54 + pub struct SetEntries<S: State = Empty>(PhantomData<fn() -> S>); 55 + impl<S: State> sealed::Sealed for SetEntries<S> {} 56 + impl<S: State> State for SetEntries<S> { 57 + type Type = S::Type; 58 + type Entries = Set<members::entries>; 59 + } 60 + /// Marker types for field names 61 + #[allow(non_camel_case_types)] 62 + pub mod members { 63 + ///Marker type for the `type` field 64 + pub struct r#type(()); 65 + ///Marker type for the `entries` field 66 + pub struct entries(()); 67 + } 68 + } 69 + 70 + /// Builder for constructing an instance of this type 71 + pub struct DirectoryBuilder<'a, S: directory_state::State> { 72 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 73 + __unsafe_private_named: ( 74 + ::core::option::Option<Vec<crate::place_wisp::fs::Entry<'a>>>, 75 + ::core::option::Option<jacquard_common::CowStr<'a>>, 76 + ), 77 + _phantom: ::core::marker::PhantomData<&'a ()>, 78 + } 79 + 80 + impl<'a> Directory<'a> { 81 + /// Create a new builder for this type 82 + pub fn new() -> DirectoryBuilder<'a, directory_state::Empty> { 83 + DirectoryBuilder::new() 84 + } 85 + } 86 + 87 + impl<'a> DirectoryBuilder<'a, directory_state::Empty> { 88 + /// Create a new builder with all fields unset 89 + pub fn new() -> Self { 90 + DirectoryBuilder { 91 + _phantom_state: ::core::marker::PhantomData, 92 + __unsafe_private_named: (None, None), 93 + _phantom: ::core::marker::PhantomData, 94 + } 95 + } 96 + } 97 + 98 + impl<'a, S> DirectoryBuilder<'a, S> 99 + where 100 + S: directory_state::State, 101 + S::Entries: directory_state::IsUnset, 102 + { 103 + /// Set the `entries` field (required) 104 + pub fn entries( 105 + mut self, 106 + value: impl Into<Vec<crate::place_wisp::fs::Entry<'a>>>, 107 + ) -> DirectoryBuilder<'a, directory_state::SetEntries<S>> { 108 + self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 109 + DirectoryBuilder { 110 + _phantom_state: ::core::marker::PhantomData, 111 + __unsafe_private_named: self.__unsafe_private_named, 112 + _phantom: ::core::marker::PhantomData, 113 + } 114 + } 115 + } 116 + 117 + impl<'a, S> DirectoryBuilder<'a, S> 118 + where 119 + S: directory_state::State, 120 + S::Type: directory_state::IsUnset, 121 + { 122 + /// Set the `type` field (required) 123 + pub fn r#type( 124 + mut self, 125 + value: impl Into<jacquard_common::CowStr<'a>>, 126 + ) -> DirectoryBuilder<'a, directory_state::SetType<S>> { 127 + self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 128 + DirectoryBuilder { 129 + _phantom_state: ::core::marker::PhantomData, 130 + __unsafe_private_named: self.__unsafe_private_named, 131 + _phantom: ::core::marker::PhantomData, 132 + } 133 + } 134 + } 135 + 136 + impl<'a, S> DirectoryBuilder<'a, S> 137 + where 138 + S: directory_state::State, 139 + S::Type: directory_state::IsSet, 140 + S::Entries: directory_state::IsSet, 141 + { 142 + /// Build the final struct 143 + pub fn build(self) -> Directory<'a> { 144 + Directory { 145 + entries: self.__unsafe_private_named.0.unwrap(), 146 + r#type: self.__unsafe_private_named.1.unwrap(), 147 + extra_data: Default::default(), 148 + } 149 + } 150 + /// Build the final struct with custom extra_data 151 + pub fn build_with_data( 152 + self, 153 + extra_data: std::collections::BTreeMap< 154 + jacquard_common::smol_str::SmolStr, 155 + jacquard_common::types::value::Data<'a>, 156 + >, 157 + ) -> Directory<'a> { 158 + Directory { 159 + entries: self.__unsafe_private_named.0.unwrap(), 160 + r#type: self.__unsafe_private_named.1.unwrap(), 161 + extra_data: Some(extra_data), 162 + } 163 + } 164 + } 165 + 166 + fn lexicon_doc_place_wisp_fs() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 167 + ::jacquard_lexicon::lexicon::LexiconDoc { 168 + lexicon: ::jacquard_lexicon::lexicon::Lexicon::Lexicon1, 169 + id: ::jacquard_common::CowStr::new_static("place.wisp.fs"), 170 + revision: None, 171 + description: None, 172 + defs: { 173 + let mut map = ::std::collections::BTreeMap::new(); 174 + map.insert( 175 + ::jacquard_common::smol_str::SmolStr::new_static("directory"), 176 + ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 177 + description: None, 178 + required: Some( 179 + vec![ 180 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 181 + ::jacquard_common::smol_str::SmolStr::new_static("entries") 182 + ], 183 + ), 184 + nullable: None, 185 + properties: { 186 + #[allow(unused_mut)] 187 + let mut map = ::std::collections::BTreeMap::new(); 188 + map.insert( 189 + ::jacquard_common::smol_str::SmolStr::new_static("entries"), 190 + ::jacquard_lexicon::lexicon::LexObjectProperty::Array(::jacquard_lexicon::lexicon::LexArray { 191 + description: None, 192 + items: ::jacquard_lexicon::lexicon::LexArrayItem::Ref(::jacquard_lexicon::lexicon::LexRef { 193 + description: None, 194 + r#ref: ::jacquard_common::CowStr::new_static("#entry"), 195 + }), 196 + min_length: None, 197 + max_length: Some(500usize), 198 + }), 199 + ); 200 + map.insert( 201 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 202 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 203 + description: None, 204 + format: None, 205 + default: None, 206 + min_length: None, 207 + max_length: None, 208 + min_graphemes: None, 209 + max_graphemes: None, 210 + r#enum: None, 211 + r#const: None, 212 + known_values: None, 213 + }), 214 + ); 215 + map 216 + }, 217 + }), 218 + ); 219 + map.insert( 220 + ::jacquard_common::smol_str::SmolStr::new_static("entry"), 221 + ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 222 + description: None, 223 + required: Some( 224 + vec![ 225 + ::jacquard_common::smol_str::SmolStr::new_static("name"), 226 + ::jacquard_common::smol_str::SmolStr::new_static("node") 227 + ], 228 + ), 229 + nullable: None, 230 + properties: { 231 + #[allow(unused_mut)] 232 + let mut map = ::std::collections::BTreeMap::new(); 233 + map.insert( 234 + ::jacquard_common::smol_str::SmolStr::new_static("name"), 235 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 236 + description: None, 237 + format: None, 238 + default: None, 239 + min_length: None, 240 + max_length: Some(255usize), 241 + min_graphemes: None, 242 + max_graphemes: None, 243 + r#enum: None, 244 + r#const: None, 245 + known_values: None, 246 + }), 247 + ); 248 + map.insert( 249 + ::jacquard_common::smol_str::SmolStr::new_static("node"), 250 + ::jacquard_lexicon::lexicon::LexObjectProperty::Union(::jacquard_lexicon::lexicon::LexRefUnion { 251 + description: None, 252 + refs: vec![ 253 + ::jacquard_common::CowStr::new_static("#file"), 254 + ::jacquard_common::CowStr::new_static("#directory"), 255 + ::jacquard_common::CowStr::new_static("#subfs") 256 + ], 257 + closed: None, 258 + }), 259 + ); 260 + map 261 + }, 262 + }), 263 + ); 264 + map.insert( 265 + ::jacquard_common::smol_str::SmolStr::new_static("file"), 266 + ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 267 + description: None, 268 + required: Some( 269 + vec![ 270 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 271 + ::jacquard_common::smol_str::SmolStr::new_static("blob") 272 + ], 273 + ), 274 + nullable: None, 275 + properties: { 276 + #[allow(unused_mut)] 277 + let mut map = ::std::collections::BTreeMap::new(); 278 + map.insert( 279 + ::jacquard_common::smol_str::SmolStr::new_static("base64"), 280 + ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 281 + description: None, 282 + default: None, 283 + r#const: None, 284 + }), 285 + ); 286 + map.insert( 287 + ::jacquard_common::smol_str::SmolStr::new_static("blob"), 288 + ::jacquard_lexicon::lexicon::LexObjectProperty::Blob(::jacquard_lexicon::lexicon::LexBlob { 289 + description: None, 290 + accept: None, 291 + max_size: None, 292 + }), 293 + ); 294 + map.insert( 295 + ::jacquard_common::smol_str::SmolStr::new_static("encoding"), 296 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 297 + description: Some( 298 + ::jacquard_common::CowStr::new_static( 299 + "Content encoding (e.g., gzip for compressed files)", 300 + ), 301 + ), 302 + format: None, 303 + default: None, 304 + min_length: None, 305 + max_length: None, 306 + min_graphemes: None, 307 + max_graphemes: None, 308 + r#enum: None, 309 + r#const: None, 310 + known_values: None, 311 + }), 312 + ); 313 + map.insert( 314 + ::jacquard_common::smol_str::SmolStr::new_static("mimeType"), 315 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 316 + description: Some( 317 + ::jacquard_common::CowStr::new_static( 318 + "Original MIME type before compression", 319 + ), 320 + ), 321 + format: None, 322 + default: None, 323 + min_length: None, 324 + max_length: None, 325 + min_graphemes: None, 326 + max_graphemes: None, 327 + r#enum: None, 328 + r#const: None, 329 + known_values: None, 330 + }), 331 + ); 332 + map.insert( 333 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 334 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 335 + description: None, 336 + format: None, 337 + default: None, 338 + min_length: None, 339 + max_length: None, 340 + min_graphemes: None, 341 + max_graphemes: None, 342 + r#enum: None, 343 + r#const: None, 344 + known_values: None, 345 + }), 346 + ); 347 + map 348 + }, 349 + }), 350 + ); 351 + map.insert( 352 + ::jacquard_common::smol_str::SmolStr::new_static("main"), 353 + ::jacquard_lexicon::lexicon::LexUserType::Record(::jacquard_lexicon::lexicon::LexRecord { 354 + description: Some( 355 + ::jacquard_common::CowStr::new_static( 356 + "Virtual filesystem manifest for a Wisp site", 357 + ), 358 + ), 359 + key: None, 360 + record: ::jacquard_lexicon::lexicon::LexRecordRecord::Object(::jacquard_lexicon::lexicon::LexObject { 361 + description: None, 362 + required: Some( 363 + vec![ 364 + ::jacquard_common::smol_str::SmolStr::new_static("site"), 365 + ::jacquard_common::smol_str::SmolStr::new_static("root"), 366 + ::jacquard_common::smol_str::SmolStr::new_static("createdAt") 367 + ], 368 + ), 369 + nullable: None, 370 + properties: { 371 + #[allow(unused_mut)] 372 + let mut map = ::std::collections::BTreeMap::new(); 373 + map.insert( 374 + ::jacquard_common::smol_str::SmolStr::new_static( 375 + "createdAt", 376 + ), 377 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 378 + description: None, 379 + format: Some( 380 + ::jacquard_lexicon::lexicon::LexStringFormat::Datetime, 381 + ), 382 + default: None, 383 + min_length: None, 384 + max_length: None, 385 + min_graphemes: None, 386 + max_graphemes: None, 387 + r#enum: None, 388 + r#const: None, 389 + known_values: None, 390 + }), 391 + ); 392 + map.insert( 393 + ::jacquard_common::smol_str::SmolStr::new_static( 394 + "fileCount", 395 + ), 396 + ::jacquard_lexicon::lexicon::LexObjectProperty::Integer(::jacquard_lexicon::lexicon::LexInteger { 397 + description: None, 398 + default: None, 399 + minimum: Some(0i64), 400 + maximum: Some(1000i64), 401 + r#enum: None, 402 + r#const: None, 403 + }), 404 + ); 405 + map.insert( 406 + ::jacquard_common::smol_str::SmolStr::new_static("root"), 407 + ::jacquard_lexicon::lexicon::LexObjectProperty::Ref(::jacquard_lexicon::lexicon::LexRef { 408 + description: None, 409 + r#ref: ::jacquard_common::CowStr::new_static("#directory"), 410 + }), 411 + ); 412 + map.insert( 413 + ::jacquard_common::smol_str::SmolStr::new_static("site"), 414 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 415 + description: None, 416 + format: None, 417 + default: None, 418 + min_length: None, 419 + max_length: None, 420 + min_graphemes: None, 421 + max_graphemes: None, 422 + r#enum: None, 423 + r#const: None, 424 + known_values: None, 425 + }), 426 + ); 427 + map 428 + }, 429 + }), 430 + }), 431 + ); 432 + map.insert( 433 + ::jacquard_common::smol_str::SmolStr::new_static("subfs"), 434 + ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 435 + description: None, 436 + required: Some( 437 + vec![ 438 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 439 + ::jacquard_common::smol_str::SmolStr::new_static("subject") 440 + ], 441 + ), 442 + nullable: None, 443 + properties: { 444 + #[allow(unused_mut)] 445 + let mut map = ::std::collections::BTreeMap::new(); 446 + map.insert( 447 + ::jacquard_common::smol_str::SmolStr::new_static("flat"), 448 + ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 449 + description: None, 450 + default: None, 451 + r#const: None, 452 + }), 453 + ); 454 + map.insert( 455 + ::jacquard_common::smol_str::SmolStr::new_static("subject"), 456 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 457 + description: Some( 458 + ::jacquard_common::CowStr::new_static( 459 + "AT-URI pointing to a place.wisp.subfs record containing this subtree.", 460 + ), 461 + ), 462 + format: Some( 463 + ::jacquard_lexicon::lexicon::LexStringFormat::AtUri, 464 + ), 465 + default: None, 466 + min_length: None, 467 + max_length: None, 468 + min_graphemes: None, 469 + max_graphemes: None, 470 + r#enum: None, 471 + r#const: None, 472 + known_values: None, 473 + }), 474 + ); 475 + map.insert( 476 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 477 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 478 + description: None, 479 + format: None, 480 + default: None, 481 + min_length: None, 482 + max_length: None, 483 + min_graphemes: None, 484 + max_graphemes: None, 485 + r#enum: None, 486 + r#const: None, 487 + known_values: None, 488 + }), 489 + ); 490 + map 491 + }, 492 + }), 493 + ); 494 + map 495 + }, 496 + } 497 + } 498 + 499 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Directory<'a> { 500 + fn nsid() -> &'static str { 501 + "place.wisp.fs" 502 + } 503 + fn def_name() -> &'static str { 504 + "directory" 505 + } 506 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 507 + lexicon_doc_place_wisp_fs() 508 + } 509 + fn validate( 510 + &self, 511 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 512 + { 513 + let value = &self.entries; 514 + #[allow(unused_comparisons)] 515 + if value.len() > 500usize { 516 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 517 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 518 + "entries", 519 + ), 520 + max: 500usize, 521 + actual: value.len(), 522 + }); 523 + } 524 + } 525 + Ok(()) 526 + } 527 + } 528 + 529 + #[jacquard_derive::lexicon] 530 + #[derive( 531 + serde::Serialize, 532 + serde::Deserialize, 533 + Debug, 534 + Clone, 535 + PartialEq, 536 + Eq, 537 + jacquard_derive::IntoStatic 538 + )] 539 + #[serde(rename_all = "camelCase")] 540 + pub struct Entry<'a> { 541 + #[serde(borrow)] 542 + pub name: jacquard_common::CowStr<'a>, 543 + #[serde(borrow)] 544 + pub node: EntryNode<'a>, 545 + } 546 + 547 + pub mod entry_state { 548 + 549 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 550 + #[allow(unused)] 551 + use ::core::marker::PhantomData; 552 + mod sealed { 553 + pub trait Sealed {} 554 + } 555 + /// State trait tracking which required fields have been set 556 + pub trait State: sealed::Sealed { 557 + type Node; 558 + type Name; 559 + } 560 + /// Empty state - all required fields are unset 561 + pub struct Empty(()); 562 + impl sealed::Sealed for Empty {} 563 + impl State for Empty { 564 + type Node = Unset; 565 + type Name = Unset; 566 + } 567 + ///State transition - sets the `node` field to Set 568 + pub struct SetNode<S: State = Empty>(PhantomData<fn() -> S>); 569 + impl<S: State> sealed::Sealed for SetNode<S> {} 570 + impl<S: State> State for SetNode<S> { 571 + type Node = Set<members::node>; 572 + type Name = S::Name; 573 + } 574 + ///State transition - sets the `name` field to Set 575 + pub struct SetName<S: State = Empty>(PhantomData<fn() -> S>); 576 + impl<S: State> sealed::Sealed for SetName<S> {} 577 + impl<S: State> State for SetName<S> { 578 + type Node = S::Node; 579 + type Name = Set<members::name>; 580 + } 581 + /// Marker types for field names 582 + #[allow(non_camel_case_types)] 583 + pub mod members { 584 + ///Marker type for the `node` field 585 + pub struct node(()); 586 + ///Marker type for the `name` field 587 + pub struct name(()); 588 + } 589 + } 590 + 591 + /// Builder for constructing an instance of this type 592 + pub struct EntryBuilder<'a, S: entry_state::State> { 593 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 594 + __unsafe_private_named: ( 595 + ::core::option::Option<jacquard_common::CowStr<'a>>, 596 + ::core::option::Option<EntryNode<'a>>, 597 + ), 598 + _phantom: ::core::marker::PhantomData<&'a ()>, 599 + } 600 + 601 + impl<'a> Entry<'a> { 602 + /// Create a new builder for this type 603 + pub fn new() -> EntryBuilder<'a, entry_state::Empty> { 604 + EntryBuilder::new() 605 + } 606 + } 607 + 608 + impl<'a> EntryBuilder<'a, entry_state::Empty> { 609 + /// Create a new builder with all fields unset 610 + pub fn new() -> Self { 611 + EntryBuilder { 612 + _phantom_state: ::core::marker::PhantomData, 613 + __unsafe_private_named: (None, None), 614 + _phantom: ::core::marker::PhantomData, 615 + } 616 + } 617 + } 618 + 619 + impl<'a, S> EntryBuilder<'a, S> 620 + where 621 + S: entry_state::State, 622 + S::Name: entry_state::IsUnset, 623 + { 624 + /// Set the `name` field (required) 625 + pub fn name( 626 + mut self, 627 + value: impl Into<jacquard_common::CowStr<'a>>, 628 + ) -> EntryBuilder<'a, entry_state::SetName<S>> { 629 + self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 630 + EntryBuilder { 631 + _phantom_state: ::core::marker::PhantomData, 632 + __unsafe_private_named: self.__unsafe_private_named, 633 + _phantom: ::core::marker::PhantomData, 634 + } 635 + } 636 + } 637 + 638 + impl<'a, S> EntryBuilder<'a, S> 639 + where 640 + S: entry_state::State, 641 + S::Node: entry_state::IsUnset, 642 + { 643 + /// Set the `node` field (required) 644 + pub fn node( 645 + mut self, 646 + value: impl Into<EntryNode<'a>>, 647 + ) -> EntryBuilder<'a, entry_state::SetNode<S>> { 648 + self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 649 + EntryBuilder { 650 + _phantom_state: ::core::marker::PhantomData, 651 + __unsafe_private_named: self.__unsafe_private_named, 652 + _phantom: ::core::marker::PhantomData, 653 + } 654 + } 655 + } 656 + 657 + impl<'a, S> EntryBuilder<'a, S> 658 + where 659 + S: entry_state::State, 660 + S::Node: entry_state::IsSet, 661 + S::Name: entry_state::IsSet, 662 + { 663 + /// Build the final struct 664 + pub fn build(self) -> Entry<'a> { 665 + Entry { 666 + name: self.__unsafe_private_named.0.unwrap(), 667 + node: self.__unsafe_private_named.1.unwrap(), 668 + extra_data: Default::default(), 669 + } 670 + } 671 + /// Build the final struct with custom extra_data 672 + pub fn build_with_data( 673 + self, 674 + extra_data: std::collections::BTreeMap< 675 + jacquard_common::smol_str::SmolStr, 676 + jacquard_common::types::value::Data<'a>, 677 + >, 678 + ) -> Entry<'a> { 679 + Entry { 680 + name: self.__unsafe_private_named.0.unwrap(), 681 + node: self.__unsafe_private_named.1.unwrap(), 682 + extra_data: Some(extra_data), 683 + } 684 + } 685 + } 686 + 687 + #[jacquard_derive::open_union] 688 + #[derive( 689 + serde::Serialize, 690 + serde::Deserialize, 691 + Debug, 692 + Clone, 693 + PartialEq, 694 + Eq, 695 + jacquard_derive::IntoStatic 696 + )] 697 + #[serde(tag = "$type")] 698 + #[serde(bound(deserialize = "'de: 'a"))] 699 + pub enum EntryNode<'a> { 700 + #[serde(rename = "place.wisp.fs#file")] 701 + File(Box<crate::place_wisp::fs::File<'a>>), 702 + #[serde(rename = "place.wisp.fs#directory")] 703 + Directory(Box<crate::place_wisp::fs::Directory<'a>>), 704 + #[serde(rename = "place.wisp.fs#subfs")] 705 + Subfs(Box<crate::place_wisp::fs::Subfs<'a>>), 706 + } 707 + 708 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Entry<'a> { 709 + fn nsid() -> &'static str { 710 + "place.wisp.fs" 711 + } 712 + fn def_name() -> &'static str { 713 + "entry" 714 + } 715 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 716 + lexicon_doc_place_wisp_fs() 717 + } 718 + fn validate( 719 + &self, 720 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 721 + { 722 + let value = &self.name; 723 + #[allow(unused_comparisons)] 724 + if <str>::len(value.as_ref()) > 255usize { 725 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 726 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 727 + "name", 728 + ), 729 + max: 255usize, 730 + actual: <str>::len(value.as_ref()), 731 + }); 732 + } 733 + } 734 + Ok(()) 735 + } 736 + } 737 + 738 + #[jacquard_derive::lexicon] 739 + #[derive( 740 + serde::Serialize, 741 + serde::Deserialize, 742 + Debug, 743 + Clone, 744 + PartialEq, 745 + Eq, 746 + jacquard_derive::IntoStatic 747 + )] 748 + #[serde(rename_all = "camelCase")] 749 + pub struct File<'a> { 750 + /// True if blob content is base64-encoded (used to bypass PDS content sniffing) 751 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 752 + pub base64: std::option::Option<bool>, 753 + /// Content blob ref 754 + #[serde(borrow)] 755 + pub blob: jacquard_common::types::blob::BlobRef<'a>, 756 + /// Content encoding (e.g., gzip for compressed files) 757 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 758 + #[serde(borrow)] 759 + pub encoding: std::option::Option<jacquard_common::CowStr<'a>>, 760 + /// Original MIME type before compression 761 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 762 + #[serde(borrow)] 763 + pub mime_type: std::option::Option<jacquard_common::CowStr<'a>>, 764 + #[serde(borrow)] 765 + pub r#type: jacquard_common::CowStr<'a>, 766 + } 767 + 768 + pub mod file_state { 769 + 770 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 771 + #[allow(unused)] 772 + use ::core::marker::PhantomData; 773 + mod sealed { 774 + pub trait Sealed {} 775 + } 776 + /// State trait tracking which required fields have been set 777 + pub trait State: sealed::Sealed { 778 + type Type; 779 + type Blob; 780 + } 781 + /// Empty state - all required fields are unset 782 + pub struct Empty(()); 783 + impl sealed::Sealed for Empty {} 784 + impl State for Empty { 785 + type Type = Unset; 786 + type Blob = Unset; 787 + } 788 + ///State transition - sets the `type` field to Set 789 + pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 790 + impl<S: State> sealed::Sealed for SetType<S> {} 791 + impl<S: State> State for SetType<S> { 792 + type Type = Set<members::r#type>; 793 + type Blob = S::Blob; 794 + } 795 + ///State transition - sets the `blob` field to Set 796 + pub struct SetBlob<S: State = Empty>(PhantomData<fn() -> S>); 797 + impl<S: State> sealed::Sealed for SetBlob<S> {} 798 + impl<S: State> State for SetBlob<S> { 799 + type Type = S::Type; 800 + type Blob = Set<members::blob>; 801 + } 802 + /// Marker types for field names 803 + #[allow(non_camel_case_types)] 804 + pub mod members { 805 + ///Marker type for the `type` field 806 + pub struct r#type(()); 807 + ///Marker type for the `blob` field 808 + pub struct blob(()); 809 + } 810 + } 811 + 812 + /// Builder for constructing an instance of this type 813 + pub struct FileBuilder<'a, S: file_state::State> { 814 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 815 + __unsafe_private_named: ( 816 + ::core::option::Option<bool>, 817 + ::core::option::Option<jacquard_common::types::blob::BlobRef<'a>>, 818 + ::core::option::Option<jacquard_common::CowStr<'a>>, 819 + ::core::option::Option<jacquard_common::CowStr<'a>>, 820 + ::core::option::Option<jacquard_common::CowStr<'a>>, 821 + ), 822 + _phantom: ::core::marker::PhantomData<&'a ()>, 823 + } 824 + 825 + impl<'a> File<'a> { 826 + /// Create a new builder for this type 827 + pub fn new() -> FileBuilder<'a, file_state::Empty> { 828 + FileBuilder::new() 829 + } 830 + } 831 + 832 + impl<'a> FileBuilder<'a, file_state::Empty> { 833 + /// Create a new builder with all fields unset 834 + pub fn new() -> Self { 835 + FileBuilder { 836 + _phantom_state: ::core::marker::PhantomData, 837 + __unsafe_private_named: (None, None, None, None, None), 838 + _phantom: ::core::marker::PhantomData, 839 + } 840 + } 841 + } 842 + 843 + impl<'a, S: file_state::State> FileBuilder<'a, S> { 844 + /// Set the `base64` field (optional) 845 + pub fn base64(mut self, value: impl Into<Option<bool>>) -> Self { 846 + self.__unsafe_private_named.0 = value.into(); 847 + self 848 + } 849 + /// Set the `base64` field to an Option value (optional) 850 + pub fn maybe_base64(mut self, value: Option<bool>) -> Self { 851 + self.__unsafe_private_named.0 = value; 852 + self 853 + } 854 + } 855 + 856 + impl<'a, S> FileBuilder<'a, S> 857 + where 858 + S: file_state::State, 859 + S::Blob: file_state::IsUnset, 860 + { 861 + /// Set the `blob` field (required) 862 + pub fn blob( 863 + mut self, 864 + value: impl Into<jacquard_common::types::blob::BlobRef<'a>>, 865 + ) -> FileBuilder<'a, file_state::SetBlob<S>> { 866 + self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 867 + FileBuilder { 868 + _phantom_state: ::core::marker::PhantomData, 869 + __unsafe_private_named: self.__unsafe_private_named, 870 + _phantom: ::core::marker::PhantomData, 871 + } 872 + } 873 + } 874 + 875 + impl<'a, S: file_state::State> FileBuilder<'a, S> { 876 + /// Set the `encoding` field (optional) 877 + pub fn encoding( 878 + mut self, 879 + value: impl Into<Option<jacquard_common::CowStr<'a>>>, 880 + ) -> Self { 881 + self.__unsafe_private_named.2 = value.into(); 882 + self 883 + } 884 + /// Set the `encoding` field to an Option value (optional) 885 + pub fn maybe_encoding(mut self, value: Option<jacquard_common::CowStr<'a>>) -> Self { 886 + self.__unsafe_private_named.2 = value; 887 + self 888 + } 889 + } 890 + 891 + impl<'a, S: file_state::State> FileBuilder<'a, S> { 892 + /// Set the `mimeType` field (optional) 893 + pub fn mime_type( 894 + mut self, 895 + value: impl Into<Option<jacquard_common::CowStr<'a>>>, 896 + ) -> Self { 897 + self.__unsafe_private_named.3 = value.into(); 898 + self 899 + } 900 + /// Set the `mimeType` field to an Option value (optional) 901 + pub fn maybe_mime_type( 902 + mut self, 903 + value: Option<jacquard_common::CowStr<'a>>, 904 + ) -> Self { 905 + self.__unsafe_private_named.3 = value; 906 + self 907 + } 908 + } 909 + 910 + impl<'a, S> FileBuilder<'a, S> 911 + where 912 + S: file_state::State, 913 + S::Type: file_state::IsUnset, 914 + { 915 + /// Set the `type` field (required) 916 + pub fn r#type( 917 + mut self, 918 + value: impl Into<jacquard_common::CowStr<'a>>, 919 + ) -> FileBuilder<'a, file_state::SetType<S>> { 920 + self.__unsafe_private_named.4 = ::core::option::Option::Some(value.into()); 921 + FileBuilder { 922 + _phantom_state: ::core::marker::PhantomData, 923 + __unsafe_private_named: self.__unsafe_private_named, 924 + _phantom: ::core::marker::PhantomData, 925 + } 926 + } 927 + } 928 + 929 + impl<'a, S> FileBuilder<'a, S> 930 + where 931 + S: file_state::State, 932 + S::Type: file_state::IsSet, 933 + S::Blob: file_state::IsSet, 934 + { 935 + /// Build the final struct 936 + pub fn build(self) -> File<'a> { 937 + File { 938 + base64: self.__unsafe_private_named.0, 939 + blob: self.__unsafe_private_named.1.unwrap(), 940 + encoding: self.__unsafe_private_named.2, 941 + mime_type: self.__unsafe_private_named.3, 942 + r#type: self.__unsafe_private_named.4.unwrap(), 943 + extra_data: Default::default(), 944 + } 945 + } 946 + /// Build the final struct with custom extra_data 947 + pub fn build_with_data( 948 + self, 949 + extra_data: std::collections::BTreeMap< 950 + jacquard_common::smol_str::SmolStr, 951 + jacquard_common::types::value::Data<'a>, 952 + >, 953 + ) -> File<'a> { 954 + File { 955 + base64: self.__unsafe_private_named.0, 956 + blob: self.__unsafe_private_named.1.unwrap(), 957 + encoding: self.__unsafe_private_named.2, 958 + mime_type: self.__unsafe_private_named.3, 959 + r#type: self.__unsafe_private_named.4.unwrap(), 960 + extra_data: Some(extra_data), 961 + } 962 + } 963 + } 964 + 965 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for File<'a> { 966 + fn nsid() -> &'static str { 967 + "place.wisp.fs" 968 + } 969 + fn def_name() -> &'static str { 970 + "file" 971 + } 972 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 973 + lexicon_doc_place_wisp_fs() 974 + } 975 + fn validate( 976 + &self, 977 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 978 + Ok(()) 979 + } 980 + } 981 + 982 + /// Virtual filesystem manifest for a Wisp site 983 + #[jacquard_derive::lexicon] 984 + #[derive( 985 + serde::Serialize, 986 + serde::Deserialize, 987 + Debug, 988 + Clone, 989 + PartialEq, 990 + Eq, 991 + jacquard_derive::IntoStatic 992 + )] 993 + #[serde(rename_all = "camelCase")] 994 + pub struct Fs<'a> { 995 + pub created_at: jacquard_common::types::string::Datetime, 996 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 997 + pub file_count: std::option::Option<i64>, 998 + #[serde(borrow)] 999 + pub root: crate::place_wisp::fs::Directory<'a>, 1000 + #[serde(borrow)] 1001 + pub site: jacquard_common::CowStr<'a>, 1002 + } 1003 + 1004 + pub mod fs_state { 1005 + 1006 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 1007 + #[allow(unused)] 1008 + use ::core::marker::PhantomData; 1009 + mod sealed { 1010 + pub trait Sealed {} 1011 + } 1012 + /// State trait tracking which required fields have been set 1013 + pub trait State: sealed::Sealed { 1014 + type CreatedAt; 1015 + type Site; 1016 + type Root; 1017 + } 1018 + /// Empty state - all required fields are unset 1019 + pub struct Empty(()); 1020 + impl sealed::Sealed for Empty {} 1021 + impl State for Empty { 1022 + type CreatedAt = Unset; 1023 + type Site = Unset; 1024 + type Root = Unset; 1025 + } 1026 + ///State transition - sets the `created_at` field to Set 1027 + pub struct SetCreatedAt<S: State = Empty>(PhantomData<fn() -> S>); 1028 + impl<S: State> sealed::Sealed for SetCreatedAt<S> {} 1029 + impl<S: State> State for SetCreatedAt<S> { 1030 + type CreatedAt = Set<members::created_at>; 1031 + type Site = S::Site; 1032 + type Root = S::Root; 1033 + } 1034 + ///State transition - sets the `site` field to Set 1035 + pub struct SetSite<S: State = Empty>(PhantomData<fn() -> S>); 1036 + impl<S: State> sealed::Sealed for SetSite<S> {} 1037 + impl<S: State> State for SetSite<S> { 1038 + type CreatedAt = S::CreatedAt; 1039 + type Site = Set<members::site>; 1040 + type Root = S::Root; 1041 + } 1042 + ///State transition - sets the `root` field to Set 1043 + pub struct SetRoot<S: State = Empty>(PhantomData<fn() -> S>); 1044 + impl<S: State> sealed::Sealed for SetRoot<S> {} 1045 + impl<S: State> State for SetRoot<S> { 1046 + type CreatedAt = S::CreatedAt; 1047 + type Site = S::Site; 1048 + type Root = Set<members::root>; 1049 + } 1050 + /// Marker types for field names 1051 + #[allow(non_camel_case_types)] 1052 + pub mod members { 1053 + ///Marker type for the `created_at` field 1054 + pub struct created_at(()); 1055 + ///Marker type for the `site` field 1056 + pub struct site(()); 1057 + ///Marker type for the `root` field 1058 + pub struct root(()); 1059 + } 1060 + } 1061 + 1062 + /// Builder for constructing an instance of this type 1063 + pub struct FsBuilder<'a, S: fs_state::State> { 1064 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 1065 + __unsafe_private_named: ( 1066 + ::core::option::Option<jacquard_common::types::string::Datetime>, 1067 + ::core::option::Option<i64>, 1068 + ::core::option::Option<crate::place_wisp::fs::Directory<'a>>, 1069 + ::core::option::Option<jacquard_common::CowStr<'a>>, 1070 + ), 1071 + _phantom: ::core::marker::PhantomData<&'a ()>, 1072 + } 1073 + 1074 + impl<'a> Fs<'a> { 1075 + /// Create a new builder for this type 1076 + pub fn new() -> FsBuilder<'a, fs_state::Empty> { 1077 + FsBuilder::new() 1078 + } 1079 + } 1080 + 1081 + impl<'a> FsBuilder<'a, fs_state::Empty> { 1082 + /// Create a new builder with all fields unset 1083 + pub fn new() -> Self { 1084 + FsBuilder { 1085 + _phantom_state: ::core::marker::PhantomData, 1086 + __unsafe_private_named: (None, None, None, None), 1087 + _phantom: ::core::marker::PhantomData, 1088 + } 1089 + } 1090 + } 1091 + 1092 + impl<'a, S> FsBuilder<'a, S> 1093 + where 1094 + S: fs_state::State, 1095 + S::CreatedAt: fs_state::IsUnset, 1096 + { 1097 + /// Set the `createdAt` field (required) 1098 + pub fn created_at( 1099 + mut self, 1100 + value: impl Into<jacquard_common::types::string::Datetime>, 1101 + ) -> FsBuilder<'a, fs_state::SetCreatedAt<S>> { 1102 + self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 1103 + FsBuilder { 1104 + _phantom_state: ::core::marker::PhantomData, 1105 + __unsafe_private_named: self.__unsafe_private_named, 1106 + _phantom: ::core::marker::PhantomData, 1107 + } 1108 + } 1109 + } 1110 + 1111 + impl<'a, S: fs_state::State> FsBuilder<'a, S> { 1112 + /// Set the `fileCount` field (optional) 1113 + pub fn file_count(mut self, value: impl Into<Option<i64>>) -> Self { 1114 + self.__unsafe_private_named.1 = value.into(); 1115 + self 1116 + } 1117 + /// Set the `fileCount` field to an Option value (optional) 1118 + pub fn maybe_file_count(mut self, value: Option<i64>) -> Self { 1119 + self.__unsafe_private_named.1 = value; 1120 + self 1121 + } 1122 + } 1123 + 1124 + impl<'a, S> FsBuilder<'a, S> 1125 + where 1126 + S: fs_state::State, 1127 + S::Root: fs_state::IsUnset, 1128 + { 1129 + /// Set the `root` field (required) 1130 + pub fn root( 1131 + mut self, 1132 + value: impl Into<crate::place_wisp::fs::Directory<'a>>, 1133 + ) -> FsBuilder<'a, fs_state::SetRoot<S>> { 1134 + self.__unsafe_private_named.2 = ::core::option::Option::Some(value.into()); 1135 + FsBuilder { 1136 + _phantom_state: ::core::marker::PhantomData, 1137 + __unsafe_private_named: self.__unsafe_private_named, 1138 + _phantom: ::core::marker::PhantomData, 1139 + } 1140 + } 1141 + } 1142 + 1143 + impl<'a, S> FsBuilder<'a, S> 1144 + where 1145 + S: fs_state::State, 1146 + S::Site: fs_state::IsUnset, 1147 + { 1148 + /// Set the `site` field (required) 1149 + pub fn site( 1150 + mut self, 1151 + value: impl Into<jacquard_common::CowStr<'a>>, 1152 + ) -> FsBuilder<'a, fs_state::SetSite<S>> { 1153 + self.__unsafe_private_named.3 = ::core::option::Option::Some(value.into()); 1154 + FsBuilder { 1155 + _phantom_state: ::core::marker::PhantomData, 1156 + __unsafe_private_named: self.__unsafe_private_named, 1157 + _phantom: ::core::marker::PhantomData, 1158 + } 1159 + } 1160 + } 1161 + 1162 + impl<'a, S> FsBuilder<'a, S> 1163 + where 1164 + S: fs_state::State, 1165 + S::CreatedAt: fs_state::IsSet, 1166 + S::Site: fs_state::IsSet, 1167 + S::Root: fs_state::IsSet, 1168 + { 1169 + /// Build the final struct 1170 + pub fn build(self) -> Fs<'a> { 1171 + Fs { 1172 + created_at: self.__unsafe_private_named.0.unwrap(), 1173 + file_count: self.__unsafe_private_named.1, 1174 + root: self.__unsafe_private_named.2.unwrap(), 1175 + site: self.__unsafe_private_named.3.unwrap(), 1176 + extra_data: Default::default(), 1177 + } 1178 + } 1179 + /// Build the final struct with custom extra_data 1180 + pub fn build_with_data( 1181 + self, 1182 + extra_data: std::collections::BTreeMap< 1183 + jacquard_common::smol_str::SmolStr, 1184 + jacquard_common::types::value::Data<'a>, 1185 + >, 1186 + ) -> Fs<'a> { 1187 + Fs { 1188 + created_at: self.__unsafe_private_named.0.unwrap(), 1189 + file_count: self.__unsafe_private_named.1, 1190 + root: self.__unsafe_private_named.2.unwrap(), 1191 + site: self.__unsafe_private_named.3.unwrap(), 1192 + extra_data: Some(extra_data), 1193 + } 1194 + } 1195 + } 1196 + 1197 + impl<'a> Fs<'a> { 1198 + pub fn uri( 1199 + uri: impl Into<jacquard_common::CowStr<'a>>, 1200 + ) -> Result< 1201 + jacquard_common::types::uri::RecordUri<'a, FsRecord>, 1202 + jacquard_common::types::uri::UriError, 1203 + > { 1204 + jacquard_common::types::uri::RecordUri::try_from_uri( 1205 + jacquard_common::types::string::AtUri::new_cow(uri.into())?, 1206 + ) 1207 + } 1208 + } 1209 + 1210 + /// Typed wrapper for GetRecord response with this collection's record type. 1211 + #[derive( 1212 + serde::Serialize, 1213 + serde::Deserialize, 1214 + Debug, 1215 + Clone, 1216 + PartialEq, 1217 + Eq, 1218 + jacquard_derive::IntoStatic 1219 + )] 1220 + #[serde(rename_all = "camelCase")] 1221 + pub struct FsGetRecordOutput<'a> { 1222 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 1223 + #[serde(borrow)] 1224 + pub cid: std::option::Option<jacquard_common::types::string::Cid<'a>>, 1225 + #[serde(borrow)] 1226 + pub uri: jacquard_common::types::string::AtUri<'a>, 1227 + #[serde(borrow)] 1228 + pub value: Fs<'a>, 1229 + } 1230 + 1231 + impl From<FsGetRecordOutput<'_>> for Fs<'_> { 1232 + fn from(output: FsGetRecordOutput<'_>) -> Self { 1233 + use jacquard_common::IntoStatic; 1234 + output.value.into_static() 1235 + } 1236 + } 1237 + 1238 + impl jacquard_common::types::collection::Collection for Fs<'_> { 1239 + const NSID: &'static str = "place.wisp.fs"; 1240 + type Record = FsRecord; 1241 + } 1242 + 1243 + /// Marker type for deserializing records from this collection. 1244 + #[derive(Debug, serde::Serialize, serde::Deserialize)] 1245 + pub struct FsRecord; 1246 + impl jacquard_common::xrpc::XrpcResp for FsRecord { 1247 + const NSID: &'static str = "place.wisp.fs"; 1248 + const ENCODING: &'static str = "application/json"; 1249 + type Output<'de> = FsGetRecordOutput<'de>; 1250 + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; 1251 + } 1252 + 1253 + impl jacquard_common::types::collection::Collection for FsRecord { 1254 + const NSID: &'static str = "place.wisp.fs"; 1255 + type Record = FsRecord; 1256 + } 1257 + 1258 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Fs<'a> { 1259 + fn nsid() -> &'static str { 1260 + "place.wisp.fs" 1261 + } 1262 + fn def_name() -> &'static str { 1263 + "main" 1264 + } 1265 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 1266 + lexicon_doc_place_wisp_fs() 1267 + } 1268 + fn validate( 1269 + &self, 1270 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 1271 + if let Some(ref value) = self.file_count { 1272 + if *value > 1000i64 { 1273 + return Err(::jacquard_lexicon::validation::ConstraintError::Maximum { 1274 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 1275 + "file_count", 1276 + ), 1277 + max: 1000i64, 1278 + actual: *value, 1279 + }); 1280 + } 1281 + } 1282 + if let Some(ref value) = self.file_count { 1283 + if *value < 0i64 { 1284 + return Err(::jacquard_lexicon::validation::ConstraintError::Minimum { 1285 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 1286 + "file_count", 1287 + ), 1288 + min: 0i64, 1289 + actual: *value, 1290 + }); 1291 + } 1292 + } 1293 + Ok(()) 1294 + } 1295 + } 1296 + 1297 + #[jacquard_derive::lexicon] 1298 + #[derive( 1299 + serde::Serialize, 1300 + serde::Deserialize, 1301 + Debug, 1302 + Clone, 1303 + PartialEq, 1304 + Eq, 1305 + jacquard_derive::IntoStatic 1306 + )] 1307 + #[serde(rename_all = "camelCase")] 1308 + pub struct Subfs<'a> { 1309 + /// If true (default), the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false, the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure. 1310 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 1311 + pub flat: std::option::Option<bool>, 1312 + /// AT-URI pointing to a place.wisp.subfs record containing this subtree. 1313 + #[serde(borrow)] 1314 + pub subject: jacquard_common::types::string::AtUri<'a>, 1315 + #[serde(borrow)] 1316 + pub r#type: jacquard_common::CowStr<'a>, 1317 + } 1318 + 1319 + pub mod subfs_state { 1320 + 1321 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 1322 + #[allow(unused)] 1323 + use ::core::marker::PhantomData; 1324 + mod sealed { 1325 + pub trait Sealed {} 1326 + } 1327 + /// State trait tracking which required fields have been set 1328 + pub trait State: sealed::Sealed { 1329 + type Type; 1330 + type Subject; 1331 + } 1332 + /// Empty state - all required fields are unset 1333 + pub struct Empty(()); 1334 + impl sealed::Sealed for Empty {} 1335 + impl State for Empty { 1336 + type Type = Unset; 1337 + type Subject = Unset; 1338 + } 1339 + ///State transition - sets the `type` field to Set 1340 + pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 1341 + impl<S: State> sealed::Sealed for SetType<S> {} 1342 + impl<S: State> State for SetType<S> { 1343 + type Type = Set<members::r#type>; 1344 + type Subject = S::Subject; 1345 + } 1346 + ///State transition - sets the `subject` field to Set 1347 + pub struct SetSubject<S: State = Empty>(PhantomData<fn() -> S>); 1348 + impl<S: State> sealed::Sealed for SetSubject<S> {} 1349 + impl<S: State> State for SetSubject<S> { 1350 + type Type = S::Type; 1351 + type Subject = Set<members::subject>; 1352 + } 1353 + /// Marker types for field names 1354 + #[allow(non_camel_case_types)] 1355 + pub mod members { 1356 + ///Marker type for the `type` field 1357 + pub struct r#type(()); 1358 + ///Marker type for the `subject` field 1359 + pub struct subject(()); 1360 + } 1361 + } 1362 + 1363 + /// Builder for constructing an instance of this type 1364 + pub struct SubfsBuilder<'a, S: subfs_state::State> { 1365 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 1366 + __unsafe_private_named: ( 1367 + ::core::option::Option<bool>, 1368 + ::core::option::Option<jacquard_common::types::string::AtUri<'a>>, 1369 + ::core::option::Option<jacquard_common::CowStr<'a>>, 1370 + ), 1371 + _phantom: ::core::marker::PhantomData<&'a ()>, 1372 + } 1373 + 1374 + impl<'a> Subfs<'a> { 1375 + /// Create a new builder for this type 1376 + pub fn new() -> SubfsBuilder<'a, subfs_state::Empty> { 1377 + SubfsBuilder::new() 1378 + } 1379 + } 1380 + 1381 + impl<'a> SubfsBuilder<'a, subfs_state::Empty> { 1382 + /// Create a new builder with all fields unset 1383 + pub fn new() -> Self { 1384 + SubfsBuilder { 1385 + _phantom_state: ::core::marker::PhantomData, 1386 + __unsafe_private_named: (None, None, None), 1387 + _phantom: ::core::marker::PhantomData, 1388 + } 1389 + } 1390 + } 1391 + 1392 + impl<'a, S: subfs_state::State> SubfsBuilder<'a, S> { 1393 + /// Set the `flat` field (optional) 1394 + pub fn flat(mut self, value: impl Into<Option<bool>>) -> Self { 1395 + self.__unsafe_private_named.0 = value.into(); 1396 + self 1397 + } 1398 + /// Set the `flat` field to an Option value (optional) 1399 + pub fn maybe_flat(mut self, value: Option<bool>) -> Self { 1400 + self.__unsafe_private_named.0 = value; 1401 + self 1402 + } 1403 + } 1404 + 1405 + impl<'a, S> SubfsBuilder<'a, S> 1406 + where 1407 + S: subfs_state::State, 1408 + S::Subject: subfs_state::IsUnset, 1409 + { 1410 + /// Set the `subject` field (required) 1411 + pub fn subject( 1412 + mut self, 1413 + value: impl Into<jacquard_common::types::string::AtUri<'a>>, 1414 + ) -> SubfsBuilder<'a, subfs_state::SetSubject<S>> { 1415 + self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 1416 + SubfsBuilder { 1417 + _phantom_state: ::core::marker::PhantomData, 1418 + __unsafe_private_named: self.__unsafe_private_named, 1419 + _phantom: ::core::marker::PhantomData, 1420 + } 1421 + } 1422 + } 1423 + 1424 + impl<'a, S> SubfsBuilder<'a, S> 1425 + where 1426 + S: subfs_state::State, 1427 + S::Type: subfs_state::IsUnset, 1428 + { 1429 + /// Set the `type` field (required) 1430 + pub fn r#type( 1431 + mut self, 1432 + value: impl Into<jacquard_common::CowStr<'a>>, 1433 + ) -> SubfsBuilder<'a, subfs_state::SetType<S>> { 1434 + self.__unsafe_private_named.2 = ::core::option::Option::Some(value.into()); 1435 + SubfsBuilder { 1436 + _phantom_state: ::core::marker::PhantomData, 1437 + __unsafe_private_named: self.__unsafe_private_named, 1438 + _phantom: ::core::marker::PhantomData, 1439 + } 1440 + } 1441 + } 1442 + 1443 + impl<'a, S> SubfsBuilder<'a, S> 1444 + where 1445 + S: subfs_state::State, 1446 + S::Type: subfs_state::IsSet, 1447 + S::Subject: subfs_state::IsSet, 1448 + { 1449 + /// Build the final struct 1450 + pub fn build(self) -> Subfs<'a> { 1451 + Subfs { 1452 + flat: self.__unsafe_private_named.0, 1453 + subject: self.__unsafe_private_named.1.unwrap(), 1454 + r#type: self.__unsafe_private_named.2.unwrap(), 1455 + extra_data: Default::default(), 1456 + } 1457 + } 1458 + /// Build the final struct with custom extra_data 1459 + pub fn build_with_data( 1460 + self, 1461 + extra_data: std::collections::BTreeMap< 1462 + jacquard_common::smol_str::SmolStr, 1463 + jacquard_common::types::value::Data<'a>, 1464 + >, 1465 + ) -> Subfs<'a> { 1466 + Subfs { 1467 + flat: self.__unsafe_private_named.0, 1468 + subject: self.__unsafe_private_named.1.unwrap(), 1469 + r#type: self.__unsafe_private_named.2.unwrap(), 1470 + extra_data: Some(extra_data), 1471 + } 1472 + } 1473 + } 1474 + 1475 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Subfs<'a> { 1476 + fn nsid() -> &'static str { 1477 + "place.wisp.fs" 1478 + } 1479 + fn def_name() -> &'static str { 1480 + "subfs" 1481 + } 1482 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 1483 + lexicon_doc_place_wisp_fs() 1484 + } 1485 + fn validate( 1486 + &self, 1487 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 1488 + Ok(()) 1489 + } 1490 + }
+653
cli/crates/lexicons/src/place_wisp/settings.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: place.wisp.settings 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + /// Custom HTTP header configuration 9 + #[jacquard_derive::lexicon] 10 + #[derive( 11 + serde::Serialize, 12 + serde::Deserialize, 13 + Debug, 14 + Clone, 15 + PartialEq, 16 + Eq, 17 + jacquard_derive::IntoStatic, 18 + Default 19 + )] 20 + #[serde(rename_all = "camelCase")] 21 + pub struct CustomHeader<'a> { 22 + /// HTTP header name (e.g., 'Cache-Control', 'X-Frame-Options') 23 + #[serde(borrow)] 24 + pub name: jacquard_common::CowStr<'a>, 25 + /// Optional glob pattern to apply this header to specific paths (e.g., '*.html', '/assets/*'). If not specified, applies to all paths. 26 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 27 + #[serde(borrow)] 28 + pub path: std::option::Option<jacquard_common::CowStr<'a>>, 29 + /// HTTP header value 30 + #[serde(borrow)] 31 + pub value: jacquard_common::CowStr<'a>, 32 + } 33 + 34 + fn lexicon_doc_place_wisp_settings() -> ::jacquard_lexicon::lexicon::LexiconDoc< 35 + 'static, 36 + > { 37 + ::jacquard_lexicon::lexicon::LexiconDoc { 38 + lexicon: ::jacquard_lexicon::lexicon::Lexicon::Lexicon1, 39 + id: ::jacquard_common::CowStr::new_static("place.wisp.settings"), 40 + revision: None, 41 + description: None, 42 + defs: { 43 + let mut map = ::std::collections::BTreeMap::new(); 44 + map.insert( 45 + ::jacquard_common::smol_str::SmolStr::new_static("customHeader"), 46 + ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 47 + description: Some( 48 + ::jacquard_common::CowStr::new_static( 49 + "Custom HTTP header configuration", 50 + ), 51 + ), 52 + required: Some( 53 + vec![ 54 + ::jacquard_common::smol_str::SmolStr::new_static("name"), 55 + ::jacquard_common::smol_str::SmolStr::new_static("value") 56 + ], 57 + ), 58 + nullable: None, 59 + properties: { 60 + #[allow(unused_mut)] 61 + let mut map = ::std::collections::BTreeMap::new(); 62 + map.insert( 63 + ::jacquard_common::smol_str::SmolStr::new_static("name"), 64 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 65 + description: Some( 66 + ::jacquard_common::CowStr::new_static( 67 + "HTTP header name (e.g., 'Cache-Control', 'X-Frame-Options')", 68 + ), 69 + ), 70 + format: None, 71 + default: None, 72 + min_length: None, 73 + max_length: Some(100usize), 74 + min_graphemes: None, 75 + max_graphemes: None, 76 + r#enum: None, 77 + r#const: None, 78 + known_values: None, 79 + }), 80 + ); 81 + map.insert( 82 + ::jacquard_common::smol_str::SmolStr::new_static("path"), 83 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 84 + description: Some( 85 + ::jacquard_common::CowStr::new_static( 86 + "Optional glob pattern to apply this header to specific paths (e.g., '*.html', '/assets/*'). If not specified, applies to all paths.", 87 + ), 88 + ), 89 + format: None, 90 + default: None, 91 + min_length: None, 92 + max_length: Some(500usize), 93 + min_graphemes: None, 94 + max_graphemes: None, 95 + r#enum: None, 96 + r#const: None, 97 + known_values: None, 98 + }), 99 + ); 100 + map.insert( 101 + ::jacquard_common::smol_str::SmolStr::new_static("value"), 102 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 103 + description: Some( 104 + ::jacquard_common::CowStr::new_static("HTTP header value"), 105 + ), 106 + format: None, 107 + default: None, 108 + min_length: None, 109 + max_length: Some(1000usize), 110 + min_graphemes: None, 111 + max_graphemes: None, 112 + r#enum: None, 113 + r#const: None, 114 + known_values: None, 115 + }), 116 + ); 117 + map 118 + }, 119 + }), 120 + ); 121 + map.insert( 122 + ::jacquard_common::smol_str::SmolStr::new_static("main"), 123 + ::jacquard_lexicon::lexicon::LexUserType::Record(::jacquard_lexicon::lexicon::LexRecord { 124 + description: Some( 125 + ::jacquard_common::CowStr::new_static( 126 + "Configuration settings for a static site hosted on wisp.place", 127 + ), 128 + ), 129 + key: Some(::jacquard_common::CowStr::new_static("any")), 130 + record: ::jacquard_lexicon::lexicon::LexRecordRecord::Object(::jacquard_lexicon::lexicon::LexObject { 131 + description: None, 132 + required: None, 133 + nullable: None, 134 + properties: { 135 + #[allow(unused_mut)] 136 + let mut map = ::std::collections::BTreeMap::new(); 137 + map.insert( 138 + ::jacquard_common::smol_str::SmolStr::new_static( 139 + "cleanUrls", 140 + ), 141 + ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 142 + description: None, 143 + default: None, 144 + r#const: None, 145 + }), 146 + ); 147 + map.insert( 148 + ::jacquard_common::smol_str::SmolStr::new_static( 149 + "custom404", 150 + ), 151 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 152 + description: Some( 153 + ::jacquard_common::CowStr::new_static( 154 + "Custom 404 error page file path. Incompatible with directoryListing and spaMode.", 155 + ), 156 + ), 157 + format: None, 158 + default: None, 159 + min_length: None, 160 + max_length: Some(500usize), 161 + min_graphemes: None, 162 + max_graphemes: None, 163 + r#enum: None, 164 + r#const: None, 165 + known_values: None, 166 + }), 167 + ); 168 + map.insert( 169 + ::jacquard_common::smol_str::SmolStr::new_static( 170 + "directoryListing", 171 + ), 172 + ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 173 + description: None, 174 + default: None, 175 + r#const: None, 176 + }), 177 + ); 178 + map.insert( 179 + ::jacquard_common::smol_str::SmolStr::new_static("headers"), 180 + ::jacquard_lexicon::lexicon::LexObjectProperty::Array(::jacquard_lexicon::lexicon::LexArray { 181 + description: Some( 182 + ::jacquard_common::CowStr::new_static( 183 + "Custom HTTP headers to set on responses", 184 + ), 185 + ), 186 + items: ::jacquard_lexicon::lexicon::LexArrayItem::Ref(::jacquard_lexicon::lexicon::LexRef { 187 + description: None, 188 + r#ref: ::jacquard_common::CowStr::new_static( 189 + "#customHeader", 190 + ), 191 + }), 192 + min_length: None, 193 + max_length: Some(50usize), 194 + }), 195 + ); 196 + map.insert( 197 + ::jacquard_common::smol_str::SmolStr::new_static( 198 + "indexFiles", 199 + ), 200 + ::jacquard_lexicon::lexicon::LexObjectProperty::Array(::jacquard_lexicon::lexicon::LexArray { 201 + description: Some( 202 + ::jacquard_common::CowStr::new_static( 203 + "Ordered list of files to try when serving a directory. Defaults to ['index.html'] if not specified.", 204 + ), 205 + ), 206 + items: ::jacquard_lexicon::lexicon::LexArrayItem::String(::jacquard_lexicon::lexicon::LexString { 207 + description: None, 208 + format: None, 209 + default: None, 210 + min_length: None, 211 + max_length: Some(255usize), 212 + min_graphemes: None, 213 + max_graphemes: None, 214 + r#enum: None, 215 + r#const: None, 216 + known_values: None, 217 + }), 218 + min_length: None, 219 + max_length: Some(10usize), 220 + }), 221 + ); 222 + map.insert( 223 + ::jacquard_common::smol_str::SmolStr::new_static("spaMode"), 224 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 225 + description: Some( 226 + ::jacquard_common::CowStr::new_static( 227 + "File to serve for all routes (e.g., 'index.html'). When set, enables SPA mode where all non-file requests are routed to this file. Incompatible with directoryListing and custom404.", 228 + ), 229 + ), 230 + format: None, 231 + default: None, 232 + min_length: None, 233 + max_length: Some(500usize), 234 + min_graphemes: None, 235 + max_graphemes: None, 236 + r#enum: None, 237 + r#const: None, 238 + known_values: None, 239 + }), 240 + ); 241 + map 242 + }, 243 + }), 244 + }), 245 + ); 246 + map 247 + }, 248 + } 249 + } 250 + 251 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for CustomHeader<'a> { 252 + fn nsid() -> &'static str { 253 + "place.wisp.settings" 254 + } 255 + fn def_name() -> &'static str { 256 + "customHeader" 257 + } 258 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 259 + lexicon_doc_place_wisp_settings() 260 + } 261 + fn validate( 262 + &self, 263 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 264 + { 265 + let value = &self.name; 266 + #[allow(unused_comparisons)] 267 + if <str>::len(value.as_ref()) > 100usize { 268 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 269 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 270 + "name", 271 + ), 272 + max: 100usize, 273 + actual: <str>::len(value.as_ref()), 274 + }); 275 + } 276 + } 277 + if let Some(ref value) = self.path { 278 + #[allow(unused_comparisons)] 279 + if <str>::len(value.as_ref()) > 500usize { 280 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 281 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 282 + "path", 283 + ), 284 + max: 500usize, 285 + actual: <str>::len(value.as_ref()), 286 + }); 287 + } 288 + } 289 + { 290 + let value = &self.value; 291 + #[allow(unused_comparisons)] 292 + if <str>::len(value.as_ref()) > 1000usize { 293 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 294 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 295 + "value", 296 + ), 297 + max: 1000usize, 298 + actual: <str>::len(value.as_ref()), 299 + }); 300 + } 301 + } 302 + Ok(()) 303 + } 304 + } 305 + 306 + /// Configuration settings for a static site hosted on wisp.place 307 + #[jacquard_derive::lexicon] 308 + #[derive( 309 + serde::Serialize, 310 + serde::Deserialize, 311 + Debug, 312 + Clone, 313 + PartialEq, 314 + Eq, 315 + jacquard_derive::IntoStatic 316 + )] 317 + #[serde(rename_all = "camelCase")] 318 + pub struct Settings<'a> { 319 + /// Enable clean URL routing. When enabled, '/about' will attempt to serve '/about.html' or '/about/index.html' automatically. 320 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 321 + pub clean_urls: std::option::Option<bool>, 322 + /// Custom 404 error page file path. Incompatible with directoryListing and spaMode. 323 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 324 + #[serde(borrow)] 325 + pub custom404: std::option::Option<jacquard_common::CowStr<'a>>, 326 + /// Enable directory listing mode for paths that resolve to directories without an index file. Incompatible with spaMode. 327 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 328 + pub directory_listing: std::option::Option<bool>, 329 + /// Custom HTTP headers to set on responses 330 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 331 + #[serde(borrow)] 332 + pub headers: std::option::Option<Vec<crate::place_wisp::settings::CustomHeader<'a>>>, 333 + /// Ordered list of files to try when serving a directory. Defaults to ['index.html'] if not specified. 334 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 335 + #[serde(borrow)] 336 + pub index_files: std::option::Option<Vec<jacquard_common::CowStr<'a>>>, 337 + /// File to serve for all routes (e.g., 'index.html'). When set, enables SPA mode where all non-file requests are routed to this file. Incompatible with directoryListing and custom404. 338 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 339 + #[serde(borrow)] 340 + pub spa_mode: std::option::Option<jacquard_common::CowStr<'a>>, 341 + } 342 + 343 + pub mod settings_state { 344 + 345 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 346 + #[allow(unused)] 347 + use ::core::marker::PhantomData; 348 + mod sealed { 349 + pub trait Sealed {} 350 + } 351 + /// State trait tracking which required fields have been set 352 + pub trait State: sealed::Sealed {} 353 + /// Empty state - all required fields are unset 354 + pub struct Empty(()); 355 + impl sealed::Sealed for Empty {} 356 + impl State for Empty {} 357 + /// Marker types for field names 358 + #[allow(non_camel_case_types)] 359 + pub mod members {} 360 + } 361 + 362 + /// Builder for constructing an instance of this type 363 + pub struct SettingsBuilder<'a, S: settings_state::State> { 364 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 365 + __unsafe_private_named: ( 366 + ::core::option::Option<bool>, 367 + ::core::option::Option<jacquard_common::CowStr<'a>>, 368 + ::core::option::Option<bool>, 369 + ::core::option::Option<Vec<crate::place_wisp::settings::CustomHeader<'a>>>, 370 + ::core::option::Option<Vec<jacquard_common::CowStr<'a>>>, 371 + ::core::option::Option<jacquard_common::CowStr<'a>>, 372 + ), 373 + _phantom: ::core::marker::PhantomData<&'a ()>, 374 + } 375 + 376 + impl<'a> Settings<'a> { 377 + /// Create a new builder for this type 378 + pub fn new() -> SettingsBuilder<'a, settings_state::Empty> { 379 + SettingsBuilder::new() 380 + } 381 + } 382 + 383 + impl<'a> SettingsBuilder<'a, settings_state::Empty> { 384 + /// Create a new builder with all fields unset 385 + pub fn new() -> Self { 386 + SettingsBuilder { 387 + _phantom_state: ::core::marker::PhantomData, 388 + __unsafe_private_named: (None, None, None, None, None, None), 389 + _phantom: ::core::marker::PhantomData, 390 + } 391 + } 392 + } 393 + 394 + impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 395 + /// Set the `cleanUrls` field (optional) 396 + pub fn clean_urls(mut self, value: impl Into<Option<bool>>) -> Self { 397 + self.__unsafe_private_named.0 = value.into(); 398 + self 399 + } 400 + /// Set the `cleanUrls` field to an Option value (optional) 401 + pub fn maybe_clean_urls(mut self, value: Option<bool>) -> Self { 402 + self.__unsafe_private_named.0 = value; 403 + self 404 + } 405 + } 406 + 407 + impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 408 + /// Set the `custom404` field (optional) 409 + pub fn custom404( 410 + mut self, 411 + value: impl Into<Option<jacquard_common::CowStr<'a>>>, 412 + ) -> Self { 413 + self.__unsafe_private_named.1 = value.into(); 414 + self 415 + } 416 + /// Set the `custom404` field to an Option value (optional) 417 + pub fn maybe_custom404( 418 + mut self, 419 + value: Option<jacquard_common::CowStr<'a>>, 420 + ) -> Self { 421 + self.__unsafe_private_named.1 = value; 422 + self 423 + } 424 + } 425 + 426 + impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 427 + /// Set the `directoryListing` field (optional) 428 + pub fn directory_listing(mut self, value: impl Into<Option<bool>>) -> Self { 429 + self.__unsafe_private_named.2 = value.into(); 430 + self 431 + } 432 + /// Set the `directoryListing` field to an Option value (optional) 433 + pub fn maybe_directory_listing(mut self, value: Option<bool>) -> Self { 434 + self.__unsafe_private_named.2 = value; 435 + self 436 + } 437 + } 438 + 439 + impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 440 + /// Set the `headers` field (optional) 441 + pub fn headers( 442 + mut self, 443 + value: impl Into<Option<Vec<crate::place_wisp::settings::CustomHeader<'a>>>>, 444 + ) -> Self { 445 + self.__unsafe_private_named.3 = value.into(); 446 + self 447 + } 448 + /// Set the `headers` field to an Option value (optional) 449 + pub fn maybe_headers( 450 + mut self, 451 + value: Option<Vec<crate::place_wisp::settings::CustomHeader<'a>>>, 452 + ) -> Self { 453 + self.__unsafe_private_named.3 = value; 454 + self 455 + } 456 + } 457 + 458 + impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 459 + /// Set the `indexFiles` field (optional) 460 + pub fn index_files( 461 + mut self, 462 + value: impl Into<Option<Vec<jacquard_common::CowStr<'a>>>>, 463 + ) -> Self { 464 + self.__unsafe_private_named.4 = value.into(); 465 + self 466 + } 467 + /// Set the `indexFiles` field to an Option value (optional) 468 + pub fn maybe_index_files( 469 + mut self, 470 + value: Option<Vec<jacquard_common::CowStr<'a>>>, 471 + ) -> Self { 472 + self.__unsafe_private_named.4 = value; 473 + self 474 + } 475 + } 476 + 477 + impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 478 + /// Set the `spaMode` field (optional) 479 + pub fn spa_mode( 480 + mut self, 481 + value: impl Into<Option<jacquard_common::CowStr<'a>>>, 482 + ) -> Self { 483 + self.__unsafe_private_named.5 = value.into(); 484 + self 485 + } 486 + /// Set the `spaMode` field to an Option value (optional) 487 + pub fn maybe_spa_mode(mut self, value: Option<jacquard_common::CowStr<'a>>) -> Self { 488 + self.__unsafe_private_named.5 = value; 489 + self 490 + } 491 + } 492 + 493 + impl<'a, S> SettingsBuilder<'a, S> 494 + where 495 + S: settings_state::State, 496 + { 497 + /// Build the final struct 498 + pub fn build(self) -> Settings<'a> { 499 + Settings { 500 + clean_urls: self.__unsafe_private_named.0, 501 + custom404: self.__unsafe_private_named.1, 502 + directory_listing: self.__unsafe_private_named.2, 503 + headers: self.__unsafe_private_named.3, 504 + index_files: self.__unsafe_private_named.4, 505 + spa_mode: self.__unsafe_private_named.5, 506 + extra_data: Default::default(), 507 + } 508 + } 509 + /// Build the final struct with custom extra_data 510 + pub fn build_with_data( 511 + self, 512 + extra_data: std::collections::BTreeMap< 513 + jacquard_common::smol_str::SmolStr, 514 + jacquard_common::types::value::Data<'a>, 515 + >, 516 + ) -> Settings<'a> { 517 + Settings { 518 + clean_urls: self.__unsafe_private_named.0, 519 + custom404: self.__unsafe_private_named.1, 520 + directory_listing: self.__unsafe_private_named.2, 521 + headers: self.__unsafe_private_named.3, 522 + index_files: self.__unsafe_private_named.4, 523 + spa_mode: self.__unsafe_private_named.5, 524 + extra_data: Some(extra_data), 525 + } 526 + } 527 + } 528 + 529 + impl<'a> Settings<'a> { 530 + pub fn uri( 531 + uri: impl Into<jacquard_common::CowStr<'a>>, 532 + ) -> Result< 533 + jacquard_common::types::uri::RecordUri<'a, SettingsRecord>, 534 + jacquard_common::types::uri::UriError, 535 + > { 536 + jacquard_common::types::uri::RecordUri::try_from_uri( 537 + jacquard_common::types::string::AtUri::new_cow(uri.into())?, 538 + ) 539 + } 540 + } 541 + 542 + /// Typed wrapper for GetRecord response with this collection's record type. 543 + #[derive( 544 + serde::Serialize, 545 + serde::Deserialize, 546 + Debug, 547 + Clone, 548 + PartialEq, 549 + Eq, 550 + jacquard_derive::IntoStatic 551 + )] 552 + #[serde(rename_all = "camelCase")] 553 + pub struct SettingsGetRecordOutput<'a> { 554 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 555 + #[serde(borrow)] 556 + pub cid: std::option::Option<jacquard_common::types::string::Cid<'a>>, 557 + #[serde(borrow)] 558 + pub uri: jacquard_common::types::string::AtUri<'a>, 559 + #[serde(borrow)] 560 + pub value: Settings<'a>, 561 + } 562 + 563 + impl From<SettingsGetRecordOutput<'_>> for Settings<'_> { 564 + fn from(output: SettingsGetRecordOutput<'_>) -> Self { 565 + use jacquard_common::IntoStatic; 566 + output.value.into_static() 567 + } 568 + } 569 + 570 + impl jacquard_common::types::collection::Collection for Settings<'_> { 571 + const NSID: &'static str = "place.wisp.settings"; 572 + type Record = SettingsRecord; 573 + } 574 + 575 + /// Marker type for deserializing records from this collection. 576 + #[derive(Debug, serde::Serialize, serde::Deserialize)] 577 + pub struct SettingsRecord; 578 + impl jacquard_common::xrpc::XrpcResp for SettingsRecord { 579 + const NSID: &'static str = "place.wisp.settings"; 580 + const ENCODING: &'static str = "application/json"; 581 + type Output<'de> = SettingsGetRecordOutput<'de>; 582 + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; 583 + } 584 + 585 + impl jacquard_common::types::collection::Collection for SettingsRecord { 586 + const NSID: &'static str = "place.wisp.settings"; 587 + type Record = SettingsRecord; 588 + } 589 + 590 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Settings<'a> { 591 + fn nsid() -> &'static str { 592 + "place.wisp.settings" 593 + } 594 + fn def_name() -> &'static str { 595 + "main" 596 + } 597 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 598 + lexicon_doc_place_wisp_settings() 599 + } 600 + fn validate( 601 + &self, 602 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 603 + if let Some(ref value) = self.custom404 { 604 + #[allow(unused_comparisons)] 605 + if <str>::len(value.as_ref()) > 500usize { 606 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 607 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 608 + "custom404", 609 + ), 610 + max: 500usize, 611 + actual: <str>::len(value.as_ref()), 612 + }); 613 + } 614 + } 615 + if let Some(ref value) = self.headers { 616 + #[allow(unused_comparisons)] 617 + if value.len() > 50usize { 618 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 619 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 620 + "headers", 621 + ), 622 + max: 50usize, 623 + actual: value.len(), 624 + }); 625 + } 626 + } 627 + if let Some(ref value) = self.index_files { 628 + #[allow(unused_comparisons)] 629 + if value.len() > 10usize { 630 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 631 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 632 + "index_files", 633 + ), 634 + max: 10usize, 635 + actual: value.len(), 636 + }); 637 + } 638 + } 639 + if let Some(ref value) = self.spa_mode { 640 + #[allow(unused_comparisons)] 641 + if <str>::len(value.as_ref()) > 500usize { 642 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 643 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 644 + "spa_mode", 645 + ), 646 + max: 500usize, 647 + actual: <str>::len(value.as_ref()), 648 + }); 649 + } 650 + } 651 + Ok(()) 652 + } 653 + }
+1408
cli/crates/lexicons/src/place_wisp/subfs.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // Lexicon: place.wisp.subfs 4 + // 5 + // This file was automatically generated from Lexicon schemas. 6 + // Any manual changes will be overwritten on the next regeneration. 7 + 8 + #[jacquard_derive::lexicon] 9 + #[derive( 10 + serde::Serialize, 11 + serde::Deserialize, 12 + Debug, 13 + Clone, 14 + PartialEq, 15 + Eq, 16 + jacquard_derive::IntoStatic 17 + )] 18 + #[serde(rename_all = "camelCase")] 19 + pub struct Directory<'a> { 20 + #[serde(borrow)] 21 + pub entries: Vec<crate::place_wisp::subfs::Entry<'a>>, 22 + #[serde(borrow)] 23 + pub r#type: jacquard_common::CowStr<'a>, 24 + } 25 + 26 + pub mod directory_state { 27 + 28 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 29 + #[allow(unused)] 30 + use ::core::marker::PhantomData; 31 + mod sealed { 32 + pub trait Sealed {} 33 + } 34 + /// State trait tracking which required fields have been set 35 + pub trait State: sealed::Sealed { 36 + type Type; 37 + type Entries; 38 + } 39 + /// Empty state - all required fields are unset 40 + pub struct Empty(()); 41 + impl sealed::Sealed for Empty {} 42 + impl State for Empty { 43 + type Type = Unset; 44 + type Entries = Unset; 45 + } 46 + ///State transition - sets the `type` field to Set 47 + pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 48 + impl<S: State> sealed::Sealed for SetType<S> {} 49 + impl<S: State> State for SetType<S> { 50 + type Type = Set<members::r#type>; 51 + type Entries = S::Entries; 52 + } 53 + ///State transition - sets the `entries` field to Set 54 + pub struct SetEntries<S: State = Empty>(PhantomData<fn() -> S>); 55 + impl<S: State> sealed::Sealed for SetEntries<S> {} 56 + impl<S: State> State for SetEntries<S> { 57 + type Type = S::Type; 58 + type Entries = Set<members::entries>; 59 + } 60 + /// Marker types for field names 61 + #[allow(non_camel_case_types)] 62 + pub mod members { 63 + ///Marker type for the `type` field 64 + pub struct r#type(()); 65 + ///Marker type for the `entries` field 66 + pub struct entries(()); 67 + } 68 + } 69 + 70 + /// Builder for constructing an instance of this type 71 + pub struct DirectoryBuilder<'a, S: directory_state::State> { 72 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 73 + __unsafe_private_named: ( 74 + ::core::option::Option<Vec<crate::place_wisp::subfs::Entry<'a>>>, 75 + ::core::option::Option<jacquard_common::CowStr<'a>>, 76 + ), 77 + _phantom: ::core::marker::PhantomData<&'a ()>, 78 + } 79 + 80 + impl<'a> Directory<'a> { 81 + /// Create a new builder for this type 82 + pub fn new() -> DirectoryBuilder<'a, directory_state::Empty> { 83 + DirectoryBuilder::new() 84 + } 85 + } 86 + 87 + impl<'a> DirectoryBuilder<'a, directory_state::Empty> { 88 + /// Create a new builder with all fields unset 89 + pub fn new() -> Self { 90 + DirectoryBuilder { 91 + _phantom_state: ::core::marker::PhantomData, 92 + __unsafe_private_named: (None, None), 93 + _phantom: ::core::marker::PhantomData, 94 + } 95 + } 96 + } 97 + 98 + impl<'a, S> DirectoryBuilder<'a, S> 99 + where 100 + S: directory_state::State, 101 + S::Entries: directory_state::IsUnset, 102 + { 103 + /// Set the `entries` field (required) 104 + pub fn entries( 105 + mut self, 106 + value: impl Into<Vec<crate::place_wisp::subfs::Entry<'a>>>, 107 + ) -> DirectoryBuilder<'a, directory_state::SetEntries<S>> { 108 + self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 109 + DirectoryBuilder { 110 + _phantom_state: ::core::marker::PhantomData, 111 + __unsafe_private_named: self.__unsafe_private_named, 112 + _phantom: ::core::marker::PhantomData, 113 + } 114 + } 115 + } 116 + 117 + impl<'a, S> DirectoryBuilder<'a, S> 118 + where 119 + S: directory_state::State, 120 + S::Type: directory_state::IsUnset, 121 + { 122 + /// Set the `type` field (required) 123 + pub fn r#type( 124 + mut self, 125 + value: impl Into<jacquard_common::CowStr<'a>>, 126 + ) -> DirectoryBuilder<'a, directory_state::SetType<S>> { 127 + self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 128 + DirectoryBuilder { 129 + _phantom_state: ::core::marker::PhantomData, 130 + __unsafe_private_named: self.__unsafe_private_named, 131 + _phantom: ::core::marker::PhantomData, 132 + } 133 + } 134 + } 135 + 136 + impl<'a, S> DirectoryBuilder<'a, S> 137 + where 138 + S: directory_state::State, 139 + S::Type: directory_state::IsSet, 140 + S::Entries: directory_state::IsSet, 141 + { 142 + /// Build the final struct 143 + pub fn build(self) -> Directory<'a> { 144 + Directory { 145 + entries: self.__unsafe_private_named.0.unwrap(), 146 + r#type: self.__unsafe_private_named.1.unwrap(), 147 + extra_data: Default::default(), 148 + } 149 + } 150 + /// Build the final struct with custom extra_data 151 + pub fn build_with_data( 152 + self, 153 + extra_data: std::collections::BTreeMap< 154 + jacquard_common::smol_str::SmolStr, 155 + jacquard_common::types::value::Data<'a>, 156 + >, 157 + ) -> Directory<'a> { 158 + Directory { 159 + entries: self.__unsafe_private_named.0.unwrap(), 160 + r#type: self.__unsafe_private_named.1.unwrap(), 161 + extra_data: Some(extra_data), 162 + } 163 + } 164 + } 165 + 166 + fn lexicon_doc_place_wisp_subfs() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 167 + ::jacquard_lexicon::lexicon::LexiconDoc { 168 + lexicon: ::jacquard_lexicon::lexicon::Lexicon::Lexicon1, 169 + id: ::jacquard_common::CowStr::new_static("place.wisp.subfs"), 170 + revision: None, 171 + description: None, 172 + defs: { 173 + let mut map = ::std::collections::BTreeMap::new(); 174 + map.insert( 175 + ::jacquard_common::smol_str::SmolStr::new_static("directory"), 176 + ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 177 + description: None, 178 + required: Some( 179 + vec![ 180 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 181 + ::jacquard_common::smol_str::SmolStr::new_static("entries") 182 + ], 183 + ), 184 + nullable: None, 185 + properties: { 186 + #[allow(unused_mut)] 187 + let mut map = ::std::collections::BTreeMap::new(); 188 + map.insert( 189 + ::jacquard_common::smol_str::SmolStr::new_static("entries"), 190 + ::jacquard_lexicon::lexicon::LexObjectProperty::Array(::jacquard_lexicon::lexicon::LexArray { 191 + description: None, 192 + items: ::jacquard_lexicon::lexicon::LexArrayItem::Ref(::jacquard_lexicon::lexicon::LexRef { 193 + description: None, 194 + r#ref: ::jacquard_common::CowStr::new_static("#entry"), 195 + }), 196 + min_length: None, 197 + max_length: Some(500usize), 198 + }), 199 + ); 200 + map.insert( 201 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 202 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 203 + description: None, 204 + format: None, 205 + default: None, 206 + min_length: None, 207 + max_length: None, 208 + min_graphemes: None, 209 + max_graphemes: None, 210 + r#enum: None, 211 + r#const: None, 212 + known_values: None, 213 + }), 214 + ); 215 + map 216 + }, 217 + }), 218 + ); 219 + map.insert( 220 + ::jacquard_common::smol_str::SmolStr::new_static("entry"), 221 + ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 222 + description: None, 223 + required: Some( 224 + vec![ 225 + ::jacquard_common::smol_str::SmolStr::new_static("name"), 226 + ::jacquard_common::smol_str::SmolStr::new_static("node") 227 + ], 228 + ), 229 + nullable: None, 230 + properties: { 231 + #[allow(unused_mut)] 232 + let mut map = ::std::collections::BTreeMap::new(); 233 + map.insert( 234 + ::jacquard_common::smol_str::SmolStr::new_static("name"), 235 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 236 + description: None, 237 + format: None, 238 + default: None, 239 + min_length: None, 240 + max_length: Some(255usize), 241 + min_graphemes: None, 242 + max_graphemes: None, 243 + r#enum: None, 244 + r#const: None, 245 + known_values: None, 246 + }), 247 + ); 248 + map.insert( 249 + ::jacquard_common::smol_str::SmolStr::new_static("node"), 250 + ::jacquard_lexicon::lexicon::LexObjectProperty::Union(::jacquard_lexicon::lexicon::LexRefUnion { 251 + description: None, 252 + refs: vec![ 253 + ::jacquard_common::CowStr::new_static("#file"), 254 + ::jacquard_common::CowStr::new_static("#directory"), 255 + ::jacquard_common::CowStr::new_static("#subfs") 256 + ], 257 + closed: None, 258 + }), 259 + ); 260 + map 261 + }, 262 + }), 263 + ); 264 + map.insert( 265 + ::jacquard_common::smol_str::SmolStr::new_static("file"), 266 + ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 267 + description: None, 268 + required: Some( 269 + vec![ 270 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 271 + ::jacquard_common::smol_str::SmolStr::new_static("blob") 272 + ], 273 + ), 274 + nullable: None, 275 + properties: { 276 + #[allow(unused_mut)] 277 + let mut map = ::std::collections::BTreeMap::new(); 278 + map.insert( 279 + ::jacquard_common::smol_str::SmolStr::new_static("base64"), 280 + ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 281 + description: None, 282 + default: None, 283 + r#const: None, 284 + }), 285 + ); 286 + map.insert( 287 + ::jacquard_common::smol_str::SmolStr::new_static("blob"), 288 + ::jacquard_lexicon::lexicon::LexObjectProperty::Blob(::jacquard_lexicon::lexicon::LexBlob { 289 + description: None, 290 + accept: None, 291 + max_size: None, 292 + }), 293 + ); 294 + map.insert( 295 + ::jacquard_common::smol_str::SmolStr::new_static("encoding"), 296 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 297 + description: Some( 298 + ::jacquard_common::CowStr::new_static( 299 + "Content encoding (e.g., gzip for compressed files)", 300 + ), 301 + ), 302 + format: None, 303 + default: None, 304 + min_length: None, 305 + max_length: None, 306 + min_graphemes: None, 307 + max_graphemes: None, 308 + r#enum: None, 309 + r#const: None, 310 + known_values: None, 311 + }), 312 + ); 313 + map.insert( 314 + ::jacquard_common::smol_str::SmolStr::new_static("mimeType"), 315 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 316 + description: Some( 317 + ::jacquard_common::CowStr::new_static( 318 + "Original MIME type before compression", 319 + ), 320 + ), 321 + format: None, 322 + default: None, 323 + min_length: None, 324 + max_length: None, 325 + min_graphemes: None, 326 + max_graphemes: None, 327 + r#enum: None, 328 + r#const: None, 329 + known_values: None, 330 + }), 331 + ); 332 + map.insert( 333 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 334 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 335 + description: None, 336 + format: None, 337 + default: None, 338 + min_length: None, 339 + max_length: None, 340 + min_graphemes: None, 341 + max_graphemes: None, 342 + r#enum: None, 343 + r#const: None, 344 + known_values: None, 345 + }), 346 + ); 347 + map 348 + }, 349 + }), 350 + ); 351 + map.insert( 352 + ::jacquard_common::smol_str::SmolStr::new_static("main"), 353 + ::jacquard_lexicon::lexicon::LexUserType::Record(::jacquard_lexicon::lexicon::LexRecord { 354 + description: Some( 355 + ::jacquard_common::CowStr::new_static( 356 + "Virtual filesystem subtree referenced by place.wisp.fs records. When a subfs entry is expanded, its root entries are merged (flattened) into the parent directory, allowing large directories to be split across multiple records while maintaining a flat structure.", 357 + ), 358 + ), 359 + key: None, 360 + record: ::jacquard_lexicon::lexicon::LexRecordRecord::Object(::jacquard_lexicon::lexicon::LexObject { 361 + description: None, 362 + required: Some( 363 + vec![ 364 + ::jacquard_common::smol_str::SmolStr::new_static("root"), 365 + ::jacquard_common::smol_str::SmolStr::new_static("createdAt") 366 + ], 367 + ), 368 + nullable: None, 369 + properties: { 370 + #[allow(unused_mut)] 371 + let mut map = ::std::collections::BTreeMap::new(); 372 + map.insert( 373 + ::jacquard_common::smol_str::SmolStr::new_static( 374 + "createdAt", 375 + ), 376 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 377 + description: None, 378 + format: Some( 379 + ::jacquard_lexicon::lexicon::LexStringFormat::Datetime, 380 + ), 381 + default: None, 382 + min_length: None, 383 + max_length: None, 384 + min_graphemes: None, 385 + max_graphemes: None, 386 + r#enum: None, 387 + r#const: None, 388 + known_values: None, 389 + }), 390 + ); 391 + map.insert( 392 + ::jacquard_common::smol_str::SmolStr::new_static( 393 + "fileCount", 394 + ), 395 + ::jacquard_lexicon::lexicon::LexObjectProperty::Integer(::jacquard_lexicon::lexicon::LexInteger { 396 + description: None, 397 + default: None, 398 + minimum: Some(0i64), 399 + maximum: Some(1000i64), 400 + r#enum: None, 401 + r#const: None, 402 + }), 403 + ); 404 + map.insert( 405 + ::jacquard_common::smol_str::SmolStr::new_static("root"), 406 + ::jacquard_lexicon::lexicon::LexObjectProperty::Ref(::jacquard_lexicon::lexicon::LexRef { 407 + description: None, 408 + r#ref: ::jacquard_common::CowStr::new_static("#directory"), 409 + }), 410 + ); 411 + map 412 + }, 413 + }), 414 + }), 415 + ); 416 + map.insert( 417 + ::jacquard_common::smol_str::SmolStr::new_static("subfs"), 418 + ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 419 + description: None, 420 + required: Some( 421 + vec![ 422 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 423 + ::jacquard_common::smol_str::SmolStr::new_static("subject") 424 + ], 425 + ), 426 + nullable: None, 427 + properties: { 428 + #[allow(unused_mut)] 429 + let mut map = ::std::collections::BTreeMap::new(); 430 + map.insert( 431 + ::jacquard_common::smol_str::SmolStr::new_static("subject"), 432 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 433 + description: Some( 434 + ::jacquard_common::CowStr::new_static( 435 + "AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures.", 436 + ), 437 + ), 438 + format: Some( 439 + ::jacquard_lexicon::lexicon::LexStringFormat::AtUri, 440 + ), 441 + default: None, 442 + min_length: None, 443 + max_length: None, 444 + min_graphemes: None, 445 + max_graphemes: None, 446 + r#enum: None, 447 + r#const: None, 448 + known_values: None, 449 + }), 450 + ); 451 + map.insert( 452 + ::jacquard_common::smol_str::SmolStr::new_static("type"), 453 + ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 454 + description: None, 455 + format: None, 456 + default: None, 457 + min_length: None, 458 + max_length: None, 459 + min_graphemes: None, 460 + max_graphemes: None, 461 + r#enum: None, 462 + r#const: None, 463 + known_values: None, 464 + }), 465 + ); 466 + map 467 + }, 468 + }), 469 + ); 470 + map 471 + }, 472 + } 473 + } 474 + 475 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Directory<'a> { 476 + fn nsid() -> &'static str { 477 + "place.wisp.subfs" 478 + } 479 + fn def_name() -> &'static str { 480 + "directory" 481 + } 482 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 483 + lexicon_doc_place_wisp_subfs() 484 + } 485 + fn validate( 486 + &self, 487 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 488 + { 489 + let value = &self.entries; 490 + #[allow(unused_comparisons)] 491 + if value.len() > 500usize { 492 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 493 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 494 + "entries", 495 + ), 496 + max: 500usize, 497 + actual: value.len(), 498 + }); 499 + } 500 + } 501 + Ok(()) 502 + } 503 + } 504 + 505 + #[jacquard_derive::lexicon] 506 + #[derive( 507 + serde::Serialize, 508 + serde::Deserialize, 509 + Debug, 510 + Clone, 511 + PartialEq, 512 + Eq, 513 + jacquard_derive::IntoStatic 514 + )] 515 + #[serde(rename_all = "camelCase")] 516 + pub struct Entry<'a> { 517 + #[serde(borrow)] 518 + pub name: jacquard_common::CowStr<'a>, 519 + #[serde(borrow)] 520 + pub node: EntryNode<'a>, 521 + } 522 + 523 + pub mod entry_state { 524 + 525 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 526 + #[allow(unused)] 527 + use ::core::marker::PhantomData; 528 + mod sealed { 529 + pub trait Sealed {} 530 + } 531 + /// State trait tracking which required fields have been set 532 + pub trait State: sealed::Sealed { 533 + type Name; 534 + type Node; 535 + } 536 + /// Empty state - all required fields are unset 537 + pub struct Empty(()); 538 + impl sealed::Sealed for Empty {} 539 + impl State for Empty { 540 + type Name = Unset; 541 + type Node = Unset; 542 + } 543 + ///State transition - sets the `name` field to Set 544 + pub struct SetName<S: State = Empty>(PhantomData<fn() -> S>); 545 + impl<S: State> sealed::Sealed for SetName<S> {} 546 + impl<S: State> State for SetName<S> { 547 + type Name = Set<members::name>; 548 + type Node = S::Node; 549 + } 550 + ///State transition - sets the `node` field to Set 551 + pub struct SetNode<S: State = Empty>(PhantomData<fn() -> S>); 552 + impl<S: State> sealed::Sealed for SetNode<S> {} 553 + impl<S: State> State for SetNode<S> { 554 + type Name = S::Name; 555 + type Node = Set<members::node>; 556 + } 557 + /// Marker types for field names 558 + #[allow(non_camel_case_types)] 559 + pub mod members { 560 + ///Marker type for the `name` field 561 + pub struct name(()); 562 + ///Marker type for the `node` field 563 + pub struct node(()); 564 + } 565 + } 566 + 567 + /// Builder for constructing an instance of this type 568 + pub struct EntryBuilder<'a, S: entry_state::State> { 569 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 570 + __unsafe_private_named: ( 571 + ::core::option::Option<jacquard_common::CowStr<'a>>, 572 + ::core::option::Option<EntryNode<'a>>, 573 + ), 574 + _phantom: ::core::marker::PhantomData<&'a ()>, 575 + } 576 + 577 + impl<'a> Entry<'a> { 578 + /// Create a new builder for this type 579 + pub fn new() -> EntryBuilder<'a, entry_state::Empty> { 580 + EntryBuilder::new() 581 + } 582 + } 583 + 584 + impl<'a> EntryBuilder<'a, entry_state::Empty> { 585 + /// Create a new builder with all fields unset 586 + pub fn new() -> Self { 587 + EntryBuilder { 588 + _phantom_state: ::core::marker::PhantomData, 589 + __unsafe_private_named: (None, None), 590 + _phantom: ::core::marker::PhantomData, 591 + } 592 + } 593 + } 594 + 595 + impl<'a, S> EntryBuilder<'a, S> 596 + where 597 + S: entry_state::State, 598 + S::Name: entry_state::IsUnset, 599 + { 600 + /// Set the `name` field (required) 601 + pub fn name( 602 + mut self, 603 + value: impl Into<jacquard_common::CowStr<'a>>, 604 + ) -> EntryBuilder<'a, entry_state::SetName<S>> { 605 + self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 606 + EntryBuilder { 607 + _phantom_state: ::core::marker::PhantomData, 608 + __unsafe_private_named: self.__unsafe_private_named, 609 + _phantom: ::core::marker::PhantomData, 610 + } 611 + } 612 + } 613 + 614 + impl<'a, S> EntryBuilder<'a, S> 615 + where 616 + S: entry_state::State, 617 + S::Node: entry_state::IsUnset, 618 + { 619 + /// Set the `node` field (required) 620 + pub fn node( 621 + mut self, 622 + value: impl Into<EntryNode<'a>>, 623 + ) -> EntryBuilder<'a, entry_state::SetNode<S>> { 624 + self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 625 + EntryBuilder { 626 + _phantom_state: ::core::marker::PhantomData, 627 + __unsafe_private_named: self.__unsafe_private_named, 628 + _phantom: ::core::marker::PhantomData, 629 + } 630 + } 631 + } 632 + 633 + impl<'a, S> EntryBuilder<'a, S> 634 + where 635 + S: entry_state::State, 636 + S::Name: entry_state::IsSet, 637 + S::Node: entry_state::IsSet, 638 + { 639 + /// Build the final struct 640 + pub fn build(self) -> Entry<'a> { 641 + Entry { 642 + name: self.__unsafe_private_named.0.unwrap(), 643 + node: self.__unsafe_private_named.1.unwrap(), 644 + extra_data: Default::default(), 645 + } 646 + } 647 + /// Build the final struct with custom extra_data 648 + pub fn build_with_data( 649 + self, 650 + extra_data: std::collections::BTreeMap< 651 + jacquard_common::smol_str::SmolStr, 652 + jacquard_common::types::value::Data<'a>, 653 + >, 654 + ) -> Entry<'a> { 655 + Entry { 656 + name: self.__unsafe_private_named.0.unwrap(), 657 + node: self.__unsafe_private_named.1.unwrap(), 658 + extra_data: Some(extra_data), 659 + } 660 + } 661 + } 662 + 663 + #[jacquard_derive::open_union] 664 + #[derive( 665 + serde::Serialize, 666 + serde::Deserialize, 667 + Debug, 668 + Clone, 669 + PartialEq, 670 + Eq, 671 + jacquard_derive::IntoStatic 672 + )] 673 + #[serde(tag = "$type")] 674 + #[serde(bound(deserialize = "'de: 'a"))] 675 + pub enum EntryNode<'a> { 676 + #[serde(rename = "place.wisp.subfs#file")] 677 + File(Box<crate::place_wisp::subfs::File<'a>>), 678 + #[serde(rename = "place.wisp.subfs#directory")] 679 + Directory(Box<crate::place_wisp::subfs::Directory<'a>>), 680 + #[serde(rename = "place.wisp.subfs#subfs")] 681 + Subfs(Box<crate::place_wisp::subfs::Subfs<'a>>), 682 + } 683 + 684 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Entry<'a> { 685 + fn nsid() -> &'static str { 686 + "place.wisp.subfs" 687 + } 688 + fn def_name() -> &'static str { 689 + "entry" 690 + } 691 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 692 + lexicon_doc_place_wisp_subfs() 693 + } 694 + fn validate( 695 + &self, 696 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 697 + { 698 + let value = &self.name; 699 + #[allow(unused_comparisons)] 700 + if <str>::len(value.as_ref()) > 255usize { 701 + return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 702 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 703 + "name", 704 + ), 705 + max: 255usize, 706 + actual: <str>::len(value.as_ref()), 707 + }); 708 + } 709 + } 710 + Ok(()) 711 + } 712 + } 713 + 714 + #[jacquard_derive::lexicon] 715 + #[derive( 716 + serde::Serialize, 717 + serde::Deserialize, 718 + Debug, 719 + Clone, 720 + PartialEq, 721 + Eq, 722 + jacquard_derive::IntoStatic 723 + )] 724 + #[serde(rename_all = "camelCase")] 725 + pub struct File<'a> { 726 + /// True if blob content is base64-encoded (used to bypass PDS content sniffing) 727 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 728 + pub base64: std::option::Option<bool>, 729 + /// Content blob ref 730 + #[serde(borrow)] 731 + pub blob: jacquard_common::types::blob::BlobRef<'a>, 732 + /// Content encoding (e.g., gzip for compressed files) 733 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 734 + #[serde(borrow)] 735 + pub encoding: std::option::Option<jacquard_common::CowStr<'a>>, 736 + /// Original MIME type before compression 737 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 738 + #[serde(borrow)] 739 + pub mime_type: std::option::Option<jacquard_common::CowStr<'a>>, 740 + #[serde(borrow)] 741 + pub r#type: jacquard_common::CowStr<'a>, 742 + } 743 + 744 + pub mod file_state { 745 + 746 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 747 + #[allow(unused)] 748 + use ::core::marker::PhantomData; 749 + mod sealed { 750 + pub trait Sealed {} 751 + } 752 + /// State trait tracking which required fields have been set 753 + pub trait State: sealed::Sealed { 754 + type Blob; 755 + type Type; 756 + } 757 + /// Empty state - all required fields are unset 758 + pub struct Empty(()); 759 + impl sealed::Sealed for Empty {} 760 + impl State for Empty { 761 + type Blob = Unset; 762 + type Type = Unset; 763 + } 764 + ///State transition - sets the `blob` field to Set 765 + pub struct SetBlob<S: State = Empty>(PhantomData<fn() -> S>); 766 + impl<S: State> sealed::Sealed for SetBlob<S> {} 767 + impl<S: State> State for SetBlob<S> { 768 + type Blob = Set<members::blob>; 769 + type Type = S::Type; 770 + } 771 + ///State transition - sets the `type` field to Set 772 + pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 773 + impl<S: State> sealed::Sealed for SetType<S> {} 774 + impl<S: State> State for SetType<S> { 775 + type Blob = S::Blob; 776 + type Type = Set<members::r#type>; 777 + } 778 + /// Marker types for field names 779 + #[allow(non_camel_case_types)] 780 + pub mod members { 781 + ///Marker type for the `blob` field 782 + pub struct blob(()); 783 + ///Marker type for the `type` field 784 + pub struct r#type(()); 785 + } 786 + } 787 + 788 + /// Builder for constructing an instance of this type 789 + pub struct FileBuilder<'a, S: file_state::State> { 790 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 791 + __unsafe_private_named: ( 792 + ::core::option::Option<bool>, 793 + ::core::option::Option<jacquard_common::types::blob::BlobRef<'a>>, 794 + ::core::option::Option<jacquard_common::CowStr<'a>>, 795 + ::core::option::Option<jacquard_common::CowStr<'a>>, 796 + ::core::option::Option<jacquard_common::CowStr<'a>>, 797 + ), 798 + _phantom: ::core::marker::PhantomData<&'a ()>, 799 + } 800 + 801 + impl<'a> File<'a> { 802 + /// Create a new builder for this type 803 + pub fn new() -> FileBuilder<'a, file_state::Empty> { 804 + FileBuilder::new() 805 + } 806 + } 807 + 808 + impl<'a> FileBuilder<'a, file_state::Empty> { 809 + /// Create a new builder with all fields unset 810 + pub fn new() -> Self { 811 + FileBuilder { 812 + _phantom_state: ::core::marker::PhantomData, 813 + __unsafe_private_named: (None, None, None, None, None), 814 + _phantom: ::core::marker::PhantomData, 815 + } 816 + } 817 + } 818 + 819 + impl<'a, S: file_state::State> FileBuilder<'a, S> { 820 + /// Set the `base64` field (optional) 821 + pub fn base64(mut self, value: impl Into<Option<bool>>) -> Self { 822 + self.__unsafe_private_named.0 = value.into(); 823 + self 824 + } 825 + /// Set the `base64` field to an Option value (optional) 826 + pub fn maybe_base64(mut self, value: Option<bool>) -> Self { 827 + self.__unsafe_private_named.0 = value; 828 + self 829 + } 830 + } 831 + 832 + impl<'a, S> FileBuilder<'a, S> 833 + where 834 + S: file_state::State, 835 + S::Blob: file_state::IsUnset, 836 + { 837 + /// Set the `blob` field (required) 838 + pub fn blob( 839 + mut self, 840 + value: impl Into<jacquard_common::types::blob::BlobRef<'a>>, 841 + ) -> FileBuilder<'a, file_state::SetBlob<S>> { 842 + self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 843 + FileBuilder { 844 + _phantom_state: ::core::marker::PhantomData, 845 + __unsafe_private_named: self.__unsafe_private_named, 846 + _phantom: ::core::marker::PhantomData, 847 + } 848 + } 849 + } 850 + 851 + impl<'a, S: file_state::State> FileBuilder<'a, S> { 852 + /// Set the `encoding` field (optional) 853 + pub fn encoding( 854 + mut self, 855 + value: impl Into<Option<jacquard_common::CowStr<'a>>>, 856 + ) -> Self { 857 + self.__unsafe_private_named.2 = value.into(); 858 + self 859 + } 860 + /// Set the `encoding` field to an Option value (optional) 861 + pub fn maybe_encoding(mut self, value: Option<jacquard_common::CowStr<'a>>) -> Self { 862 + self.__unsafe_private_named.2 = value; 863 + self 864 + } 865 + } 866 + 867 + impl<'a, S: file_state::State> FileBuilder<'a, S> { 868 + /// Set the `mimeType` field (optional) 869 + pub fn mime_type( 870 + mut self, 871 + value: impl Into<Option<jacquard_common::CowStr<'a>>>, 872 + ) -> Self { 873 + self.__unsafe_private_named.3 = value.into(); 874 + self 875 + } 876 + /// Set the `mimeType` field to an Option value (optional) 877 + pub fn maybe_mime_type( 878 + mut self, 879 + value: Option<jacquard_common::CowStr<'a>>, 880 + ) -> Self { 881 + self.__unsafe_private_named.3 = value; 882 + self 883 + } 884 + } 885 + 886 + impl<'a, S> FileBuilder<'a, S> 887 + where 888 + S: file_state::State, 889 + S::Type: file_state::IsUnset, 890 + { 891 + /// Set the `type` field (required) 892 + pub fn r#type( 893 + mut self, 894 + value: impl Into<jacquard_common::CowStr<'a>>, 895 + ) -> FileBuilder<'a, file_state::SetType<S>> { 896 + self.__unsafe_private_named.4 = ::core::option::Option::Some(value.into()); 897 + FileBuilder { 898 + _phantom_state: ::core::marker::PhantomData, 899 + __unsafe_private_named: self.__unsafe_private_named, 900 + _phantom: ::core::marker::PhantomData, 901 + } 902 + } 903 + } 904 + 905 + impl<'a, S> FileBuilder<'a, S> 906 + where 907 + S: file_state::State, 908 + S::Blob: file_state::IsSet, 909 + S::Type: file_state::IsSet, 910 + { 911 + /// Build the final struct 912 + pub fn build(self) -> File<'a> { 913 + File { 914 + base64: self.__unsafe_private_named.0, 915 + blob: self.__unsafe_private_named.1.unwrap(), 916 + encoding: self.__unsafe_private_named.2, 917 + mime_type: self.__unsafe_private_named.3, 918 + r#type: self.__unsafe_private_named.4.unwrap(), 919 + extra_data: Default::default(), 920 + } 921 + } 922 + /// Build the final struct with custom extra_data 923 + pub fn build_with_data( 924 + self, 925 + extra_data: std::collections::BTreeMap< 926 + jacquard_common::smol_str::SmolStr, 927 + jacquard_common::types::value::Data<'a>, 928 + >, 929 + ) -> File<'a> { 930 + File { 931 + base64: self.__unsafe_private_named.0, 932 + blob: self.__unsafe_private_named.1.unwrap(), 933 + encoding: self.__unsafe_private_named.2, 934 + mime_type: self.__unsafe_private_named.3, 935 + r#type: self.__unsafe_private_named.4.unwrap(), 936 + extra_data: Some(extra_data), 937 + } 938 + } 939 + } 940 + 941 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for File<'a> { 942 + fn nsid() -> &'static str { 943 + "place.wisp.subfs" 944 + } 945 + fn def_name() -> &'static str { 946 + "file" 947 + } 948 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 949 + lexicon_doc_place_wisp_subfs() 950 + } 951 + fn validate( 952 + &self, 953 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 954 + Ok(()) 955 + } 956 + } 957 + 958 + /// Virtual filesystem subtree referenced by place.wisp.fs records. When a subfs entry is expanded, its root entries are merged (flattened) into the parent directory, allowing large directories to be split across multiple records while maintaining a flat structure. 959 + #[jacquard_derive::lexicon] 960 + #[derive( 961 + serde::Serialize, 962 + serde::Deserialize, 963 + Debug, 964 + Clone, 965 + PartialEq, 966 + Eq, 967 + jacquard_derive::IntoStatic 968 + )] 969 + #[serde(rename_all = "camelCase")] 970 + pub struct SubfsRecord<'a> { 971 + pub created_at: jacquard_common::types::string::Datetime, 972 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 973 + pub file_count: std::option::Option<i64>, 974 + #[serde(borrow)] 975 + pub root: crate::place_wisp::subfs::Directory<'a>, 976 + } 977 + 978 + pub mod subfs_record_state { 979 + 980 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 981 + #[allow(unused)] 982 + use ::core::marker::PhantomData; 983 + mod sealed { 984 + pub trait Sealed {} 985 + } 986 + /// State trait tracking which required fields have been set 987 + pub trait State: sealed::Sealed { 988 + type Root; 989 + type CreatedAt; 990 + } 991 + /// Empty state - all required fields are unset 992 + pub struct Empty(()); 993 + impl sealed::Sealed for Empty {} 994 + impl State for Empty { 995 + type Root = Unset; 996 + type CreatedAt = Unset; 997 + } 998 + ///State transition - sets the `root` field to Set 999 + pub struct SetRoot<S: State = Empty>(PhantomData<fn() -> S>); 1000 + impl<S: State> sealed::Sealed for SetRoot<S> {} 1001 + impl<S: State> State for SetRoot<S> { 1002 + type Root = Set<members::root>; 1003 + type CreatedAt = S::CreatedAt; 1004 + } 1005 + ///State transition - sets the `created_at` field to Set 1006 + pub struct SetCreatedAt<S: State = Empty>(PhantomData<fn() -> S>); 1007 + impl<S: State> sealed::Sealed for SetCreatedAt<S> {} 1008 + impl<S: State> State for SetCreatedAt<S> { 1009 + type Root = S::Root; 1010 + type CreatedAt = Set<members::created_at>; 1011 + } 1012 + /// Marker types for field names 1013 + #[allow(non_camel_case_types)] 1014 + pub mod members { 1015 + ///Marker type for the `root` field 1016 + pub struct root(()); 1017 + ///Marker type for the `created_at` field 1018 + pub struct created_at(()); 1019 + } 1020 + } 1021 + 1022 + /// Builder for constructing an instance of this type 1023 + pub struct SubfsRecordBuilder<'a, S: subfs_record_state::State> { 1024 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 1025 + __unsafe_private_named: ( 1026 + ::core::option::Option<jacquard_common::types::string::Datetime>, 1027 + ::core::option::Option<i64>, 1028 + ::core::option::Option<crate::place_wisp::subfs::Directory<'a>>, 1029 + ), 1030 + _phantom: ::core::marker::PhantomData<&'a ()>, 1031 + } 1032 + 1033 + impl<'a> SubfsRecord<'a> { 1034 + /// Create a new builder for this type 1035 + pub fn new() -> SubfsRecordBuilder<'a, subfs_record_state::Empty> { 1036 + SubfsRecordBuilder::new() 1037 + } 1038 + } 1039 + 1040 + impl<'a> SubfsRecordBuilder<'a, subfs_record_state::Empty> { 1041 + /// Create a new builder with all fields unset 1042 + pub fn new() -> Self { 1043 + SubfsRecordBuilder { 1044 + _phantom_state: ::core::marker::PhantomData, 1045 + __unsafe_private_named: (None, None, None), 1046 + _phantom: ::core::marker::PhantomData, 1047 + } 1048 + } 1049 + } 1050 + 1051 + impl<'a, S> SubfsRecordBuilder<'a, S> 1052 + where 1053 + S: subfs_record_state::State, 1054 + S::CreatedAt: subfs_record_state::IsUnset, 1055 + { 1056 + /// Set the `createdAt` field (required) 1057 + pub fn created_at( 1058 + mut self, 1059 + value: impl Into<jacquard_common::types::string::Datetime>, 1060 + ) -> SubfsRecordBuilder<'a, subfs_record_state::SetCreatedAt<S>> { 1061 + self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 1062 + SubfsRecordBuilder { 1063 + _phantom_state: ::core::marker::PhantomData, 1064 + __unsafe_private_named: self.__unsafe_private_named, 1065 + _phantom: ::core::marker::PhantomData, 1066 + } 1067 + } 1068 + } 1069 + 1070 + impl<'a, S: subfs_record_state::State> SubfsRecordBuilder<'a, S> { 1071 + /// Set the `fileCount` field (optional) 1072 + pub fn file_count(mut self, value: impl Into<Option<i64>>) -> Self { 1073 + self.__unsafe_private_named.1 = value.into(); 1074 + self 1075 + } 1076 + /// Set the `fileCount` field to an Option value (optional) 1077 + pub fn maybe_file_count(mut self, value: Option<i64>) -> Self { 1078 + self.__unsafe_private_named.1 = value; 1079 + self 1080 + } 1081 + } 1082 + 1083 + impl<'a, S> SubfsRecordBuilder<'a, S> 1084 + where 1085 + S: subfs_record_state::State, 1086 + S::Root: subfs_record_state::IsUnset, 1087 + { 1088 + /// Set the `root` field (required) 1089 + pub fn root( 1090 + mut self, 1091 + value: impl Into<crate::place_wisp::subfs::Directory<'a>>, 1092 + ) -> SubfsRecordBuilder<'a, subfs_record_state::SetRoot<S>> { 1093 + self.__unsafe_private_named.2 = ::core::option::Option::Some(value.into()); 1094 + SubfsRecordBuilder { 1095 + _phantom_state: ::core::marker::PhantomData, 1096 + __unsafe_private_named: self.__unsafe_private_named, 1097 + _phantom: ::core::marker::PhantomData, 1098 + } 1099 + } 1100 + } 1101 + 1102 + impl<'a, S> SubfsRecordBuilder<'a, S> 1103 + where 1104 + S: subfs_record_state::State, 1105 + S::Root: subfs_record_state::IsSet, 1106 + S::CreatedAt: subfs_record_state::IsSet, 1107 + { 1108 + /// Build the final struct 1109 + pub fn build(self) -> SubfsRecord<'a> { 1110 + SubfsRecord { 1111 + created_at: self.__unsafe_private_named.0.unwrap(), 1112 + file_count: self.__unsafe_private_named.1, 1113 + root: self.__unsafe_private_named.2.unwrap(), 1114 + extra_data: Default::default(), 1115 + } 1116 + } 1117 + /// Build the final struct with custom extra_data 1118 + pub fn build_with_data( 1119 + self, 1120 + extra_data: std::collections::BTreeMap< 1121 + jacquard_common::smol_str::SmolStr, 1122 + jacquard_common::types::value::Data<'a>, 1123 + >, 1124 + ) -> SubfsRecord<'a> { 1125 + SubfsRecord { 1126 + created_at: self.__unsafe_private_named.0.unwrap(), 1127 + file_count: self.__unsafe_private_named.1, 1128 + root: self.__unsafe_private_named.2.unwrap(), 1129 + extra_data: Some(extra_data), 1130 + } 1131 + } 1132 + } 1133 + 1134 + impl<'a> SubfsRecord<'a> { 1135 + pub fn uri( 1136 + uri: impl Into<jacquard_common::CowStr<'a>>, 1137 + ) -> Result< 1138 + jacquard_common::types::uri::RecordUri<'a, SubfsRecordRecord>, 1139 + jacquard_common::types::uri::UriError, 1140 + > { 1141 + jacquard_common::types::uri::RecordUri::try_from_uri( 1142 + jacquard_common::types::string::AtUri::new_cow(uri.into())?, 1143 + ) 1144 + } 1145 + } 1146 + 1147 + /// Typed wrapper for GetRecord response with this collection's record type. 1148 + #[derive( 1149 + serde::Serialize, 1150 + serde::Deserialize, 1151 + Debug, 1152 + Clone, 1153 + PartialEq, 1154 + Eq, 1155 + jacquard_derive::IntoStatic 1156 + )] 1157 + #[serde(rename_all = "camelCase")] 1158 + pub struct SubfsRecordGetRecordOutput<'a> { 1159 + #[serde(skip_serializing_if = "std::option::Option::is_none")] 1160 + #[serde(borrow)] 1161 + pub cid: std::option::Option<jacquard_common::types::string::Cid<'a>>, 1162 + #[serde(borrow)] 1163 + pub uri: jacquard_common::types::string::AtUri<'a>, 1164 + #[serde(borrow)] 1165 + pub value: SubfsRecord<'a>, 1166 + } 1167 + 1168 + impl From<SubfsRecordGetRecordOutput<'_>> for SubfsRecord<'_> { 1169 + fn from(output: SubfsRecordGetRecordOutput<'_>) -> Self { 1170 + use jacquard_common::IntoStatic; 1171 + output.value.into_static() 1172 + } 1173 + } 1174 + 1175 + impl jacquard_common::types::collection::Collection for SubfsRecord<'_> { 1176 + const NSID: &'static str = "place.wisp.subfs"; 1177 + type Record = SubfsRecordRecord; 1178 + } 1179 + 1180 + /// Marker type for deserializing records from this collection. 1181 + #[derive(Debug, serde::Serialize, serde::Deserialize)] 1182 + pub struct SubfsRecordRecord; 1183 + impl jacquard_common::xrpc::XrpcResp for SubfsRecordRecord { 1184 + const NSID: &'static str = "place.wisp.subfs"; 1185 + const ENCODING: &'static str = "application/json"; 1186 + type Output<'de> = SubfsRecordGetRecordOutput<'de>; 1187 + type Err<'de> = jacquard_common::types::collection::RecordError<'de>; 1188 + } 1189 + 1190 + impl jacquard_common::types::collection::Collection for SubfsRecordRecord { 1191 + const NSID: &'static str = "place.wisp.subfs"; 1192 + type Record = SubfsRecordRecord; 1193 + } 1194 + 1195 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for SubfsRecord<'a> { 1196 + fn nsid() -> &'static str { 1197 + "place.wisp.subfs" 1198 + } 1199 + fn def_name() -> &'static str { 1200 + "main" 1201 + } 1202 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 1203 + lexicon_doc_place_wisp_subfs() 1204 + } 1205 + fn validate( 1206 + &self, 1207 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 1208 + if let Some(ref value) = self.file_count { 1209 + if *value > 1000i64 { 1210 + return Err(::jacquard_lexicon::validation::ConstraintError::Maximum { 1211 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 1212 + "file_count", 1213 + ), 1214 + max: 1000i64, 1215 + actual: *value, 1216 + }); 1217 + } 1218 + } 1219 + if let Some(ref value) = self.file_count { 1220 + if *value < 0i64 { 1221 + return Err(::jacquard_lexicon::validation::ConstraintError::Minimum { 1222 + path: ::jacquard_lexicon::validation::ValidationPath::from_field( 1223 + "file_count", 1224 + ), 1225 + min: 0i64, 1226 + actual: *value, 1227 + }); 1228 + } 1229 + } 1230 + Ok(()) 1231 + } 1232 + } 1233 + 1234 + #[jacquard_derive::lexicon] 1235 + #[derive( 1236 + serde::Serialize, 1237 + serde::Deserialize, 1238 + Debug, 1239 + Clone, 1240 + PartialEq, 1241 + Eq, 1242 + jacquard_derive::IntoStatic 1243 + )] 1244 + #[serde(rename_all = "camelCase")] 1245 + pub struct Subfs<'a> { 1246 + /// AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures. 1247 + #[serde(borrow)] 1248 + pub subject: jacquard_common::types::string::AtUri<'a>, 1249 + #[serde(borrow)] 1250 + pub r#type: jacquard_common::CowStr<'a>, 1251 + } 1252 + 1253 + pub mod subfs_state { 1254 + 1255 + pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 1256 + #[allow(unused)] 1257 + use ::core::marker::PhantomData; 1258 + mod sealed { 1259 + pub trait Sealed {} 1260 + } 1261 + /// State trait tracking which required fields have been set 1262 + pub trait State: sealed::Sealed { 1263 + type Subject; 1264 + type Type; 1265 + } 1266 + /// Empty state - all required fields are unset 1267 + pub struct Empty(()); 1268 + impl sealed::Sealed for Empty {} 1269 + impl State for Empty { 1270 + type Subject = Unset; 1271 + type Type = Unset; 1272 + } 1273 + ///State transition - sets the `subject` field to Set 1274 + pub struct SetSubject<S: State = Empty>(PhantomData<fn() -> S>); 1275 + impl<S: State> sealed::Sealed for SetSubject<S> {} 1276 + impl<S: State> State for SetSubject<S> { 1277 + type Subject = Set<members::subject>; 1278 + type Type = S::Type; 1279 + } 1280 + ///State transition - sets the `type` field to Set 1281 + pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 1282 + impl<S: State> sealed::Sealed for SetType<S> {} 1283 + impl<S: State> State for SetType<S> { 1284 + type Subject = S::Subject; 1285 + type Type = Set<members::r#type>; 1286 + } 1287 + /// Marker types for field names 1288 + #[allow(non_camel_case_types)] 1289 + pub mod members { 1290 + ///Marker type for the `subject` field 1291 + pub struct subject(()); 1292 + ///Marker type for the `type` field 1293 + pub struct r#type(()); 1294 + } 1295 + } 1296 + 1297 + /// Builder for constructing an instance of this type 1298 + pub struct SubfsBuilder<'a, S: subfs_state::State> { 1299 + _phantom_state: ::core::marker::PhantomData<fn() -> S>, 1300 + __unsafe_private_named: ( 1301 + ::core::option::Option<jacquard_common::types::string::AtUri<'a>>, 1302 + ::core::option::Option<jacquard_common::CowStr<'a>>, 1303 + ), 1304 + _phantom: ::core::marker::PhantomData<&'a ()>, 1305 + } 1306 + 1307 + impl<'a> Subfs<'a> { 1308 + /// Create a new builder for this type 1309 + pub fn new() -> SubfsBuilder<'a, subfs_state::Empty> { 1310 + SubfsBuilder::new() 1311 + } 1312 + } 1313 + 1314 + impl<'a> SubfsBuilder<'a, subfs_state::Empty> { 1315 + /// Create a new builder with all fields unset 1316 + pub fn new() -> Self { 1317 + SubfsBuilder { 1318 + _phantom_state: ::core::marker::PhantomData, 1319 + __unsafe_private_named: (None, None), 1320 + _phantom: ::core::marker::PhantomData, 1321 + } 1322 + } 1323 + } 1324 + 1325 + impl<'a, S> SubfsBuilder<'a, S> 1326 + where 1327 + S: subfs_state::State, 1328 + S::Subject: subfs_state::IsUnset, 1329 + { 1330 + /// Set the `subject` field (required) 1331 + pub fn subject( 1332 + mut self, 1333 + value: impl Into<jacquard_common::types::string::AtUri<'a>>, 1334 + ) -> SubfsBuilder<'a, subfs_state::SetSubject<S>> { 1335 + self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 1336 + SubfsBuilder { 1337 + _phantom_state: ::core::marker::PhantomData, 1338 + __unsafe_private_named: self.__unsafe_private_named, 1339 + _phantom: ::core::marker::PhantomData, 1340 + } 1341 + } 1342 + } 1343 + 1344 + impl<'a, S> SubfsBuilder<'a, S> 1345 + where 1346 + S: subfs_state::State, 1347 + S::Type: subfs_state::IsUnset, 1348 + { 1349 + /// Set the `type` field (required) 1350 + pub fn r#type( 1351 + mut self, 1352 + value: impl Into<jacquard_common::CowStr<'a>>, 1353 + ) -> SubfsBuilder<'a, subfs_state::SetType<S>> { 1354 + self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 1355 + SubfsBuilder { 1356 + _phantom_state: ::core::marker::PhantomData, 1357 + __unsafe_private_named: self.__unsafe_private_named, 1358 + _phantom: ::core::marker::PhantomData, 1359 + } 1360 + } 1361 + } 1362 + 1363 + impl<'a, S> SubfsBuilder<'a, S> 1364 + where 1365 + S: subfs_state::State, 1366 + S::Subject: subfs_state::IsSet, 1367 + S::Type: subfs_state::IsSet, 1368 + { 1369 + /// Build the final struct 1370 + pub fn build(self) -> Subfs<'a> { 1371 + Subfs { 1372 + subject: self.__unsafe_private_named.0.unwrap(), 1373 + r#type: self.__unsafe_private_named.1.unwrap(), 1374 + extra_data: Default::default(), 1375 + } 1376 + } 1377 + /// Build the final struct with custom extra_data 1378 + pub fn build_with_data( 1379 + self, 1380 + extra_data: std::collections::BTreeMap< 1381 + jacquard_common::smol_str::SmolStr, 1382 + jacquard_common::types::value::Data<'a>, 1383 + >, 1384 + ) -> Subfs<'a> { 1385 + Subfs { 1386 + subject: self.__unsafe_private_named.0.unwrap(), 1387 + r#type: self.__unsafe_private_named.1.unwrap(), 1388 + extra_data: Some(extra_data), 1389 + } 1390 + } 1391 + } 1392 + 1393 + impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Subfs<'a> { 1394 + fn nsid() -> &'static str { 1395 + "place.wisp.subfs" 1396 + } 1397 + fn def_name() -> &'static str { 1398 + "subfs" 1399 + } 1400 + fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 1401 + lexicon_doc_place_wisp_subfs() 1402 + } 1403 + fn validate( 1404 + &self, 1405 + ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 1406 + Ok(()) 1407 + } 1408 + }
+8
cli/crates/lexicons/src/place_wisp.rs
··· 1 + // @generated by jacquard-lexicon. DO NOT EDIT. 2 + // 3 + // This file was automatically generated from Lexicon schemas. 4 + // Any manual changes will be overwritten on the next regeneration. 5 + 6 + pub mod fs; 7 + pub mod settings; 8 + pub mod subfs;
+16
cli/default.nix
··· 1 + { 2 + rustPlatform, 3 + glibc, 4 + }: 5 + rustPlatform.buildRustPackage { 6 + name = "wisp-cli"; 7 + src = ./.; 8 + cargoLock = { 9 + lockFile = ./Cargo.lock; 10 + outputHashes = { 11 + "jacquard-0.9.5" = "sha256-75bas4VAYFcZAcBspSqS4vlJe8nmFn9ncTgeoT/OvnA="; 12 + }; 13 + }; 14 + buildInputs = [glibc.static]; 15 + RUSTFLAGS = ["-C" "target-feature=+crt-static"]; 16 + }
+96
cli/flake.lock
··· 1 + { 2 + "nodes": { 3 + "flake-utils": { 4 + "inputs": { 5 + "systems": "systems" 6 + }, 7 + "locked": { 8 + "lastModified": 1731533236, 9 + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 10 + "owner": "numtide", 11 + "repo": "flake-utils", 12 + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 13 + "type": "github" 14 + }, 15 + "original": { 16 + "owner": "numtide", 17 + "repo": "flake-utils", 18 + "type": "github" 19 + } 20 + }, 21 + "nixpkgs": { 22 + "locked": { 23 + "lastModified": 1767640445, 24 + "narHash": "sha256-UWYqmD7JFBEDBHWYcqE6s6c77pWdcU/i+bwD6XxMb8A=", 25 + "owner": "NixOS", 26 + "repo": "nixpkgs", 27 + "rev": "9f0c42f8bc7151b8e7e5840fb3bd454ad850d8c5", 28 + "type": "github" 29 + }, 30 + "original": { 31 + "owner": "NixOS", 32 + "ref": "nixos-unstable", 33 + "repo": "nixpkgs", 34 + "type": "github" 35 + } 36 + }, 37 + "nixpkgs_2": { 38 + "locked": { 39 + "lastModified": 1744536153, 40 + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", 41 + "owner": "NixOS", 42 + "repo": "nixpkgs", 43 + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", 44 + "type": "github" 45 + }, 46 + "original": { 47 + "owner": "NixOS", 48 + "ref": "nixpkgs-unstable", 49 + "repo": "nixpkgs", 50 + "type": "github" 51 + } 52 + }, 53 + "root": { 54 + "inputs": { 55 + "flake-utils": "flake-utils", 56 + "nixpkgs": "nixpkgs", 57 + "rust-overlay": "rust-overlay" 58 + } 59 + }, 60 + "rust-overlay": { 61 + "inputs": { 62 + "nixpkgs": "nixpkgs_2" 63 + }, 64 + "locked": { 65 + "lastModified": 1767667566, 66 + "narHash": "sha256-COy+yxZGuhQRVD1r4bWVgeFt1GB+IB1k5WRpDKbLfI8=", 67 + "owner": "oxalica", 68 + "repo": "rust-overlay", 69 + "rev": "056ce5b125ab32ffe78c7d3e394d9da44733c95e", 70 + "type": "github" 71 + }, 72 + "original": { 73 + "owner": "oxalica", 74 + "repo": "rust-overlay", 75 + "type": "github" 76 + } 77 + }, 78 + "systems": { 79 + "locked": { 80 + "lastModified": 1681028828, 81 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 82 + "owner": "nix-systems", 83 + "repo": "default", 84 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 85 + "type": "github" 86 + }, 87 + "original": { 88 + "owner": "nix-systems", 89 + "repo": "default", 90 + "type": "github" 91 + } 92 + } 93 + }, 94 + "root": "root", 95 + "version": 7 96 + }
+136
cli/flake.nix
··· 1 + { 2 + inputs = { 3 + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 4 + rust-overlay.url = "github:oxalica/rust-overlay"; 5 + flake-utils.url = "github:numtide/flake-utils"; 6 + }; 7 + 8 + outputs = { self, nixpkgs, rust-overlay, flake-utils }: 9 + flake-utils.lib.eachDefaultSystem (system: 10 + let 11 + overlays = [ (import rust-overlay) ]; 12 + pkgs = import nixpkgs { inherit system overlays; }; 13 + 14 + rustToolchain = pkgs.rust-bin.stable.latest.default.override { 15 + targets = [ 16 + "x86_64-unknown-linux-musl" 17 + "aarch64-unknown-linux-musl" 18 + "x86_64-apple-darwin" 19 + "aarch64-apple-darwin" 20 + ]; 21 + }; 22 + 23 + cargoLockConfig = { 24 + lockFile = ./Cargo.lock; 25 + outputHashes = { 26 + "jacquard-0.9.5" = "sha256-75bas4VAYFcZAcBspSqS4vlJe8nmFn9ncTgeoT/OvnA="; 27 + }; 28 + }; 29 + 30 + # Native build for current system 31 + native = pkgs.rustPlatform.buildRustPackage { 32 + pname = "wisp-cli"; 33 + version = "0.4.2"; 34 + src = ./.; 35 + cargoLock = cargoLockConfig; 36 + nativeBuildInputs = [ rustToolchain ]; 37 + }; 38 + 39 + # Cross-compilation targets (Linux from macOS needs zigbuild) 40 + linuxTargets = let 41 + zigbuildPkgs = pkgs; 42 + in { 43 + x86_64-linux = pkgs.stdenv.mkDerivation { 44 + pname = "wisp-cli-x86_64-linux"; 45 + version = "0.4.2"; 46 + src = ./.; 47 + 48 + nativeBuildInputs = [ 49 + rustToolchain 50 + pkgs.cargo-zigbuild 51 + pkgs.zig 52 + ]; 53 + 54 + buildPhase = '' 55 + export HOME=$(mktemp -d) 56 + cargo zigbuild --release --target x86_64-unknown-linux-musl 57 + ''; 58 + 59 + installPhase = '' 60 + mkdir -p $out/bin 61 + cp target/x86_64-unknown-linux-musl/release/wisp-cli $out/bin/ 62 + ''; 63 + 64 + # Skip Nix's cargo vendor - we use network 65 + dontConfigure = true; 66 + dontFixup = true; 67 + }; 68 + 69 + aarch64-linux = pkgs.stdenv.mkDerivation { 70 + pname = "wisp-cli-aarch64-linux"; 71 + version = "0.4.2"; 72 + src = ./.; 73 + 74 + nativeBuildInputs = [ 75 + rustToolchain 76 + pkgs.cargo-zigbuild 77 + pkgs.zig 78 + ]; 79 + 80 + buildPhase = '' 81 + export HOME=$(mktemp -d) 82 + cargo zigbuild --release --target aarch64-unknown-linux-musl 83 + ''; 84 + 85 + installPhase = '' 86 + mkdir -p $out/bin 87 + cp target/aarch64-unknown-linux-musl/release/wisp-cli $out/bin/ 88 + ''; 89 + 90 + dontConfigure = true; 91 + dontFixup = true; 92 + }; 93 + }; 94 + 95 + in { 96 + packages = { 97 + default = native; 98 + inherit native; 99 + 100 + # macOS universal binary 101 + macos-universal = pkgs.stdenv.mkDerivation { 102 + pname = "wisp-cli-macos-universal"; 103 + version = "0.4.2"; 104 + src = ./.; 105 + 106 + nativeBuildInputs = [ rustToolchain pkgs.darwin.lipo ]; 107 + 108 + buildPhase = '' 109 + export HOME=$(mktemp -d) 110 + cargo build --release --target aarch64-apple-darwin 111 + cargo build --release --target x86_64-apple-darwin 112 + ''; 113 + 114 + installPhase = '' 115 + mkdir -p $out/bin 116 + lipo -create \ 117 + target/aarch64-apple-darwin/release/wisp-cli \ 118 + target/x86_64-apple-darwin/release/wisp-cli \ 119 + -output $out/bin/wisp-cli 120 + ''; 121 + 122 + dontConfigure = true; 123 + dontFixup = true; 124 + }; 125 + } // (if pkgs.stdenv.isDarwin then linuxTargets else {}); 126 + 127 + devShells.default = pkgs.mkShell { 128 + buildInputs = [ 129 + rustToolchain 130 + pkgs.cargo-zigbuild 131 + pkgs.zig 132 + ]; 133 + }; 134 + } 135 + ); 136 + }
+1 -1
cli/src/blob_map.rs
··· 2 2 use jacquard_common::IntoStatic; 3 3 use std::collections::HashMap; 4 4 5 - use crate::place_wisp::fs::{Directory, EntryNode}; 5 + use wisp_lexicons::place_wisp::fs::{Directory, EntryNode}; 6 6 7 7 /// Extract blob information from a directory tree 8 8 /// Returns a map of file paths to their blob refs and CIDs
-43
cli/src/builder_types.rs
··· 1 - // @generated by jacquard-lexicon. DO NOT EDIT. 2 - // 3 - // This file was automatically generated from Lexicon schemas. 4 - // Any manual changes will be overwritten on the next regeneration. 5 - 6 - /// Marker type indicating a builder field has been set 7 - pub struct Set<T>(pub T); 8 - impl<T> Set<T> { 9 - /// Extract the inner value 10 - #[inline] 11 - pub fn into_inner(self) -> T { 12 - self.0 13 - } 14 - } 15 - 16 - /// Marker type indicating a builder field has not been set 17 - pub struct Unset; 18 - /// Trait indicating a builder field is set (has a value) 19 - #[rustversion::attr( 20 - since(1.78.0), 21 - diagnostic::on_unimplemented( 22 - message = "the field `{Self}` was not set, but this method requires it to be set", 23 - label = "the field `{Self}` was not set" 24 - ) 25 - )] 26 - pub trait IsSet: private::Sealed {} 27 - /// Trait indicating a builder field is unset (no value yet) 28 - #[rustversion::attr( 29 - since(1.78.0), 30 - diagnostic::on_unimplemented( 31 - message = "the field `{Self}` was already set, but this method requires it to be unset", 32 - label = "the field `{Self}` was already set" 33 - ) 34 - )] 35 - pub trait IsUnset: private::Sealed {} 36 - impl<T> IsSet for Set<T> {} 37 - impl IsUnset for Unset {} 38 - mod private { 39 - /// Sealed trait to prevent external implementations 40 - pub trait Sealed {} 41 - impl<T> Sealed for super::Set<T> {} 42 - impl Sealed for super::Unset {} 43 - }
-9
cli/src/lib.rs
··· 1 - // @generated by jacquard-lexicon. DO NOT EDIT. 2 - // 3 - // This file was automatically generated from Lexicon schemas. 4 - // Any manual changes will be overwritten on the next regeneration. 5 - 6 - pub mod builder_types; 7 - 8 - #[cfg(feature = "place_wisp")] 9 - pub mod place_wisp;
+152 -40
cli/src/main.rs
··· 1 - mod builder_types; 2 - mod place_wisp; 3 1 mod cid; 4 2 mod blob_map; 5 3 mod metadata; ··· 28 26 use futures::stream::{self, StreamExt}; 29 27 use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; 30 28 31 - use place_wisp::fs::*; 32 - use place_wisp::settings::*; 29 + use wisp_lexicons::place_wisp::fs::*; 30 + use wisp_lexicons::place_wisp::settings::*; 33 31 34 32 /// Maximum number of concurrent file uploads to the PDS 35 33 const MAX_CONCURRENT_UPLOADS: usize = 2; 36 34 35 + /// Limits for caching on wisp.place (from @wisp/constants) 36 + const MAX_FILE_COUNT: usize = 1000; 37 + const MAX_SITE_SIZE: usize = 300 * 1024 * 1024; // 300MB 38 + 37 39 #[derive(Parser, Debug)] 38 40 #[command(author, version, about = "wisp.place CLI tool")] 39 41 struct Args { 40 42 #[command(subcommand)] 41 43 command: Option<Commands>, 42 - 44 + 43 45 // Deploy arguments (when no subcommand is specified) 44 46 /// Handle (e.g., alice.bsky.social), DID, or PDS URL 45 - #[arg(global = true, conflicts_with = "command")] 46 47 input: Option<CowStr<'static>>, 47 48 48 49 /// Path to the directory containing your static site 49 - #[arg(short, long, global = true, conflicts_with = "command")] 50 + #[arg(short, long)] 50 51 path: Option<PathBuf>, 51 52 52 53 /// Site name (defaults to directory name) 53 - #[arg(short, long, global = true, conflicts_with = "command")] 54 + #[arg(short, long)] 54 55 site: Option<String>, 55 56 56 57 /// Path to auth store file 57 - #[arg(long, global = true, conflicts_with = "command")] 58 + #[arg(long)] 58 59 store: Option<String>, 59 60 60 61 /// App Password for authentication 61 - #[arg(long, global = true, conflicts_with = "command")] 62 + #[arg(long)] 62 63 password: Option<CowStr<'static>>, 63 64 64 65 /// Enable directory listing mode for paths without index files 65 - #[arg(long, global = true, conflicts_with = "command")] 66 + #[arg(long)] 66 67 directory: bool, 67 68 68 69 /// Enable SPA mode (serve index.html for all routes) 69 - #[arg(long, global = true, conflicts_with = "command")] 70 + #[arg(long)] 70 71 spa: bool, 72 + 73 + /// Skip confirmation prompts (automatically accept warnings) 74 + #[arg(short = 'y', long)] 75 + yes: bool, 71 76 } 72 77 73 78 #[derive(Subcommand, Debug)] ··· 100 105 /// Enable SPA mode (serve index.html for all routes) 101 106 #[arg(long)] 102 107 spa: bool, 108 + 109 + /// Skip confirmation prompts (automatically accept warnings) 110 + #[arg(short = 'y', long)] 111 + yes: bool, 103 112 }, 104 113 /// Pull a site from the PDS to a local directory 105 114 Pull { ··· 112 121 113 122 /// Output directory for the downloaded site 114 123 #[arg(short, long, default_value = ".")] 115 - output: PathBuf, 124 + path: PathBuf, 116 125 }, 117 126 /// Serve a site locally with real-time firehose updates 118 127 Serve { ··· 125 134 126 135 /// Output directory for the site files 127 136 #[arg(short, long, default_value = ".")] 128 - output: PathBuf, 137 + path: PathBuf, 129 138 130 139 /// Port to serve on 131 - #[arg(short, long, default_value = "8080")] 140 + #[arg(short = 'P', long, default_value = "8080")] 132 141 port: u16, 133 142 }, 134 143 } ··· 138 147 let args = Args::parse(); 139 148 140 149 let result = match args.command { 141 - Some(Commands::Deploy { input, path, site, store, password, directory, spa }) => { 150 + Some(Commands::Deploy { input, path, site, store, password, directory, spa, yes }) => { 142 151 // Dispatch to appropriate authentication method 143 152 if let Some(password) = password { 144 - run_with_app_password(input, password, path, site, directory, spa).await 153 + run_with_app_password(input, password, path, site, directory, spa, yes).await 145 154 } else { 146 - run_with_oauth(input, store, path, site, directory, spa).await 155 + run_with_oauth(input, store, path, site, directory, spa, yes).await 147 156 } 148 157 } 149 - Some(Commands::Pull { input, site, output }) => { 150 - pull::pull_site(input, CowStr::from(site), output).await 158 + Some(Commands::Pull { input, site, path }) => { 159 + pull::pull_site(input, CowStr::from(site), path).await 151 160 } 152 - Some(Commands::Serve { input, site, output, port }) => { 153 - serve::serve_site(input, CowStr::from(site), output, port).await 161 + Some(Commands::Serve { input, site, path, port }) => { 162 + serve::serve_site(input, CowStr::from(site), path, port).await 154 163 } 155 164 None => { 156 165 // Legacy mode: if input is provided, assume deploy command ··· 160 169 161 170 // Dispatch to appropriate authentication method 162 171 if let Some(password) = args.password { 163 - run_with_app_password(input, password, path, args.site, args.directory, args.spa).await 172 + run_with_app_password(input, password, path, args.site, args.directory, args.spa, args.yes).await 164 173 } else { 165 - run_with_oauth(input, store, path, args.site, args.directory, args.spa).await 174 + run_with_oauth(input, store, path, args.site, args.directory, args.spa, args.yes).await 166 175 } 167 176 } else { 168 177 // No command and no input, show help ··· 191 200 site: Option<String>, 192 201 directory: bool, 193 202 spa: bool, 203 + yes: bool, 194 204 ) -> miette::Result<()> { 195 205 let (session, auth) = 196 206 MemoryCredentialSession::authenticated(input, password, None, None).await?; 197 207 println!("Signed in as {}", auth.handle); 198 208 199 209 let agent: Agent<_> = Agent::from(session); 200 - deploy_site(&agent, path, site, directory, spa).await 210 + deploy_site(&agent, path, site, directory, spa, yes).await 201 211 } 202 212 203 213 /// Run deployment with OAuth authentication ··· 208 218 site: Option<String>, 209 219 directory: bool, 210 220 spa: bool, 221 + yes: bool, 211 222 ) -> miette::Result<()> { 212 223 use jacquard::oauth::scopes::Scope; 213 224 use jacquard::oauth::atproto::AtprotoClientMetadata; ··· 240 251 .await?; 241 252 242 253 let agent: Agent<_> = Agent::from(session); 243 - deploy_site(&agent, path, site, directory, spa).await 254 + deploy_site(&agent, path, site, directory, spa, yes).await 255 + } 256 + 257 + /// Scan directory to count files and calculate total size 258 + /// Returns (file_count, total_size_bytes) 259 + fn scan_directory_stats( 260 + dir_path: &Path, 261 + ignore_matcher: &ignore_patterns::IgnoreMatcher, 262 + current_path: String, 263 + ) -> miette::Result<(usize, u64)> { 264 + let mut file_count = 0; 265 + let mut total_size = 0u64; 266 + 267 + let dir_entries: Vec<_> = std::fs::read_dir(dir_path) 268 + .into_diagnostic()? 269 + .collect::<Result<Vec<_>, _>>() 270 + .into_diagnostic()?; 271 + 272 + for entry in dir_entries { 273 + let path = entry.path(); 274 + let name = entry.file_name(); 275 + let name_str = name.to_str() 276 + .ok_or_else(|| miette::miette!("Invalid filename: {:?}", name))? 277 + .to_string(); 278 + 279 + let full_path = if current_path.is_empty() { 280 + name_str.clone() 281 + } else { 282 + format!("{}/{}", current_path, name_str) 283 + }; 284 + 285 + // Skip files/directories that match ignore patterns 286 + if ignore_matcher.is_ignored(&full_path) || ignore_matcher.is_filename_ignored(&name_str) { 287 + continue; 288 + } 289 + 290 + let metadata = entry.metadata().into_diagnostic()?; 291 + 292 + if metadata.is_file() { 293 + file_count += 1; 294 + total_size += metadata.len(); 295 + } else if metadata.is_dir() { 296 + let subdir_path = if current_path.is_empty() { 297 + name_str 298 + } else { 299 + format!("{}/{}", current_path, name_str) 300 + }; 301 + let (sub_count, sub_size) = scan_directory_stats(&path, ignore_matcher, subdir_path)?; 302 + file_count += sub_count; 303 + total_size += sub_size; 304 + } 305 + } 306 + 307 + Ok((file_count, total_size)) 244 308 } 245 309 246 310 /// Deploy the site using the provided agent ··· 250 314 site: Option<String>, 251 315 directory_listing: bool, 252 316 spa_mode: bool, 317 + skip_prompts: bool, 253 318 ) -> miette::Result<()> { 254 319 // Verify the path exists 255 320 if !path.exists() { ··· 267 332 268 333 println!("Deploying site '{}'...", site_name); 269 334 335 + // Scan directory to check file count and size 336 + let ignore_matcher = ignore_patterns::IgnoreMatcher::new(&path)?; 337 + let (file_count, total_size) = scan_directory_stats(&path, &ignore_matcher, String::new())?; 338 + 339 + let size_mb = total_size as f64 / (1024.0 * 1024.0); 340 + println!("Scanned: {} files, {:.1} MB total", file_count, size_mb); 341 + 342 + // Check if limits are exceeded 343 + let exceeds_file_count = file_count > MAX_FILE_COUNT; 344 + let exceeds_size = total_size > MAX_SITE_SIZE as u64; 345 + 346 + if exceeds_file_count || exceeds_size { 347 + println!("\nโš ๏ธ Warning: Your site exceeds wisp.place caching limits:"); 348 + 349 + if exceeds_file_count { 350 + println!(" โ€ข File count: {} (limit: {})", file_count, MAX_FILE_COUNT); 351 + } 352 + 353 + if exceeds_size { 354 + let size_mb = total_size as f64 / (1024.0 * 1024.0); 355 + let limit_mb = MAX_SITE_SIZE as f64 / (1024.0 * 1024.0); 356 + println!(" โ€ข Total size: {:.1} MB (limit: {:.0} MB)", size_mb, limit_mb); 357 + } 358 + 359 + println!("\nwisp.place will NOT serve your site if you proceed."); 360 + println!("Your site will be uploaded to your PDS, but will only be accessible via:"); 361 + println!(" โ€ข wisp-cli serve (local hosting)"); 362 + println!(" โ€ข Other hosting services with more generous limits"); 363 + 364 + if !skip_prompts { 365 + // Prompt for confirmation 366 + use std::io::{self, Write}; 367 + print!("\nDo you want to upload anyway? (y/N): "); 368 + io::stdout().flush().into_diagnostic()?; 369 + 370 + let mut input = String::new(); 371 + io::stdin().read_line(&mut input).into_diagnostic()?; 372 + let input = input.trim().to_lowercase(); 373 + 374 + if input != "y" && input != "yes" { 375 + println!("Upload cancelled."); 376 + return Ok(()); 377 + } 378 + } else { 379 + println!("\nSkipping confirmation (--yes flag set)."); 380 + } 381 + 382 + println!("\nProceeding with upload...\n"); 383 + } 384 + 270 385 // Try to fetch existing manifest for incremental updates 271 386 let (existing_blob_map, old_subfs_uris): (HashMap<String, (jacquard_common::types::blob::BlobRef<'static>, String)>, Vec<(String, String)>) = { 272 387 use jacquard_common::types::string::AtUri; ··· 328 443 } 329 444 }; 330 445 331 - // Build directory tree with ignore patterns 332 - let ignore_matcher = ignore_patterns::IgnoreMatcher::new(&path)?; 333 - 334 446 // Create progress tracking (spinner style since we don't know total count upfront) 335 447 let multi_progress = MultiProgress::new(); 336 448 let progress = multi_progress.add(ProgressBar::new_spinner()); ··· 397 509 let chunk_file_count = subfs_utils::count_files_in_directory(chunk); 398 510 let chunk_size = subfs_utils::estimate_directory_size(chunk); 399 511 400 - let chunk_manifest = crate::place_wisp::subfs::SubfsRecord::new() 512 + let chunk_manifest = wisp_lexicons::place_wisp::subfs::SubfsRecord::new() 401 513 .root(convert_fs_dir_to_subfs_dir(chunk.clone())) 402 514 .file_count(Some(chunk_file_count as i64)) 403 515 .created_at(Datetime::now()) ··· 420 532 // Each chunk reference MUST have flat: true to merge chunk contents 421 533 println!(" โ†’ Creating parent subfs with {} chunk references...", chunk_uris.len()); 422 534 use jacquard_common::CowStr; 423 - use crate::place_wisp::fs::{Subfs}; 535 + use wisp_lexicons::place_wisp::fs::{Subfs}; 424 536 425 537 // Convert to fs::Subfs (which has the 'flat' field) instead of subfs::Subfs 426 538 let parent_entries_fs: Vec<Entry> = chunk_uris.iter().enumerate().map(|(i, (uri, _))| { ··· 450 562 let parent_tid = Tid::now_0(); 451 563 let parent_rkey = parent_tid.to_string(); 452 564 453 - let parent_manifest = crate::place_wisp::subfs::SubfsRecord::new() 565 + let parent_manifest = wisp_lexicons::place_wisp::subfs::SubfsRecord::new() 454 566 .root(parent_root_subfs) 455 567 .file_count(Some(largest_dir.file_count as i64)) 456 568 .created_at(Datetime::now()) ··· 469 581 let subfs_tid = Tid::now_0(); 470 582 let subfs_rkey = subfs_tid.to_string(); 471 583 472 - let subfs_manifest = crate::place_wisp::subfs::SubfsRecord::new() 584 + let subfs_manifest = wisp_lexicons::place_wisp::subfs::SubfsRecord::new() 473 585 .root(convert_fs_dir_to_subfs_dir(largest_dir.directory.clone())) 474 586 .file_count(Some(largest_dir.file_count as i64)) 475 587 .created_at(Datetime::now()) ··· 837 949 838 950 /// Convert fs::Directory to subfs::Directory 839 951 /// They have the same structure, but different types 840 - fn convert_fs_dir_to_subfs_dir(fs_dir: place_wisp::fs::Directory<'static>) -> place_wisp::subfs::Directory<'static> { 841 - use place_wisp::subfs::{Directory as SubfsDirectory, Entry as SubfsEntry, EntryNode as SubfsEntryNode, File as SubfsFile}; 952 + fn convert_fs_dir_to_subfs_dir(fs_dir: wisp_lexicons::place_wisp::fs::Directory<'static>) -> wisp_lexicons::place_wisp::subfs::Directory<'static> { 953 + use wisp_lexicons::place_wisp::subfs::{Directory as SubfsDirectory, Entry as SubfsEntry, EntryNode as SubfsEntryNode, File as SubfsFile}; 842 954 843 955 let subfs_entries: Vec<SubfsEntry> = fs_dir.entries.into_iter().map(|entry| { 844 956 let node = match entry.node { 845 - place_wisp::fs::EntryNode::File(file) => { 957 + wisp_lexicons::place_wisp::fs::EntryNode::File(file) => { 846 958 SubfsEntryNode::File(Box::new(SubfsFile::new() 847 959 .r#type(file.r#type) 848 960 .blob(file.blob) ··· 851 963 .base64(file.base64) 852 964 .build())) 853 965 } 854 - place_wisp::fs::EntryNode::Directory(dir) => { 966 + wisp_lexicons::place_wisp::fs::EntryNode::Directory(dir) => { 855 967 SubfsEntryNode::Directory(Box::new(convert_fs_dir_to_subfs_dir(*dir))) 856 968 } 857 - place_wisp::fs::EntryNode::Subfs(subfs) => { 969 + wisp_lexicons::place_wisp::fs::EntryNode::Subfs(subfs) => { 858 970 // Nested subfs in the directory we're converting 859 971 // Note: subfs::Subfs doesn't have the 'flat' field - that's only in fs::Subfs 860 - SubfsEntryNode::Subfs(Box::new(place_wisp::subfs::Subfs::new() 972 + SubfsEntryNode::Subfs(Box::new(wisp_lexicons::place_wisp::subfs::Subfs::new() 861 973 .r#type(subfs.r#type) 862 974 .subject(subfs.subject) 863 975 .build())) 864 976 } 865 - place_wisp::fs::EntryNode::Unknown(unknown) => { 977 + wisp_lexicons::place_wisp::fs::EntryNode::Unknown(unknown) => { 866 978 SubfsEntryNode::Unknown(unknown) 867 979 } 868 980 };
-9
cli/src/mod.rs
··· 1 - // @generated by jacquard-lexicon. DO NOT EDIT. 2 - // 3 - // This file was automatically generated from Lexicon schemas. 4 - // Any manual changes will be overwritten on the next regeneration. 5 - 6 - pub mod builder_types; 7 - 8 - #[cfg(feature = "place_wisp")] 9 - pub mod place_wisp;
-1490
cli/src/place_wisp/fs.rs
··· 1 - // @generated by jacquard-lexicon. DO NOT EDIT. 2 - // 3 - // Lexicon: place.wisp.fs 4 - // 5 - // This file was automatically generated from Lexicon schemas. 6 - // Any manual changes will be overwritten on the next regeneration. 7 - 8 - #[jacquard_derive::lexicon] 9 - #[derive( 10 - serde::Serialize, 11 - serde::Deserialize, 12 - Debug, 13 - Clone, 14 - PartialEq, 15 - Eq, 16 - jacquard_derive::IntoStatic 17 - )] 18 - #[serde(rename_all = "camelCase")] 19 - pub struct Directory<'a> { 20 - #[serde(borrow)] 21 - pub entries: Vec<crate::place_wisp::fs::Entry<'a>>, 22 - #[serde(borrow)] 23 - pub r#type: jacquard_common::CowStr<'a>, 24 - } 25 - 26 - pub mod directory_state { 27 - 28 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 29 - #[allow(unused)] 30 - use ::core::marker::PhantomData; 31 - mod sealed { 32 - pub trait Sealed {} 33 - } 34 - /// State trait tracking which required fields have been set 35 - pub trait State: sealed::Sealed { 36 - type Type; 37 - type Entries; 38 - } 39 - /// Empty state - all required fields are unset 40 - pub struct Empty(()); 41 - impl sealed::Sealed for Empty {} 42 - impl State for Empty { 43 - type Type = Unset; 44 - type Entries = Unset; 45 - } 46 - ///State transition - sets the `type` field to Set 47 - pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 48 - impl<S: State> sealed::Sealed for SetType<S> {} 49 - impl<S: State> State for SetType<S> { 50 - type Type = Set<members::r#type>; 51 - type Entries = S::Entries; 52 - } 53 - ///State transition - sets the `entries` field to Set 54 - pub struct SetEntries<S: State = Empty>(PhantomData<fn() -> S>); 55 - impl<S: State> sealed::Sealed for SetEntries<S> {} 56 - impl<S: State> State for SetEntries<S> { 57 - type Type = S::Type; 58 - type Entries = Set<members::entries>; 59 - } 60 - /// Marker types for field names 61 - #[allow(non_camel_case_types)] 62 - pub mod members { 63 - ///Marker type for the `type` field 64 - pub struct r#type(()); 65 - ///Marker type for the `entries` field 66 - pub struct entries(()); 67 - } 68 - } 69 - 70 - /// Builder for constructing an instance of this type 71 - pub struct DirectoryBuilder<'a, S: directory_state::State> { 72 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 73 - __unsafe_private_named: ( 74 - ::core::option::Option<Vec<crate::place_wisp::fs::Entry<'a>>>, 75 - ::core::option::Option<jacquard_common::CowStr<'a>>, 76 - ), 77 - _phantom: ::core::marker::PhantomData<&'a ()>, 78 - } 79 - 80 - impl<'a> Directory<'a> { 81 - /// Create a new builder for this type 82 - pub fn new() -> DirectoryBuilder<'a, directory_state::Empty> { 83 - DirectoryBuilder::new() 84 - } 85 - } 86 - 87 - impl<'a> DirectoryBuilder<'a, directory_state::Empty> { 88 - /// Create a new builder with all fields unset 89 - pub fn new() -> Self { 90 - DirectoryBuilder { 91 - _phantom_state: ::core::marker::PhantomData, 92 - __unsafe_private_named: (None, None), 93 - _phantom: ::core::marker::PhantomData, 94 - } 95 - } 96 - } 97 - 98 - impl<'a, S> DirectoryBuilder<'a, S> 99 - where 100 - S: directory_state::State, 101 - S::Entries: directory_state::IsUnset, 102 - { 103 - /// Set the `entries` field (required) 104 - pub fn entries( 105 - mut self, 106 - value: impl Into<Vec<crate::place_wisp::fs::Entry<'a>>>, 107 - ) -> DirectoryBuilder<'a, directory_state::SetEntries<S>> { 108 - self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 109 - DirectoryBuilder { 110 - _phantom_state: ::core::marker::PhantomData, 111 - __unsafe_private_named: self.__unsafe_private_named, 112 - _phantom: ::core::marker::PhantomData, 113 - } 114 - } 115 - } 116 - 117 - impl<'a, S> DirectoryBuilder<'a, S> 118 - where 119 - S: directory_state::State, 120 - S::Type: directory_state::IsUnset, 121 - { 122 - /// Set the `type` field (required) 123 - pub fn r#type( 124 - mut self, 125 - value: impl Into<jacquard_common::CowStr<'a>>, 126 - ) -> DirectoryBuilder<'a, directory_state::SetType<S>> { 127 - self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 128 - DirectoryBuilder { 129 - _phantom_state: ::core::marker::PhantomData, 130 - __unsafe_private_named: self.__unsafe_private_named, 131 - _phantom: ::core::marker::PhantomData, 132 - } 133 - } 134 - } 135 - 136 - impl<'a, S> DirectoryBuilder<'a, S> 137 - where 138 - S: directory_state::State, 139 - S::Type: directory_state::IsSet, 140 - S::Entries: directory_state::IsSet, 141 - { 142 - /// Build the final struct 143 - pub fn build(self) -> Directory<'a> { 144 - Directory { 145 - entries: self.__unsafe_private_named.0.unwrap(), 146 - r#type: self.__unsafe_private_named.1.unwrap(), 147 - extra_data: Default::default(), 148 - } 149 - } 150 - /// Build the final struct with custom extra_data 151 - pub fn build_with_data( 152 - self, 153 - extra_data: std::collections::BTreeMap< 154 - jacquard_common::smol_str::SmolStr, 155 - jacquard_common::types::value::Data<'a>, 156 - >, 157 - ) -> Directory<'a> { 158 - Directory { 159 - entries: self.__unsafe_private_named.0.unwrap(), 160 - r#type: self.__unsafe_private_named.1.unwrap(), 161 - extra_data: Some(extra_data), 162 - } 163 - } 164 - } 165 - 166 - fn lexicon_doc_place_wisp_fs() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 167 - ::jacquard_lexicon::lexicon::LexiconDoc { 168 - lexicon: ::jacquard_lexicon::lexicon::Lexicon::Lexicon1, 169 - id: ::jacquard_common::CowStr::new_static("place.wisp.fs"), 170 - revision: None, 171 - description: None, 172 - defs: { 173 - let mut map = ::std::collections::BTreeMap::new(); 174 - map.insert( 175 - ::jacquard_common::smol_str::SmolStr::new_static("directory"), 176 - ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 177 - description: None, 178 - required: Some( 179 - vec![ 180 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 181 - ::jacquard_common::smol_str::SmolStr::new_static("entries") 182 - ], 183 - ), 184 - nullable: None, 185 - properties: { 186 - #[allow(unused_mut)] 187 - let mut map = ::std::collections::BTreeMap::new(); 188 - map.insert( 189 - ::jacquard_common::smol_str::SmolStr::new_static("entries"), 190 - ::jacquard_lexicon::lexicon::LexObjectProperty::Array(::jacquard_lexicon::lexicon::LexArray { 191 - description: None, 192 - items: ::jacquard_lexicon::lexicon::LexArrayItem::Ref(::jacquard_lexicon::lexicon::LexRef { 193 - description: None, 194 - r#ref: ::jacquard_common::CowStr::new_static("#entry"), 195 - }), 196 - min_length: None, 197 - max_length: Some(500usize), 198 - }), 199 - ); 200 - map.insert( 201 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 202 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 203 - description: None, 204 - format: None, 205 - default: None, 206 - min_length: None, 207 - max_length: None, 208 - min_graphemes: None, 209 - max_graphemes: None, 210 - r#enum: None, 211 - r#const: None, 212 - known_values: None, 213 - }), 214 - ); 215 - map 216 - }, 217 - }), 218 - ); 219 - map.insert( 220 - ::jacquard_common::smol_str::SmolStr::new_static("entry"), 221 - ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 222 - description: None, 223 - required: Some( 224 - vec![ 225 - ::jacquard_common::smol_str::SmolStr::new_static("name"), 226 - ::jacquard_common::smol_str::SmolStr::new_static("node") 227 - ], 228 - ), 229 - nullable: None, 230 - properties: { 231 - #[allow(unused_mut)] 232 - let mut map = ::std::collections::BTreeMap::new(); 233 - map.insert( 234 - ::jacquard_common::smol_str::SmolStr::new_static("name"), 235 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 236 - description: None, 237 - format: None, 238 - default: None, 239 - min_length: None, 240 - max_length: Some(255usize), 241 - min_graphemes: None, 242 - max_graphemes: None, 243 - r#enum: None, 244 - r#const: None, 245 - known_values: None, 246 - }), 247 - ); 248 - map.insert( 249 - ::jacquard_common::smol_str::SmolStr::new_static("node"), 250 - ::jacquard_lexicon::lexicon::LexObjectProperty::Union(::jacquard_lexicon::lexicon::LexRefUnion { 251 - description: None, 252 - refs: vec![ 253 - ::jacquard_common::CowStr::new_static("#file"), 254 - ::jacquard_common::CowStr::new_static("#directory"), 255 - ::jacquard_common::CowStr::new_static("#subfs") 256 - ], 257 - closed: None, 258 - }), 259 - ); 260 - map 261 - }, 262 - }), 263 - ); 264 - map.insert( 265 - ::jacquard_common::smol_str::SmolStr::new_static("file"), 266 - ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 267 - description: None, 268 - required: Some( 269 - vec![ 270 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 271 - ::jacquard_common::smol_str::SmolStr::new_static("blob") 272 - ], 273 - ), 274 - nullable: None, 275 - properties: { 276 - #[allow(unused_mut)] 277 - let mut map = ::std::collections::BTreeMap::new(); 278 - map.insert( 279 - ::jacquard_common::smol_str::SmolStr::new_static("base64"), 280 - ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 281 - description: None, 282 - default: None, 283 - r#const: None, 284 - }), 285 - ); 286 - map.insert( 287 - ::jacquard_common::smol_str::SmolStr::new_static("blob"), 288 - ::jacquard_lexicon::lexicon::LexObjectProperty::Blob(::jacquard_lexicon::lexicon::LexBlob { 289 - description: None, 290 - accept: None, 291 - max_size: None, 292 - }), 293 - ); 294 - map.insert( 295 - ::jacquard_common::smol_str::SmolStr::new_static("encoding"), 296 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 297 - description: Some( 298 - ::jacquard_common::CowStr::new_static( 299 - "Content encoding (e.g., gzip for compressed files)", 300 - ), 301 - ), 302 - format: None, 303 - default: None, 304 - min_length: None, 305 - max_length: None, 306 - min_graphemes: None, 307 - max_graphemes: None, 308 - r#enum: None, 309 - r#const: None, 310 - known_values: None, 311 - }), 312 - ); 313 - map.insert( 314 - ::jacquard_common::smol_str::SmolStr::new_static("mimeType"), 315 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 316 - description: Some( 317 - ::jacquard_common::CowStr::new_static( 318 - "Original MIME type before compression", 319 - ), 320 - ), 321 - format: None, 322 - default: None, 323 - min_length: None, 324 - max_length: None, 325 - min_graphemes: None, 326 - max_graphemes: None, 327 - r#enum: None, 328 - r#const: None, 329 - known_values: None, 330 - }), 331 - ); 332 - map.insert( 333 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 334 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 335 - description: None, 336 - format: None, 337 - default: None, 338 - min_length: None, 339 - max_length: None, 340 - min_graphemes: None, 341 - max_graphemes: None, 342 - r#enum: None, 343 - r#const: None, 344 - known_values: None, 345 - }), 346 - ); 347 - map 348 - }, 349 - }), 350 - ); 351 - map.insert( 352 - ::jacquard_common::smol_str::SmolStr::new_static("main"), 353 - ::jacquard_lexicon::lexicon::LexUserType::Record(::jacquard_lexicon::lexicon::LexRecord { 354 - description: Some( 355 - ::jacquard_common::CowStr::new_static( 356 - "Virtual filesystem manifest for a Wisp site", 357 - ), 358 - ), 359 - key: None, 360 - record: ::jacquard_lexicon::lexicon::LexRecordRecord::Object(::jacquard_lexicon::lexicon::LexObject { 361 - description: None, 362 - required: Some( 363 - vec![ 364 - ::jacquard_common::smol_str::SmolStr::new_static("site"), 365 - ::jacquard_common::smol_str::SmolStr::new_static("root"), 366 - ::jacquard_common::smol_str::SmolStr::new_static("createdAt") 367 - ], 368 - ), 369 - nullable: None, 370 - properties: { 371 - #[allow(unused_mut)] 372 - let mut map = ::std::collections::BTreeMap::new(); 373 - map.insert( 374 - ::jacquard_common::smol_str::SmolStr::new_static( 375 - "createdAt", 376 - ), 377 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 378 - description: None, 379 - format: Some( 380 - ::jacquard_lexicon::lexicon::LexStringFormat::Datetime, 381 - ), 382 - default: None, 383 - min_length: None, 384 - max_length: None, 385 - min_graphemes: None, 386 - max_graphemes: None, 387 - r#enum: None, 388 - r#const: None, 389 - known_values: None, 390 - }), 391 - ); 392 - map.insert( 393 - ::jacquard_common::smol_str::SmolStr::new_static( 394 - "fileCount", 395 - ), 396 - ::jacquard_lexicon::lexicon::LexObjectProperty::Integer(::jacquard_lexicon::lexicon::LexInteger { 397 - description: None, 398 - default: None, 399 - minimum: Some(0i64), 400 - maximum: Some(1000i64), 401 - r#enum: None, 402 - r#const: None, 403 - }), 404 - ); 405 - map.insert( 406 - ::jacquard_common::smol_str::SmolStr::new_static("root"), 407 - ::jacquard_lexicon::lexicon::LexObjectProperty::Ref(::jacquard_lexicon::lexicon::LexRef { 408 - description: None, 409 - r#ref: ::jacquard_common::CowStr::new_static("#directory"), 410 - }), 411 - ); 412 - map.insert( 413 - ::jacquard_common::smol_str::SmolStr::new_static("site"), 414 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 415 - description: None, 416 - format: None, 417 - default: None, 418 - min_length: None, 419 - max_length: None, 420 - min_graphemes: None, 421 - max_graphemes: None, 422 - r#enum: None, 423 - r#const: None, 424 - known_values: None, 425 - }), 426 - ); 427 - map 428 - }, 429 - }), 430 - }), 431 - ); 432 - map.insert( 433 - ::jacquard_common::smol_str::SmolStr::new_static("subfs"), 434 - ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 435 - description: None, 436 - required: Some( 437 - vec![ 438 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 439 - ::jacquard_common::smol_str::SmolStr::new_static("subject") 440 - ], 441 - ), 442 - nullable: None, 443 - properties: { 444 - #[allow(unused_mut)] 445 - let mut map = ::std::collections::BTreeMap::new(); 446 - map.insert( 447 - ::jacquard_common::smol_str::SmolStr::new_static("flat"), 448 - ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 449 - description: None, 450 - default: None, 451 - r#const: None, 452 - }), 453 - ); 454 - map.insert( 455 - ::jacquard_common::smol_str::SmolStr::new_static("subject"), 456 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 457 - description: Some( 458 - ::jacquard_common::CowStr::new_static( 459 - "AT-URI pointing to a place.wisp.subfs record containing this subtree.", 460 - ), 461 - ), 462 - format: Some( 463 - ::jacquard_lexicon::lexicon::LexStringFormat::AtUri, 464 - ), 465 - default: None, 466 - min_length: None, 467 - max_length: None, 468 - min_graphemes: None, 469 - max_graphemes: None, 470 - r#enum: None, 471 - r#const: None, 472 - known_values: None, 473 - }), 474 - ); 475 - map.insert( 476 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 477 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 478 - description: None, 479 - format: None, 480 - default: None, 481 - min_length: None, 482 - max_length: None, 483 - min_graphemes: None, 484 - max_graphemes: None, 485 - r#enum: None, 486 - r#const: None, 487 - known_values: None, 488 - }), 489 - ); 490 - map 491 - }, 492 - }), 493 - ); 494 - map 495 - }, 496 - } 497 - } 498 - 499 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Directory<'a> { 500 - fn nsid() -> &'static str { 501 - "place.wisp.fs" 502 - } 503 - fn def_name() -> &'static str { 504 - "directory" 505 - } 506 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 507 - lexicon_doc_place_wisp_fs() 508 - } 509 - fn validate( 510 - &self, 511 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 512 - { 513 - let value = &self.entries; 514 - #[allow(unused_comparisons)] 515 - if value.len() > 500usize { 516 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 517 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 518 - "entries", 519 - ), 520 - max: 500usize, 521 - actual: value.len(), 522 - }); 523 - } 524 - } 525 - Ok(()) 526 - } 527 - } 528 - 529 - #[jacquard_derive::lexicon] 530 - #[derive( 531 - serde::Serialize, 532 - serde::Deserialize, 533 - Debug, 534 - Clone, 535 - PartialEq, 536 - Eq, 537 - jacquard_derive::IntoStatic 538 - )] 539 - #[serde(rename_all = "camelCase")] 540 - pub struct Entry<'a> { 541 - #[serde(borrow)] 542 - pub name: jacquard_common::CowStr<'a>, 543 - #[serde(borrow)] 544 - pub node: EntryNode<'a>, 545 - } 546 - 547 - pub mod entry_state { 548 - 549 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 550 - #[allow(unused)] 551 - use ::core::marker::PhantomData; 552 - mod sealed { 553 - pub trait Sealed {} 554 - } 555 - /// State trait tracking which required fields have been set 556 - pub trait State: sealed::Sealed { 557 - type Name; 558 - type Node; 559 - } 560 - /// Empty state - all required fields are unset 561 - pub struct Empty(()); 562 - impl sealed::Sealed for Empty {} 563 - impl State for Empty { 564 - type Name = Unset; 565 - type Node = Unset; 566 - } 567 - ///State transition - sets the `name` field to Set 568 - pub struct SetName<S: State = Empty>(PhantomData<fn() -> S>); 569 - impl<S: State> sealed::Sealed for SetName<S> {} 570 - impl<S: State> State for SetName<S> { 571 - type Name = Set<members::name>; 572 - type Node = S::Node; 573 - } 574 - ///State transition - sets the `node` field to Set 575 - pub struct SetNode<S: State = Empty>(PhantomData<fn() -> S>); 576 - impl<S: State> sealed::Sealed for SetNode<S> {} 577 - impl<S: State> State for SetNode<S> { 578 - type Name = S::Name; 579 - type Node = Set<members::node>; 580 - } 581 - /// Marker types for field names 582 - #[allow(non_camel_case_types)] 583 - pub mod members { 584 - ///Marker type for the `name` field 585 - pub struct name(()); 586 - ///Marker type for the `node` field 587 - pub struct node(()); 588 - } 589 - } 590 - 591 - /// Builder for constructing an instance of this type 592 - pub struct EntryBuilder<'a, S: entry_state::State> { 593 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 594 - __unsafe_private_named: ( 595 - ::core::option::Option<jacquard_common::CowStr<'a>>, 596 - ::core::option::Option<EntryNode<'a>>, 597 - ), 598 - _phantom: ::core::marker::PhantomData<&'a ()>, 599 - } 600 - 601 - impl<'a> Entry<'a> { 602 - /// Create a new builder for this type 603 - pub fn new() -> EntryBuilder<'a, entry_state::Empty> { 604 - EntryBuilder::new() 605 - } 606 - } 607 - 608 - impl<'a> EntryBuilder<'a, entry_state::Empty> { 609 - /// Create a new builder with all fields unset 610 - pub fn new() -> Self { 611 - EntryBuilder { 612 - _phantom_state: ::core::marker::PhantomData, 613 - __unsafe_private_named: (None, None), 614 - _phantom: ::core::marker::PhantomData, 615 - } 616 - } 617 - } 618 - 619 - impl<'a, S> EntryBuilder<'a, S> 620 - where 621 - S: entry_state::State, 622 - S::Name: entry_state::IsUnset, 623 - { 624 - /// Set the `name` field (required) 625 - pub fn name( 626 - mut self, 627 - value: impl Into<jacquard_common::CowStr<'a>>, 628 - ) -> EntryBuilder<'a, entry_state::SetName<S>> { 629 - self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 630 - EntryBuilder { 631 - _phantom_state: ::core::marker::PhantomData, 632 - __unsafe_private_named: self.__unsafe_private_named, 633 - _phantom: ::core::marker::PhantomData, 634 - } 635 - } 636 - } 637 - 638 - impl<'a, S> EntryBuilder<'a, S> 639 - where 640 - S: entry_state::State, 641 - S::Node: entry_state::IsUnset, 642 - { 643 - /// Set the `node` field (required) 644 - pub fn node( 645 - mut self, 646 - value: impl Into<EntryNode<'a>>, 647 - ) -> EntryBuilder<'a, entry_state::SetNode<S>> { 648 - self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 649 - EntryBuilder { 650 - _phantom_state: ::core::marker::PhantomData, 651 - __unsafe_private_named: self.__unsafe_private_named, 652 - _phantom: ::core::marker::PhantomData, 653 - } 654 - } 655 - } 656 - 657 - impl<'a, S> EntryBuilder<'a, S> 658 - where 659 - S: entry_state::State, 660 - S::Name: entry_state::IsSet, 661 - S::Node: entry_state::IsSet, 662 - { 663 - /// Build the final struct 664 - pub fn build(self) -> Entry<'a> { 665 - Entry { 666 - name: self.__unsafe_private_named.0.unwrap(), 667 - node: self.__unsafe_private_named.1.unwrap(), 668 - extra_data: Default::default(), 669 - } 670 - } 671 - /// Build the final struct with custom extra_data 672 - pub fn build_with_data( 673 - self, 674 - extra_data: std::collections::BTreeMap< 675 - jacquard_common::smol_str::SmolStr, 676 - jacquard_common::types::value::Data<'a>, 677 - >, 678 - ) -> Entry<'a> { 679 - Entry { 680 - name: self.__unsafe_private_named.0.unwrap(), 681 - node: self.__unsafe_private_named.1.unwrap(), 682 - extra_data: Some(extra_data), 683 - } 684 - } 685 - } 686 - 687 - #[jacquard_derive::open_union] 688 - #[derive( 689 - serde::Serialize, 690 - serde::Deserialize, 691 - Debug, 692 - Clone, 693 - PartialEq, 694 - Eq, 695 - jacquard_derive::IntoStatic 696 - )] 697 - #[serde(tag = "$type")] 698 - #[serde(bound(deserialize = "'de: 'a"))] 699 - pub enum EntryNode<'a> { 700 - #[serde(rename = "place.wisp.fs#file")] 701 - File(Box<crate::place_wisp::fs::File<'a>>), 702 - #[serde(rename = "place.wisp.fs#directory")] 703 - Directory(Box<crate::place_wisp::fs::Directory<'a>>), 704 - #[serde(rename = "place.wisp.fs#subfs")] 705 - Subfs(Box<crate::place_wisp::fs::Subfs<'a>>), 706 - } 707 - 708 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Entry<'a> { 709 - fn nsid() -> &'static str { 710 - "place.wisp.fs" 711 - } 712 - fn def_name() -> &'static str { 713 - "entry" 714 - } 715 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 716 - lexicon_doc_place_wisp_fs() 717 - } 718 - fn validate( 719 - &self, 720 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 721 - { 722 - let value = &self.name; 723 - #[allow(unused_comparisons)] 724 - if <str>::len(value.as_ref()) > 255usize { 725 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 726 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 727 - "name", 728 - ), 729 - max: 255usize, 730 - actual: <str>::len(value.as_ref()), 731 - }); 732 - } 733 - } 734 - Ok(()) 735 - } 736 - } 737 - 738 - #[jacquard_derive::lexicon] 739 - #[derive( 740 - serde::Serialize, 741 - serde::Deserialize, 742 - Debug, 743 - Clone, 744 - PartialEq, 745 - Eq, 746 - jacquard_derive::IntoStatic 747 - )] 748 - #[serde(rename_all = "camelCase")] 749 - pub struct File<'a> { 750 - /// True if blob content is base64-encoded (used to bypass PDS content sniffing) 751 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 752 - pub base64: Option<bool>, 753 - /// Content blob ref 754 - #[serde(borrow)] 755 - pub blob: jacquard_common::types::blob::BlobRef<'a>, 756 - /// Content encoding (e.g., gzip for compressed files) 757 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 758 - #[serde(borrow)] 759 - pub encoding: Option<jacquard_common::CowStr<'a>>, 760 - /// Original MIME type before compression 761 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 762 - #[serde(borrow)] 763 - pub mime_type: Option<jacquard_common::CowStr<'a>>, 764 - #[serde(borrow)] 765 - pub r#type: jacquard_common::CowStr<'a>, 766 - } 767 - 768 - pub mod file_state { 769 - 770 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 771 - #[allow(unused)] 772 - use ::core::marker::PhantomData; 773 - mod sealed { 774 - pub trait Sealed {} 775 - } 776 - /// State trait tracking which required fields have been set 777 - pub trait State: sealed::Sealed { 778 - type Type; 779 - type Blob; 780 - } 781 - /// Empty state - all required fields are unset 782 - pub struct Empty(()); 783 - impl sealed::Sealed for Empty {} 784 - impl State for Empty { 785 - type Type = Unset; 786 - type Blob = Unset; 787 - } 788 - ///State transition - sets the `type` field to Set 789 - pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 790 - impl<S: State> sealed::Sealed for SetType<S> {} 791 - impl<S: State> State for SetType<S> { 792 - type Type = Set<members::r#type>; 793 - type Blob = S::Blob; 794 - } 795 - ///State transition - sets the `blob` field to Set 796 - pub struct SetBlob<S: State = Empty>(PhantomData<fn() -> S>); 797 - impl<S: State> sealed::Sealed for SetBlob<S> {} 798 - impl<S: State> State for SetBlob<S> { 799 - type Type = S::Type; 800 - type Blob = Set<members::blob>; 801 - } 802 - /// Marker types for field names 803 - #[allow(non_camel_case_types)] 804 - pub mod members { 805 - ///Marker type for the `type` field 806 - pub struct r#type(()); 807 - ///Marker type for the `blob` field 808 - pub struct blob(()); 809 - } 810 - } 811 - 812 - /// Builder for constructing an instance of this type 813 - pub struct FileBuilder<'a, S: file_state::State> { 814 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 815 - __unsafe_private_named: ( 816 - ::core::option::Option<bool>, 817 - ::core::option::Option<jacquard_common::types::blob::BlobRef<'a>>, 818 - ::core::option::Option<jacquard_common::CowStr<'a>>, 819 - ::core::option::Option<jacquard_common::CowStr<'a>>, 820 - ::core::option::Option<jacquard_common::CowStr<'a>>, 821 - ), 822 - _phantom: ::core::marker::PhantomData<&'a ()>, 823 - } 824 - 825 - impl<'a> File<'a> { 826 - /// Create a new builder for this type 827 - pub fn new() -> FileBuilder<'a, file_state::Empty> { 828 - FileBuilder::new() 829 - } 830 - } 831 - 832 - impl<'a> FileBuilder<'a, file_state::Empty> { 833 - /// Create a new builder with all fields unset 834 - pub fn new() -> Self { 835 - FileBuilder { 836 - _phantom_state: ::core::marker::PhantomData, 837 - __unsafe_private_named: (None, None, None, None, None), 838 - _phantom: ::core::marker::PhantomData, 839 - } 840 - } 841 - } 842 - 843 - impl<'a, S: file_state::State> FileBuilder<'a, S> { 844 - /// Set the `base64` field (optional) 845 - pub fn base64(mut self, value: impl Into<Option<bool>>) -> Self { 846 - self.__unsafe_private_named.0 = value.into(); 847 - self 848 - } 849 - /// Set the `base64` field to an Option value (optional) 850 - pub fn maybe_base64(mut self, value: Option<bool>) -> Self { 851 - self.__unsafe_private_named.0 = value; 852 - self 853 - } 854 - } 855 - 856 - impl<'a, S> FileBuilder<'a, S> 857 - where 858 - S: file_state::State, 859 - S::Blob: file_state::IsUnset, 860 - { 861 - /// Set the `blob` field (required) 862 - pub fn blob( 863 - mut self, 864 - value: impl Into<jacquard_common::types::blob::BlobRef<'a>>, 865 - ) -> FileBuilder<'a, file_state::SetBlob<S>> { 866 - self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 867 - FileBuilder { 868 - _phantom_state: ::core::marker::PhantomData, 869 - __unsafe_private_named: self.__unsafe_private_named, 870 - _phantom: ::core::marker::PhantomData, 871 - } 872 - } 873 - } 874 - 875 - impl<'a, S: file_state::State> FileBuilder<'a, S> { 876 - /// Set the `encoding` field (optional) 877 - pub fn encoding( 878 - mut self, 879 - value: impl Into<Option<jacquard_common::CowStr<'a>>>, 880 - ) -> Self { 881 - self.__unsafe_private_named.2 = value.into(); 882 - self 883 - } 884 - /// Set the `encoding` field to an Option value (optional) 885 - pub fn maybe_encoding(mut self, value: Option<jacquard_common::CowStr<'a>>) -> Self { 886 - self.__unsafe_private_named.2 = value; 887 - self 888 - } 889 - } 890 - 891 - impl<'a, S: file_state::State> FileBuilder<'a, S> { 892 - /// Set the `mimeType` field (optional) 893 - pub fn mime_type( 894 - mut self, 895 - value: impl Into<Option<jacquard_common::CowStr<'a>>>, 896 - ) -> Self { 897 - self.__unsafe_private_named.3 = value.into(); 898 - self 899 - } 900 - /// Set the `mimeType` field to an Option value (optional) 901 - pub fn maybe_mime_type( 902 - mut self, 903 - value: Option<jacquard_common::CowStr<'a>>, 904 - ) -> Self { 905 - self.__unsafe_private_named.3 = value; 906 - self 907 - } 908 - } 909 - 910 - impl<'a, S> FileBuilder<'a, S> 911 - where 912 - S: file_state::State, 913 - S::Type: file_state::IsUnset, 914 - { 915 - /// Set the `type` field (required) 916 - pub fn r#type( 917 - mut self, 918 - value: impl Into<jacquard_common::CowStr<'a>>, 919 - ) -> FileBuilder<'a, file_state::SetType<S>> { 920 - self.__unsafe_private_named.4 = ::core::option::Option::Some(value.into()); 921 - FileBuilder { 922 - _phantom_state: ::core::marker::PhantomData, 923 - __unsafe_private_named: self.__unsafe_private_named, 924 - _phantom: ::core::marker::PhantomData, 925 - } 926 - } 927 - } 928 - 929 - impl<'a, S> FileBuilder<'a, S> 930 - where 931 - S: file_state::State, 932 - S::Type: file_state::IsSet, 933 - S::Blob: file_state::IsSet, 934 - { 935 - /// Build the final struct 936 - pub fn build(self) -> File<'a> { 937 - File { 938 - base64: self.__unsafe_private_named.0, 939 - blob: self.__unsafe_private_named.1.unwrap(), 940 - encoding: self.__unsafe_private_named.2, 941 - mime_type: self.__unsafe_private_named.3, 942 - r#type: self.__unsafe_private_named.4.unwrap(), 943 - extra_data: Default::default(), 944 - } 945 - } 946 - /// Build the final struct with custom extra_data 947 - pub fn build_with_data( 948 - self, 949 - extra_data: std::collections::BTreeMap< 950 - jacquard_common::smol_str::SmolStr, 951 - jacquard_common::types::value::Data<'a>, 952 - >, 953 - ) -> File<'a> { 954 - File { 955 - base64: self.__unsafe_private_named.0, 956 - blob: self.__unsafe_private_named.1.unwrap(), 957 - encoding: self.__unsafe_private_named.2, 958 - mime_type: self.__unsafe_private_named.3, 959 - r#type: self.__unsafe_private_named.4.unwrap(), 960 - extra_data: Some(extra_data), 961 - } 962 - } 963 - } 964 - 965 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for File<'a> { 966 - fn nsid() -> &'static str { 967 - "place.wisp.fs" 968 - } 969 - fn def_name() -> &'static str { 970 - "file" 971 - } 972 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 973 - lexicon_doc_place_wisp_fs() 974 - } 975 - fn validate( 976 - &self, 977 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 978 - Ok(()) 979 - } 980 - } 981 - 982 - /// Virtual filesystem manifest for a Wisp site 983 - #[jacquard_derive::lexicon] 984 - #[derive( 985 - serde::Serialize, 986 - serde::Deserialize, 987 - Debug, 988 - Clone, 989 - PartialEq, 990 - Eq, 991 - jacquard_derive::IntoStatic 992 - )] 993 - #[serde(rename_all = "camelCase")] 994 - pub struct Fs<'a> { 995 - pub created_at: jacquard_common::types::string::Datetime, 996 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 997 - pub file_count: Option<i64>, 998 - #[serde(borrow)] 999 - pub root: crate::place_wisp::fs::Directory<'a>, 1000 - #[serde(borrow)] 1001 - pub site: jacquard_common::CowStr<'a>, 1002 - } 1003 - 1004 - pub mod fs_state { 1005 - 1006 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 1007 - #[allow(unused)] 1008 - use ::core::marker::PhantomData; 1009 - mod sealed { 1010 - pub trait Sealed {} 1011 - } 1012 - /// State trait tracking which required fields have been set 1013 - pub trait State: sealed::Sealed { 1014 - type Site; 1015 - type Root; 1016 - type CreatedAt; 1017 - } 1018 - /// Empty state - all required fields are unset 1019 - pub struct Empty(()); 1020 - impl sealed::Sealed for Empty {} 1021 - impl State for Empty { 1022 - type Site = Unset; 1023 - type Root = Unset; 1024 - type CreatedAt = Unset; 1025 - } 1026 - ///State transition - sets the `site` field to Set 1027 - pub struct SetSite<S: State = Empty>(PhantomData<fn() -> S>); 1028 - impl<S: State> sealed::Sealed for SetSite<S> {} 1029 - impl<S: State> State for SetSite<S> { 1030 - type Site = Set<members::site>; 1031 - type Root = S::Root; 1032 - type CreatedAt = S::CreatedAt; 1033 - } 1034 - ///State transition - sets the `root` field to Set 1035 - pub struct SetRoot<S: State = Empty>(PhantomData<fn() -> S>); 1036 - impl<S: State> sealed::Sealed for SetRoot<S> {} 1037 - impl<S: State> State for SetRoot<S> { 1038 - type Site = S::Site; 1039 - type Root = Set<members::root>; 1040 - type CreatedAt = S::CreatedAt; 1041 - } 1042 - ///State transition - sets the `created_at` field to Set 1043 - pub struct SetCreatedAt<S: State = Empty>(PhantomData<fn() -> S>); 1044 - impl<S: State> sealed::Sealed for SetCreatedAt<S> {} 1045 - impl<S: State> State for SetCreatedAt<S> { 1046 - type Site = S::Site; 1047 - type Root = S::Root; 1048 - type CreatedAt = Set<members::created_at>; 1049 - } 1050 - /// Marker types for field names 1051 - #[allow(non_camel_case_types)] 1052 - pub mod members { 1053 - ///Marker type for the `site` field 1054 - pub struct site(()); 1055 - ///Marker type for the `root` field 1056 - pub struct root(()); 1057 - ///Marker type for the `created_at` field 1058 - pub struct created_at(()); 1059 - } 1060 - } 1061 - 1062 - /// Builder for constructing an instance of this type 1063 - pub struct FsBuilder<'a, S: fs_state::State> { 1064 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 1065 - __unsafe_private_named: ( 1066 - ::core::option::Option<jacquard_common::types::string::Datetime>, 1067 - ::core::option::Option<i64>, 1068 - ::core::option::Option<crate::place_wisp::fs::Directory<'a>>, 1069 - ::core::option::Option<jacquard_common::CowStr<'a>>, 1070 - ), 1071 - _phantom: ::core::marker::PhantomData<&'a ()>, 1072 - } 1073 - 1074 - impl<'a> Fs<'a> { 1075 - /// Create a new builder for this type 1076 - pub fn new() -> FsBuilder<'a, fs_state::Empty> { 1077 - FsBuilder::new() 1078 - } 1079 - } 1080 - 1081 - impl<'a> FsBuilder<'a, fs_state::Empty> { 1082 - /// Create a new builder with all fields unset 1083 - pub fn new() -> Self { 1084 - FsBuilder { 1085 - _phantom_state: ::core::marker::PhantomData, 1086 - __unsafe_private_named: (None, None, None, None), 1087 - _phantom: ::core::marker::PhantomData, 1088 - } 1089 - } 1090 - } 1091 - 1092 - impl<'a, S> FsBuilder<'a, S> 1093 - where 1094 - S: fs_state::State, 1095 - S::CreatedAt: fs_state::IsUnset, 1096 - { 1097 - /// Set the `createdAt` field (required) 1098 - pub fn created_at( 1099 - mut self, 1100 - value: impl Into<jacquard_common::types::string::Datetime>, 1101 - ) -> FsBuilder<'a, fs_state::SetCreatedAt<S>> { 1102 - self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 1103 - FsBuilder { 1104 - _phantom_state: ::core::marker::PhantomData, 1105 - __unsafe_private_named: self.__unsafe_private_named, 1106 - _phantom: ::core::marker::PhantomData, 1107 - } 1108 - } 1109 - } 1110 - 1111 - impl<'a, S: fs_state::State> FsBuilder<'a, S> { 1112 - /// Set the `fileCount` field (optional) 1113 - pub fn file_count(mut self, value: impl Into<Option<i64>>) -> Self { 1114 - self.__unsafe_private_named.1 = value.into(); 1115 - self 1116 - } 1117 - /// Set the `fileCount` field to an Option value (optional) 1118 - pub fn maybe_file_count(mut self, value: Option<i64>) -> Self { 1119 - self.__unsafe_private_named.1 = value; 1120 - self 1121 - } 1122 - } 1123 - 1124 - impl<'a, S> FsBuilder<'a, S> 1125 - where 1126 - S: fs_state::State, 1127 - S::Root: fs_state::IsUnset, 1128 - { 1129 - /// Set the `root` field (required) 1130 - pub fn root( 1131 - mut self, 1132 - value: impl Into<crate::place_wisp::fs::Directory<'a>>, 1133 - ) -> FsBuilder<'a, fs_state::SetRoot<S>> { 1134 - self.__unsafe_private_named.2 = ::core::option::Option::Some(value.into()); 1135 - FsBuilder { 1136 - _phantom_state: ::core::marker::PhantomData, 1137 - __unsafe_private_named: self.__unsafe_private_named, 1138 - _phantom: ::core::marker::PhantomData, 1139 - } 1140 - } 1141 - } 1142 - 1143 - impl<'a, S> FsBuilder<'a, S> 1144 - where 1145 - S: fs_state::State, 1146 - S::Site: fs_state::IsUnset, 1147 - { 1148 - /// Set the `site` field (required) 1149 - pub fn site( 1150 - mut self, 1151 - value: impl Into<jacquard_common::CowStr<'a>>, 1152 - ) -> FsBuilder<'a, fs_state::SetSite<S>> { 1153 - self.__unsafe_private_named.3 = ::core::option::Option::Some(value.into()); 1154 - FsBuilder { 1155 - _phantom_state: ::core::marker::PhantomData, 1156 - __unsafe_private_named: self.__unsafe_private_named, 1157 - _phantom: ::core::marker::PhantomData, 1158 - } 1159 - } 1160 - } 1161 - 1162 - impl<'a, S> FsBuilder<'a, S> 1163 - where 1164 - S: fs_state::State, 1165 - S::Site: fs_state::IsSet, 1166 - S::Root: fs_state::IsSet, 1167 - S::CreatedAt: fs_state::IsSet, 1168 - { 1169 - /// Build the final struct 1170 - pub fn build(self) -> Fs<'a> { 1171 - Fs { 1172 - created_at: self.__unsafe_private_named.0.unwrap(), 1173 - file_count: self.__unsafe_private_named.1, 1174 - root: self.__unsafe_private_named.2.unwrap(), 1175 - site: self.__unsafe_private_named.3.unwrap(), 1176 - extra_data: Default::default(), 1177 - } 1178 - } 1179 - /// Build the final struct with custom extra_data 1180 - pub fn build_with_data( 1181 - self, 1182 - extra_data: std::collections::BTreeMap< 1183 - jacquard_common::smol_str::SmolStr, 1184 - jacquard_common::types::value::Data<'a>, 1185 - >, 1186 - ) -> Fs<'a> { 1187 - Fs { 1188 - created_at: self.__unsafe_private_named.0.unwrap(), 1189 - file_count: self.__unsafe_private_named.1, 1190 - root: self.__unsafe_private_named.2.unwrap(), 1191 - site: self.__unsafe_private_named.3.unwrap(), 1192 - extra_data: Some(extra_data), 1193 - } 1194 - } 1195 - } 1196 - 1197 - impl<'a> Fs<'a> { 1198 - pub fn uri( 1199 - uri: impl Into<jacquard_common::CowStr<'a>>, 1200 - ) -> Result< 1201 - jacquard_common::types::uri::RecordUri<'a, FsRecord>, 1202 - jacquard_common::types::uri::UriError, 1203 - > { 1204 - jacquard_common::types::uri::RecordUri::try_from_uri( 1205 - jacquard_common::types::string::AtUri::new_cow(uri.into())?, 1206 - ) 1207 - } 1208 - } 1209 - 1210 - /// Typed wrapper for GetRecord response with this collection's record type. 1211 - #[derive( 1212 - serde::Serialize, 1213 - serde::Deserialize, 1214 - Debug, 1215 - Clone, 1216 - PartialEq, 1217 - Eq, 1218 - jacquard_derive::IntoStatic 1219 - )] 1220 - #[serde(rename_all = "camelCase")] 1221 - pub struct FsGetRecordOutput<'a> { 1222 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 1223 - #[serde(borrow)] 1224 - pub cid: std::option::Option<jacquard_common::types::string::Cid<'a>>, 1225 - #[serde(borrow)] 1226 - pub uri: jacquard_common::types::string::AtUri<'a>, 1227 - #[serde(borrow)] 1228 - pub value: Fs<'a>, 1229 - } 1230 - 1231 - impl From<FsGetRecordOutput<'_>> for Fs<'_> { 1232 - fn from(output: FsGetRecordOutput<'_>) -> Self { 1233 - use jacquard_common::IntoStatic; 1234 - output.value.into_static() 1235 - } 1236 - } 1237 - 1238 - impl jacquard_common::types::collection::Collection for Fs<'_> { 1239 - const NSID: &'static str = "place.wisp.fs"; 1240 - type Record = FsRecord; 1241 - } 1242 - 1243 - /// Marker type for deserializing records from this collection. 1244 - #[derive(Debug, serde::Serialize, serde::Deserialize)] 1245 - pub struct FsRecord; 1246 - impl jacquard_common::xrpc::XrpcResp for FsRecord { 1247 - const NSID: &'static str = "place.wisp.fs"; 1248 - const ENCODING: &'static str = "application/json"; 1249 - type Output<'de> = FsGetRecordOutput<'de>; 1250 - type Err<'de> = jacquard_common::types::collection::RecordError<'de>; 1251 - } 1252 - 1253 - impl jacquard_common::types::collection::Collection for FsRecord { 1254 - const NSID: &'static str = "place.wisp.fs"; 1255 - type Record = FsRecord; 1256 - } 1257 - 1258 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Fs<'a> { 1259 - fn nsid() -> &'static str { 1260 - "place.wisp.fs" 1261 - } 1262 - fn def_name() -> &'static str { 1263 - "main" 1264 - } 1265 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 1266 - lexicon_doc_place_wisp_fs() 1267 - } 1268 - fn validate( 1269 - &self, 1270 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 1271 - if let Some(ref value) = self.file_count { 1272 - if *value > 1000i64 { 1273 - return Err(::jacquard_lexicon::validation::ConstraintError::Maximum { 1274 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 1275 - "file_count", 1276 - ), 1277 - max: 1000i64, 1278 - actual: *value, 1279 - }); 1280 - } 1281 - } 1282 - if let Some(ref value) = self.file_count { 1283 - if *value < 0i64 { 1284 - return Err(::jacquard_lexicon::validation::ConstraintError::Minimum { 1285 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 1286 - "file_count", 1287 - ), 1288 - min: 0i64, 1289 - actual: *value, 1290 - }); 1291 - } 1292 - } 1293 - Ok(()) 1294 - } 1295 - } 1296 - 1297 - #[jacquard_derive::lexicon] 1298 - #[derive( 1299 - serde::Serialize, 1300 - serde::Deserialize, 1301 - Debug, 1302 - Clone, 1303 - PartialEq, 1304 - Eq, 1305 - jacquard_derive::IntoStatic 1306 - )] 1307 - #[serde(rename_all = "camelCase")] 1308 - pub struct Subfs<'a> { 1309 - /// If true, the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false (default), the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure. 1310 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 1311 - pub flat: Option<bool>, 1312 - /// AT-URI pointing to a place.wisp.subfs record containing this subtree. 1313 - #[serde(borrow)] 1314 - pub subject: jacquard_common::types::string::AtUri<'a>, 1315 - #[serde(borrow)] 1316 - pub r#type: jacquard_common::CowStr<'a>, 1317 - } 1318 - 1319 - pub mod subfs_state { 1320 - 1321 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 1322 - #[allow(unused)] 1323 - use ::core::marker::PhantomData; 1324 - mod sealed { 1325 - pub trait Sealed {} 1326 - } 1327 - /// State trait tracking which required fields have been set 1328 - pub trait State: sealed::Sealed { 1329 - type Type; 1330 - type Subject; 1331 - } 1332 - /// Empty state - all required fields are unset 1333 - pub struct Empty(()); 1334 - impl sealed::Sealed for Empty {} 1335 - impl State for Empty { 1336 - type Type = Unset; 1337 - type Subject = Unset; 1338 - } 1339 - ///State transition - sets the `type` field to Set 1340 - pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 1341 - impl<S: State> sealed::Sealed for SetType<S> {} 1342 - impl<S: State> State for SetType<S> { 1343 - type Type = Set<members::r#type>; 1344 - type Subject = S::Subject; 1345 - } 1346 - ///State transition - sets the `subject` field to Set 1347 - pub struct SetSubject<S: State = Empty>(PhantomData<fn() -> S>); 1348 - impl<S: State> sealed::Sealed for SetSubject<S> {} 1349 - impl<S: State> State for SetSubject<S> { 1350 - type Type = S::Type; 1351 - type Subject = Set<members::subject>; 1352 - } 1353 - /// Marker types for field names 1354 - #[allow(non_camel_case_types)] 1355 - pub mod members { 1356 - ///Marker type for the `type` field 1357 - pub struct r#type(()); 1358 - ///Marker type for the `subject` field 1359 - pub struct subject(()); 1360 - } 1361 - } 1362 - 1363 - /// Builder for constructing an instance of this type 1364 - pub struct SubfsBuilder<'a, S: subfs_state::State> { 1365 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 1366 - __unsafe_private_named: ( 1367 - ::core::option::Option<bool>, 1368 - ::core::option::Option<jacquard_common::types::string::AtUri<'a>>, 1369 - ::core::option::Option<jacquard_common::CowStr<'a>>, 1370 - ), 1371 - _phantom: ::core::marker::PhantomData<&'a ()>, 1372 - } 1373 - 1374 - impl<'a> Subfs<'a> { 1375 - /// Create a new builder for this type 1376 - pub fn new() -> SubfsBuilder<'a, subfs_state::Empty> { 1377 - SubfsBuilder::new() 1378 - } 1379 - } 1380 - 1381 - impl<'a> SubfsBuilder<'a, subfs_state::Empty> { 1382 - /// Create a new builder with all fields unset 1383 - pub fn new() -> Self { 1384 - SubfsBuilder { 1385 - _phantom_state: ::core::marker::PhantomData, 1386 - __unsafe_private_named: (None, None, None), 1387 - _phantom: ::core::marker::PhantomData, 1388 - } 1389 - } 1390 - } 1391 - 1392 - impl<'a, S: subfs_state::State> SubfsBuilder<'a, S> { 1393 - /// Set the `flat` field (optional) 1394 - pub fn flat(mut self, value: impl Into<Option<bool>>) -> Self { 1395 - self.__unsafe_private_named.0 = value.into(); 1396 - self 1397 - } 1398 - /// Set the `flat` field to an Option value (optional) 1399 - pub fn maybe_flat(mut self, value: Option<bool>) -> Self { 1400 - self.__unsafe_private_named.0 = value; 1401 - self 1402 - } 1403 - } 1404 - 1405 - impl<'a, S> SubfsBuilder<'a, S> 1406 - where 1407 - S: subfs_state::State, 1408 - S::Subject: subfs_state::IsUnset, 1409 - { 1410 - /// Set the `subject` field (required) 1411 - pub fn subject( 1412 - mut self, 1413 - value: impl Into<jacquard_common::types::string::AtUri<'a>>, 1414 - ) -> SubfsBuilder<'a, subfs_state::SetSubject<S>> { 1415 - self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 1416 - SubfsBuilder { 1417 - _phantom_state: ::core::marker::PhantomData, 1418 - __unsafe_private_named: self.__unsafe_private_named, 1419 - _phantom: ::core::marker::PhantomData, 1420 - } 1421 - } 1422 - } 1423 - 1424 - impl<'a, S> SubfsBuilder<'a, S> 1425 - where 1426 - S: subfs_state::State, 1427 - S::Type: subfs_state::IsUnset, 1428 - { 1429 - /// Set the `type` field (required) 1430 - pub fn r#type( 1431 - mut self, 1432 - value: impl Into<jacquard_common::CowStr<'a>>, 1433 - ) -> SubfsBuilder<'a, subfs_state::SetType<S>> { 1434 - self.__unsafe_private_named.2 = ::core::option::Option::Some(value.into()); 1435 - SubfsBuilder { 1436 - _phantom_state: ::core::marker::PhantomData, 1437 - __unsafe_private_named: self.__unsafe_private_named, 1438 - _phantom: ::core::marker::PhantomData, 1439 - } 1440 - } 1441 - } 1442 - 1443 - impl<'a, S> SubfsBuilder<'a, S> 1444 - where 1445 - S: subfs_state::State, 1446 - S::Type: subfs_state::IsSet, 1447 - S::Subject: subfs_state::IsSet, 1448 - { 1449 - /// Build the final struct 1450 - pub fn build(self) -> Subfs<'a> { 1451 - Subfs { 1452 - flat: self.__unsafe_private_named.0, 1453 - subject: self.__unsafe_private_named.1.unwrap(), 1454 - r#type: self.__unsafe_private_named.2.unwrap(), 1455 - extra_data: Default::default(), 1456 - } 1457 - } 1458 - /// Build the final struct with custom extra_data 1459 - pub fn build_with_data( 1460 - self, 1461 - extra_data: std::collections::BTreeMap< 1462 - jacquard_common::smol_str::SmolStr, 1463 - jacquard_common::types::value::Data<'a>, 1464 - >, 1465 - ) -> Subfs<'a> { 1466 - Subfs { 1467 - flat: self.__unsafe_private_named.0, 1468 - subject: self.__unsafe_private_named.1.unwrap(), 1469 - r#type: self.__unsafe_private_named.2.unwrap(), 1470 - extra_data: Some(extra_data), 1471 - } 1472 - } 1473 - } 1474 - 1475 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Subfs<'a> { 1476 - fn nsid() -> &'static str { 1477 - "place.wisp.fs" 1478 - } 1479 - fn def_name() -> &'static str { 1480 - "subfs" 1481 - } 1482 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 1483 - lexicon_doc_place_wisp_fs() 1484 - } 1485 - fn validate( 1486 - &self, 1487 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 1488 - Ok(()) 1489 - } 1490 - }
-653
cli/src/place_wisp/settings.rs
··· 1 - // @generated by jacquard-lexicon. DO NOT EDIT. 2 - // 3 - // Lexicon: place.wisp.settings 4 - // 5 - // This file was automatically generated from Lexicon schemas. 6 - // Any manual changes will be overwritten on the next regeneration. 7 - 8 - /// Custom HTTP header configuration 9 - #[jacquard_derive::lexicon] 10 - #[derive( 11 - serde::Serialize, 12 - serde::Deserialize, 13 - Debug, 14 - Clone, 15 - PartialEq, 16 - Eq, 17 - jacquard_derive::IntoStatic, 18 - Default 19 - )] 20 - #[serde(rename_all = "camelCase")] 21 - pub struct CustomHeader<'a> { 22 - /// HTTP header name (e.g., 'Cache-Control', 'X-Frame-Options') 23 - #[serde(borrow)] 24 - pub name: jacquard_common::CowStr<'a>, 25 - /// Optional glob pattern to apply this header to specific paths (e.g., '*.html', '/assets/*'). If not specified, applies to all paths. 26 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 27 - #[serde(borrow)] 28 - pub path: std::option::Option<jacquard_common::CowStr<'a>>, 29 - /// HTTP header value 30 - #[serde(borrow)] 31 - pub value: jacquard_common::CowStr<'a>, 32 - } 33 - 34 - fn lexicon_doc_place_wisp_settings() -> ::jacquard_lexicon::lexicon::LexiconDoc< 35 - 'static, 36 - > { 37 - ::jacquard_lexicon::lexicon::LexiconDoc { 38 - lexicon: ::jacquard_lexicon::lexicon::Lexicon::Lexicon1, 39 - id: ::jacquard_common::CowStr::new_static("place.wisp.settings"), 40 - revision: None, 41 - description: None, 42 - defs: { 43 - let mut map = ::std::collections::BTreeMap::new(); 44 - map.insert( 45 - ::jacquard_common::smol_str::SmolStr::new_static("customHeader"), 46 - ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 47 - description: Some( 48 - ::jacquard_common::CowStr::new_static( 49 - "Custom HTTP header configuration", 50 - ), 51 - ), 52 - required: Some( 53 - vec![ 54 - ::jacquard_common::smol_str::SmolStr::new_static("name"), 55 - ::jacquard_common::smol_str::SmolStr::new_static("value") 56 - ], 57 - ), 58 - nullable: None, 59 - properties: { 60 - #[allow(unused_mut)] 61 - let mut map = ::std::collections::BTreeMap::new(); 62 - map.insert( 63 - ::jacquard_common::smol_str::SmolStr::new_static("name"), 64 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 65 - description: Some( 66 - ::jacquard_common::CowStr::new_static( 67 - "HTTP header name (e.g., 'Cache-Control', 'X-Frame-Options')", 68 - ), 69 - ), 70 - format: None, 71 - default: None, 72 - min_length: None, 73 - max_length: Some(100usize), 74 - min_graphemes: None, 75 - max_graphemes: None, 76 - r#enum: None, 77 - r#const: None, 78 - known_values: None, 79 - }), 80 - ); 81 - map.insert( 82 - ::jacquard_common::smol_str::SmolStr::new_static("path"), 83 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 84 - description: Some( 85 - ::jacquard_common::CowStr::new_static( 86 - "Optional glob pattern to apply this header to specific paths (e.g., '*.html', '/assets/*'). If not specified, applies to all paths.", 87 - ), 88 - ), 89 - format: None, 90 - default: None, 91 - min_length: None, 92 - max_length: Some(500usize), 93 - min_graphemes: None, 94 - max_graphemes: None, 95 - r#enum: None, 96 - r#const: None, 97 - known_values: None, 98 - }), 99 - ); 100 - map.insert( 101 - ::jacquard_common::smol_str::SmolStr::new_static("value"), 102 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 103 - description: Some( 104 - ::jacquard_common::CowStr::new_static("HTTP header value"), 105 - ), 106 - format: None, 107 - default: None, 108 - min_length: None, 109 - max_length: Some(1000usize), 110 - min_graphemes: None, 111 - max_graphemes: None, 112 - r#enum: None, 113 - r#const: None, 114 - known_values: None, 115 - }), 116 - ); 117 - map 118 - }, 119 - }), 120 - ); 121 - map.insert( 122 - ::jacquard_common::smol_str::SmolStr::new_static("main"), 123 - ::jacquard_lexicon::lexicon::LexUserType::Record(::jacquard_lexicon::lexicon::LexRecord { 124 - description: Some( 125 - ::jacquard_common::CowStr::new_static( 126 - "Configuration settings for a static site hosted on wisp.place", 127 - ), 128 - ), 129 - key: Some(::jacquard_common::CowStr::new_static("any")), 130 - record: ::jacquard_lexicon::lexicon::LexRecordRecord::Object(::jacquard_lexicon::lexicon::LexObject { 131 - description: None, 132 - required: None, 133 - nullable: None, 134 - properties: { 135 - #[allow(unused_mut)] 136 - let mut map = ::std::collections::BTreeMap::new(); 137 - map.insert( 138 - ::jacquard_common::smol_str::SmolStr::new_static( 139 - "cleanUrls", 140 - ), 141 - ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 142 - description: None, 143 - default: None, 144 - r#const: None, 145 - }), 146 - ); 147 - map.insert( 148 - ::jacquard_common::smol_str::SmolStr::new_static( 149 - "custom404", 150 - ), 151 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 152 - description: Some( 153 - ::jacquard_common::CowStr::new_static( 154 - "Custom 404 error page file path. Incompatible with directoryListing and spaMode.", 155 - ), 156 - ), 157 - format: None, 158 - default: None, 159 - min_length: None, 160 - max_length: Some(500usize), 161 - min_graphemes: None, 162 - max_graphemes: None, 163 - r#enum: None, 164 - r#const: None, 165 - known_values: None, 166 - }), 167 - ); 168 - map.insert( 169 - ::jacquard_common::smol_str::SmolStr::new_static( 170 - "directoryListing", 171 - ), 172 - ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 173 - description: None, 174 - default: None, 175 - r#const: None, 176 - }), 177 - ); 178 - map.insert( 179 - ::jacquard_common::smol_str::SmolStr::new_static("headers"), 180 - ::jacquard_lexicon::lexicon::LexObjectProperty::Array(::jacquard_lexicon::lexicon::LexArray { 181 - description: Some( 182 - ::jacquard_common::CowStr::new_static( 183 - "Custom HTTP headers to set on responses", 184 - ), 185 - ), 186 - items: ::jacquard_lexicon::lexicon::LexArrayItem::Ref(::jacquard_lexicon::lexicon::LexRef { 187 - description: None, 188 - r#ref: ::jacquard_common::CowStr::new_static( 189 - "#customHeader", 190 - ), 191 - }), 192 - min_length: None, 193 - max_length: Some(50usize), 194 - }), 195 - ); 196 - map.insert( 197 - ::jacquard_common::smol_str::SmolStr::new_static( 198 - "indexFiles", 199 - ), 200 - ::jacquard_lexicon::lexicon::LexObjectProperty::Array(::jacquard_lexicon::lexicon::LexArray { 201 - description: Some( 202 - ::jacquard_common::CowStr::new_static( 203 - "Ordered list of files to try when serving a directory. Defaults to ['index.html'] if not specified.", 204 - ), 205 - ), 206 - items: ::jacquard_lexicon::lexicon::LexArrayItem::String(::jacquard_lexicon::lexicon::LexString { 207 - description: None, 208 - format: None, 209 - default: None, 210 - min_length: None, 211 - max_length: Some(255usize), 212 - min_graphemes: None, 213 - max_graphemes: None, 214 - r#enum: None, 215 - r#const: None, 216 - known_values: None, 217 - }), 218 - min_length: None, 219 - max_length: Some(10usize), 220 - }), 221 - ); 222 - map.insert( 223 - ::jacquard_common::smol_str::SmolStr::new_static("spaMode"), 224 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 225 - description: Some( 226 - ::jacquard_common::CowStr::new_static( 227 - "File to serve for all routes (e.g., 'index.html'). When set, enables SPA mode where all non-file requests are routed to this file. Incompatible with directoryListing and custom404.", 228 - ), 229 - ), 230 - format: None, 231 - default: None, 232 - min_length: None, 233 - max_length: Some(500usize), 234 - min_graphemes: None, 235 - max_graphemes: None, 236 - r#enum: None, 237 - r#const: None, 238 - known_values: None, 239 - }), 240 - ); 241 - map 242 - }, 243 - }), 244 - }), 245 - ); 246 - map 247 - }, 248 - } 249 - } 250 - 251 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for CustomHeader<'a> { 252 - fn nsid() -> &'static str { 253 - "place.wisp.settings" 254 - } 255 - fn def_name() -> &'static str { 256 - "customHeader" 257 - } 258 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 259 - lexicon_doc_place_wisp_settings() 260 - } 261 - fn validate( 262 - &self, 263 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 264 - { 265 - let value = &self.name; 266 - #[allow(unused_comparisons)] 267 - if <str>::len(value.as_ref()) > 100usize { 268 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 269 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 270 - "name", 271 - ), 272 - max: 100usize, 273 - actual: <str>::len(value.as_ref()), 274 - }); 275 - } 276 - } 277 - if let Some(ref value) = self.path { 278 - #[allow(unused_comparisons)] 279 - if <str>::len(value.as_ref()) > 500usize { 280 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 281 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 282 - "path", 283 - ), 284 - max: 500usize, 285 - actual: <str>::len(value.as_ref()), 286 - }); 287 - } 288 - } 289 - { 290 - let value = &self.value; 291 - #[allow(unused_comparisons)] 292 - if <str>::len(value.as_ref()) > 1000usize { 293 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 294 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 295 - "value", 296 - ), 297 - max: 1000usize, 298 - actual: <str>::len(value.as_ref()), 299 - }); 300 - } 301 - } 302 - Ok(()) 303 - } 304 - } 305 - 306 - /// Configuration settings for a static site hosted on wisp.place 307 - #[jacquard_derive::lexicon] 308 - #[derive( 309 - serde::Serialize, 310 - serde::Deserialize, 311 - Debug, 312 - Clone, 313 - PartialEq, 314 - Eq, 315 - jacquard_derive::IntoStatic 316 - )] 317 - #[serde(rename_all = "camelCase")] 318 - pub struct Settings<'a> { 319 - /// Enable clean URL routing. When enabled, '/about' will attempt to serve '/about.html' or '/about/index.html' automatically. 320 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 321 - pub clean_urls: std::option::Option<bool>, 322 - /// Custom 404 error page file path. Incompatible with directoryListing and spaMode. 323 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 324 - #[serde(borrow)] 325 - pub custom404: std::option::Option<jacquard_common::CowStr<'a>>, 326 - /// Enable directory listing mode for paths that resolve to directories without an index file. Incompatible with spaMode. 327 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 328 - pub directory_listing: std::option::Option<bool>, 329 - /// Custom HTTP headers to set on responses 330 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 331 - #[serde(borrow)] 332 - pub headers: std::option::Option<Vec<crate::place_wisp::settings::CustomHeader<'a>>>, 333 - /// Ordered list of files to try when serving a directory. Defaults to ['index.html'] if not specified. 334 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 335 - #[serde(borrow)] 336 - pub index_files: std::option::Option<Vec<jacquard_common::CowStr<'a>>>, 337 - /// File to serve for all routes (e.g., 'index.html'). When set, enables SPA mode where all non-file requests are routed to this file. Incompatible with directoryListing and custom404. 338 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 339 - #[serde(borrow)] 340 - pub spa_mode: std::option::Option<jacquard_common::CowStr<'a>>, 341 - } 342 - 343 - pub mod settings_state { 344 - 345 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 346 - #[allow(unused)] 347 - use ::core::marker::PhantomData; 348 - mod sealed { 349 - pub trait Sealed {} 350 - } 351 - /// State trait tracking which required fields have been set 352 - pub trait State: sealed::Sealed {} 353 - /// Empty state - all required fields are unset 354 - pub struct Empty(()); 355 - impl sealed::Sealed for Empty {} 356 - impl State for Empty {} 357 - /// Marker types for field names 358 - #[allow(non_camel_case_types)] 359 - pub mod members {} 360 - } 361 - 362 - /// Builder for constructing an instance of this type 363 - pub struct SettingsBuilder<'a, S: settings_state::State> { 364 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 365 - __unsafe_private_named: ( 366 - ::core::option::Option<bool>, 367 - ::core::option::Option<jacquard_common::CowStr<'a>>, 368 - ::core::option::Option<bool>, 369 - ::core::option::Option<Vec<crate::place_wisp::settings::CustomHeader<'a>>>, 370 - ::core::option::Option<Vec<jacquard_common::CowStr<'a>>>, 371 - ::core::option::Option<jacquard_common::CowStr<'a>>, 372 - ), 373 - _phantom: ::core::marker::PhantomData<&'a ()>, 374 - } 375 - 376 - impl<'a> Settings<'a> { 377 - /// Create a new builder for this type 378 - pub fn new() -> SettingsBuilder<'a, settings_state::Empty> { 379 - SettingsBuilder::new() 380 - } 381 - } 382 - 383 - impl<'a> SettingsBuilder<'a, settings_state::Empty> { 384 - /// Create a new builder with all fields unset 385 - pub fn new() -> Self { 386 - SettingsBuilder { 387 - _phantom_state: ::core::marker::PhantomData, 388 - __unsafe_private_named: (None, None, None, None, None, None), 389 - _phantom: ::core::marker::PhantomData, 390 - } 391 - } 392 - } 393 - 394 - impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 395 - /// Set the `cleanUrls` field (optional) 396 - pub fn clean_urls(mut self, value: impl Into<Option<bool>>) -> Self { 397 - self.__unsafe_private_named.0 = value.into(); 398 - self 399 - } 400 - /// Set the `cleanUrls` field to an Option value (optional) 401 - pub fn maybe_clean_urls(mut self, value: Option<bool>) -> Self { 402 - self.__unsafe_private_named.0 = value; 403 - self 404 - } 405 - } 406 - 407 - impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 408 - /// Set the `custom404` field (optional) 409 - pub fn custom404( 410 - mut self, 411 - value: impl Into<Option<jacquard_common::CowStr<'a>>>, 412 - ) -> Self { 413 - self.__unsafe_private_named.1 = value.into(); 414 - self 415 - } 416 - /// Set the `custom404` field to an Option value (optional) 417 - pub fn maybe_custom404( 418 - mut self, 419 - value: Option<jacquard_common::CowStr<'a>>, 420 - ) -> Self { 421 - self.__unsafe_private_named.1 = value; 422 - self 423 - } 424 - } 425 - 426 - impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 427 - /// Set the `directoryListing` field (optional) 428 - pub fn directory_listing(mut self, value: impl Into<Option<bool>>) -> Self { 429 - self.__unsafe_private_named.2 = value.into(); 430 - self 431 - } 432 - /// Set the `directoryListing` field to an Option value (optional) 433 - pub fn maybe_directory_listing(mut self, value: Option<bool>) -> Self { 434 - self.__unsafe_private_named.2 = value; 435 - self 436 - } 437 - } 438 - 439 - impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 440 - /// Set the `headers` field (optional) 441 - pub fn headers( 442 - mut self, 443 - value: impl Into<Option<Vec<crate::place_wisp::settings::CustomHeader<'a>>>>, 444 - ) -> Self { 445 - self.__unsafe_private_named.3 = value.into(); 446 - self 447 - } 448 - /// Set the `headers` field to an Option value (optional) 449 - pub fn maybe_headers( 450 - mut self, 451 - value: Option<Vec<crate::place_wisp::settings::CustomHeader<'a>>>, 452 - ) -> Self { 453 - self.__unsafe_private_named.3 = value; 454 - self 455 - } 456 - } 457 - 458 - impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 459 - /// Set the `indexFiles` field (optional) 460 - pub fn index_files( 461 - mut self, 462 - value: impl Into<Option<Vec<jacquard_common::CowStr<'a>>>>, 463 - ) -> Self { 464 - self.__unsafe_private_named.4 = value.into(); 465 - self 466 - } 467 - /// Set the `indexFiles` field to an Option value (optional) 468 - pub fn maybe_index_files( 469 - mut self, 470 - value: Option<Vec<jacquard_common::CowStr<'a>>>, 471 - ) -> Self { 472 - self.__unsafe_private_named.4 = value; 473 - self 474 - } 475 - } 476 - 477 - impl<'a, S: settings_state::State> SettingsBuilder<'a, S> { 478 - /// Set the `spaMode` field (optional) 479 - pub fn spa_mode( 480 - mut self, 481 - value: impl Into<Option<jacquard_common::CowStr<'a>>>, 482 - ) -> Self { 483 - self.__unsafe_private_named.5 = value.into(); 484 - self 485 - } 486 - /// Set the `spaMode` field to an Option value (optional) 487 - pub fn maybe_spa_mode(mut self, value: Option<jacquard_common::CowStr<'a>>) -> Self { 488 - self.__unsafe_private_named.5 = value; 489 - self 490 - } 491 - } 492 - 493 - impl<'a, S> SettingsBuilder<'a, S> 494 - where 495 - S: settings_state::State, 496 - { 497 - /// Build the final struct 498 - pub fn build(self) -> Settings<'a> { 499 - Settings { 500 - clean_urls: self.__unsafe_private_named.0, 501 - custom404: self.__unsafe_private_named.1, 502 - directory_listing: self.__unsafe_private_named.2, 503 - headers: self.__unsafe_private_named.3, 504 - index_files: self.__unsafe_private_named.4, 505 - spa_mode: self.__unsafe_private_named.5, 506 - extra_data: Default::default(), 507 - } 508 - } 509 - /// Build the final struct with custom extra_data 510 - pub fn build_with_data( 511 - self, 512 - extra_data: std::collections::BTreeMap< 513 - jacquard_common::smol_str::SmolStr, 514 - jacquard_common::types::value::Data<'a>, 515 - >, 516 - ) -> Settings<'a> { 517 - Settings { 518 - clean_urls: self.__unsafe_private_named.0, 519 - custom404: self.__unsafe_private_named.1, 520 - directory_listing: self.__unsafe_private_named.2, 521 - headers: self.__unsafe_private_named.3, 522 - index_files: self.__unsafe_private_named.4, 523 - spa_mode: self.__unsafe_private_named.5, 524 - extra_data: Some(extra_data), 525 - } 526 - } 527 - } 528 - 529 - impl<'a> Settings<'a> { 530 - pub fn uri( 531 - uri: impl Into<jacquard_common::CowStr<'a>>, 532 - ) -> Result< 533 - jacquard_common::types::uri::RecordUri<'a, SettingsRecord>, 534 - jacquard_common::types::uri::UriError, 535 - > { 536 - jacquard_common::types::uri::RecordUri::try_from_uri( 537 - jacquard_common::types::string::AtUri::new_cow(uri.into())?, 538 - ) 539 - } 540 - } 541 - 542 - /// Typed wrapper for GetRecord response with this collection's record type. 543 - #[derive( 544 - serde::Serialize, 545 - serde::Deserialize, 546 - Debug, 547 - Clone, 548 - PartialEq, 549 - Eq, 550 - jacquard_derive::IntoStatic 551 - )] 552 - #[serde(rename_all = "camelCase")] 553 - pub struct SettingsGetRecordOutput<'a> { 554 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 555 - #[serde(borrow)] 556 - pub cid: std::option::Option<jacquard_common::types::string::Cid<'a>>, 557 - #[serde(borrow)] 558 - pub uri: jacquard_common::types::string::AtUri<'a>, 559 - #[serde(borrow)] 560 - pub value: Settings<'a>, 561 - } 562 - 563 - impl From<SettingsGetRecordOutput<'_>> for Settings<'_> { 564 - fn from(output: SettingsGetRecordOutput<'_>) -> Self { 565 - use jacquard_common::IntoStatic; 566 - output.value.into_static() 567 - } 568 - } 569 - 570 - impl jacquard_common::types::collection::Collection for Settings<'_> { 571 - const NSID: &'static str = "place.wisp.settings"; 572 - type Record = SettingsRecord; 573 - } 574 - 575 - /// Marker type for deserializing records from this collection. 576 - #[derive(Debug, serde::Serialize, serde::Deserialize)] 577 - pub struct SettingsRecord; 578 - impl jacquard_common::xrpc::XrpcResp for SettingsRecord { 579 - const NSID: &'static str = "place.wisp.settings"; 580 - const ENCODING: &'static str = "application/json"; 581 - type Output<'de> = SettingsGetRecordOutput<'de>; 582 - type Err<'de> = jacquard_common::types::collection::RecordError<'de>; 583 - } 584 - 585 - impl jacquard_common::types::collection::Collection for SettingsRecord { 586 - const NSID: &'static str = "place.wisp.settings"; 587 - type Record = SettingsRecord; 588 - } 589 - 590 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Settings<'a> { 591 - fn nsid() -> &'static str { 592 - "place.wisp.settings" 593 - } 594 - fn def_name() -> &'static str { 595 - "main" 596 - } 597 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 598 - lexicon_doc_place_wisp_settings() 599 - } 600 - fn validate( 601 - &self, 602 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 603 - if let Some(ref value) = self.custom404 { 604 - #[allow(unused_comparisons)] 605 - if <str>::len(value.as_ref()) > 500usize { 606 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 607 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 608 - "custom404", 609 - ), 610 - max: 500usize, 611 - actual: <str>::len(value.as_ref()), 612 - }); 613 - } 614 - } 615 - if let Some(ref value) = self.headers { 616 - #[allow(unused_comparisons)] 617 - if value.len() > 50usize { 618 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 619 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 620 - "headers", 621 - ), 622 - max: 50usize, 623 - actual: value.len(), 624 - }); 625 - } 626 - } 627 - if let Some(ref value) = self.index_files { 628 - #[allow(unused_comparisons)] 629 - if value.len() > 10usize { 630 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 631 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 632 - "index_files", 633 - ), 634 - max: 10usize, 635 - actual: value.len(), 636 - }); 637 - } 638 - } 639 - if let Some(ref value) = self.spa_mode { 640 - #[allow(unused_comparisons)] 641 - if <str>::len(value.as_ref()) > 500usize { 642 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 643 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 644 - "spa_mode", 645 - ), 646 - max: 500usize, 647 - actual: <str>::len(value.as_ref()), 648 - }); 649 - } 650 - } 651 - Ok(()) 652 - } 653 - }
-1408
cli/src/place_wisp/subfs.rs
··· 1 - // @generated by jacquard-lexicon. DO NOT EDIT. 2 - // 3 - // Lexicon: place.wisp.subfs 4 - // 5 - // This file was automatically generated from Lexicon schemas. 6 - // Any manual changes will be overwritten on the next regeneration. 7 - 8 - #[jacquard_derive::lexicon] 9 - #[derive( 10 - serde::Serialize, 11 - serde::Deserialize, 12 - Debug, 13 - Clone, 14 - PartialEq, 15 - Eq, 16 - jacquard_derive::IntoStatic 17 - )] 18 - #[serde(rename_all = "camelCase")] 19 - pub struct Directory<'a> { 20 - #[serde(borrow)] 21 - pub entries: Vec<crate::place_wisp::subfs::Entry<'a>>, 22 - #[serde(borrow)] 23 - pub r#type: jacquard_common::CowStr<'a>, 24 - } 25 - 26 - pub mod directory_state { 27 - 28 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 29 - #[allow(unused)] 30 - use ::core::marker::PhantomData; 31 - mod sealed { 32 - pub trait Sealed {} 33 - } 34 - /// State trait tracking which required fields have been set 35 - pub trait State: sealed::Sealed { 36 - type Type; 37 - type Entries; 38 - } 39 - /// Empty state - all required fields are unset 40 - pub struct Empty(()); 41 - impl sealed::Sealed for Empty {} 42 - impl State for Empty { 43 - type Type = Unset; 44 - type Entries = Unset; 45 - } 46 - ///State transition - sets the `type` field to Set 47 - pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 48 - impl<S: State> sealed::Sealed for SetType<S> {} 49 - impl<S: State> State for SetType<S> { 50 - type Type = Set<members::r#type>; 51 - type Entries = S::Entries; 52 - } 53 - ///State transition - sets the `entries` field to Set 54 - pub struct SetEntries<S: State = Empty>(PhantomData<fn() -> S>); 55 - impl<S: State> sealed::Sealed for SetEntries<S> {} 56 - impl<S: State> State for SetEntries<S> { 57 - type Type = S::Type; 58 - type Entries = Set<members::entries>; 59 - } 60 - /// Marker types for field names 61 - #[allow(non_camel_case_types)] 62 - pub mod members { 63 - ///Marker type for the `type` field 64 - pub struct r#type(()); 65 - ///Marker type for the `entries` field 66 - pub struct entries(()); 67 - } 68 - } 69 - 70 - /// Builder for constructing an instance of this type 71 - pub struct DirectoryBuilder<'a, S: directory_state::State> { 72 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 73 - __unsafe_private_named: ( 74 - ::core::option::Option<Vec<crate::place_wisp::subfs::Entry<'a>>>, 75 - ::core::option::Option<jacquard_common::CowStr<'a>>, 76 - ), 77 - _phantom: ::core::marker::PhantomData<&'a ()>, 78 - } 79 - 80 - impl<'a> Directory<'a> { 81 - /// Create a new builder for this type 82 - pub fn new() -> DirectoryBuilder<'a, directory_state::Empty> { 83 - DirectoryBuilder::new() 84 - } 85 - } 86 - 87 - impl<'a> DirectoryBuilder<'a, directory_state::Empty> { 88 - /// Create a new builder with all fields unset 89 - pub fn new() -> Self { 90 - DirectoryBuilder { 91 - _phantom_state: ::core::marker::PhantomData, 92 - __unsafe_private_named: (None, None), 93 - _phantom: ::core::marker::PhantomData, 94 - } 95 - } 96 - } 97 - 98 - impl<'a, S> DirectoryBuilder<'a, S> 99 - where 100 - S: directory_state::State, 101 - S::Entries: directory_state::IsUnset, 102 - { 103 - /// Set the `entries` field (required) 104 - pub fn entries( 105 - mut self, 106 - value: impl Into<Vec<crate::place_wisp::subfs::Entry<'a>>>, 107 - ) -> DirectoryBuilder<'a, directory_state::SetEntries<S>> { 108 - self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 109 - DirectoryBuilder { 110 - _phantom_state: ::core::marker::PhantomData, 111 - __unsafe_private_named: self.__unsafe_private_named, 112 - _phantom: ::core::marker::PhantomData, 113 - } 114 - } 115 - } 116 - 117 - impl<'a, S> DirectoryBuilder<'a, S> 118 - where 119 - S: directory_state::State, 120 - S::Type: directory_state::IsUnset, 121 - { 122 - /// Set the `type` field (required) 123 - pub fn r#type( 124 - mut self, 125 - value: impl Into<jacquard_common::CowStr<'a>>, 126 - ) -> DirectoryBuilder<'a, directory_state::SetType<S>> { 127 - self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 128 - DirectoryBuilder { 129 - _phantom_state: ::core::marker::PhantomData, 130 - __unsafe_private_named: self.__unsafe_private_named, 131 - _phantom: ::core::marker::PhantomData, 132 - } 133 - } 134 - } 135 - 136 - impl<'a, S> DirectoryBuilder<'a, S> 137 - where 138 - S: directory_state::State, 139 - S::Type: directory_state::IsSet, 140 - S::Entries: directory_state::IsSet, 141 - { 142 - /// Build the final struct 143 - pub fn build(self) -> Directory<'a> { 144 - Directory { 145 - entries: self.__unsafe_private_named.0.unwrap(), 146 - r#type: self.__unsafe_private_named.1.unwrap(), 147 - extra_data: Default::default(), 148 - } 149 - } 150 - /// Build the final struct with custom extra_data 151 - pub fn build_with_data( 152 - self, 153 - extra_data: std::collections::BTreeMap< 154 - jacquard_common::smol_str::SmolStr, 155 - jacquard_common::types::value::Data<'a>, 156 - >, 157 - ) -> Directory<'a> { 158 - Directory { 159 - entries: self.__unsafe_private_named.0.unwrap(), 160 - r#type: self.__unsafe_private_named.1.unwrap(), 161 - extra_data: Some(extra_data), 162 - } 163 - } 164 - } 165 - 166 - fn lexicon_doc_place_wisp_subfs() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 167 - ::jacquard_lexicon::lexicon::LexiconDoc { 168 - lexicon: ::jacquard_lexicon::lexicon::Lexicon::Lexicon1, 169 - id: ::jacquard_common::CowStr::new_static("place.wisp.subfs"), 170 - revision: None, 171 - description: None, 172 - defs: { 173 - let mut map = ::std::collections::BTreeMap::new(); 174 - map.insert( 175 - ::jacquard_common::smol_str::SmolStr::new_static("directory"), 176 - ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 177 - description: None, 178 - required: Some( 179 - vec![ 180 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 181 - ::jacquard_common::smol_str::SmolStr::new_static("entries") 182 - ], 183 - ), 184 - nullable: None, 185 - properties: { 186 - #[allow(unused_mut)] 187 - let mut map = ::std::collections::BTreeMap::new(); 188 - map.insert( 189 - ::jacquard_common::smol_str::SmolStr::new_static("entries"), 190 - ::jacquard_lexicon::lexicon::LexObjectProperty::Array(::jacquard_lexicon::lexicon::LexArray { 191 - description: None, 192 - items: ::jacquard_lexicon::lexicon::LexArrayItem::Ref(::jacquard_lexicon::lexicon::LexRef { 193 - description: None, 194 - r#ref: ::jacquard_common::CowStr::new_static("#entry"), 195 - }), 196 - min_length: None, 197 - max_length: Some(500usize), 198 - }), 199 - ); 200 - map.insert( 201 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 202 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 203 - description: None, 204 - format: None, 205 - default: None, 206 - min_length: None, 207 - max_length: None, 208 - min_graphemes: None, 209 - max_graphemes: None, 210 - r#enum: None, 211 - r#const: None, 212 - known_values: None, 213 - }), 214 - ); 215 - map 216 - }, 217 - }), 218 - ); 219 - map.insert( 220 - ::jacquard_common::smol_str::SmolStr::new_static("entry"), 221 - ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 222 - description: None, 223 - required: Some( 224 - vec![ 225 - ::jacquard_common::smol_str::SmolStr::new_static("name"), 226 - ::jacquard_common::smol_str::SmolStr::new_static("node") 227 - ], 228 - ), 229 - nullable: None, 230 - properties: { 231 - #[allow(unused_mut)] 232 - let mut map = ::std::collections::BTreeMap::new(); 233 - map.insert( 234 - ::jacquard_common::smol_str::SmolStr::new_static("name"), 235 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 236 - description: None, 237 - format: None, 238 - default: None, 239 - min_length: None, 240 - max_length: Some(255usize), 241 - min_graphemes: None, 242 - max_graphemes: None, 243 - r#enum: None, 244 - r#const: None, 245 - known_values: None, 246 - }), 247 - ); 248 - map.insert( 249 - ::jacquard_common::smol_str::SmolStr::new_static("node"), 250 - ::jacquard_lexicon::lexicon::LexObjectProperty::Union(::jacquard_lexicon::lexicon::LexRefUnion { 251 - description: None, 252 - refs: vec![ 253 - ::jacquard_common::CowStr::new_static("#file"), 254 - ::jacquard_common::CowStr::new_static("#directory"), 255 - ::jacquard_common::CowStr::new_static("#subfs") 256 - ], 257 - closed: None, 258 - }), 259 - ); 260 - map 261 - }, 262 - }), 263 - ); 264 - map.insert( 265 - ::jacquard_common::smol_str::SmolStr::new_static("file"), 266 - ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 267 - description: None, 268 - required: Some( 269 - vec![ 270 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 271 - ::jacquard_common::smol_str::SmolStr::new_static("blob") 272 - ], 273 - ), 274 - nullable: None, 275 - properties: { 276 - #[allow(unused_mut)] 277 - let mut map = ::std::collections::BTreeMap::new(); 278 - map.insert( 279 - ::jacquard_common::smol_str::SmolStr::new_static("base64"), 280 - ::jacquard_lexicon::lexicon::LexObjectProperty::Boolean(::jacquard_lexicon::lexicon::LexBoolean { 281 - description: None, 282 - default: None, 283 - r#const: None, 284 - }), 285 - ); 286 - map.insert( 287 - ::jacquard_common::smol_str::SmolStr::new_static("blob"), 288 - ::jacquard_lexicon::lexicon::LexObjectProperty::Blob(::jacquard_lexicon::lexicon::LexBlob { 289 - description: None, 290 - accept: None, 291 - max_size: None, 292 - }), 293 - ); 294 - map.insert( 295 - ::jacquard_common::smol_str::SmolStr::new_static("encoding"), 296 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 297 - description: Some( 298 - ::jacquard_common::CowStr::new_static( 299 - "Content encoding (e.g., gzip for compressed files)", 300 - ), 301 - ), 302 - format: None, 303 - default: None, 304 - min_length: None, 305 - max_length: None, 306 - min_graphemes: None, 307 - max_graphemes: None, 308 - r#enum: None, 309 - r#const: None, 310 - known_values: None, 311 - }), 312 - ); 313 - map.insert( 314 - ::jacquard_common::smol_str::SmolStr::new_static("mimeType"), 315 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 316 - description: Some( 317 - ::jacquard_common::CowStr::new_static( 318 - "Original MIME type before compression", 319 - ), 320 - ), 321 - format: None, 322 - default: None, 323 - min_length: None, 324 - max_length: None, 325 - min_graphemes: None, 326 - max_graphemes: None, 327 - r#enum: None, 328 - r#const: None, 329 - known_values: None, 330 - }), 331 - ); 332 - map.insert( 333 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 334 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 335 - description: None, 336 - format: None, 337 - default: None, 338 - min_length: None, 339 - max_length: None, 340 - min_graphemes: None, 341 - max_graphemes: None, 342 - r#enum: None, 343 - r#const: None, 344 - known_values: None, 345 - }), 346 - ); 347 - map 348 - }, 349 - }), 350 - ); 351 - map.insert( 352 - ::jacquard_common::smol_str::SmolStr::new_static("main"), 353 - ::jacquard_lexicon::lexicon::LexUserType::Record(::jacquard_lexicon::lexicon::LexRecord { 354 - description: Some( 355 - ::jacquard_common::CowStr::new_static( 356 - "Virtual filesystem subtree referenced by place.wisp.fs records. When a subfs entry is expanded, its root entries are merged (flattened) into the parent directory, allowing large directories to be split across multiple records while maintaining a flat structure.", 357 - ), 358 - ), 359 - key: None, 360 - record: ::jacquard_lexicon::lexicon::LexRecordRecord::Object(::jacquard_lexicon::lexicon::LexObject { 361 - description: None, 362 - required: Some( 363 - vec![ 364 - ::jacquard_common::smol_str::SmolStr::new_static("root"), 365 - ::jacquard_common::smol_str::SmolStr::new_static("createdAt") 366 - ], 367 - ), 368 - nullable: None, 369 - properties: { 370 - #[allow(unused_mut)] 371 - let mut map = ::std::collections::BTreeMap::new(); 372 - map.insert( 373 - ::jacquard_common::smol_str::SmolStr::new_static( 374 - "createdAt", 375 - ), 376 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 377 - description: None, 378 - format: Some( 379 - ::jacquard_lexicon::lexicon::LexStringFormat::Datetime, 380 - ), 381 - default: None, 382 - min_length: None, 383 - max_length: None, 384 - min_graphemes: None, 385 - max_graphemes: None, 386 - r#enum: None, 387 - r#const: None, 388 - known_values: None, 389 - }), 390 - ); 391 - map.insert( 392 - ::jacquard_common::smol_str::SmolStr::new_static( 393 - "fileCount", 394 - ), 395 - ::jacquard_lexicon::lexicon::LexObjectProperty::Integer(::jacquard_lexicon::lexicon::LexInteger { 396 - description: None, 397 - default: None, 398 - minimum: Some(0i64), 399 - maximum: Some(1000i64), 400 - r#enum: None, 401 - r#const: None, 402 - }), 403 - ); 404 - map.insert( 405 - ::jacquard_common::smol_str::SmolStr::new_static("root"), 406 - ::jacquard_lexicon::lexicon::LexObjectProperty::Ref(::jacquard_lexicon::lexicon::LexRef { 407 - description: None, 408 - r#ref: ::jacquard_common::CowStr::new_static("#directory"), 409 - }), 410 - ); 411 - map 412 - }, 413 - }), 414 - }), 415 - ); 416 - map.insert( 417 - ::jacquard_common::smol_str::SmolStr::new_static("subfs"), 418 - ::jacquard_lexicon::lexicon::LexUserType::Object(::jacquard_lexicon::lexicon::LexObject { 419 - description: None, 420 - required: Some( 421 - vec![ 422 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 423 - ::jacquard_common::smol_str::SmolStr::new_static("subject") 424 - ], 425 - ), 426 - nullable: None, 427 - properties: { 428 - #[allow(unused_mut)] 429 - let mut map = ::std::collections::BTreeMap::new(); 430 - map.insert( 431 - ::jacquard_common::smol_str::SmolStr::new_static("subject"), 432 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 433 - description: Some( 434 - ::jacquard_common::CowStr::new_static( 435 - "AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures.", 436 - ), 437 - ), 438 - format: Some( 439 - ::jacquard_lexicon::lexicon::LexStringFormat::AtUri, 440 - ), 441 - default: None, 442 - min_length: None, 443 - max_length: None, 444 - min_graphemes: None, 445 - max_graphemes: None, 446 - r#enum: None, 447 - r#const: None, 448 - known_values: None, 449 - }), 450 - ); 451 - map.insert( 452 - ::jacquard_common::smol_str::SmolStr::new_static("type"), 453 - ::jacquard_lexicon::lexicon::LexObjectProperty::String(::jacquard_lexicon::lexicon::LexString { 454 - description: None, 455 - format: None, 456 - default: None, 457 - min_length: None, 458 - max_length: None, 459 - min_graphemes: None, 460 - max_graphemes: None, 461 - r#enum: None, 462 - r#const: None, 463 - known_values: None, 464 - }), 465 - ); 466 - map 467 - }, 468 - }), 469 - ); 470 - map 471 - }, 472 - } 473 - } 474 - 475 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Directory<'a> { 476 - fn nsid() -> &'static str { 477 - "place.wisp.subfs" 478 - } 479 - fn def_name() -> &'static str { 480 - "directory" 481 - } 482 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 483 - lexicon_doc_place_wisp_subfs() 484 - } 485 - fn validate( 486 - &self, 487 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 488 - { 489 - let value = &self.entries; 490 - #[allow(unused_comparisons)] 491 - if value.len() > 500usize { 492 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 493 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 494 - "entries", 495 - ), 496 - max: 500usize, 497 - actual: value.len(), 498 - }); 499 - } 500 - } 501 - Ok(()) 502 - } 503 - } 504 - 505 - #[jacquard_derive::lexicon] 506 - #[derive( 507 - serde::Serialize, 508 - serde::Deserialize, 509 - Debug, 510 - Clone, 511 - PartialEq, 512 - Eq, 513 - jacquard_derive::IntoStatic 514 - )] 515 - #[serde(rename_all = "camelCase")] 516 - pub struct Entry<'a> { 517 - #[serde(borrow)] 518 - pub name: jacquard_common::CowStr<'a>, 519 - #[serde(borrow)] 520 - pub node: EntryNode<'a>, 521 - } 522 - 523 - pub mod entry_state { 524 - 525 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 526 - #[allow(unused)] 527 - use ::core::marker::PhantomData; 528 - mod sealed { 529 - pub trait Sealed {} 530 - } 531 - /// State trait tracking which required fields have been set 532 - pub trait State: sealed::Sealed { 533 - type Name; 534 - type Node; 535 - } 536 - /// Empty state - all required fields are unset 537 - pub struct Empty(()); 538 - impl sealed::Sealed for Empty {} 539 - impl State for Empty { 540 - type Name = Unset; 541 - type Node = Unset; 542 - } 543 - ///State transition - sets the `name` field to Set 544 - pub struct SetName<S: State = Empty>(PhantomData<fn() -> S>); 545 - impl<S: State> sealed::Sealed for SetName<S> {} 546 - impl<S: State> State for SetName<S> { 547 - type Name = Set<members::name>; 548 - type Node = S::Node; 549 - } 550 - ///State transition - sets the `node` field to Set 551 - pub struct SetNode<S: State = Empty>(PhantomData<fn() -> S>); 552 - impl<S: State> sealed::Sealed for SetNode<S> {} 553 - impl<S: State> State for SetNode<S> { 554 - type Name = S::Name; 555 - type Node = Set<members::node>; 556 - } 557 - /// Marker types for field names 558 - #[allow(non_camel_case_types)] 559 - pub mod members { 560 - ///Marker type for the `name` field 561 - pub struct name(()); 562 - ///Marker type for the `node` field 563 - pub struct node(()); 564 - } 565 - } 566 - 567 - /// Builder for constructing an instance of this type 568 - pub struct EntryBuilder<'a, S: entry_state::State> { 569 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 570 - __unsafe_private_named: ( 571 - ::core::option::Option<jacquard_common::CowStr<'a>>, 572 - ::core::option::Option<EntryNode<'a>>, 573 - ), 574 - _phantom: ::core::marker::PhantomData<&'a ()>, 575 - } 576 - 577 - impl<'a> Entry<'a> { 578 - /// Create a new builder for this type 579 - pub fn new() -> EntryBuilder<'a, entry_state::Empty> { 580 - EntryBuilder::new() 581 - } 582 - } 583 - 584 - impl<'a> EntryBuilder<'a, entry_state::Empty> { 585 - /// Create a new builder with all fields unset 586 - pub fn new() -> Self { 587 - EntryBuilder { 588 - _phantom_state: ::core::marker::PhantomData, 589 - __unsafe_private_named: (None, None), 590 - _phantom: ::core::marker::PhantomData, 591 - } 592 - } 593 - } 594 - 595 - impl<'a, S> EntryBuilder<'a, S> 596 - where 597 - S: entry_state::State, 598 - S::Name: entry_state::IsUnset, 599 - { 600 - /// Set the `name` field (required) 601 - pub fn name( 602 - mut self, 603 - value: impl Into<jacquard_common::CowStr<'a>>, 604 - ) -> EntryBuilder<'a, entry_state::SetName<S>> { 605 - self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 606 - EntryBuilder { 607 - _phantom_state: ::core::marker::PhantomData, 608 - __unsafe_private_named: self.__unsafe_private_named, 609 - _phantom: ::core::marker::PhantomData, 610 - } 611 - } 612 - } 613 - 614 - impl<'a, S> EntryBuilder<'a, S> 615 - where 616 - S: entry_state::State, 617 - S::Node: entry_state::IsUnset, 618 - { 619 - /// Set the `node` field (required) 620 - pub fn node( 621 - mut self, 622 - value: impl Into<EntryNode<'a>>, 623 - ) -> EntryBuilder<'a, entry_state::SetNode<S>> { 624 - self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 625 - EntryBuilder { 626 - _phantom_state: ::core::marker::PhantomData, 627 - __unsafe_private_named: self.__unsafe_private_named, 628 - _phantom: ::core::marker::PhantomData, 629 - } 630 - } 631 - } 632 - 633 - impl<'a, S> EntryBuilder<'a, S> 634 - where 635 - S: entry_state::State, 636 - S::Name: entry_state::IsSet, 637 - S::Node: entry_state::IsSet, 638 - { 639 - /// Build the final struct 640 - pub fn build(self) -> Entry<'a> { 641 - Entry { 642 - name: self.__unsafe_private_named.0.unwrap(), 643 - node: self.__unsafe_private_named.1.unwrap(), 644 - extra_data: Default::default(), 645 - } 646 - } 647 - /// Build the final struct with custom extra_data 648 - pub fn build_with_data( 649 - self, 650 - extra_data: std::collections::BTreeMap< 651 - jacquard_common::smol_str::SmolStr, 652 - jacquard_common::types::value::Data<'a>, 653 - >, 654 - ) -> Entry<'a> { 655 - Entry { 656 - name: self.__unsafe_private_named.0.unwrap(), 657 - node: self.__unsafe_private_named.1.unwrap(), 658 - extra_data: Some(extra_data), 659 - } 660 - } 661 - } 662 - 663 - #[jacquard_derive::open_union] 664 - #[derive( 665 - serde::Serialize, 666 - serde::Deserialize, 667 - Debug, 668 - Clone, 669 - PartialEq, 670 - Eq, 671 - jacquard_derive::IntoStatic 672 - )] 673 - #[serde(tag = "$type")] 674 - #[serde(bound(deserialize = "'de: 'a"))] 675 - pub enum EntryNode<'a> { 676 - #[serde(rename = "place.wisp.subfs#file")] 677 - File(Box<crate::place_wisp::subfs::File<'a>>), 678 - #[serde(rename = "place.wisp.subfs#directory")] 679 - Directory(Box<crate::place_wisp::subfs::Directory<'a>>), 680 - #[serde(rename = "place.wisp.subfs#subfs")] 681 - Subfs(Box<crate::place_wisp::subfs::Subfs<'a>>), 682 - } 683 - 684 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Entry<'a> { 685 - fn nsid() -> &'static str { 686 - "place.wisp.subfs" 687 - } 688 - fn def_name() -> &'static str { 689 - "entry" 690 - } 691 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 692 - lexicon_doc_place_wisp_subfs() 693 - } 694 - fn validate( 695 - &self, 696 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 697 - { 698 - let value = &self.name; 699 - #[allow(unused_comparisons)] 700 - if <str>::len(value.as_ref()) > 255usize { 701 - return Err(::jacquard_lexicon::validation::ConstraintError::MaxLength { 702 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 703 - "name", 704 - ), 705 - max: 255usize, 706 - actual: <str>::len(value.as_ref()), 707 - }); 708 - } 709 - } 710 - Ok(()) 711 - } 712 - } 713 - 714 - #[jacquard_derive::lexicon] 715 - #[derive( 716 - serde::Serialize, 717 - serde::Deserialize, 718 - Debug, 719 - Clone, 720 - PartialEq, 721 - Eq, 722 - jacquard_derive::IntoStatic 723 - )] 724 - #[serde(rename_all = "camelCase")] 725 - pub struct File<'a> { 726 - /// True if blob content is base64-encoded (used to bypass PDS content sniffing) 727 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 728 - pub base64: Option<bool>, 729 - /// Content blob ref 730 - #[serde(borrow)] 731 - pub blob: jacquard_common::types::blob::BlobRef<'a>, 732 - /// Content encoding (e.g., gzip for compressed files) 733 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 734 - #[serde(borrow)] 735 - pub encoding: Option<jacquard_common::CowStr<'a>>, 736 - /// Original MIME type before compression 737 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 738 - #[serde(borrow)] 739 - pub mime_type: Option<jacquard_common::CowStr<'a>>, 740 - #[serde(borrow)] 741 - pub r#type: jacquard_common::CowStr<'a>, 742 - } 743 - 744 - pub mod file_state { 745 - 746 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 747 - #[allow(unused)] 748 - use ::core::marker::PhantomData; 749 - mod sealed { 750 - pub trait Sealed {} 751 - } 752 - /// State trait tracking which required fields have been set 753 - pub trait State: sealed::Sealed { 754 - type Type; 755 - type Blob; 756 - } 757 - /// Empty state - all required fields are unset 758 - pub struct Empty(()); 759 - impl sealed::Sealed for Empty {} 760 - impl State for Empty { 761 - type Type = Unset; 762 - type Blob = Unset; 763 - } 764 - ///State transition - sets the `type` field to Set 765 - pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 766 - impl<S: State> sealed::Sealed for SetType<S> {} 767 - impl<S: State> State for SetType<S> { 768 - type Type = Set<members::r#type>; 769 - type Blob = S::Blob; 770 - } 771 - ///State transition - sets the `blob` field to Set 772 - pub struct SetBlob<S: State = Empty>(PhantomData<fn() -> S>); 773 - impl<S: State> sealed::Sealed for SetBlob<S> {} 774 - impl<S: State> State for SetBlob<S> { 775 - type Type = S::Type; 776 - type Blob = Set<members::blob>; 777 - } 778 - /// Marker types for field names 779 - #[allow(non_camel_case_types)] 780 - pub mod members { 781 - ///Marker type for the `type` field 782 - pub struct r#type(()); 783 - ///Marker type for the `blob` field 784 - pub struct blob(()); 785 - } 786 - } 787 - 788 - /// Builder for constructing an instance of this type 789 - pub struct FileBuilder<'a, S: file_state::State> { 790 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 791 - __unsafe_private_named: ( 792 - ::core::option::Option<bool>, 793 - ::core::option::Option<jacquard_common::types::blob::BlobRef<'a>>, 794 - ::core::option::Option<jacquard_common::CowStr<'a>>, 795 - ::core::option::Option<jacquard_common::CowStr<'a>>, 796 - ::core::option::Option<jacquard_common::CowStr<'a>>, 797 - ), 798 - _phantom: ::core::marker::PhantomData<&'a ()>, 799 - } 800 - 801 - impl<'a> File<'a> { 802 - /// Create a new builder for this type 803 - pub fn new() -> FileBuilder<'a, file_state::Empty> { 804 - FileBuilder::new() 805 - } 806 - } 807 - 808 - impl<'a> FileBuilder<'a, file_state::Empty> { 809 - /// Create a new builder with all fields unset 810 - pub fn new() -> Self { 811 - FileBuilder { 812 - _phantom_state: ::core::marker::PhantomData, 813 - __unsafe_private_named: (None, None, None, None, None), 814 - _phantom: ::core::marker::PhantomData, 815 - } 816 - } 817 - } 818 - 819 - impl<'a, S: file_state::State> FileBuilder<'a, S> { 820 - /// Set the `base64` field (optional) 821 - pub fn base64(mut self, value: impl Into<Option<bool>>) -> Self { 822 - self.__unsafe_private_named.0 = value.into(); 823 - self 824 - } 825 - /// Set the `base64` field to an Option value (optional) 826 - pub fn maybe_base64(mut self, value: Option<bool>) -> Self { 827 - self.__unsafe_private_named.0 = value; 828 - self 829 - } 830 - } 831 - 832 - impl<'a, S> FileBuilder<'a, S> 833 - where 834 - S: file_state::State, 835 - S::Blob: file_state::IsUnset, 836 - { 837 - /// Set the `blob` field (required) 838 - pub fn blob( 839 - mut self, 840 - value: impl Into<jacquard_common::types::blob::BlobRef<'a>>, 841 - ) -> FileBuilder<'a, file_state::SetBlob<S>> { 842 - self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 843 - FileBuilder { 844 - _phantom_state: ::core::marker::PhantomData, 845 - __unsafe_private_named: self.__unsafe_private_named, 846 - _phantom: ::core::marker::PhantomData, 847 - } 848 - } 849 - } 850 - 851 - impl<'a, S: file_state::State> FileBuilder<'a, S> { 852 - /// Set the `encoding` field (optional) 853 - pub fn encoding( 854 - mut self, 855 - value: impl Into<Option<jacquard_common::CowStr<'a>>>, 856 - ) -> Self { 857 - self.__unsafe_private_named.2 = value.into(); 858 - self 859 - } 860 - /// Set the `encoding` field to an Option value (optional) 861 - pub fn maybe_encoding(mut self, value: Option<jacquard_common::CowStr<'a>>) -> Self { 862 - self.__unsafe_private_named.2 = value; 863 - self 864 - } 865 - } 866 - 867 - impl<'a, S: file_state::State> FileBuilder<'a, S> { 868 - /// Set the `mimeType` field (optional) 869 - pub fn mime_type( 870 - mut self, 871 - value: impl Into<Option<jacquard_common::CowStr<'a>>>, 872 - ) -> Self { 873 - self.__unsafe_private_named.3 = value.into(); 874 - self 875 - } 876 - /// Set the `mimeType` field to an Option value (optional) 877 - pub fn maybe_mime_type( 878 - mut self, 879 - value: Option<jacquard_common::CowStr<'a>>, 880 - ) -> Self { 881 - self.__unsafe_private_named.3 = value; 882 - self 883 - } 884 - } 885 - 886 - impl<'a, S> FileBuilder<'a, S> 887 - where 888 - S: file_state::State, 889 - S::Type: file_state::IsUnset, 890 - { 891 - /// Set the `type` field (required) 892 - pub fn r#type( 893 - mut self, 894 - value: impl Into<jacquard_common::CowStr<'a>>, 895 - ) -> FileBuilder<'a, file_state::SetType<S>> { 896 - self.__unsafe_private_named.4 = ::core::option::Option::Some(value.into()); 897 - FileBuilder { 898 - _phantom_state: ::core::marker::PhantomData, 899 - __unsafe_private_named: self.__unsafe_private_named, 900 - _phantom: ::core::marker::PhantomData, 901 - } 902 - } 903 - } 904 - 905 - impl<'a, S> FileBuilder<'a, S> 906 - where 907 - S: file_state::State, 908 - S::Type: file_state::IsSet, 909 - S::Blob: file_state::IsSet, 910 - { 911 - /// Build the final struct 912 - pub fn build(self) -> File<'a> { 913 - File { 914 - base64: self.__unsafe_private_named.0, 915 - blob: self.__unsafe_private_named.1.unwrap(), 916 - encoding: self.__unsafe_private_named.2, 917 - mime_type: self.__unsafe_private_named.3, 918 - r#type: self.__unsafe_private_named.4.unwrap(), 919 - extra_data: Default::default(), 920 - } 921 - } 922 - /// Build the final struct with custom extra_data 923 - pub fn build_with_data( 924 - self, 925 - extra_data: std::collections::BTreeMap< 926 - jacquard_common::smol_str::SmolStr, 927 - jacquard_common::types::value::Data<'a>, 928 - >, 929 - ) -> File<'a> { 930 - File { 931 - base64: self.__unsafe_private_named.0, 932 - blob: self.__unsafe_private_named.1.unwrap(), 933 - encoding: self.__unsafe_private_named.2, 934 - mime_type: self.__unsafe_private_named.3, 935 - r#type: self.__unsafe_private_named.4.unwrap(), 936 - extra_data: Some(extra_data), 937 - } 938 - } 939 - } 940 - 941 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for File<'a> { 942 - fn nsid() -> &'static str { 943 - "place.wisp.subfs" 944 - } 945 - fn def_name() -> &'static str { 946 - "file" 947 - } 948 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 949 - lexicon_doc_place_wisp_subfs() 950 - } 951 - fn validate( 952 - &self, 953 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 954 - Ok(()) 955 - } 956 - } 957 - 958 - /// Virtual filesystem subtree referenced by place.wisp.fs records. When a subfs entry is expanded, its root entries are merged (flattened) into the parent directory, allowing large directories to be split across multiple records while maintaining a flat structure. 959 - #[jacquard_derive::lexicon] 960 - #[derive( 961 - serde::Serialize, 962 - serde::Deserialize, 963 - Debug, 964 - Clone, 965 - PartialEq, 966 - Eq, 967 - jacquard_derive::IntoStatic 968 - )] 969 - #[serde(rename_all = "camelCase")] 970 - pub struct SubfsRecord<'a> { 971 - pub created_at: jacquard_common::types::string::Datetime, 972 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 973 - pub file_count: Option<i64>, 974 - #[serde(borrow)] 975 - pub root: crate::place_wisp::subfs::Directory<'a>, 976 - } 977 - 978 - pub mod subfs_record_state { 979 - 980 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 981 - #[allow(unused)] 982 - use ::core::marker::PhantomData; 983 - mod sealed { 984 - pub trait Sealed {} 985 - } 986 - /// State trait tracking which required fields have been set 987 - pub trait State: sealed::Sealed { 988 - type Root; 989 - type CreatedAt; 990 - } 991 - /// Empty state - all required fields are unset 992 - pub struct Empty(()); 993 - impl sealed::Sealed for Empty {} 994 - impl State for Empty { 995 - type Root = Unset; 996 - type CreatedAt = Unset; 997 - } 998 - ///State transition - sets the `root` field to Set 999 - pub struct SetRoot<S: State = Empty>(PhantomData<fn() -> S>); 1000 - impl<S: State> sealed::Sealed for SetRoot<S> {} 1001 - impl<S: State> State for SetRoot<S> { 1002 - type Root = Set<members::root>; 1003 - type CreatedAt = S::CreatedAt; 1004 - } 1005 - ///State transition - sets the `created_at` field to Set 1006 - pub struct SetCreatedAt<S: State = Empty>(PhantomData<fn() -> S>); 1007 - impl<S: State> sealed::Sealed for SetCreatedAt<S> {} 1008 - impl<S: State> State for SetCreatedAt<S> { 1009 - type Root = S::Root; 1010 - type CreatedAt = Set<members::created_at>; 1011 - } 1012 - /// Marker types for field names 1013 - #[allow(non_camel_case_types)] 1014 - pub mod members { 1015 - ///Marker type for the `root` field 1016 - pub struct root(()); 1017 - ///Marker type for the `created_at` field 1018 - pub struct created_at(()); 1019 - } 1020 - } 1021 - 1022 - /// Builder for constructing an instance of this type 1023 - pub struct SubfsRecordBuilder<'a, S: subfs_record_state::State> { 1024 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 1025 - __unsafe_private_named: ( 1026 - ::core::option::Option<jacquard_common::types::string::Datetime>, 1027 - ::core::option::Option<i64>, 1028 - ::core::option::Option<crate::place_wisp::subfs::Directory<'a>>, 1029 - ), 1030 - _phantom: ::core::marker::PhantomData<&'a ()>, 1031 - } 1032 - 1033 - impl<'a> SubfsRecord<'a> { 1034 - /// Create a new builder for this type 1035 - pub fn new() -> SubfsRecordBuilder<'a, subfs_record_state::Empty> { 1036 - SubfsRecordBuilder::new() 1037 - } 1038 - } 1039 - 1040 - impl<'a> SubfsRecordBuilder<'a, subfs_record_state::Empty> { 1041 - /// Create a new builder with all fields unset 1042 - pub fn new() -> Self { 1043 - SubfsRecordBuilder { 1044 - _phantom_state: ::core::marker::PhantomData, 1045 - __unsafe_private_named: (None, None, None), 1046 - _phantom: ::core::marker::PhantomData, 1047 - } 1048 - } 1049 - } 1050 - 1051 - impl<'a, S> SubfsRecordBuilder<'a, S> 1052 - where 1053 - S: subfs_record_state::State, 1054 - S::CreatedAt: subfs_record_state::IsUnset, 1055 - { 1056 - /// Set the `createdAt` field (required) 1057 - pub fn created_at( 1058 - mut self, 1059 - value: impl Into<jacquard_common::types::string::Datetime>, 1060 - ) -> SubfsRecordBuilder<'a, subfs_record_state::SetCreatedAt<S>> { 1061 - self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 1062 - SubfsRecordBuilder { 1063 - _phantom_state: ::core::marker::PhantomData, 1064 - __unsafe_private_named: self.__unsafe_private_named, 1065 - _phantom: ::core::marker::PhantomData, 1066 - } 1067 - } 1068 - } 1069 - 1070 - impl<'a, S: subfs_record_state::State> SubfsRecordBuilder<'a, S> { 1071 - /// Set the `fileCount` field (optional) 1072 - pub fn file_count(mut self, value: impl Into<Option<i64>>) -> Self { 1073 - self.__unsafe_private_named.1 = value.into(); 1074 - self 1075 - } 1076 - /// Set the `fileCount` field to an Option value (optional) 1077 - pub fn maybe_file_count(mut self, value: Option<i64>) -> Self { 1078 - self.__unsafe_private_named.1 = value; 1079 - self 1080 - } 1081 - } 1082 - 1083 - impl<'a, S> SubfsRecordBuilder<'a, S> 1084 - where 1085 - S: subfs_record_state::State, 1086 - S::Root: subfs_record_state::IsUnset, 1087 - { 1088 - /// Set the `root` field (required) 1089 - pub fn root( 1090 - mut self, 1091 - value: impl Into<crate::place_wisp::subfs::Directory<'a>>, 1092 - ) -> SubfsRecordBuilder<'a, subfs_record_state::SetRoot<S>> { 1093 - self.__unsafe_private_named.2 = ::core::option::Option::Some(value.into()); 1094 - SubfsRecordBuilder { 1095 - _phantom_state: ::core::marker::PhantomData, 1096 - __unsafe_private_named: self.__unsafe_private_named, 1097 - _phantom: ::core::marker::PhantomData, 1098 - } 1099 - } 1100 - } 1101 - 1102 - impl<'a, S> SubfsRecordBuilder<'a, S> 1103 - where 1104 - S: subfs_record_state::State, 1105 - S::Root: subfs_record_state::IsSet, 1106 - S::CreatedAt: subfs_record_state::IsSet, 1107 - { 1108 - /// Build the final struct 1109 - pub fn build(self) -> SubfsRecord<'a> { 1110 - SubfsRecord { 1111 - created_at: self.__unsafe_private_named.0.unwrap(), 1112 - file_count: self.__unsafe_private_named.1, 1113 - root: self.__unsafe_private_named.2.unwrap(), 1114 - extra_data: Default::default(), 1115 - } 1116 - } 1117 - /// Build the final struct with custom extra_data 1118 - pub fn build_with_data( 1119 - self, 1120 - extra_data: std::collections::BTreeMap< 1121 - jacquard_common::smol_str::SmolStr, 1122 - jacquard_common::types::value::Data<'a>, 1123 - >, 1124 - ) -> SubfsRecord<'a> { 1125 - SubfsRecord { 1126 - created_at: self.__unsafe_private_named.0.unwrap(), 1127 - file_count: self.__unsafe_private_named.1, 1128 - root: self.__unsafe_private_named.2.unwrap(), 1129 - extra_data: Some(extra_data), 1130 - } 1131 - } 1132 - } 1133 - 1134 - impl<'a> SubfsRecord<'a> { 1135 - pub fn uri( 1136 - uri: impl Into<jacquard_common::CowStr<'a>>, 1137 - ) -> Result< 1138 - jacquard_common::types::uri::RecordUri<'a, SubfsRecordRecord>, 1139 - jacquard_common::types::uri::UriError, 1140 - > { 1141 - jacquard_common::types::uri::RecordUri::try_from_uri( 1142 - jacquard_common::types::string::AtUri::new_cow(uri.into())?, 1143 - ) 1144 - } 1145 - } 1146 - 1147 - /// Typed wrapper for GetRecord response with this collection's record type. 1148 - #[derive( 1149 - serde::Serialize, 1150 - serde::Deserialize, 1151 - Debug, 1152 - Clone, 1153 - PartialEq, 1154 - Eq, 1155 - jacquard_derive::IntoStatic 1156 - )] 1157 - #[serde(rename_all = "camelCase")] 1158 - pub struct SubfsRecordGetRecordOutput<'a> { 1159 - #[serde(skip_serializing_if = "std::option::Option::is_none")] 1160 - #[serde(borrow)] 1161 - pub cid: std::option::Option<jacquard_common::types::string::Cid<'a>>, 1162 - #[serde(borrow)] 1163 - pub uri: jacquard_common::types::string::AtUri<'a>, 1164 - #[serde(borrow)] 1165 - pub value: SubfsRecord<'a>, 1166 - } 1167 - 1168 - impl From<SubfsRecordGetRecordOutput<'_>> for SubfsRecord<'_> { 1169 - fn from(output: SubfsRecordGetRecordOutput<'_>) -> Self { 1170 - use jacquard_common::IntoStatic; 1171 - output.value.into_static() 1172 - } 1173 - } 1174 - 1175 - impl jacquard_common::types::collection::Collection for SubfsRecord<'_> { 1176 - const NSID: &'static str = "place.wisp.subfs"; 1177 - type Record = SubfsRecordRecord; 1178 - } 1179 - 1180 - /// Marker type for deserializing records from this collection. 1181 - #[derive(Debug, serde::Serialize, serde::Deserialize)] 1182 - pub struct SubfsRecordRecord; 1183 - impl jacquard_common::xrpc::XrpcResp for SubfsRecordRecord { 1184 - const NSID: &'static str = "place.wisp.subfs"; 1185 - const ENCODING: &'static str = "application/json"; 1186 - type Output<'de> = SubfsRecordGetRecordOutput<'de>; 1187 - type Err<'de> = jacquard_common::types::collection::RecordError<'de>; 1188 - } 1189 - 1190 - impl jacquard_common::types::collection::Collection for SubfsRecordRecord { 1191 - const NSID: &'static str = "place.wisp.subfs"; 1192 - type Record = SubfsRecordRecord; 1193 - } 1194 - 1195 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for SubfsRecord<'a> { 1196 - fn nsid() -> &'static str { 1197 - "place.wisp.subfs" 1198 - } 1199 - fn def_name() -> &'static str { 1200 - "main" 1201 - } 1202 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 1203 - lexicon_doc_place_wisp_subfs() 1204 - } 1205 - fn validate( 1206 - &self, 1207 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 1208 - if let Some(ref value) = self.file_count { 1209 - if *value > 1000i64 { 1210 - return Err(::jacquard_lexicon::validation::ConstraintError::Maximum { 1211 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 1212 - "file_count", 1213 - ), 1214 - max: 1000i64, 1215 - actual: *value, 1216 - }); 1217 - } 1218 - } 1219 - if let Some(ref value) = self.file_count { 1220 - if *value < 0i64 { 1221 - return Err(::jacquard_lexicon::validation::ConstraintError::Minimum { 1222 - path: ::jacquard_lexicon::validation::ValidationPath::from_field( 1223 - "file_count", 1224 - ), 1225 - min: 0i64, 1226 - actual: *value, 1227 - }); 1228 - } 1229 - } 1230 - Ok(()) 1231 - } 1232 - } 1233 - 1234 - #[jacquard_derive::lexicon] 1235 - #[derive( 1236 - serde::Serialize, 1237 - serde::Deserialize, 1238 - Debug, 1239 - Clone, 1240 - PartialEq, 1241 - Eq, 1242 - jacquard_derive::IntoStatic 1243 - )] 1244 - #[serde(rename_all = "camelCase")] 1245 - pub struct Subfs<'a> { 1246 - /// AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures. 1247 - #[serde(borrow)] 1248 - pub subject: jacquard_common::types::string::AtUri<'a>, 1249 - #[serde(borrow)] 1250 - pub r#type: jacquard_common::CowStr<'a>, 1251 - } 1252 - 1253 - pub mod subfs_state { 1254 - 1255 - pub use crate::builder_types::{Set, Unset, IsSet, IsUnset}; 1256 - #[allow(unused)] 1257 - use ::core::marker::PhantomData; 1258 - mod sealed { 1259 - pub trait Sealed {} 1260 - } 1261 - /// State trait tracking which required fields have been set 1262 - pub trait State: sealed::Sealed { 1263 - type Type; 1264 - type Subject; 1265 - } 1266 - /// Empty state - all required fields are unset 1267 - pub struct Empty(()); 1268 - impl sealed::Sealed for Empty {} 1269 - impl State for Empty { 1270 - type Type = Unset; 1271 - type Subject = Unset; 1272 - } 1273 - ///State transition - sets the `type` field to Set 1274 - pub struct SetType<S: State = Empty>(PhantomData<fn() -> S>); 1275 - impl<S: State> sealed::Sealed for SetType<S> {} 1276 - impl<S: State> State for SetType<S> { 1277 - type Type = Set<members::r#type>; 1278 - type Subject = S::Subject; 1279 - } 1280 - ///State transition - sets the `subject` field to Set 1281 - pub struct SetSubject<S: State = Empty>(PhantomData<fn() -> S>); 1282 - impl<S: State> sealed::Sealed for SetSubject<S> {} 1283 - impl<S: State> State for SetSubject<S> { 1284 - type Type = S::Type; 1285 - type Subject = Set<members::subject>; 1286 - } 1287 - /// Marker types for field names 1288 - #[allow(non_camel_case_types)] 1289 - pub mod members { 1290 - ///Marker type for the `type` field 1291 - pub struct r#type(()); 1292 - ///Marker type for the `subject` field 1293 - pub struct subject(()); 1294 - } 1295 - } 1296 - 1297 - /// Builder for constructing an instance of this type 1298 - pub struct SubfsBuilder<'a, S: subfs_state::State> { 1299 - _phantom_state: ::core::marker::PhantomData<fn() -> S>, 1300 - __unsafe_private_named: ( 1301 - ::core::option::Option<jacquard_common::types::string::AtUri<'a>>, 1302 - ::core::option::Option<jacquard_common::CowStr<'a>>, 1303 - ), 1304 - _phantom: ::core::marker::PhantomData<&'a ()>, 1305 - } 1306 - 1307 - impl<'a> Subfs<'a> { 1308 - /// Create a new builder for this type 1309 - pub fn new() -> SubfsBuilder<'a, subfs_state::Empty> { 1310 - SubfsBuilder::new() 1311 - } 1312 - } 1313 - 1314 - impl<'a> SubfsBuilder<'a, subfs_state::Empty> { 1315 - /// Create a new builder with all fields unset 1316 - pub fn new() -> Self { 1317 - SubfsBuilder { 1318 - _phantom_state: ::core::marker::PhantomData, 1319 - __unsafe_private_named: (None, None), 1320 - _phantom: ::core::marker::PhantomData, 1321 - } 1322 - } 1323 - } 1324 - 1325 - impl<'a, S> SubfsBuilder<'a, S> 1326 - where 1327 - S: subfs_state::State, 1328 - S::Subject: subfs_state::IsUnset, 1329 - { 1330 - /// Set the `subject` field (required) 1331 - pub fn subject( 1332 - mut self, 1333 - value: impl Into<jacquard_common::types::string::AtUri<'a>>, 1334 - ) -> SubfsBuilder<'a, subfs_state::SetSubject<S>> { 1335 - self.__unsafe_private_named.0 = ::core::option::Option::Some(value.into()); 1336 - SubfsBuilder { 1337 - _phantom_state: ::core::marker::PhantomData, 1338 - __unsafe_private_named: self.__unsafe_private_named, 1339 - _phantom: ::core::marker::PhantomData, 1340 - } 1341 - } 1342 - } 1343 - 1344 - impl<'a, S> SubfsBuilder<'a, S> 1345 - where 1346 - S: subfs_state::State, 1347 - S::Type: subfs_state::IsUnset, 1348 - { 1349 - /// Set the `type` field (required) 1350 - pub fn r#type( 1351 - mut self, 1352 - value: impl Into<jacquard_common::CowStr<'a>>, 1353 - ) -> SubfsBuilder<'a, subfs_state::SetType<S>> { 1354 - self.__unsafe_private_named.1 = ::core::option::Option::Some(value.into()); 1355 - SubfsBuilder { 1356 - _phantom_state: ::core::marker::PhantomData, 1357 - __unsafe_private_named: self.__unsafe_private_named, 1358 - _phantom: ::core::marker::PhantomData, 1359 - } 1360 - } 1361 - } 1362 - 1363 - impl<'a, S> SubfsBuilder<'a, S> 1364 - where 1365 - S: subfs_state::State, 1366 - S::Type: subfs_state::IsSet, 1367 - S::Subject: subfs_state::IsSet, 1368 - { 1369 - /// Build the final struct 1370 - pub fn build(self) -> Subfs<'a> { 1371 - Subfs { 1372 - subject: self.__unsafe_private_named.0.unwrap(), 1373 - r#type: self.__unsafe_private_named.1.unwrap(), 1374 - extra_data: Default::default(), 1375 - } 1376 - } 1377 - /// Build the final struct with custom extra_data 1378 - pub fn build_with_data( 1379 - self, 1380 - extra_data: std::collections::BTreeMap< 1381 - jacquard_common::smol_str::SmolStr, 1382 - jacquard_common::types::value::Data<'a>, 1383 - >, 1384 - ) -> Subfs<'a> { 1385 - Subfs { 1386 - subject: self.__unsafe_private_named.0.unwrap(), 1387 - r#type: self.__unsafe_private_named.1.unwrap(), 1388 - extra_data: Some(extra_data), 1389 - } 1390 - } 1391 - } 1392 - 1393 - impl<'a> ::jacquard_lexicon::schema::LexiconSchema for Subfs<'a> { 1394 - fn nsid() -> &'static str { 1395 - "place.wisp.subfs" 1396 - } 1397 - fn def_name() -> &'static str { 1398 - "subfs" 1399 - } 1400 - fn lexicon_doc() -> ::jacquard_lexicon::lexicon::LexiconDoc<'static> { 1401 - lexicon_doc_place_wisp_subfs() 1402 - } 1403 - fn validate( 1404 - &self, 1405 - ) -> ::std::result::Result<(), ::jacquard_lexicon::validation::ConstraintError> { 1406 - Ok(()) 1407 - } 1408 - }
-8
cli/src/place_wisp.rs
··· 1 - // @generated by jacquard-lexicon. DO NOT EDIT. 2 - // 3 - // This file was automatically generated from Lexicon schemas. 4 - // Any manual changes will be overwritten on the next regeneration. 5 - 6 - pub mod fs; 7 - pub mod settings; 8 - pub mod subfs;
+12 -12
cli/src/pull.rs
··· 1 1 use crate::blob_map; 2 2 use crate::download; 3 3 use crate::metadata::SiteMetadata; 4 - use crate::place_wisp::fs::*; 4 + use wisp_lexicons::place_wisp::fs::*; 5 5 use crate::subfs_utils; 6 6 use jacquard::CowStr; 7 7 use jacquard::prelude::IdentityResolver; ··· 410 410 ) -> miette::Result<Directory<'static>> { 411 411 use jacquard_common::IntoStatic; 412 412 use jacquard_common::types::value::from_data; 413 - use crate::place_wisp::subfs::SubfsRecord; 413 + use wisp_lexicons::place_wisp::subfs::SubfsRecord; 414 414 415 - let mut all_subfs_map: HashMap<String, crate::place_wisp::subfs::Directory> = HashMap::new(); 415 + let mut all_subfs_map: HashMap<String, wisp_lexicons::place_wisp::subfs::Directory> = HashMap::new(); 416 416 let mut to_fetch = subfs_utils::extract_subfs_uris(directory, String::new()); 417 417 418 418 if to_fetch.is_empty() { ··· 516 516 517 517 /// Extract subfs URIs from a subfs::Directory (helper for pull) 518 518 fn extract_subfs_uris_from_subfs_dir( 519 - directory: &crate::place_wisp::subfs::Directory, 519 + directory: &wisp_lexicons::place_wisp::subfs::Directory, 520 520 current_path: String, 521 521 ) -> Vec<(String, String)> { 522 522 let mut uris = Vec::new(); ··· 529 529 }; 530 530 531 531 match &entry.node { 532 - crate::place_wisp::subfs::EntryNode::Subfs(subfs_node) => { 532 + wisp_lexicons::place_wisp::subfs::EntryNode::Subfs(subfs_node) => { 533 533 uris.push((subfs_node.subject.to_string(), full_path.clone())); 534 534 } 535 - crate::place_wisp::subfs::EntryNode::Directory(subdir) => { 535 + wisp_lexicons::place_wisp::subfs::EntryNode::Directory(subdir) => { 536 536 let nested = extract_subfs_uris_from_subfs_dir(subdir, full_path); 537 537 uris.extend(nested); 538 538 } ··· 546 546 /// Recursively replace subfs nodes with their actual content 547 547 fn replace_subfs_with_content( 548 548 directory: Directory, 549 - subfs_map: &HashMap<String, crate::place_wisp::subfs::Directory>, 549 + subfs_map: &HashMap<String, wisp_lexicons::place_wisp::subfs::Directory>, 550 550 current_path: String, 551 551 ) -> Directory<'static> { 552 552 use jacquard_common::IntoStatic; ··· 628 628 } 629 629 630 630 /// Convert a subfs entry to a fs entry (they have the same structure but different types) 631 - fn convert_subfs_entry_to_fs(subfs_entry: crate::place_wisp::subfs::Entry<'static>) -> Entry<'static> { 631 + fn convert_subfs_entry_to_fs(subfs_entry: wisp_lexicons::place_wisp::subfs::Entry<'static>) -> Entry<'static> { 632 632 use jacquard_common::IntoStatic; 633 633 634 634 let node = match subfs_entry.node { 635 - crate::place_wisp::subfs::EntryNode::File(file) => { 635 + wisp_lexicons::place_wisp::subfs::EntryNode::File(file) => { 636 636 EntryNode::File(Box::new( 637 637 File::new() 638 638 .r#type(file.r#type.into_static()) ··· 643 643 .build() 644 644 )) 645 645 } 646 - crate::place_wisp::subfs::EntryNode::Directory(dir) => { 646 + wisp_lexicons::place_wisp::subfs::EntryNode::Directory(dir) => { 647 647 let converted_entries: Vec<Entry<'static>> = dir 648 648 .entries 649 649 .into_iter() ··· 657 657 .build() 658 658 )) 659 659 } 660 - crate::place_wisp::subfs::EntryNode::Subfs(_nested_subfs) => { 660 + wisp_lexicons::place_wisp::subfs::EntryNode::Subfs(_nested_subfs) => { 661 661 // Nested subfs should have been expanded already - if we get here, it means expansion failed 662 662 // Treat it like a directory reference that should have been expanded 663 663 eprintln!(" โš ๏ธ Warning: unexpanded nested subfs at path, treating as empty directory"); ··· 668 668 .build() 669 669 )) 670 670 } 671 - crate::place_wisp::subfs::EntryNode::Unknown(unknown) => { 671 + wisp_lexicons::place_wisp::subfs::EntryNode::Unknown(unknown) => { 672 672 EntryNode::Unknown(unknown) 673 673 } 674 674 };
+1 -1
cli/src/serve.rs
··· 1 1 use crate::pull::pull_site; 2 2 use crate::redirects::{load_redirect_rules, match_redirect_rule, RedirectRule}; 3 - use crate::place_wisp::settings::Settings; 3 + use wisp_lexicons::place_wisp::settings::Settings; 4 4 use axum::{ 5 5 Router, 6 6 extract::Request,
+14 -14
cli/src/subfs_utils.rs
··· 6 6 use miette::IntoDiagnostic; 7 7 use std::collections::HashMap; 8 8 9 - use crate::place_wisp::fs::{Directory as FsDirectory, EntryNode as FsEntryNode}; 10 - use crate::place_wisp::subfs::SubfsRecord; 9 + use wisp_lexicons::place_wisp::fs::{Directory as FsDirectory, EntryNode as FsEntryNode}; 10 + use wisp_lexicons::place_wisp::subfs::SubfsRecord; 11 11 12 12 /// Extract all subfs URIs from a directory tree with their mount paths 13 13 pub fn extract_subfs_uris(directory: &FsDirectory, current_path: String) -> Vec<(String, String)> { ··· 145 145 146 146 /// Extract subfs URIs from a subfs::Directory 147 147 fn extract_subfs_uris_from_subfs_dir( 148 - directory: &crate::place_wisp::subfs::Directory, 148 + directory: &wisp_lexicons::place_wisp::subfs::Directory, 149 149 current_path: String, 150 150 ) -> Vec<(String, String)> { 151 151 let mut uris = Vec::new(); 152 152 153 153 for entry in &directory.entries { 154 154 match &entry.node { 155 - crate::place_wisp::subfs::EntryNode::Subfs(subfs_node) => { 155 + wisp_lexicons::place_wisp::subfs::EntryNode::Subfs(subfs_node) => { 156 156 // Check if this is a chunk entry (chunk0, chunk1, etc.) 157 157 // Chunks should be flat-merged, so use the parent's path 158 158 let mount_path = if entry.name.starts_with("chunk") && ··· 171 171 172 172 uris.push((subfs_node.subject.to_string(), mount_path)); 173 173 } 174 - crate::place_wisp::subfs::EntryNode::Directory(subdir) => { 174 + wisp_lexicons::place_wisp::subfs::EntryNode::Directory(subdir) => { 175 175 let full_path = if current_path.is_empty() { 176 176 entry.name.to_string() 177 177 } else { ··· 204 204 for (mount_path, subfs_record) in all_subfs { 205 205 // Check if this record only contains chunk subfs references (no files) 206 206 let only_has_chunks = subfs_record.root.entries.iter().all(|e| { 207 - matches!(&e.node, crate::place_wisp::subfs::EntryNode::Subfs(_)) && 207 + matches!(&e.node, wisp_lexicons::place_wisp::subfs::EntryNode::Subfs(_)) && 208 208 e.name.starts_with("chunk") && 209 209 e.name.chars().skip(5).all(|c| c.is_ascii_digit()) 210 210 }); ··· 232 232 /// Extract blobs from a subfs directory (works with subfs::Directory) 233 233 /// Returns a map of file paths to their blob refs and CIDs 234 234 fn extract_subfs_blobs( 235 - directory: &crate::place_wisp::subfs::Directory, 235 + directory: &wisp_lexicons::place_wisp::subfs::Directory, 236 236 current_path: String, 237 237 ) -> HashMap<String, (BlobRef<'static>, String)> { 238 238 let mut blob_map = HashMap::new(); ··· 245 245 }; 246 246 247 247 match &entry.node { 248 - crate::place_wisp::subfs::EntryNode::File(file_node) => { 248 + wisp_lexicons::place_wisp::subfs::EntryNode::File(file_node) => { 249 249 let blob_ref = &file_node.blob; 250 250 let cid_string = blob_ref.blob().r#ref.to_string(); 251 251 blob_map.insert( ··· 253 253 (blob_ref.clone().into_static(), cid_string) 254 254 ); 255 255 } 256 - crate::place_wisp::subfs::EntryNode::Directory(subdir) => { 256 + wisp_lexicons::place_wisp::subfs::EntryNode::Directory(subdir) => { 257 257 let sub_map = extract_subfs_blobs(subdir, full_path); 258 258 blob_map.extend(sub_map); 259 259 } 260 - crate::place_wisp::subfs::EntryNode::Subfs(_nested_subfs) => { 260 + wisp_lexicons::place_wisp::subfs::EntryNode::Subfs(_nested_subfs) => { 261 261 // Nested subfs - these should be resolved recursively in the main flow 262 262 // For now, we skip them (they'll be fetched separately) 263 263 eprintln!(" โš ๏ธ Found nested subfs at {}, skipping (should be fetched separately)", full_path); 264 264 } 265 - crate::place_wisp::subfs::EntryNode::Unknown(_) => { 265 + wisp_lexicons::place_wisp::subfs::EntryNode::Unknown(_) => { 266 266 // Skip unknown nodes 267 267 } 268 268 } ··· 352 352 flat: bool, 353 353 ) -> miette::Result<FsDirectory<'static>> { 354 354 use jacquard_common::CowStr; 355 - use crate::place_wisp::fs::{Entry, Subfs}; 355 + use wisp_lexicons::place_wisp::fs::{Entry, Subfs}; 356 356 357 357 let path_parts: Vec<&str> = target_path.split('/').collect(); 358 358 ··· 430 430 431 431 // Construct AT-URI and convert to RecordUri 432 432 let at_uri = AtUri::new(uri).into_diagnostic()?; 433 - let record_uri: RecordUri<'_, crate::place_wisp::subfs::SubfsRecordRecord> = RecordUri::try_from_uri(at_uri).into_diagnostic()?; 433 + let record_uri: RecordUri<'_, wisp_lexicons::place_wisp::subfs::SubfsRecordRecord> = RecordUri::try_from_uri(at_uri).into_diagnostic()?; 434 434 435 435 let rkey = record_uri.rkey() 436 436 .ok_or_else(|| miette::miette!("Invalid subfs URI: missing rkey"))? ··· 489 489 } 490 490 491 491 /// Estimate the JSON size of a single entry 492 - fn estimate_entry_size(entry: &crate::place_wisp::fs::Entry) -> usize { 492 + fn estimate_entry_size(entry: &wisp_lexicons::place_wisp::fs::Entry) -> usize { 493 493 match serde_json::to_string(entry) { 494 494 Ok(json) => json.len(), 495 495 Err(_) => 500, // Conservative estimate if serialization fails
+21
docker-compose.yml
··· 1 + services: 2 + postgres: 3 + image: postgres:16-alpine 4 + container_name: wisp-postgres 5 + restart: unless-stopped 6 + environment: 7 + POSTGRES_USER: postgres 8 + POSTGRES_PASSWORD: postgres 9 + POSTGRES_DB: wisp 10 + ports: 11 + - "5432:5432" 12 + volumes: 13 + - postgres_data:/var/lib/postgresql/data 14 + healthcheck: 15 + test: ["CMD-SHELL", "pg_isready -U postgres"] 16 + interval: 5s 17 + timeout: 5s 18 + retries: 5 19 + 20 + volumes: 21 + postgres_data:
+4 -1
docs/astro.config.mjs
··· 7 7 integrations: [ 8 8 starlight({ 9 9 title: 'Wisp.place Docs', 10 - social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/tangled-org/wisp.place' }], 10 + components: { 11 + SocialIcons: './src/components/SocialIcons.astro', 12 + }, 11 13 sidebar: [ 12 14 { 13 15 label: 'Getting Started', ··· 24 26 label: 'Guides', 25 27 items: [ 26 28 { label: 'Self-Hosting', slug: 'deployment' }, 29 + { label: 'Monitoring & Metrics', slug: 'monitoring' }, 27 30 { label: 'Redirects & Rewrites', slug: 'redirects' }, 28 31 ], 29 32 },
+9
docs/src/assets/tangled-icon.svg
··· 1 + <svg xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1" id="svg1" width="25" height="25" viewBox="0 0 25 25" sodipodi:docname="tangled_dolly_silhouette.png"> 2 + <defs id="defs1"/> 3 + <sodipodi:namedview id="namedview1" pagecolor="#ffffff" bordercolor="#000000" borderopacity="0.25" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="true" inkscape:deskcolor="#d1d1d1"> 4 + <inkscape:page x="0" y="0" width="25" height="25" id="page2" margin="0" bleed="0"/> 5 + </sodipodi:namedview> 6 + <g inkscape:groupmode="layer" inkscape:label="Image" id="g1"> 7 + <path style="fill:#000000;stroke-width:1.12248" d="m 16.208435,23.914069 c -0.06147,-0.02273 -0.147027,-0.03034 -0.190158,-0.01691 -0.197279,0.06145 -1.31068,-0.230493 -1.388819,-0.364153 -0.01956,-0.03344 -0.163274,-0.134049 -0.319377,-0.223561 -0.550395,-0.315603 -1.010951,-0.696643 -1.428383,-1.181771 -0.264598,-0.307509 -0.597257,-0.785384 -0.597257,-0.857979 0,-0.0216 -0.02841,-0.06243 -0.06313,-0.0907 -0.04977,-0.04053 -0.160873,0.0436 -0.52488,0.397463 -0.479803,0.466432 -0.78924,0.689475 -1.355603,0.977118 -0.183693,0.0933 -0.323426,0.179989 -0.310516,0.192658 0.02801,0.02748 -0.7656391,0.270031 -1.209129,0.369517 -0.5378332,0.120647 -1.6341809,0.08626 -1.9721503,-0.06186 C 6.7977157,23.031391 6.56735,22.957551 6.3371134,22.889782 4.9717169,22.487902 3.7511914,21.481518 3.1172396,20.234838 2.6890391,19.392772 2.5582276,18.827446 2.5610489,17.831154 2.5639589,16.802192 2.7366641,16.125844 3.2142117,15.273187 3.3040457,15.112788 3.3713143,14.976533 3.3636956,14.9704 3.3560756,14.9643 3.2459634,14.90305 3.1189994,14.834381 1.7582586,14.098312 0.77760984,12.777439 0.44909837,11.23818 0.33531456,10.705039 0.33670119,9.7067968 0.45195381,9.1778795 0.72259241,7.9359287 1.3827188,6.8888436 2.4297498,6.0407205 2.6856126,5.8334648 3.2975489,5.4910878 3.6885849,5.3364049 L 4.0584319,5.190106 4.2333984,4.860432 C 4.8393906,3.7186139 5.8908314,2.7968028 7.1056396,2.3423025 7.7690673,2.0940921 8.2290216,2.0150935 9.01853,2.0137575 c 0.9625627,-0.00163 1.629181,0.1532762 2.485864,0.5776514 l 0.271744,0.1346134 0.42911,-0.3607688 c 1.082666,-0.9102346 2.185531,-1.3136811 3.578383,-1.3090327 0.916696,0.00306 1.573918,0.1517893 2.356121,0.5331927 1.465948,0.7148 2.54506,2.0625628 2.865177,3.57848 l 0.07653,0.362429 0.515095,0.2556611 c 1.022872,0.5076874 1.756122,1.1690944 2.288361,2.0641468 0.401896,0.6758594 0.537303,1.0442682 0.675505,1.8378683 0.288575,1.6570823 -0.266229,3.3548023 -1.490464,4.5608743 -0.371074,0.36557 -0.840205,0.718265 -1.203442,0.904754 -0.144112,0.07398 -0.271303,0.15826 -0.282647,0.187269 -0.01134,0.02901 0.02121,0.142764 0.07234,0.25279 0.184248,0.396467 0.451371,1.331823 0.619371,2.168779 0.463493,2.30908 -0.754646,4.693707 -2.92278,5.721632 -0.479538,0.227352 -0.717629,0.309322 -1.144194,0.39393 -0.321869,0.06383 -1.850573,0.09139 -2.000174,0.03604 z M 12.25443,18.636956 c 0.739923,-0.24652 1.382521,-0.718922 1.874623,-1.37812 0.0752,-0.100718 0.213883,-0.275851 0.308198,-0.389167 0.09432,-0.113318 0.210136,-0.271056 0.257381,-0.350531 0.416347,-0.700389 0.680936,-1.176102 0.766454,-1.378041 0.05594,-0.132087 0.114653,-0.239607 0.130477,-0.238929 0.01583,6.79e-4 0.08126,0.08531 0.145412,0.188069 0.178029,0.285173 0.614305,0.658998 0.868158,0.743878 0.259802,0.08686 0.656158,0.09598 0.911369,0.02095 0.213812,-0.06285 0.507296,-0.298016 0.645179,-0.516947 0.155165,-0.246374 0.327989,-0.989595 0.327989,-1.410501 0,-1.26718 -0.610975,-3.143405 -1.237774,-3.801045 -0.198483,-0.2082486 -0.208557,-0.2319396 -0.208557,-0.4904655 0,-0.2517771 -0.08774,-0.5704927 -0.258476,-0.938956 C 16.694963,8.50313 16.375697,8.1377479 16.135846,7.9543702 L 15.932296,7.7987471 15.683004,7.9356529 C 15.131767,8.2383821 14.435638,8.1945733 13.943459,7.8261812 L 13.782862,7.7059758 13.686773,7.8908012 C 13.338849,8.5600578 12.487087,8.8811064 11.743178,8.6233891 11.487199,8.5347109 11.358897,8.4505994 11.063189,8.1776138 L 10.69871,7.8411436 10.453484,8.0579255 C 10.318608,8.1771557 10.113778,8.3156283 9.9983037,8.3656417 9.7041488,8.4930449 9.1808299,8.5227884 8.8979004,8.4281886 8.7754792,8.3872574 8.6687415,8.3537661 8.6607053,8.3537661 c -0.03426,0 -0.3092864,0.3066098 -0.3791974,0.42275 -0.041935,0.069664 -0.1040482,0.1266636 -0.1380294,0.1266636 -0.1316419,0 -0.4197402,0.1843928 -0.6257041,0.4004735 -0.1923125,0.2017571 -0.6853701,0.9036038 -0.8926582,1.2706578 -0.042662,0.07554 -0.1803555,0.353687 -0.3059848,0.618091 -0.1256293,0.264406 -0.3270073,0.686768 -0.4475067,0.938581 -0.1204992,0.251816 -0.2469926,0.519654 -0.2810961,0.595199 -0.2592829,0.574347 -0.285919,1.391094 -0.057822,1.77304 0.1690683,0.283105 0.4224039,0.480895 0.7285507,0.568809 0.487122,0.139885 0.9109638,-0.004 1.6013422,-0.543768 l 0.4560939,-0.356568 0.0036,0.172041 c 0.01635,0.781837 0.1831084,1.813183 0.4016641,2.484154 0.1160449,0.356262 0.3781448,0.83968 0.5614081,1.035462 0.2171883,0.232025 0.7140951,0.577268 1.0100284,0.701749 0.121485,0.0511 0.351032,0.110795 0.510105,0.132647 0.396966,0.05452 1.2105,0.02265 1.448934,-0.05679 z" id="path1"/> 8 + </g> 9 + </svg>
+26
docs/src/components/SocialIcons.astro
··· 1 + --- 2 + // Custom social icons component to use the Tangled icon 3 + --- 4 + 5 + <div class="sl-flex"> 6 + <a 7 + href="https://tangled.org/nekomimi.pet/wisp.place-monorepo" 8 + rel="me" 9 + class="sl-flex" 10 + aria-label="Tangled" 11 + > 12 + <svg 13 + xmlns="http://www.w3.org/2000/svg" 14 + viewBox="0 0 25 25" 15 + width="16" 16 + height="16" 17 + aria-hidden="true" 18 + focusable="false" 19 + > 20 + <path 21 + style="fill:currentColor;stroke-width:1.12248" 22 + d="m 16.208435,23.914069 c -0.06147,-0.02273 -0.147027,-0.03034 -0.190158,-0.01691 -0.197279,0.06145 -1.31068,-0.230493 -1.388819,-0.364153 -0.01956,-0.03344 -0.163274,-0.134049 -0.319377,-0.223561 -0.550395,-0.315603 -1.010951,-0.696643 -1.428383,-1.181771 -0.264598,-0.307509 -0.597257,-0.785384 -0.597257,-0.857979 0,-0.0216 -0.02841,-0.06243 -0.06313,-0.0907 -0.04977,-0.04053 -0.160873,0.0436 -0.52488,0.397463 -0.479803,0.466432 -0.78924,0.689475 -1.355603,0.977118 -0.183693,0.0933 -0.323426,0.179989 -0.310516,0.192658 0.02801,0.02748 -0.7656391,0.270031 -1.209129,0.369517 -0.5378332,0.120647 -1.6341809,0.08626 -1.9721503,-0.06186 C 6.7977157,23.031391 6.56735,22.957551 6.3371134,22.889782 4.9717169,22.487902 3.7511914,21.481518 3.1172396,20.234838 2.6890391,19.392772 2.5582276,18.827446 2.5610489,17.831154 2.5639589,16.802192 2.7366641,16.125844 3.2142117,15.273187 3.3040457,15.112788 3.3713143,14.976533 3.3636956,14.9704 3.3560756,14.9643 3.2459634,14.90305 3.1189994,14.834381 1.7582586,14.098312 0.77760984,12.777439 0.44909837,11.23818 0.33531456,10.705039 0.33670119,9.7067968 0.45195381,9.1778795 0.72259241,7.9359287 1.3827188,6.8888436 2.4297498,6.0407205 2.6856126,5.8334648 3.2975489,5.4910878 3.6885849,5.3364049 L 4.0584319,5.190106 4.2333984,4.860432 C 4.8393906,3.7186139 5.8908314,2.7968028 7.1056396,2.3423025 7.7690673,2.0940921 8.2290216,2.0150935 9.01853,2.0137575 c 0.9625627,-0.00163 1.629181,0.1532762 2.485864,0.5776514 l 0.271744,0.1346134 0.42911,-0.3607688 c 1.082666,-0.9102346 2.185531,-1.3136811 3.578383,-1.3090327 0.916696,0.00306 1.573918,0.1517893 2.356121,0.5331927 1.465948,0.7148 2.54506,2.0625628 2.865177,3.57848 l 0.07653,0.362429 0.515095,0.2556611 c 1.022872,0.5076874 1.756122,1.1690944 2.288361,2.0641468 0.401896,0.6758594 0.537303,1.0442682 0.675505,1.8378683 0.288575,1.6570823 -0.266229,3.3548023 -1.490464,4.5608743 -0.371074,0.36557 -0.840205,0.718265 -1.203442,0.904754 -0.144112,0.07398 -0.271303,0.15826 -0.282647,0.187269 -0.01134,0.02901 0.02121,0.142764 0.07234,0.25279 0.184248,0.396467 0.451371,1.331823 0.619371,2.168779 0.463493,2.30908 -0.754646,4.693707 -2.92278,5.721632 -0.479538,0.227352 -0.717629,0.309322 -1.144194,0.39393 -0.321869,0.06383 -1.850573,0.09139 -2.000174,0.03604 z M 12.25443,18.636956 c 0.739923,-0.24652 1.382521,-0.718922 1.874623,-1.37812 0.0752,-0.100718 0.213883,-0.275851 0.308198,-0.389167 0.09432,-0.113318 0.210136,-0.271056 0.257381,-0.350531 0.416347,-0.700389 0.680936,-1.176102 0.766454,-1.378041 0.05594,-0.132087 0.114653,-0.239607 0.130477,-0.238929 0.01583,6.79e-4 0.08126,0.08531 0.145412,0.188069 0.178029,0.285173 0.614305,0.658998 0.868158,0.743878 0.259802,0.08686 0.656158,0.09598 0.911369,0.02095 0.213812,-0.06285 0.507296,-0.298016 0.645179,-0.516947 0.155165,-0.246374 0.327989,-0.989595 0.327989,-1.410501 0,-1.26718 -0.610975,-3.143405 -1.237774,-3.801045 -0.198483,-0.2082486 -0.208557,-0.2319396 -0.208557,-0.4904655 0,-0.2517771 -0.08774,-0.5704927 -0.258476,-0.938956 C 16.694963,8.50313 16.375697,8.1377479 16.135846,7.9543702 L 15.932296,7.7987471 15.683004,7.9356529 C 15.131767,8.2383821 14.435638,8.1945733 13.943459,7.8261812 L 13.782862,7.7059758 13.686773,7.8908012 C 13.338849,8.5600578 12.487087,8.8811064 11.743178,8.6233891 11.487199,8.5347109 11.358897,8.4505994 11.063189,8.1776138 L 10.69871,7.8411436 10.453484,8.0579255 C 10.318608,8.1771557 10.113778,8.3156283 9.9983037,8.3656417 9.7041488,8.4930449 9.1808299,8.5227884 8.8979004,8.4281886 8.7754792,8.3872574 8.6687415,8.3537661 8.6607053,8.3537661 c -0.03426,0 -0.3092864,0.3066098 -0.3791974,0.42275 -0.041935,0.069664 -0.1040482,0.1266636 -0.1380294,0.1266636 -0.1316419,0 -0.4197402,0.1843928 -0.6257041,0.4004735 -0.1923125,0.2017571 -0.6853701,0.9036038 -0.8926582,1.2706578 -0.042662,0.07554 -0.1803555,0.353687 -0.3059848,0.618091 -0.1256293,0.264406 -0.3270073,0.686768 -0.4475067,0.938581 -0.1204992,0.251816 -0.2469926,0.519654 -0.2810961,0.595199 -0.2592829,0.574347 -0.285919,1.391094 -0.057822,1.77304 0.1690683,0.283105 0.4224039,0.480895 0.7285507,0.568809 0.487122,0.139885 0.9109638,-0.004 1.6013422,-0.543768 l 0.4560939,-0.356568 0.0036,0.172041 c 0.01635,0.781837 0.1831084,1.813183 0.4016641,2.484154 0.1160449,0.356262 0.3781448,0.83968 0.5614081,1.035462 0.2171883,0.232025 0.7140951,0.577268 1.0100284,0.701749 0.121485,0.0511 0.351032,0.110795 0.510105,0.132647 0.396966,0.05452 1.2105,0.02265 1.448934,-0.05679 z" 23 + ></path> 24 + </svg> 25 + </a> 26 + </div>
+6 -11
docs/src/content/docs/cli.md
··· 71 71 72 72 engine: 'nixery' 73 73 74 - clone: 75 - skip: false 76 - depth: 1 77 - submodules: false 78 - 79 74 dependencies: 80 75 nixpkgs: 81 76 - nodejs ··· 141 136 # Pull a site to a specific directory 142 137 wisp-cli pull your-handle.bsky.social \ 143 138 --site my-site \ 144 - --output ./my-site 139 + --path ./my-site 145 140 146 141 # Pull to current directory 147 142 wisp-cli pull your-handle.bsky.social \ ··· 238 233 ### Pull Command 239 234 240 235 ```bash 241 - wisp-cli pull [OPTIONS] <INPUT> 236 + wisp-cli pull [OPTIONS] --site <SITE> <INPUT> 242 237 243 238 Arguments: 244 239 <INPUT> Handle or DID 245 240 246 241 Options: 247 242 -s, --site <SITE> Site name to download 248 - -o, --output <OUTPUT> Output directory [default: .] 243 + -p, --path <PATH> Output directory [default: .] 249 244 -h, --help Print help 250 245 ``` 251 246 252 247 ### Serve Command 253 248 254 249 ```bash 255 - wisp-cli serve [OPTIONS] <INPUT> 250 + wisp-cli serve [OPTIONS] --site <SITE> <INPUT> 256 251 257 252 Arguments: 258 253 <INPUT> Handle or DID 259 254 260 255 Options: 261 256 -s, --site <SITE> Site name to serve 262 - -o, --output <OUTPUT> Site files directory [default: .] 263 - -p, --port <PORT> Port to serve on [default: 8080] 257 + -p, --path <PATH> Site files directory [default: .] 258 + -P, --port <PORT> Port to serve on [default: 8080] 264 259 --spa Enable SPA mode (serve index.html for all routes) 265 260 --directory Enable directory listing mode for paths without index files 266 261 -h, --help Print help
+85
docs/src/content/docs/guides/grafana-setup.md
··· 1 + --- 2 + title: Grafana Setup Example 3 + description: Quick setup for Grafana Cloud monitoring 4 + --- 5 + 6 + Example setup for monitoring Wisp.place with Grafana Cloud. 7 + 8 + ## 1. Create Grafana Cloud Account 9 + 10 + Sign up at [grafana.com](https://grafana.com) for a free tier account. 11 + 12 + ## 2. Get Credentials 13 + 14 + Navigate to your stack and find: 15 + 16 + **Loki** (Connections โ†’ Loki โ†’ Details): 17 + - Push endpoint: `https://logs-prod-XXX.grafana.net` 18 + - Create API token with write permissions 19 + 20 + **Prometheus** (Connections โ†’ Prometheus โ†’ Details): 21 + - Remote Write endpoint: `https://prometheus-prod-XXX.grafana.net/api/prom` 22 + - Create API token with write permissions 23 + 24 + ## 3. Configure Wisp.place 25 + 26 + Add to your `.env`: 27 + 28 + ```bash 29 + GRAFANA_LOKI_URL=https://logs-prod-XXX.grafana.net 30 + GRAFANA_LOKI_TOKEN=glc_eyJ... 31 + 32 + GRAFANA_PROMETHEUS_URL=https://prometheus-prod-XXX.grafana.net/api/prom 33 + GRAFANA_PROMETHEUS_TOKEN=glc_eyJ... 34 + ``` 35 + 36 + ## 4. Create Dashboard 37 + 38 + Import this dashboard JSON or build your own: 39 + 40 + ```json 41 + { 42 + "panels": [ 43 + { 44 + "title": "Request Rate", 45 + "targets": [{ 46 + "expr": "sum(rate(http_requests_total[1m])) by (service)" 47 + }] 48 + }, 49 + { 50 + "title": "P95 Latency", 51 + "targets": [{ 52 + "expr": "histogram_quantile(0.95, rate(http_request_duration_ms_bucket[5m]))" 53 + }] 54 + }, 55 + { 56 + "title": "Error Rate", 57 + "targets": [{ 58 + "expr": "sum(rate(errors_total[5m])) / sum(rate(http_requests_total[5m]))" 59 + }] 60 + } 61 + ] 62 + } 63 + ``` 64 + 65 + ## 5. Set Alerts 66 + 67 + Example alert for high error rate: 68 + 69 + ```yaml 70 + alert: HighErrorRate 71 + expr: | 72 + sum(rate(errors_total[5m])) by (service) / 73 + sum(rate(http_requests_total[5m])) by (service) > 0.05 74 + for: 5m 75 + annotations: 76 + summary: "High error rate in {{ $labels.service }}" 77 + ``` 78 + 79 + ## Verify Data Flow 80 + 81 + Check Grafana Explore: 82 + - Loki: `{job="main-app"} | json` 83 + - Prometheus: `http_requests_total` 84 + 85 + Data should appear within 30 seconds of service startup.
+156
docs/src/content/docs/monitoring.md
··· 1 + --- 2 + title: Monitoring & Metrics 3 + description: Track performance and debug issues with Grafana integration 4 + --- 5 + 6 + Wisp.place includes built-in observability with automatic Grafana integration for logs and metrics. Monitor request performance, track errors, and analyze usage patterns across both the main backend and hosting service. 7 + 8 + ## Quick Start 9 + 10 + Set environment variables to enable Grafana export: 11 + 12 + ```bash 13 + # Grafana Cloud 14 + GRAFANA_LOKI_URL=https://logs-prod-xxx.grafana.net 15 + GRAFANA_LOKI_TOKEN=glc_xxx 16 + 17 + GRAFANA_PROMETHEUS_URL=https://prometheus-prod-xxx.grafana.net/api/prom 18 + GRAFANA_PROMETHEUS_TOKEN=glc_xxx 19 + 20 + # Self-hosted Grafana 21 + GRAFANA_LOKI_USERNAME=your-username 22 + GRAFANA_LOKI_PASSWORD=your-password 23 + ``` 24 + 25 + Restart services. Metrics and logs now flow to Grafana automatically. 26 + 27 + ## Metrics Collected 28 + 29 + ### HTTP Requests 30 + - `http_requests_total` - Total request count by path, method, status 31 + - `http_request_duration_ms` - Request duration histogram 32 + - `errors_total` - Error count by service 33 + 34 + ### Performance Stats 35 + - P50, P95, P99 response times 36 + - Requests per minute 37 + - Error rates 38 + - Average duration by endpoint 39 + 40 + ## Log Aggregation 41 + 42 + Logs are sent to Loki with automatic categorization: 43 + 44 + ``` 45 + {job="main-app"} |= "error" # OAuth and upload errors 46 + {job="hosting-service"} |= "cache" # Cache operations 47 + {service="hosting-service", level="warn"} # Warnings only 48 + ``` 49 + 50 + ## Service Identification 51 + 52 + Each service is tagged separately: 53 + - `main-app` - OAuth, uploads, domain management 54 + - `hosting-service` - Firehose, caching, content serving 55 + 56 + ## Configuration Options 57 + 58 + ### Environment Variables 59 + 60 + ```bash 61 + # Required 62 + GRAFANA_LOKI_URL # Loki endpoint 63 + GRAFANA_PROMETHEUS_URL # Prometheus endpoint (add /api/prom for OTLP) 64 + 65 + # Authentication (use one) 66 + GRAFANA_LOKI_TOKEN # Bearer token (Grafana Cloud) 67 + GRAFANA_LOKI_USERNAME # Basic auth (self-hosted) 68 + GRAFANA_LOKI_PASSWORD 69 + 70 + # Optional 71 + GRAFANA_BATCH_SIZE=100 # Batch size before flush 72 + GRAFANA_FLUSH_INTERVAL=5000 # Flush interval in ms 73 + ``` 74 + 75 + ### Programmatic Setup 76 + 77 + ```typescript 78 + import { initializeGrafanaExporters } from '@wisp/observability' 79 + 80 + initializeGrafanaExporters({ 81 + lokiUrl: 'https://logs.grafana.net', 82 + lokiAuth: { bearerToken: 'token' }, 83 + prometheusUrl: 'https://prometheus.grafana.net/api/prom', 84 + prometheusAuth: { bearerToken: 'token' }, 85 + serviceName: 'my-service', 86 + batchSize: 100, 87 + flushIntervalMs: 5000 88 + }) 89 + ``` 90 + 91 + ## Grafana Dashboard Queries 92 + 93 + ### Request Performance 94 + ```promql 95 + # Average response time by endpoint 96 + avg by (path) ( 97 + rate(http_request_duration_ms_sum[5m]) / 98 + rate(http_request_duration_ms_count[5m]) 99 + ) 100 + 101 + # Request rate 102 + sum(rate(http_requests_total[1m])) by (service) 103 + 104 + # Error rate 105 + sum(rate(errors_total[5m])) by (service) / 106 + sum(rate(http_requests_total[5m])) by (service) 107 + ``` 108 + 109 + ### Log Analysis 110 + ```logql 111 + # Recent errors 112 + {job="main-app"} |= "error" | json 113 + 114 + # Slow requests (>1s) 115 + {job="hosting-service"} |~ "duration.*[1-9][0-9]{3,}" 116 + 117 + # Failed OAuth attempts 118 + {job="main-app"} |= "OAuth" |= "failed" 119 + ``` 120 + 121 + ## Troubleshooting 122 + 123 + ### Logs not appearing 124 + - Check `GRAFANA_LOKI_URL` is correct (no trailing `/loki/api/v1/push`) 125 + - Verify authentication token/credentials 126 + - Look for connection errors in service logs 127 + 128 + ### Metrics missing 129 + - Ensure `GRAFANA_PROMETHEUS_URL` includes `/api/prom` suffix 130 + - Check firewall rules allow outbound HTTPS 131 + - Verify OpenTelemetry export errors in logs 132 + 133 + ### High memory usage 134 + - Reduce `GRAFANA_BATCH_SIZE` (default: 100) 135 + - Lower `GRAFANA_FLUSH_INTERVAL` to flush more frequently 136 + 137 + ## Local Development 138 + 139 + Metrics and logs are stored in-memory when Grafana isn't configured. Access them via: 140 + 141 + - `http://localhost:8000/api/observability/logs` 142 + - `http://localhost:8000/api/observability/metrics` 143 + - `http://localhost:8000/api/observability/errors` 144 + 145 + ## Testing Integration 146 + 147 + Run integration tests to verify setup: 148 + 149 + ```bash 150 + cd packages/@wisp/observability 151 + bun test src/integration-test.test.ts 152 + 153 + # Test with live Grafana 154 + GRAFANA_LOKI_URL=... GRAFANA_LOKI_USERNAME=... GRAFANA_LOKI_PASSWORD=... \ 155 + bun test src/integration-test.test.ts 156 + ```
+15 -15
docs/src/styles/custom.css
··· 5 5 /* Increase base font size by 10% */ 6 6 font-size: 110%; 7 7 8 - /* Light theme - Warm beige background from app */ 9 - --sl-color-bg: oklch(0.90 0.012 35); 10 - --sl-color-bg-sidebar: oklch(0.93 0.01 35); 11 - --sl-color-bg-nav: oklch(0.93 0.01 35); 12 - --sl-color-text: oklch(0.18 0.01 30); 13 - --sl-color-text-accent: oklch(0.78 0.15 345); 14 - --sl-color-accent: oklch(0.78 0.15 345); 15 - --sl-color-accent-low: oklch(0.95 0.03 345); 16 - --sl-color-border: oklch(0.75 0.015 30); 17 - --sl-color-gray-1: oklch(0.52 0.015 30); 18 - --sl-color-gray-2: oklch(0.42 0.015 30); 19 - --sl-color-gray-3: oklch(0.33 0.015 30); 20 - --sl-color-gray-4: oklch(0.25 0.015 30); 21 - --sl-color-gray-5: oklch(0.75 0.015 30); 8 + /* Light theme - Warm beige with improved contrast */ 9 + --sl-color-bg: oklch(0.92 0.012 35); 10 + --sl-color-bg-sidebar: oklch(0.95 0.008 35); 11 + --sl-color-bg-nav: oklch(0.95 0.008 35); 12 + --sl-color-text: oklch(0.15 0.015 30); 13 + --sl-color-text-accent: oklch(0.65 0.18 345); 14 + --sl-color-accent: oklch(0.65 0.18 345); 15 + --sl-color-accent-low: oklch(0.92 0.05 345); 16 + --sl-color-border: oklch(0.65 0.02 30); 17 + --sl-color-gray-1: oklch(0.45 0.02 30); 18 + --sl-color-gray-2: oklch(0.35 0.02 30); 19 + --sl-color-gray-3: oklch(0.28 0.02 30); 20 + --sl-color-gray-4: oklch(0.20 0.015 30); 21 + --sl-color-gray-5: oklch(0.65 0.02 30); 22 22 --sl-color-bg-accent: oklch(0.88 0.01 35); 23 23 } 24 24 ··· 70 70 /* Sidebar active/hover state text contrast fix */ 71 71 .sidebar a[aria-current="page"], 72 72 .sidebar a[aria-current="page"] span { 73 - color: oklch(0.23 0.015 285) !important; 73 + color: oklch(0.15 0.015 30) !important; 74 74 } 75 75 76 76 [data-theme="dark"] .sidebar a[aria-current="page"],
-318
flake.lock
··· 1 - { 2 - "nodes": { 3 - "crane": { 4 - "flake": false, 5 - "locked": { 6 - "lastModified": 1758758545, 7 - "narHash": "sha256-NU5WaEdfwF6i8faJ2Yh+jcK9vVFrofLcwlD/mP65JrI=", 8 - "owner": "ipetkov", 9 - "repo": "crane", 10 - "rev": "95d528a5f54eaba0d12102249ce42f4d01f4e364", 11 - "type": "github" 12 - }, 13 - "original": { 14 - "owner": "ipetkov", 15 - "ref": "v0.21.1", 16 - "repo": "crane", 17 - "type": "github" 18 - } 19 - }, 20 - "dream2nix": { 21 - "inputs": { 22 - "nixpkgs": [ 23 - "nci", 24 - "nixpkgs" 25 - ], 26 - "purescript-overlay": "purescript-overlay", 27 - "pyproject-nix": "pyproject-nix" 28 - }, 29 - "locked": { 30 - "lastModified": 1754978539, 31 - "narHash": "sha256-nrDovydywSKRbWim9Ynmgj8SBm8LK3DI2WuhIqzOHYI=", 32 - "owner": "nix-community", 33 - "repo": "dream2nix", 34 - "rev": "fbec3263cb4895ac86ee9506cdc4e6919a1a2214", 35 - "type": "github" 36 - }, 37 - "original": { 38 - "owner": "nix-community", 39 - "repo": "dream2nix", 40 - "type": "github" 41 - } 42 - }, 43 - "fenix": { 44 - "inputs": { 45 - "nixpkgs": [ 46 - "nixpkgs" 47 - ], 48 - "rust-analyzer-src": "rust-analyzer-src" 49 - }, 50 - "locked": { 51 - "lastModified": 1762584108, 52 - "narHash": "sha256-wZUW7dlXMXaRdvNbaADqhF8gg9bAfFiMV+iyFQiDv+Y=", 53 - "owner": "nix-community", 54 - "repo": "fenix", 55 - "rev": "32f3ad3b6c690061173e1ac16708874975ec6056", 56 - "type": "github" 57 - }, 58 - "original": { 59 - "owner": "nix-community", 60 - "repo": "fenix", 61 - "type": "github" 62 - } 63 - }, 64 - "flake-compat": { 65 - "flake": false, 66 - "locked": { 67 - "lastModified": 1696426674, 68 - "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 69 - "owner": "edolstra", 70 - "repo": "flake-compat", 71 - "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 72 - "type": "github" 73 - }, 74 - "original": { 75 - "owner": "edolstra", 76 - "repo": "flake-compat", 77 - "type": "github" 78 - } 79 - }, 80 - "mk-naked-shell": { 81 - "flake": false, 82 - "locked": { 83 - "lastModified": 1681286841, 84 - "narHash": "sha256-3XlJrwlR0nBiREnuogoa5i1b4+w/XPe0z8bbrJASw0g=", 85 - "owner": "90-008", 86 - "repo": "mk-naked-shell", 87 - "rev": "7612f828dd6f22b7fb332cc69440e839d7ffe6bd", 88 - "type": "github" 89 - }, 90 - "original": { 91 - "owner": "90-008", 92 - "repo": "mk-naked-shell", 93 - "type": "github" 94 - } 95 - }, 96 - "nci": { 97 - "inputs": { 98 - "crane": "crane", 99 - "dream2nix": "dream2nix", 100 - "mk-naked-shell": "mk-naked-shell", 101 - "nixpkgs": [ 102 - "nixpkgs" 103 - ], 104 - "parts": "parts", 105 - "rust-overlay": "rust-overlay", 106 - "treefmt": "treefmt" 107 - }, 108 - "locked": { 109 - "lastModified": 1762582646, 110 - "narHash": "sha256-MMzE4xccG+8qbLhdaZoeFDUKWUOn3B4lhp5dZmgukmM=", 111 - "owner": "90-008", 112 - "repo": "nix-cargo-integration", 113 - "rev": "0993c449377049fa8868a664e8290ac6658e0b9a", 114 - "type": "github" 115 - }, 116 - "original": { 117 - "owner": "90-008", 118 - "repo": "nix-cargo-integration", 119 - "type": "github" 120 - } 121 - }, 122 - "nixpkgs": { 123 - "locked": { 124 - "lastModified": 1762361079, 125 - "narHash": "sha256-lz718rr1BDpZBYk7+G8cE6wee3PiBUpn8aomG/vLLiY=", 126 - "owner": "nixos", 127 - "repo": "nixpkgs", 128 - "rev": "ffcdcf99d65c61956d882df249a9be53e5902ea5", 129 - "type": "github" 130 - }, 131 - "original": { 132 - "owner": "nixos", 133 - "ref": "nixpkgs-unstable", 134 - "repo": "nixpkgs", 135 - "type": "github" 136 - } 137 - }, 138 - "parts": { 139 - "inputs": { 140 - "nixpkgs-lib": [ 141 - "nci", 142 - "nixpkgs" 143 - ] 144 - }, 145 - "locked": { 146 - "lastModified": 1762440070, 147 - "narHash": "sha256-xxdepIcb39UJ94+YydGP221rjnpkDZUlykKuF54PsqI=", 148 - "owner": "hercules-ci", 149 - "repo": "flake-parts", 150 - "rev": "26d05891e14c88eb4a5d5bee659c0db5afb609d8", 151 - "type": "github" 152 - }, 153 - "original": { 154 - "owner": "hercules-ci", 155 - "repo": "flake-parts", 156 - "type": "github" 157 - } 158 - }, 159 - "parts_2": { 160 - "inputs": { 161 - "nixpkgs-lib": [ 162 - "nixpkgs" 163 - ] 164 - }, 165 - "locked": { 166 - "lastModified": 1762440070, 167 - "narHash": "sha256-xxdepIcb39UJ94+YydGP221rjnpkDZUlykKuF54PsqI=", 168 - "owner": "hercules-ci", 169 - "repo": "flake-parts", 170 - "rev": "26d05891e14c88eb4a5d5bee659c0db5afb609d8", 171 - "type": "github" 172 - }, 173 - "original": { 174 - "owner": "hercules-ci", 175 - "repo": "flake-parts", 176 - "type": "github" 177 - } 178 - }, 179 - "purescript-overlay": { 180 - "inputs": { 181 - "flake-compat": "flake-compat", 182 - "nixpkgs": [ 183 - "nci", 184 - "dream2nix", 185 - "nixpkgs" 186 - ], 187 - "slimlock": "slimlock" 188 - }, 189 - "locked": { 190 - "lastModified": 1728546539, 191 - "narHash": "sha256-Sws7w0tlnjD+Bjck1nv29NjC5DbL6nH5auL9Ex9Iz2A=", 192 - "owner": "thomashoneyman", 193 - "repo": "purescript-overlay", 194 - "rev": "4ad4c15d07bd899d7346b331f377606631eb0ee4", 195 - "type": "github" 196 - }, 197 - "original": { 198 - "owner": "thomashoneyman", 199 - "repo": "purescript-overlay", 200 - "type": "github" 201 - } 202 - }, 203 - "pyproject-nix": { 204 - "inputs": { 205 - "nixpkgs": [ 206 - "nci", 207 - "dream2nix", 208 - "nixpkgs" 209 - ] 210 - }, 211 - "locked": { 212 - "lastModified": 1752481895, 213 - "narHash": "sha256-luVj97hIMpCbwhx3hWiRwjP2YvljWy8FM+4W9njDhLA=", 214 - "owner": "pyproject-nix", 215 - "repo": "pyproject.nix", 216 - "rev": "16ee295c25107a94e59a7fc7f2e5322851781162", 217 - "type": "github" 218 - }, 219 - "original": { 220 - "owner": "pyproject-nix", 221 - "repo": "pyproject.nix", 222 - "type": "github" 223 - } 224 - }, 225 - "root": { 226 - "inputs": { 227 - "fenix": "fenix", 228 - "nci": "nci", 229 - "nixpkgs": "nixpkgs", 230 - "parts": "parts_2" 231 - } 232 - }, 233 - "rust-analyzer-src": { 234 - "flake": false, 235 - "locked": { 236 - "lastModified": 1762438844, 237 - "narHash": "sha256-ApIKJf6CcMsV2nYBXhGF95BmZMO/QXPhgfSnkA/rVUo=", 238 - "owner": "rust-lang", 239 - "repo": "rust-analyzer", 240 - "rev": "4bf516ee5a960c1e2eee9fedd9b1c9e976a19c86", 241 - "type": "github" 242 - }, 243 - "original": { 244 - "owner": "rust-lang", 245 - "ref": "nightly", 246 - "repo": "rust-analyzer", 247 - "type": "github" 248 - } 249 - }, 250 - "rust-overlay": { 251 - "inputs": { 252 - "nixpkgs": [ 253 - "nci", 254 - "nixpkgs" 255 - ] 256 - }, 257 - "locked": { 258 - "lastModified": 1762569282, 259 - "narHash": "sha256-vINZAJpXQTZd5cfh06Rcw7hesH7sGSvi+Tn+HUieJn8=", 260 - "owner": "oxalica", 261 - "repo": "rust-overlay", 262 - "rev": "a35a6144b976f70827c2fe2f5c89d16d8f9179d8", 263 - "type": "github" 264 - }, 265 - "original": { 266 - "owner": "oxalica", 267 - "repo": "rust-overlay", 268 - "type": "github" 269 - } 270 - }, 271 - "slimlock": { 272 - "inputs": { 273 - "nixpkgs": [ 274 - "nci", 275 - "dream2nix", 276 - "purescript-overlay", 277 - "nixpkgs" 278 - ] 279 - }, 280 - "locked": { 281 - "lastModified": 1688756706, 282 - "narHash": "sha256-xzkkMv3neJJJ89zo3o2ojp7nFeaZc2G0fYwNXNJRFlo=", 283 - "owner": "thomashoneyman", 284 - "repo": "slimlock", 285 - "rev": "cf72723f59e2340d24881fd7bf61cb113b4c407c", 286 - "type": "github" 287 - }, 288 - "original": { 289 - "owner": "thomashoneyman", 290 - "repo": "slimlock", 291 - "type": "github" 292 - } 293 - }, 294 - "treefmt": { 295 - "inputs": { 296 - "nixpkgs": [ 297 - "nci", 298 - "nixpkgs" 299 - ] 300 - }, 301 - "locked": { 302 - "lastModified": 1762410071, 303 - "narHash": "sha256-aF5fvoZeoXNPxT0bejFUBXeUjXfHLSL7g+mjR/p5TEg=", 304 - "owner": "numtide", 305 - "repo": "treefmt-nix", 306 - "rev": "97a30861b13c3731a84e09405414398fbf3e109f", 307 - "type": "github" 308 - }, 309 - "original": { 310 - "owner": "numtide", 311 - "repo": "treefmt-nix", 312 - "type": "github" 313 - } 314 - } 315 - }, 316 - "root": "root", 317 - "version": 7 318 - }
-59
flake.nix
··· 1 - { 2 - inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 3 - inputs.nci.url = "github:90-008/nix-cargo-integration"; 4 - inputs.nci.inputs.nixpkgs.follows = "nixpkgs"; 5 - inputs.parts.url = "github:hercules-ci/flake-parts"; 6 - inputs.parts.inputs.nixpkgs-lib.follows = "nixpkgs"; 7 - inputs.fenix = { 8 - url = "github:nix-community/fenix"; 9 - inputs.nixpkgs.follows = "nixpkgs"; 10 - }; 11 - 12 - outputs = inputs @ { 13 - parts, 14 - nci, 15 - ... 16 - }: 17 - parts.lib.mkFlake {inherit inputs;} { 18 - systems = ["x86_64-linux" "aarch64-darwin"]; 19 - imports = [ 20 - nci.flakeModule 21 - ./crates.nix 22 - ]; 23 - perSystem = { 24 - pkgs, 25 - config, 26 - ... 27 - }: let 28 - crateOutputs = config.nci.outputs."wisp-cli"; 29 - mkRenamedPackage = name: pkg: isWindows: pkgs.runCommand name {} '' 30 - mkdir -p $out/bin 31 - if [ -f ${pkg}/bin/wisp-cli.exe ]; then 32 - cp ${pkg}/bin/wisp-cli.exe $out/bin/${name} 33 - elif [ -f ${pkg}/bin/wisp-cli ]; then 34 - cp ${pkg}/bin/wisp-cli $out/bin/${name} 35 - else 36 - echo "Error: Could not find wisp-cli binary in ${pkg}/bin/" 37 - ls -la ${pkg}/bin/ || true 38 - exit 1 39 - fi 40 - ''; 41 - in { 42 - devShells.default = crateOutputs.devShell; 43 - packages.default = crateOutputs.packages.release; 44 - packages.wisp-cli-x86_64-linux = mkRenamedPackage "wisp-cli-x86_64-linux" crateOutputs.packages.release false; 45 - packages.wisp-cli-aarch64-linux = mkRenamedPackage "wisp-cli-aarch64-linux" crateOutputs.allTargets."aarch64-unknown-linux-gnu".packages.release false; 46 - packages.wisp-cli-x86_64-windows = mkRenamedPackage "wisp-cli-x86_64-windows.exe" crateOutputs.allTargets."x86_64-pc-windows-gnu".packages.release true; 47 - packages.wisp-cli-aarch64-darwin = mkRenamedPackage "wisp-cli-aarch64-darwin" crateOutputs.allTargets."aarch64-apple-darwin".packages.release false; 48 - packages.all = pkgs.symlinkJoin { 49 - name = "wisp-cli-all"; 50 - paths = [ 51 - config.packages.wisp-cli-x86_64-linux 52 - config.packages.wisp-cli-aarch64-linux 53 - config.packages.wisp-cli-x86_64-windows 54 - config.packages.wisp-cli-aarch64-darwin 55 - ]; 56 - }; 57 - }; 58 - }; 59 - }
+59
lexicons/fs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "place.wisp.fs", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "Virtual filesystem manifest for a Wisp site", 8 + "record": { 9 + "type": "object", 10 + "required": ["site", "root", "createdAt"], 11 + "properties": { 12 + "site": { "type": "string" }, 13 + "root": { "type": "ref", "ref": "#directory" }, 14 + "fileCount": { "type": "integer", "minimum": 0, "maximum": 1000 }, 15 + "createdAt": { "type": "string", "format": "datetime" } 16 + } 17 + } 18 + }, 19 + "file": { 20 + "type": "object", 21 + "required": ["type", "blob"], 22 + "properties": { 23 + "type": { "type": "string", "const": "file" }, 24 + "blob": { "type": "blob", "accept": ["*/*"], "maxSize": 1000000000, "description": "Content blob ref" }, 25 + "encoding": { "type": "string", "enum": ["gzip"], "description": "Content encoding (e.g., gzip for compressed files)" }, 26 + "mimeType": { "type": "string", "description": "Original MIME type before compression" }, 27 + "base64": { "type": "boolean", "description": "True if blob content is base64-encoded (used to bypass PDS content sniffing)" } } 28 + }, 29 + "directory": { 30 + "type": "object", 31 + "required": ["type", "entries"], 32 + "properties": { 33 + "type": { "type": "string", "const": "directory" }, 34 + "entries": { 35 + "type": "array", 36 + "maxLength": 500, 37 + "items": { "type": "ref", "ref": "#entry" } 38 + } 39 + } 40 + }, 41 + "entry": { 42 + "type": "object", 43 + "required": ["name", "node"], 44 + "properties": { 45 + "name": { "type": "string", "maxLength": 255 }, 46 + "node": { "type": "union", "refs": ["#file", "#directory", "#subfs"] } 47 + } 48 + }, 49 + "subfs": { 50 + "type": "object", 51 + "required": ["type", "subject"], 52 + "properties": { 53 + "type": { "type": "string", "const": "subfs" }, 54 + "subject": { "type": "string", "format": "at-uri", "description": "AT-URI pointing to a place.wisp.subfs record containing this subtree." }, 55 + "flat": { "type": "boolean", "description": "If true (default), the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false, the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure." } 56 + } 57 + } 58 + } 59 + }
+76
lexicons/settings.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "place.wisp.settings", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "Configuration settings for a static site hosted on wisp.place", 8 + "key": "any", 9 + "record": { 10 + "type": "object", 11 + "properties": { 12 + "directoryListing": { 13 + "type": "boolean", 14 + "description": "Enable directory listing mode for paths that resolve to directories without an index file. Incompatible with spaMode.", 15 + "default": false 16 + }, 17 + "spaMode": { 18 + "type": "string", 19 + "description": "File to serve for all routes (e.g., 'index.html'). When set, enables SPA mode where all non-file requests are routed to this file. Incompatible with directoryListing and custom404.", 20 + "maxLength": 500 21 + }, 22 + "custom404": { 23 + "type": "string", 24 + "description": "Custom 404 error page file path. Incompatible with directoryListing and spaMode.", 25 + "maxLength": 500 26 + }, 27 + "indexFiles": { 28 + "type": "array", 29 + "description": "Ordered list of files to try when serving a directory. Defaults to ['index.html'] if not specified.", 30 + "items": { 31 + "type": "string", 32 + "maxLength": 255 33 + }, 34 + "maxLength": 10 35 + }, 36 + "cleanUrls": { 37 + "type": "boolean", 38 + "description": "Enable clean URL routing. When enabled, '/about' will attempt to serve '/about.html' or '/about/index.html' automatically.", 39 + "default": false 40 + }, 41 + "headers": { 42 + "type": "array", 43 + "description": "Custom HTTP headers to set on responses", 44 + "items": { 45 + "type": "ref", 46 + "ref": "#customHeader" 47 + }, 48 + "maxLength": 50 49 + } 50 + } 51 + } 52 + }, 53 + "customHeader": { 54 + "type": "object", 55 + "description": "Custom HTTP header configuration", 56 + "required": ["name", "value"], 57 + "properties": { 58 + "name": { 59 + "type": "string", 60 + "description": "HTTP header name (e.g., 'Cache-Control', 'X-Frame-Options')", 61 + "maxLength": 100 62 + }, 63 + "value": { 64 + "type": "string", 65 + "description": "HTTP header value", 66 + "maxLength": 1000 67 + }, 68 + "path": { 69 + "type": "string", 70 + "description": "Optional glob pattern to apply this header to specific paths (e.g., '*.html', '/assets/*'). If not specified, applies to all paths.", 71 + "maxLength": 500 72 + } 73 + } 74 + } 75 + } 76 + }
+59
lexicons/subfs.json
··· 1 + { 2 + "lexicon": 1, 3 + "id": "place.wisp.subfs", 4 + "defs": { 5 + "main": { 6 + "type": "record", 7 + "description": "Virtual filesystem subtree referenced by place.wisp.fs records. When a subfs entry is expanded, its root entries are merged (flattened) into the parent directory, allowing large directories to be split across multiple records while maintaining a flat structure.", 8 + "record": { 9 + "type": "object", 10 + "required": ["root", "createdAt"], 11 + "properties": { 12 + "root": { "type": "ref", "ref": "#directory" }, 13 + "fileCount": { "type": "integer", "minimum": 0, "maximum": 1000 }, 14 + "createdAt": { "type": "string", "format": "datetime" } 15 + } 16 + } 17 + }, 18 + "file": { 19 + "type": "object", 20 + "required": ["type", "blob"], 21 + "properties": { 22 + "type": { "type": "string", "const": "file" }, 23 + "blob": { "type": "blob", "accept": ["*/*"], "maxSize": 1000000000, "description": "Content blob ref" }, 24 + "encoding": { "type": "string", "enum": ["gzip"], "description": "Content encoding (e.g., gzip for compressed files)" }, 25 + "mimeType": { "type": "string", "description": "Original MIME type before compression" }, 26 + "base64": { "type": "boolean", "description": "True if blob content is base64-encoded (used to bypass PDS content sniffing)" } 27 + } 28 + }, 29 + "directory": { 30 + "type": "object", 31 + "required": ["type", "entries"], 32 + "properties": { 33 + "type": { "type": "string", "const": "directory" }, 34 + "entries": { 35 + "type": "array", 36 + "maxLength": 500, 37 + "items": { "type": "ref", "ref": "#entry" } 38 + } 39 + } 40 + }, 41 + "entry": { 42 + "type": "object", 43 + "required": ["name", "node"], 44 + "properties": { 45 + "name": { "type": "string", "maxLength": 255 }, 46 + "node": { "type": "union", "refs": ["#file", "#directory", "#subfs"] } 47 + } 48 + }, 49 + "subfs": { 50 + "type": "object", 51 + "required": ["type", "subject"], 52 + "properties": { 53 + "type": { "type": "string", "const": "subfs" }, 54 + "subject": { "type": "string", "format": "at-uri", "description": "AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures." } 55 + } 56 + } 57 + } 58 + } 59 +
+7 -2
package.json
··· 11 11 "@tailwindcss/cli": "^4.1.17", 12 12 "atproto-ui": "^0.12.0", 13 13 "bun-plugin-tailwind": "^0.1.2", 14 + "elysia": "^1.4.18", 14 15 "tailwindcss": "^4.1.17" 15 16 }, 16 17 "scripts": { ··· 23 24 "check": "cd apps/main-app && npm run check && cd ../hosting-service && npm run check", 24 25 "screenshot": "bun run apps/main-app/scripts/screenshot-sites.ts", 25 26 "hosting:dev": "cd apps/hosting-service && npm run dev", 26 - "hosting:start": "cd apps/hosting-service && npm run start" 27 + "hosting:start": "cd apps/hosting-service && npm run start", 28 + "codegen": "./scripts/codegen.sh" 27 29 }, 28 30 "trustedDependencies": [ 29 31 "@parcel/watcher", 30 32 "bun", 31 33 "esbuild" 32 - ] 34 + ], 35 + "devDependencies": { 36 + "@types/bun": "^1.3.5" 37 + } 33 38 }
+244
packages/@wisp/fs-utils/src/tree.test.ts
··· 1 + import { describe, test, expect } from 'bun:test' 2 + import { processUploadedFiles, type UploadedFile } from './tree' 3 + 4 + describe('processUploadedFiles', () => { 5 + test('should preserve nested directory structure', () => { 6 + const files: UploadedFile[] = [ 7 + { 8 + name: 'mysite/index.html', 9 + content: Buffer.from('<html>'), 10 + mimeType: 'text/html', 11 + size: 6 12 + }, 13 + { 14 + name: 'mysite/_astro/main.js', 15 + content: Buffer.from('console.log()'), 16 + mimeType: 'application/javascript', 17 + size: 13 18 + }, 19 + { 20 + name: 'mysite/_astro/styles.css', 21 + content: Buffer.from('body {}'), 22 + mimeType: 'text/css', 23 + size: 7 24 + }, 25 + { 26 + name: 'mysite/images/logo.png', 27 + content: Buffer.from([0x89, 0x50, 0x4e, 0x47]), 28 + mimeType: 'image/png', 29 + size: 4 30 + } 31 + ] 32 + 33 + const result = processUploadedFiles(files) 34 + 35 + expect(result.fileCount).toBe(4) 36 + expect(result.directory.entries).toHaveLength(3) // index.html, _astro/, images/ 37 + 38 + // Check _astro directory exists 39 + const astroEntry = result.directory.entries.find(e => e.name === '_astro') 40 + expect(astroEntry).toBeTruthy() 41 + expect('type' in astroEntry!.node && astroEntry!.node.type).toBe('directory') 42 + 43 + if ('entries' in astroEntry!.node) { 44 + const astroDir = astroEntry!.node 45 + expect(astroDir.entries).toHaveLength(2) // main.js, styles.css 46 + expect(astroDir.entries.find(e => e.name === 'main.js')).toBeTruthy() 47 + expect(astroDir.entries.find(e => e.name === 'styles.css')).toBeTruthy() 48 + } 49 + 50 + // Check images directory exists 51 + const imagesEntry = result.directory.entries.find(e => e.name === 'images') 52 + expect(imagesEntry).toBeTruthy() 53 + expect('type' in imagesEntry!.node && imagesEntry!.node.type).toBe('directory') 54 + 55 + if ('entries' in imagesEntry!.node) { 56 + const imagesDir = imagesEntry!.node 57 + expect(imagesDir.entries).toHaveLength(1) // logo.png 58 + expect(imagesDir.entries.find(e => e.name === 'logo.png')).toBeTruthy() 59 + } 60 + }) 61 + 62 + test('should handle deeply nested directories', () => { 63 + const files: UploadedFile[] = [ 64 + { 65 + name: 'site/a/b/c/d/deep.txt', 66 + content: Buffer.from('deep'), 67 + mimeType: 'text/plain', 68 + size: 4 69 + } 70 + ] 71 + 72 + const result = processUploadedFiles(files) 73 + 74 + expect(result.fileCount).toBe(1) 75 + 76 + // Navigate through nested structure 77 + const aEntry = result.directory.entries.find(e => e.name === 'a') 78 + expect(aEntry).toBeTruthy() 79 + expect('type' in aEntry!.node && aEntry!.node.type).toBe('directory') 80 + 81 + if ('entries' in aEntry!.node) { 82 + const bEntry = aEntry!.node.entries.find(e => e.name === 'b') 83 + expect(bEntry).toBeTruthy() 84 + expect('type' in bEntry!.node && bEntry!.node.type).toBe('directory') 85 + 86 + if ('entries' in bEntry!.node) { 87 + const cEntry = bEntry!.node.entries.find(e => e.name === 'c') 88 + expect(cEntry).toBeTruthy() 89 + expect('type' in cEntry!.node && cEntry!.node.type).toBe('directory') 90 + 91 + if ('entries' in cEntry!.node) { 92 + const dEntry = cEntry!.node.entries.find(e => e.name === 'd') 93 + expect(dEntry).toBeTruthy() 94 + expect('type' in dEntry!.node && dEntry!.node.type).toBe('directory') 95 + 96 + if ('entries' in dEntry!.node) { 97 + const fileEntry = dEntry!.node.entries.find(e => e.name === 'deep.txt') 98 + expect(fileEntry).toBeTruthy() 99 + expect('type' in fileEntry!.node && fileEntry!.node.type).toBe('file') 100 + } 101 + } 102 + } 103 + } 104 + }) 105 + 106 + test('should handle files at root level', () => { 107 + const files: UploadedFile[] = [ 108 + { 109 + name: 'mysite/index.html', 110 + content: Buffer.from('<html>'), 111 + mimeType: 'text/html', 112 + size: 6 113 + }, 114 + { 115 + name: 'mysite/robots.txt', 116 + content: Buffer.from('User-agent: *'), 117 + mimeType: 'text/plain', 118 + size: 13 119 + } 120 + ] 121 + 122 + const result = processUploadedFiles(files) 123 + 124 + expect(result.fileCount).toBe(2) 125 + expect(result.directory.entries).toHaveLength(2) 126 + expect(result.directory.entries.find(e => e.name === 'index.html')).toBeTruthy() 127 + expect(result.directory.entries.find(e => e.name === 'robots.txt')).toBeTruthy() 128 + }) 129 + 130 + test('should skip .git directories', () => { 131 + const files: UploadedFile[] = [ 132 + { 133 + name: 'mysite/index.html', 134 + content: Buffer.from('<html>'), 135 + mimeType: 'text/html', 136 + size: 6 137 + }, 138 + { 139 + name: 'mysite/.git/config', 140 + content: Buffer.from('[core]'), 141 + mimeType: 'text/plain', 142 + size: 6 143 + }, 144 + { 145 + name: 'mysite/.gitignore', 146 + content: Buffer.from('node_modules'), 147 + mimeType: 'text/plain', 148 + size: 12 149 + } 150 + ] 151 + 152 + const result = processUploadedFiles(files) 153 + 154 + expect(result.fileCount).toBe(2) // Only index.html and .gitignore 155 + expect(result.directory.entries).toHaveLength(2) 156 + expect(result.directory.entries.find(e => e.name === 'index.html')).toBeTruthy() 157 + expect(result.directory.entries.find(e => e.name === '.gitignore')).toBeTruthy() 158 + expect(result.directory.entries.find(e => e.name === '.git')).toBeFalsy() 159 + }) 160 + 161 + test('should handle mixed root and nested files', () => { 162 + const files: UploadedFile[] = [ 163 + { 164 + name: 'mysite/index.html', 165 + content: Buffer.from('<html>'), 166 + mimeType: 'text/html', 167 + size: 6 168 + }, 169 + { 170 + name: 'mysite/about/index.html', 171 + content: Buffer.from('<html>'), 172 + mimeType: 'text/html', 173 + size: 6 174 + }, 175 + { 176 + name: 'mysite/about/team.html', 177 + content: Buffer.from('<html>'), 178 + mimeType: 'text/html', 179 + size: 6 180 + }, 181 + { 182 + name: 'mysite/robots.txt', 183 + content: Buffer.from('User-agent: *'), 184 + mimeType: 'text/plain', 185 + size: 13 186 + } 187 + ] 188 + 189 + const result = processUploadedFiles(files) 190 + 191 + expect(result.fileCount).toBe(4) 192 + expect(result.directory.entries).toHaveLength(3) // index.html, about/, robots.txt 193 + 194 + const aboutEntry = result.directory.entries.find(e => e.name === 'about') 195 + expect(aboutEntry).toBeTruthy() 196 + expect('type' in aboutEntry!.node && aboutEntry!.node.type).toBe('directory') 197 + 198 + if ('entries' in aboutEntry!.node) { 199 + const aboutDir = aboutEntry!.node 200 + expect(aboutDir.entries).toHaveLength(2) // index.html, team.html 201 + expect(aboutDir.entries.find(e => e.name === 'index.html')).toBeTruthy() 202 + expect(aboutDir.entries.find(e => e.name === 'team.html')).toBeTruthy() 203 + } 204 + }) 205 + 206 + test('should handle empty file array', () => { 207 + const files: UploadedFile[] = [] 208 + 209 + const result = processUploadedFiles(files) 210 + 211 + expect(result.fileCount).toBe(0) 212 + expect(result.directory.entries).toHaveLength(0) 213 + }) 214 + 215 + test('should strip base folder name from paths', () => { 216 + // This tests the behavior where file.name includes the base folder 217 + // e.g., "mysite/index.html" should become "index.html" at root 218 + const files: UploadedFile[] = [ 219 + { 220 + name: 'build-output/index.html', 221 + content: Buffer.from('<html>'), 222 + mimeType: 'text/html', 223 + size: 6 224 + }, 225 + { 226 + name: 'build-output/assets/main.js', 227 + content: Buffer.from('console.log()'), 228 + mimeType: 'application/javascript', 229 + size: 13 230 + } 231 + ] 232 + 233 + const result = processUploadedFiles(files) 234 + 235 + expect(result.fileCount).toBe(2) 236 + 237 + // Should have index.html at root and assets/ directory 238 + expect(result.directory.entries.find(e => e.name === 'index.html')).toBeTruthy() 239 + expect(result.directory.entries.find(e => e.name === 'assets')).toBeTruthy() 240 + 241 + // Should NOT have 'build-output' directory 242 + expect(result.directory.entries.find(e => e.name === 'build-output')).toBeFalsy() 243 + }) 244 + })
-59
packages/@wisp/lexicons/lexicons/fs.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "place.wisp.fs", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "description": "Virtual filesystem manifest for a Wisp site", 8 - "record": { 9 - "type": "object", 10 - "required": ["site", "root", "createdAt"], 11 - "properties": { 12 - "site": { "type": "string" }, 13 - "root": { "type": "ref", "ref": "#directory" }, 14 - "fileCount": { "type": "integer", "minimum": 0, "maximum": 1000 }, 15 - "createdAt": { "type": "string", "format": "datetime" } 16 - } 17 - } 18 - }, 19 - "file": { 20 - "type": "object", 21 - "required": ["type", "blob"], 22 - "properties": { 23 - "type": { "type": "string", "const": "file" }, 24 - "blob": { "type": "blob", "accept": ["*/*"], "maxSize": 1000000000, "description": "Content blob ref" }, 25 - "encoding": { "type": "string", "enum": ["gzip"], "description": "Content encoding (e.g., gzip for compressed files)" }, 26 - "mimeType": { "type": "string", "description": "Original MIME type before compression" }, 27 - "base64": { "type": "boolean", "description": "True if blob content is base64-encoded (used to bypass PDS content sniffing)" } } 28 - }, 29 - "directory": { 30 - "type": "object", 31 - "required": ["type", "entries"], 32 - "properties": { 33 - "type": { "type": "string", "const": "directory" }, 34 - "entries": { 35 - "type": "array", 36 - "maxLength": 500, 37 - "items": { "type": "ref", "ref": "#entry" } 38 - } 39 - } 40 - }, 41 - "entry": { 42 - "type": "object", 43 - "required": ["name", "node"], 44 - "properties": { 45 - "name": { "type": "string", "maxLength": 255 }, 46 - "node": { "type": "union", "refs": ["#file", "#directory", "#subfs"] } 47 - } 48 - }, 49 - "subfs": { 50 - "type": "object", 51 - "required": ["type", "subject"], 52 - "properties": { 53 - "type": { "type": "string", "const": "subfs" }, 54 - "subject": { "type": "string", "format": "at-uri", "description": "AT-URI pointing to a place.wisp.subfs record containing this subtree." }, 55 - "flat": { "type": "boolean", "description": "If true (default), the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false, the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure." } 56 - } 57 - } 58 - } 59 - }
-76
packages/@wisp/lexicons/lexicons/settings.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "place.wisp.settings", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "description": "Configuration settings for a static site hosted on wisp.place", 8 - "key": "any", 9 - "record": { 10 - "type": "object", 11 - "properties": { 12 - "directoryListing": { 13 - "type": "boolean", 14 - "description": "Enable directory listing mode for paths that resolve to directories without an index file. Incompatible with spaMode.", 15 - "default": false 16 - }, 17 - "spaMode": { 18 - "type": "string", 19 - "description": "File to serve for all routes (e.g., 'index.html'). When set, enables SPA mode where all non-file requests are routed to this file. Incompatible with directoryListing and custom404.", 20 - "maxLength": 500 21 - }, 22 - "custom404": { 23 - "type": "string", 24 - "description": "Custom 404 error page file path. Incompatible with directoryListing and spaMode.", 25 - "maxLength": 500 26 - }, 27 - "indexFiles": { 28 - "type": "array", 29 - "description": "Ordered list of files to try when serving a directory. Defaults to ['index.html'] if not specified.", 30 - "items": { 31 - "type": "string", 32 - "maxLength": 255 33 - }, 34 - "maxLength": 10 35 - }, 36 - "cleanUrls": { 37 - "type": "boolean", 38 - "description": "Enable clean URL routing. When enabled, '/about' will attempt to serve '/about.html' or '/about/index.html' automatically.", 39 - "default": false 40 - }, 41 - "headers": { 42 - "type": "array", 43 - "description": "Custom HTTP headers to set on responses", 44 - "items": { 45 - "type": "ref", 46 - "ref": "#customHeader" 47 - }, 48 - "maxLength": 50 49 - } 50 - } 51 - } 52 - }, 53 - "customHeader": { 54 - "type": "object", 55 - "description": "Custom HTTP header configuration", 56 - "required": ["name", "value"], 57 - "properties": { 58 - "name": { 59 - "type": "string", 60 - "description": "HTTP header name (e.g., 'Cache-Control', 'X-Frame-Options')", 61 - "maxLength": 100 62 - }, 63 - "value": { 64 - "type": "string", 65 - "description": "HTTP header value", 66 - "maxLength": 1000 67 - }, 68 - "path": { 69 - "type": "string", 70 - "description": "Optional glob pattern to apply this header to specific paths (e.g., '*.html', '/assets/*'). If not specified, applies to all paths.", 71 - "maxLength": 500 72 - } 73 - } 74 - } 75 - } 76 - }
-59
packages/@wisp/lexicons/lexicons/subfs.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "place.wisp.subfs", 4 - "defs": { 5 - "main": { 6 - "type": "record", 7 - "description": "Virtual filesystem subtree referenced by place.wisp.fs records. When a subfs entry is expanded, its root entries are merged (flattened) into the parent directory, allowing large directories to be split across multiple records while maintaining a flat structure.", 8 - "record": { 9 - "type": "object", 10 - "required": ["root", "createdAt"], 11 - "properties": { 12 - "root": { "type": "ref", "ref": "#directory" }, 13 - "fileCount": { "type": "integer", "minimum": 0, "maximum": 1000 }, 14 - "createdAt": { "type": "string", "format": "datetime" } 15 - } 16 - } 17 - }, 18 - "file": { 19 - "type": "object", 20 - "required": ["type", "blob"], 21 - "properties": { 22 - "type": { "type": "string", "const": "file" }, 23 - "blob": { "type": "blob", "accept": ["*/*"], "maxSize": 1000000000, "description": "Content blob ref" }, 24 - "encoding": { "type": "string", "enum": ["gzip"], "description": "Content encoding (e.g., gzip for compressed files)" }, 25 - "mimeType": { "type": "string", "description": "Original MIME type before compression" }, 26 - "base64": { "type": "boolean", "description": "True if blob content is base64-encoded (used to bypass PDS content sniffing)" } 27 - } 28 - }, 29 - "directory": { 30 - "type": "object", 31 - "required": ["type", "entries"], 32 - "properties": { 33 - "type": { "type": "string", "const": "directory" }, 34 - "entries": { 35 - "type": "array", 36 - "maxLength": 500, 37 - "items": { "type": "ref", "ref": "#entry" } 38 - } 39 - } 40 - }, 41 - "entry": { 42 - "type": "object", 43 - "required": ["name", "node"], 44 - "properties": { 45 - "name": { "type": "string", "maxLength": 255 }, 46 - "node": { "type": "union", "refs": ["#file", "#directory", "#subfs"] } 47 - } 48 - }, 49 - "subfs": { 50 - "type": "object", 51 - "required": ["type", "subject"], 52 - "properties": { 53 - "type": { "type": "string", "const": "subfs" }, 54 - "subject": { "type": "string", "format": "at-uri", "description": "AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures." } 55 - } 56 - } 57 - } 58 - } 59 -
+1 -1
packages/@wisp/lexicons/package.json
··· 32 32 } 33 33 }, 34 34 "scripts": { 35 - "codegen": "lex gen-server ./src ./lexicons" 35 + "codegen": "lex gen-server ./src ../../../lexicons/*.json" 36 36 }, 37 37 "dependencies": { 38 38 "@atproto/lexicon": "^0.5.1",
+1 -1
packages/@wisp/lexicons/src/index.ts
··· 9 9 type MethodConfigOrHandler, 10 10 createServer as createXrpcServer, 11 11 } from '@atproto/xrpc-server' 12 - import { schemas } from './lexicons' 12 + import { schemas } from './lexicons.js' 13 13 14 14 export function createServer(options?: XrpcOptions): Server { 15 15 return new Server(options)
+1 -1
packages/@wisp/lexicons/src/lexicons.ts
··· 7 7 ValidationError, 8 8 type ValidationResult, 9 9 } from '@atproto/lexicon' 10 - import { type $Typed, is$typed, maybe$typed } from './util' 10 + import { type $Typed, is$typed, maybe$typed } from './util.js' 11 11 12 12 export const schemaDict = { 13 13 PlaceWispFs: {
+33
packages/@wisp/observability/.env.example
··· 1 + # Grafana Cloud Configuration for @wisp/observability 2 + # Copy this file to .env and fill in your actual values 3 + 4 + # ============================================================================ 5 + # Grafana Loki (for logs) 6 + # ============================================================================ 7 + GRAFANA_LOKI_URL=https://logs-prod-xxx.grafana.net 8 + 9 + # Authentication Option 1: Bearer Token (Grafana Cloud) 10 + GRAFANA_LOKI_TOKEN=glc_xxx 11 + 12 + # Authentication Option 2: Username/Password (Self-hosted or some Grafana setups) 13 + # GRAFANA_LOKI_USERNAME=your-username 14 + # GRAFANA_LOKI_PASSWORD=your-password 15 + 16 + # ============================================================================ 17 + # Grafana Prometheus (for metrics) 18 + # ============================================================================ 19 + # Note: Add /api/prom to the base URL for OTLP export 20 + GRAFANA_PROMETHEUS_URL=https://prometheus-prod-xxx.grafana.net/api/prom 21 + 22 + # Authentication Option 1: Bearer Token (Grafana Cloud) 23 + GRAFANA_PROMETHEUS_TOKEN=glc_xxx 24 + 25 + # Authentication Option 2: Username/Password (Self-hosted or some Grafana setups) 26 + # GRAFANA_PROMETHEUS_USERNAME=your-username 27 + # GRAFANA_PROMETHEUS_PASSWORD=your-password 28 + 29 + # ============================================================================ 30 + # Optional: Override service metadata 31 + # ============================================================================ 32 + # SERVICE_NAME=wisp-app 33 + # SERVICE_VERSION=1.0.0
+217
packages/@wisp/observability/README.md
··· 1 + # @wisp/observability 2 + 3 + Framework-agnostic observability package with Grafana integration for logs and metrics persistence. 4 + 5 + ## Features 6 + 7 + - **In-memory storage** for local development 8 + - **Grafana Loki** integration for log persistence 9 + - **Prometheus/OTLP** integration for metrics 10 + - Framework middleware for Elysia and Hono 11 + - Automatic batching and buffering for efficient data transmission 12 + 13 + ## Installation 14 + 15 + ```bash 16 + bun add @wisp/observability 17 + ``` 18 + 19 + ## Basic Usage 20 + 21 + ### Without Grafana (In-Memory Only) 22 + 23 + ```typescript 24 + import { createLogger, metricsCollector } from '@wisp/observability' 25 + 26 + const logger = createLogger('my-service') 27 + 28 + // Log messages 29 + logger.info('Server started') 30 + logger.error('Failed to connect', new Error('Connection refused')) 31 + 32 + // Record metrics 33 + metricsCollector.recordRequest('/api/users', 'GET', 200, 45, 'my-service') 34 + ``` 35 + 36 + ### With Grafana Integration 37 + 38 + ```typescript 39 + import { initializeGrafanaExporters, createLogger } from '@wisp/observability' 40 + 41 + // Initialize at application startup 42 + initializeGrafanaExporters({ 43 + lokiUrl: 'https://logs-prod.grafana.net', 44 + lokiAuth: { 45 + bearerToken: 'your-loki-api-key' 46 + }, 47 + prometheusUrl: 'https://prometheus-prod.grafana.net', 48 + prometheusAuth: { 49 + bearerToken: 'your-prometheus-api-key' 50 + }, 51 + serviceName: 'wisp-app', 52 + serviceVersion: '1.0.0', 53 + batchSize: 100, 54 + flushIntervalMs: 5000 55 + }) 56 + 57 + // Now all logs and metrics will be sent to Grafana automatically 58 + const logger = createLogger('my-service') 59 + logger.info('This will be sent to Grafana Loki') 60 + ``` 61 + 62 + ## Configuration 63 + 64 + ### Environment Variables 65 + 66 + You can configure Grafana integration using environment variables: 67 + 68 + ```bash 69 + # Loki configuration 70 + GRAFANA_LOKI_URL=https://logs-prod.grafana.net 71 + 72 + # Authentication Option 1: Bearer Token (Grafana Cloud) 73 + GRAFANA_LOKI_TOKEN=your-loki-api-key 74 + 75 + # Authentication Option 2: Username/Password (Self-hosted or some Grafana setups) 76 + GRAFANA_LOKI_USERNAME=your-username 77 + GRAFANA_LOKI_PASSWORD=your-password 78 + 79 + # Prometheus configuration 80 + GRAFANA_PROMETHEUS_URL=https://prometheus-prod.grafana.net/api/prom 81 + 82 + # Authentication Option 1: Bearer Token (Grafana Cloud) 83 + GRAFANA_PROMETHEUS_TOKEN=your-prometheus-api-key 84 + 85 + # Authentication Option 2: Username/Password (Self-hosted or some Grafana setups) 86 + GRAFANA_PROMETHEUS_USERNAME=your-username 87 + GRAFANA_PROMETHEUS_PASSWORD=your-password 88 + ``` 89 + 90 + ### Programmatic Configuration 91 + 92 + ```typescript 93 + import { initializeGrafanaExporters } from '@wisp/observability' 94 + 95 + initializeGrafanaExporters({ 96 + // Loki configuration for logs 97 + lokiUrl: 'https://logs-prod.grafana.net', 98 + lokiAuth: { 99 + // Option 1: Bearer token (recommended for Grafana Cloud) 100 + bearerToken: 'your-api-key', 101 + 102 + // Option 2: Basic auth 103 + username: 'your-username', 104 + password: 'your-password' 105 + }, 106 + 107 + // Prometheus/OTLP configuration for metrics 108 + prometheusUrl: 'https://prometheus-prod.grafana.net', 109 + prometheusAuth: { 110 + bearerToken: 'your-api-key' 111 + }, 112 + 113 + // Service metadata 114 + serviceName: 'wisp-app', 115 + serviceVersion: '1.0.0', 116 + 117 + // Batching configuration 118 + batchSize: 100, // Flush after this many entries 119 + flushIntervalMs: 5000, // Flush every 5 seconds 120 + 121 + // Enable/disable exporters 122 + enabled: true 123 + }) 124 + ``` 125 + 126 + ## Middleware Integration 127 + 128 + ### Elysia 129 + 130 + ```typescript 131 + import { Elysia } from 'elysia' 132 + import { observabilityMiddleware } from '@wisp/observability/middleware/elysia' 133 + import { initializeGrafanaExporters } from '@wisp/observability' 134 + 135 + // Initialize Grafana exporters 136 + initializeGrafanaExporters({ 137 + lokiUrl: process.env.GRAFANA_LOKI_URL, 138 + lokiAuth: { bearerToken: process.env.GRAFANA_LOKI_TOKEN } 139 + }) 140 + 141 + const app = new Elysia() 142 + .use(observabilityMiddleware({ service: 'main-app' })) 143 + .get('/', () => 'Hello World') 144 + .listen(3000) 145 + ``` 146 + 147 + ### Hono 148 + 149 + ```typescript 150 + import { Hono } from 'hono' 151 + import { observabilityMiddleware, observabilityErrorHandler } from '@wisp/observability/middleware/hono' 152 + import { initializeGrafanaExporters } from '@wisp/observability' 153 + 154 + // Initialize Grafana exporters 155 + initializeGrafanaExporters({ 156 + lokiUrl: process.env.GRAFANA_LOKI_URL, 157 + lokiAuth: { bearerToken: process.env.GRAFANA_LOKI_TOKEN } 158 + }) 159 + 160 + const app = new Hono() 161 + app.use('*', observabilityMiddleware({ service: 'hosting-service' })) 162 + app.onError(observabilityErrorHandler({ service: 'hosting-service' })) 163 + ``` 164 + 165 + ## Grafana Cloud Setup 166 + 167 + 1. **Create a Grafana Cloud account** at https://grafana.com/ 168 + 169 + 2. **Get your Loki credentials:** 170 + - Go to your Grafana Cloud portal 171 + - Navigate to "Loki" โ†’ "Details" 172 + - Copy the Push endpoint URL and create an API key 173 + 174 + 3. **Get your Prometheus credentials:** 175 + - Navigate to "Prometheus" โ†’ "Details" 176 + - Copy the Remote Write endpoint and create an API key 177 + 178 + 4. **Configure your application:** 179 + ```typescript 180 + initializeGrafanaExporters({ 181 + lokiUrl: 'https://logs-prod-xxx.grafana.net', 182 + lokiAuth: { bearerToken: 'glc_xxx' }, 183 + prometheusUrl: 'https://prometheus-prod-xxx.grafana.net/api/prom', 184 + prometheusAuth: { bearerToken: 'glc_xxx' } 185 + }) 186 + ``` 187 + 188 + ## Data Flow 189 + 190 + 1. **Logs** โ†’ Buffered โ†’ Batched โ†’ Sent to Grafana Loki 191 + 2. **Metrics** โ†’ Aggregated โ†’ Exported via OTLP โ†’ Sent to Prometheus 192 + 3. **Errors** โ†’ Deduplicated โ†’ Sent to Loki with error tag 193 + 194 + ## Performance Considerations 195 + 196 + - Logs and metrics are batched to reduce network overhead 197 + - Default batch size: 100 entries 198 + - Default flush interval: 5 seconds 199 + - Failed exports are logged but don't block application 200 + - In-memory buffers are capped to prevent memory leaks 201 + 202 + ## Graceful Shutdown 203 + 204 + The exporters automatically register shutdown handlers: 205 + 206 + ```typescript 207 + import { shutdownGrafanaExporters } from '@wisp/observability' 208 + 209 + // Manual shutdown if needed 210 + process.on('beforeExit', async () => { 211 + await shutdownGrafanaExporters() 212 + }) 213 + ``` 214 + 215 + ## License 216 + 217 + MIT
+13 -1
packages/@wisp/observability/package.json
··· 24 24 } 25 25 }, 26 26 "peerDependencies": { 27 - "hono": "^4.0.0" 27 + "hono": "^4.10.7" 28 28 }, 29 29 "peerDependenciesMeta": { 30 30 "hono": { 31 31 "optional": true 32 32 } 33 + }, 34 + "dependencies": { 35 + "@opentelemetry/api": "^1.9.0", 36 + "@opentelemetry/sdk-metrics": "^1.29.0", 37 + "@opentelemetry/exporter-metrics-otlp-http": "^0.56.0", 38 + "@opentelemetry/resources": "^1.29.0", 39 + "@opentelemetry/semantic-conventions": "^1.29.0" 40 + }, 41 + "devDependencies": { 42 + "@hono/node-server": "^1.19.6", 43 + "bun-types": "^1.3.3", 44 + "typescript": "^5.9.3" 33 45 } 34 46 }
+12 -2
packages/@wisp/observability/src/core.ts
··· 3 3 * Framework-agnostic logging, error tracking, and metrics collection 4 4 */ 5 5 6 + import { lokiExporter, metricsExporter } from './exporters' 7 + 6 8 // ============================================================================ 7 9 // Types 8 10 // ============================================================================ ··· 128 130 logs.splice(MAX_LOGS) 129 131 } 130 132 133 + // Send to Loki exporter 134 + lokiExporter.pushLog(entry) 135 + 131 136 // Also log to console for compatibility 132 137 const contextStr = context ? ` ${JSON.stringify(context)}` : '' 133 138 const traceStr = traceId ? ` [trace:${traceId}]` : '' ··· 163 168 }, 164 169 165 170 debug(message: string, service: string, context?: Record<string, any>, traceId?: string) { 166 - const env = typeof Bun !== 'undefined' ? Bun.env.NODE_ENV : process.env.NODE_ENV; 167 - if (env !== 'production') { 171 + if (process.env.NODE_ENV !== 'production') { 168 172 this.log('debug', message, service, context, traceId) 169 173 } 170 174 }, ··· 233 237 234 238 errors.set(key, entry) 235 239 240 + // Send to Loki exporter 241 + lokiExporter.pushError(entry) 242 + 236 243 // Rotate if needed 237 244 if (errors.size > MAX_ERRORS) { 238 245 const oldest = Array.from(errors.keys())[0] ··· 284 291 } 285 292 286 293 metrics.unshift(entry) 294 + 295 + // Send to Prometheus/OTLP exporter 296 + metricsExporter.recordMetric(entry) 287 297 288 298 // Rotate if needed 289 299 if (metrics.length > MAX_METRICS) {
+433
packages/@wisp/observability/src/exporters.ts
··· 1 + /** 2 + * Grafana exporters for logs and metrics 3 + * Integrates with Grafana Loki for logs and Prometheus/OTLP for metrics 4 + */ 5 + 6 + import type { LogEntry, ErrorEntry, MetricEntry } from './core' 7 + import { metrics, type MeterProvider } from '@opentelemetry/api' 8 + import { MeterProvider as SdkMeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics' 9 + import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http' 10 + import { Resource } from '@opentelemetry/resources' 11 + import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions' 12 + 13 + // ============================================================================ 14 + // Types 15 + // ============================================================================ 16 + 17 + export interface GrafanaConfig { 18 + lokiUrl?: string 19 + lokiAuth?: { 20 + username?: string 21 + password?: string 22 + bearerToken?: string 23 + } 24 + prometheusUrl?: string 25 + prometheusAuth?: { 26 + username?: string 27 + password?: string 28 + bearerToken?: string 29 + } 30 + serviceName?: string 31 + serviceVersion?: string 32 + batchSize?: number 33 + flushIntervalMs?: number 34 + enabled?: boolean 35 + } 36 + 37 + interface LokiStream { 38 + stream: Record<string, string> 39 + values: Array<[string, string]> 40 + } 41 + 42 + interface LokiBatch { 43 + streams: LokiStream[] 44 + } 45 + 46 + // ============================================================================ 47 + // Configuration 48 + // ============================================================================ 49 + 50 + class GrafanaExporterConfig { 51 + private config: GrafanaConfig = { 52 + enabled: false, 53 + batchSize: 100, 54 + flushIntervalMs: 5000, 55 + serviceName: 'wisp-app', 56 + serviceVersion: '1.0.0' 57 + } 58 + 59 + initialize(config: GrafanaConfig) { 60 + this.config = { ...this.config, ...config } 61 + 62 + // Load from environment variables if not provided 63 + if (!this.config.lokiUrl) { 64 + this.config.lokiUrl = process.env.GRAFANA_LOKI_URL 65 + } 66 + 67 + if (!this.config.prometheusUrl) { 68 + this.config.prometheusUrl = process.env.GRAFANA_PROMETHEUS_URL 69 + } 70 + 71 + // Load Loki authentication from environment 72 + if (!this.config.lokiAuth?.bearerToken && !this.config.lokiAuth?.username) { 73 + const token = process.env.GRAFANA_LOKI_TOKEN 74 + const username = process.env.GRAFANA_LOKI_USERNAME 75 + const password = process.env.GRAFANA_LOKI_PASSWORD 76 + 77 + if (token) { 78 + this.config.lokiAuth = { ...this.config.lokiAuth, bearerToken: token } 79 + } else if (username && password) { 80 + this.config.lokiAuth = { ...this.config.lokiAuth, username, password } 81 + } 82 + } 83 + 84 + // Load Prometheus authentication from environment 85 + if (!this.config.prometheusAuth?.bearerToken && !this.config.prometheusAuth?.username) { 86 + const token = process.env.GRAFANA_PROMETHEUS_TOKEN 87 + const username = process.env.GRAFANA_PROMETHEUS_USERNAME 88 + const password = process.env.GRAFANA_PROMETHEUS_PASSWORD 89 + 90 + if (token) { 91 + this.config.prometheusAuth = { ...this.config.prometheusAuth, bearerToken: token } 92 + } else if (username && password) { 93 + this.config.prometheusAuth = { ...this.config.prometheusAuth, username, password } 94 + } 95 + } 96 + 97 + // Enable if URLs are configured 98 + if (this.config.lokiUrl || this.config.prometheusUrl) { 99 + this.config.enabled = true 100 + } 101 + 102 + return this 103 + } 104 + 105 + getConfig(): GrafanaConfig { 106 + return { ...this.config } 107 + } 108 + 109 + isEnabled(): boolean { 110 + return this.config.enabled === true 111 + } 112 + } 113 + 114 + export const grafanaConfig = new GrafanaExporterConfig() 115 + 116 + // ============================================================================ 117 + // Loki Exporter for Logs 118 + // ============================================================================ 119 + 120 + class LokiExporter { 121 + private buffer: LogEntry[] = [] 122 + private errorBuffer: ErrorEntry[] = [] 123 + private flushTimer?: NodeJS.Timeout 124 + private config: GrafanaConfig = {} 125 + 126 + initialize(config: GrafanaConfig) { 127 + this.config = config 128 + 129 + if (this.config.enabled && this.config.lokiUrl) { 130 + this.startBatching() 131 + } 132 + } 133 + 134 + private startBatching() { 135 + const interval = this.config.flushIntervalMs || 5000 136 + 137 + this.flushTimer = setInterval(() => { 138 + this.flush() 139 + }, interval) 140 + } 141 + 142 + stop() { 143 + if (this.flushTimer) { 144 + clearInterval(this.flushTimer) 145 + this.flushTimer = undefined 146 + } 147 + // Final flush 148 + this.flush() 149 + } 150 + 151 + pushLog(entry: LogEntry) { 152 + if (!this.config.enabled || !this.config.lokiUrl) return 153 + 154 + this.buffer.push(entry) 155 + 156 + const batchSize = this.config.batchSize || 100 157 + if (this.buffer.length >= batchSize) { 158 + this.flush() 159 + } 160 + } 161 + 162 + pushError(entry: ErrorEntry) { 163 + if (!this.config.enabled || !this.config.lokiUrl) return 164 + 165 + this.errorBuffer.push(entry) 166 + 167 + const batchSize = this.config.batchSize || 100 168 + if (this.errorBuffer.length >= batchSize) { 169 + this.flush() 170 + } 171 + } 172 + 173 + private async flush() { 174 + if (!this.config.lokiUrl) return 175 + 176 + const logsToSend = [...this.buffer] 177 + const errorsToSend = [...this.errorBuffer] 178 + 179 + this.buffer = [] 180 + this.errorBuffer = [] 181 + 182 + if (logsToSend.length === 0 && errorsToSend.length === 0) return 183 + 184 + try { 185 + const batch = this.createLokiBatch(logsToSend, errorsToSend) 186 + await this.sendToLoki(batch) 187 + } catch (error) { 188 + console.error('[LokiExporter] Failed to send logs to Loki:', error) 189 + // Optionally re-queue failed logs 190 + } 191 + } 192 + 193 + private createLokiBatch(logs: LogEntry[], errors: ErrorEntry[]): LokiBatch { 194 + const streams: LokiStream[] = [] 195 + 196 + // Group logs by service and level 197 + const logGroups = new Map<string, LogEntry[]>() 198 + 199 + for (const log of logs) { 200 + const key = `${log.service}-${log.level}` 201 + const group = logGroups.get(key) || [] 202 + group.push(log) 203 + logGroups.set(key, group) 204 + } 205 + 206 + // Create streams for logs 207 + for (const [key, entries] of logGroups) { 208 + const [service, level] = key.split('-') 209 + const values: Array<[string, string]> = entries.map(entry => { 210 + const logLine = JSON.stringify({ 211 + message: entry.message, 212 + context: entry.context, 213 + traceId: entry.traceId, 214 + eventType: entry.eventType 215 + }) 216 + 217 + // Loki expects nanosecond timestamp as string 218 + const nanoTimestamp = String(entry.timestamp.getTime() * 1000000) 219 + return [nanoTimestamp, logLine] 220 + }) 221 + 222 + streams.push({ 223 + stream: { 224 + service: service || 'unknown', 225 + level: level || 'info', 226 + job: this.config.serviceName || 'wisp-app' 227 + }, 228 + values 229 + }) 230 + } 231 + 232 + // Create streams for errors 233 + if (errors.length > 0) { 234 + const errorValues: Array<[string, string]> = errors.map(entry => { 235 + const logLine = JSON.stringify({ 236 + message: entry.message, 237 + stack: entry.stack, 238 + context: entry.context, 239 + count: entry.count 240 + }) 241 + 242 + const nanoTimestamp = String(entry.timestamp.getTime() * 1000000) 243 + return [nanoTimestamp, logLine] 244 + }) 245 + 246 + streams.push({ 247 + stream: { 248 + service: errors[0]?.service || 'unknown', 249 + level: 'error', 250 + job: this.config.serviceName || 'wisp-app', 251 + type: 'aggregated_error' 252 + }, 253 + values: errorValues 254 + }) 255 + } 256 + 257 + return { streams } 258 + } 259 + 260 + private async sendToLoki(batch: LokiBatch) { 261 + if (!this.config.lokiUrl) return 262 + 263 + const headers: Record<string, string> = { 264 + 'Content-Type': 'application/json' 265 + } 266 + 267 + // Add authentication 268 + if (this.config.lokiAuth?.bearerToken) { 269 + headers['Authorization'] = `Bearer ${this.config.lokiAuth.bearerToken}` 270 + } else if (this.config.lokiAuth?.username && this.config.lokiAuth?.password) { 271 + const auth = Buffer.from(`${this.config.lokiAuth.username}:${this.config.lokiAuth.password}`).toString('base64') 272 + headers['Authorization'] = `Basic ${auth}` 273 + } 274 + 275 + const response = await fetch(`${this.config.lokiUrl}/loki/api/v1/push`, { 276 + method: 'POST', 277 + headers, 278 + body: JSON.stringify(batch) 279 + }) 280 + 281 + if (!response.ok) { 282 + const text = await response.text() 283 + throw new Error(`Loki push failed: ${response.status} - ${text}`) 284 + } 285 + } 286 + } 287 + 288 + // ============================================================================ 289 + // OpenTelemetry Metrics Exporter 290 + // ============================================================================ 291 + 292 + class MetricsExporter { 293 + private meterProvider?: MeterProvider 294 + private requestCounter?: any 295 + private requestDuration?: any 296 + private errorCounter?: any 297 + private config: GrafanaConfig = {} 298 + 299 + initialize(config: GrafanaConfig) { 300 + this.config = config 301 + 302 + if (!this.config.enabled || !this.config.prometheusUrl) return 303 + 304 + // Create OTLP exporter with Prometheus endpoint 305 + const exporter = new OTLPMetricExporter({ 306 + url: `${this.config.prometheusUrl}/v1/metrics`, 307 + headers: this.getAuthHeaders(), 308 + timeoutMillis: 10000 309 + }) 310 + 311 + // Create meter provider with periodic exporting 312 + const meterProvider = new SdkMeterProvider({ 313 + resource: new Resource({ 314 + [ATTR_SERVICE_NAME]: this.config.serviceName || 'wisp-app', 315 + [ATTR_SERVICE_VERSION]: this.config.serviceVersion || '1.0.0' 316 + }), 317 + readers: [ 318 + new PeriodicExportingMetricReader({ 319 + exporter, 320 + exportIntervalMillis: this.config.flushIntervalMs || 5000 321 + }) 322 + ] 323 + }) 324 + 325 + // Set global meter provider 326 + metrics.setGlobalMeterProvider(meterProvider) 327 + this.meterProvider = meterProvider 328 + 329 + // Create metrics instruments 330 + const meter = metrics.getMeter(this.config.serviceName || 'wisp-app') 331 + 332 + this.requestCounter = meter.createCounter('http_requests_total', { 333 + description: 'Total number of HTTP requests' 334 + }) 335 + 336 + this.requestDuration = meter.createHistogram('http_request_duration_ms', { 337 + description: 'HTTP request duration in milliseconds', 338 + unit: 'ms' 339 + }) 340 + 341 + this.errorCounter = meter.createCounter('errors_total', { 342 + description: 'Total number of errors' 343 + }) 344 + } 345 + 346 + private getAuthHeaders(): Record<string, string> { 347 + const headers: Record<string, string> = {} 348 + 349 + if (this.config.prometheusAuth?.bearerToken) { 350 + headers['Authorization'] = `Bearer ${this.config.prometheusAuth.bearerToken}` 351 + } else if (this.config.prometheusAuth?.username && this.config.prometheusAuth?.password) { 352 + const auth = Buffer.from(`${this.config.prometheusAuth.username}:${this.config.prometheusAuth.password}`).toString('base64') 353 + headers['Authorization'] = `Basic ${auth}` 354 + } 355 + 356 + return headers 357 + } 358 + 359 + recordMetric(entry: MetricEntry) { 360 + if (!this.config.enabled) return 361 + 362 + const attributes = { 363 + method: entry.method, 364 + path: entry.path, 365 + status: String(entry.statusCode), 366 + service: entry.service 367 + } 368 + 369 + // Record request count 370 + this.requestCounter?.add(1, attributes) 371 + 372 + // Record request duration 373 + this.requestDuration?.record(entry.duration, attributes) 374 + 375 + // Record errors 376 + if (entry.statusCode >= 400) { 377 + this.errorCounter?.add(1, attributes) 378 + } 379 + } 380 + 381 + async shutdown() { 382 + if (this.meterProvider && 'shutdown' in this.meterProvider) { 383 + await (this.meterProvider as SdkMeterProvider).shutdown() 384 + } 385 + } 386 + } 387 + 388 + // ============================================================================ 389 + // Singleton Instances 390 + // ============================================================================ 391 + 392 + export const lokiExporter = new LokiExporter() 393 + export const metricsExporter = new MetricsExporter() 394 + 395 + // ============================================================================ 396 + // Initialization 397 + // ============================================================================ 398 + 399 + export function initializeGrafanaExporters(config?: GrafanaConfig) { 400 + const finalConfig = grafanaConfig.initialize(config || {}).getConfig() 401 + 402 + if (finalConfig.enabled) { 403 + console.log('[Observability] Initializing Grafana exporters', { 404 + lokiEnabled: !!finalConfig.lokiUrl, 405 + prometheusEnabled: !!finalConfig.prometheusUrl, 406 + serviceName: finalConfig.serviceName 407 + }) 408 + 409 + lokiExporter.initialize(finalConfig) 410 + metricsExporter.initialize(finalConfig) 411 + } 412 + 413 + return { 414 + lokiExporter, 415 + metricsExporter, 416 + config: finalConfig 417 + } 418 + } 419 + 420 + // ============================================================================ 421 + // Cleanup 422 + // ============================================================================ 423 + 424 + export async function shutdownGrafanaExporters() { 425 + lokiExporter.stop() 426 + await metricsExporter.shutdown() 427 + } 428 + 429 + // Graceful shutdown handlers 430 + if (typeof process !== 'undefined') { 431 + process.on('SIGTERM', shutdownGrafanaExporters) 432 + process.on('SIGINT', shutdownGrafanaExporters) 433 + }
+8
packages/@wisp/observability/src/index.ts
··· 6 6 // Export everything from core 7 7 export * from './core' 8 8 9 + // Export Grafana integration 10 + export { 11 + initializeGrafanaExporters, 12 + shutdownGrafanaExporters, 13 + grafanaConfig, 14 + type GrafanaConfig 15 + } from './exporters' 16 + 9 17 // Note: Middleware should be imported from specific subpaths: 10 18 // - import { observabilityMiddleware } from '@wisp/observability/middleware/elysia' 11 19 // - import { observabilityMiddleware, observabilityErrorHandler } from '@wisp/observability/middleware/hono'
+336
packages/@wisp/observability/src/integration-test.test.ts
··· 1 + /** 2 + * Integration tests for Grafana exporters 3 + * Tests both mock server and live server connections 4 + */ 5 + 6 + import { describe, test, expect, beforeAll, afterAll } from 'bun:test' 7 + import { createLogger, metricsCollector, initializeGrafanaExporters, shutdownGrafanaExporters } from './index' 8 + import { Hono } from 'hono' 9 + import { serve } from '@hono/node-server' 10 + import type { ServerType } from '@hono/node-server' 11 + 12 + // ============================================================================ 13 + // Mock Grafana Server 14 + // ============================================================================ 15 + 16 + interface MockRequest { 17 + method: string 18 + path: string 19 + headers: Record<string, string> 20 + body: any 21 + } 22 + 23 + class MockGrafanaServer { 24 + private app: Hono 25 + private server?: ServerType 26 + private port: number 27 + public requests: MockRequest[] = [] 28 + 29 + constructor(port: number) { 30 + this.port = port 31 + this.app = new Hono() 32 + 33 + // Mock Loki endpoint 34 + this.app.post('/loki/api/v1/push', async (c) => { 35 + const body = await c.req.json() 36 + this.requests.push({ 37 + method: 'POST', 38 + path: '/loki/api/v1/push', 39 + headers: Object.fromEntries(c.req.raw.headers.entries()), 40 + body 41 + }) 42 + return c.json({ status: 'success' }) 43 + }) 44 + 45 + // Mock Prometheus/OTLP endpoint 46 + this.app.post('/v1/metrics', async (c) => { 47 + const body = await c.req.json() 48 + this.requests.push({ 49 + method: 'POST', 50 + path: '/v1/metrics', 51 + headers: Object.fromEntries(c.req.raw.headers.entries()), 52 + body 53 + }) 54 + return c.json({ status: 'success' }) 55 + }) 56 + 57 + // Health check 58 + this.app.get('/health', (c) => c.json({ status: 'ok' })) 59 + } 60 + 61 + async start() { 62 + this.server = serve({ 63 + fetch: this.app.fetch, 64 + port: this.port 65 + }) 66 + // Wait a bit for server to be ready 67 + await new Promise(resolve => setTimeout(resolve, 100)) 68 + } 69 + 70 + async stop() { 71 + if (this.server) { 72 + this.server.close() 73 + this.server = undefined 74 + } 75 + } 76 + 77 + clearRequests() { 78 + this.requests = [] 79 + } 80 + 81 + getRequestsByPath(path: string): MockRequest[] { 82 + return this.requests.filter(r => r.path === path) 83 + } 84 + 85 + async waitForRequests(count: number, timeoutMs: number = 10000): Promise<boolean> { 86 + const startTime = Date.now() 87 + while (this.requests.length < count) { 88 + if (Date.now() - startTime > timeoutMs) { 89 + return false 90 + } 91 + await new Promise(resolve => setTimeout(resolve, 100)) 92 + } 93 + return true 94 + } 95 + } 96 + 97 + // ============================================================================ 98 + // Test Suite 99 + // ============================================================================ 100 + 101 + describe('Grafana Integration', () => { 102 + const mockServer = new MockGrafanaServer(9999) 103 + const mockUrl = 'http://localhost:9999' 104 + 105 + beforeAll(async () => { 106 + await mockServer.start() 107 + }) 108 + 109 + afterAll(async () => { 110 + await mockServer.stop() 111 + await shutdownGrafanaExporters() 112 + }) 113 + 114 + test('should initialize with username/password auth', () => { 115 + const config = initializeGrafanaExporters({ 116 + lokiUrl: mockUrl, 117 + lokiAuth: { 118 + username: 'testuser', 119 + password: 'testpass' 120 + }, 121 + prometheusUrl: mockUrl, 122 + prometheusAuth: { 123 + username: 'testuser', 124 + password: 'testpass' 125 + }, 126 + serviceName: 'test-service', 127 + batchSize: 5, 128 + flushIntervalMs: 1000 129 + }) 130 + 131 + expect(config.config.enabled).toBe(true) 132 + expect(config.config.lokiUrl).toBe(mockUrl) 133 + expect(config.config.prometheusUrl).toBe(mockUrl) 134 + expect(config.config.lokiAuth?.username).toBe('testuser') 135 + expect(config.config.prometheusAuth?.username).toBe('testuser') 136 + }) 137 + 138 + test('should send logs to Loki with basic auth', async () => { 139 + mockServer.clearRequests() 140 + 141 + // Initialize with username/password 142 + initializeGrafanaExporters({ 143 + lokiUrl: mockUrl, 144 + lokiAuth: { 145 + username: 'testuser', 146 + password: 'testpass' 147 + }, 148 + serviceName: 'test-logs', 149 + batchSize: 2, 150 + flushIntervalMs: 500 151 + }) 152 + 153 + const logger = createLogger('test-logs') 154 + 155 + // Generate logs that will trigger batch flush 156 + logger.info('Test message 1') 157 + logger.warn('Test message 2') 158 + 159 + // Wait for batch to be sent 160 + const success = await mockServer.waitForRequests(1, 5000) 161 + expect(success).toBe(true) 162 + 163 + const lokiRequests = mockServer.getRequestsByPath('/loki/api/v1/push') 164 + expect(lokiRequests.length).toBeGreaterThanOrEqual(1) 165 + 166 + const lastRequest = lokiRequests[lokiRequests.length - 1]! 167 + 168 + // Verify basic auth header 169 + expect(lastRequest.headers['authorization']).toMatch(/^Basic /) 170 + 171 + // Verify Loki batch format 172 + expect(lastRequest.body).toHaveProperty('streams') 173 + expect(Array.isArray(lastRequest.body.streams)).toBe(true) 174 + expect(lastRequest.body.streams.length).toBeGreaterThan(0) 175 + 176 + const stream = lastRequest.body.streams[0]! 177 + expect(stream).toHaveProperty('stream') 178 + expect(stream).toHaveProperty('values') 179 + expect(stream.stream.job).toBe('test-logs') 180 + 181 + await shutdownGrafanaExporters() 182 + }) 183 + 184 + test('should send metrics to Prometheus with bearer token', async () => { 185 + mockServer.clearRequests() 186 + 187 + // Initialize with bearer token only for Prometheus (no Loki) 188 + initializeGrafanaExporters({ 189 + lokiUrl: undefined, // Explicitly disable Loki 190 + prometheusUrl: mockUrl, 191 + prometheusAuth: { 192 + bearerToken: 'test-token-123' 193 + }, 194 + serviceName: 'test-metrics', 195 + flushIntervalMs: 1000 196 + }) 197 + 198 + // Generate metrics 199 + for (let i = 0; i < 5; i++) { 200 + metricsCollector.recordRequest('/api/test', 'GET', 200, 100 + i, 'test-metrics') 201 + } 202 + 203 + // Wait for metrics to be exported 204 + await new Promise(resolve => setTimeout(resolve, 2000)) 205 + 206 + const prometheusRequests = mockServer.getRequestsByPath('/v1/metrics') 207 + expect(prometheusRequests.length).toBeGreaterThan(0) 208 + 209 + // Note: Due to singleton exporters, we may see auth from previous test 210 + // The key thing is that metrics are being sent 211 + const lastRequest = prometheusRequests[prometheusRequests.length - 1]! 212 + expect(lastRequest.headers['authorization']).toBeTruthy() 213 + 214 + await shutdownGrafanaExporters() 215 + }) 216 + 217 + test('should handle errors gracefully', async () => { 218 + // Initialize with invalid URL 219 + const config = initializeGrafanaExporters({ 220 + lokiUrl: 'http://localhost:9998', // Non-existent server 221 + lokiAuth: { 222 + username: 'test', 223 + password: 'test' 224 + }, 225 + serviceName: 'test-error', 226 + batchSize: 1, 227 + flushIntervalMs: 500 228 + }) 229 + 230 + expect(config.config.enabled).toBe(true) 231 + 232 + const logger = createLogger('test-error') 233 + 234 + // This should not throw even though server doesn't exist 235 + logger.info('This should not crash') 236 + 237 + // Wait for flush attempt 238 + await new Promise(resolve => setTimeout(resolve, 1000)) 239 + 240 + // If we got here, error handling worked 241 + expect(true).toBe(true) 242 + 243 + await shutdownGrafanaExporters() 244 + }) 245 + }) 246 + 247 + // ============================================================================ 248 + // Live Server Connection Tests (Optional) 249 + // ============================================================================ 250 + 251 + describe('Live Grafana Connection (Optional)', () => { 252 + const hasLiveConfig = Boolean( 253 + process.env.GRAFANA_LOKI_URL && 254 + (process.env.GRAFANA_LOKI_TOKEN || 255 + (process.env.GRAFANA_LOKI_USERNAME && process.env.GRAFANA_LOKI_PASSWORD)) 256 + ) 257 + 258 + test.skipIf(!hasLiveConfig)('should connect to live Loki server', async () => { 259 + const config = initializeGrafanaExporters({ 260 + serviceName: 'test-live-loki', 261 + serviceVersion: '1.0.0-test', 262 + batchSize: 5, 263 + flushIntervalMs: 2000 264 + }) 265 + 266 + expect(config.config.enabled).toBe(true) 267 + expect(config.config.lokiUrl).toBeTruthy() 268 + 269 + const logger = createLogger('test-live-loki') 270 + 271 + // Send test logs 272 + logger.info('Live connection test log', { test: true, timestamp: Date.now() }) 273 + logger.warn('Test warning from integration test') 274 + logger.error('Test error (ignore)', new Error('Test error'), { safe: true }) 275 + 276 + // Wait for flush 277 + await new Promise(resolve => setTimeout(resolve, 3000)) 278 + 279 + // If we got here without errors, connection worked 280 + expect(true).toBe(true) 281 + 282 + await shutdownGrafanaExporters() 283 + }) 284 + 285 + test.skipIf(!hasLiveConfig)('should connect to live Prometheus server', async () => { 286 + const hasPrometheusConfig = Boolean( 287 + process.env.GRAFANA_PROMETHEUS_URL && 288 + (process.env.GRAFANA_PROMETHEUS_TOKEN || 289 + (process.env.GRAFANA_PROMETHEUS_USERNAME && process.env.GRAFANA_PROMETHEUS_PASSWORD)) 290 + ) 291 + 292 + if (!hasPrometheusConfig) { 293 + console.log('Skipping Prometheus test - no config provided') 294 + return 295 + } 296 + 297 + const config = initializeGrafanaExporters({ 298 + serviceName: 'test-live-prometheus', 299 + serviceVersion: '1.0.0-test', 300 + flushIntervalMs: 2000 301 + }) 302 + 303 + expect(config.config.enabled).toBe(true) 304 + expect(config.config.prometheusUrl).toBeTruthy() 305 + 306 + // Generate test metrics 307 + for (let i = 0; i < 10; i++) { 308 + metricsCollector.recordRequest( 309 + '/test/endpoint', 310 + 'GET', 311 + 200, 312 + 50 + Math.random() * 200, 313 + 'test-live-prometheus' 314 + ) 315 + } 316 + 317 + // Wait for export 318 + await new Promise(resolve => setTimeout(resolve, 3000)) 319 + 320 + expect(true).toBe(true) 321 + 322 + await shutdownGrafanaExporters() 323 + }) 324 + }) 325 + 326 + // ============================================================================ 327 + // Manual Test Runner 328 + // ============================================================================ 329 + 330 + if (import.meta.main) { 331 + console.log('๐Ÿงช Running Grafana integration tests...\n') 332 + console.log('Live server tests will run if these environment variables are set:') 333 + console.log(' - GRAFANA_LOKI_URL + (GRAFANA_LOKI_TOKEN or GRAFANA_LOKI_USERNAME/PASSWORD)') 334 + console.log(' - GRAFANA_PROMETHEUS_URL + (GRAFANA_PROMETHEUS_TOKEN or GRAFANA_PROMETHEUS_USERNAME/PASSWORD)') 335 + console.log('') 336 + }
+128 -27
packages/@wisp/safe-fetch/src/index.ts
··· 28 28 const MAX_BLOB_SIZE = 500 * 1024 * 1024; // 500MB 29 29 const MAX_REDIRECTS = 10; 30 30 31 + // Retry configuration 32 + const MAX_RETRIES = 3; 33 + const INITIAL_RETRY_DELAY = 1000; // 1 second 34 + const MAX_RETRY_DELAY = 10000; // 10 seconds 35 + 31 36 function isBlockedHost(hostname: string): boolean { 32 37 const lowerHost = hostname.toLowerCase(); 33 38 ··· 44 49 return false; 45 50 } 46 51 52 + /** 53 + * Check if an error is retryable (network/SSL errors, not HTTP errors) 54 + */ 55 + function isRetryableError(err: unknown): boolean { 56 + if (!(err instanceof Error)) return false; 57 + 58 + // Network errors (ECONNRESET, ENOTFOUND, etc.) 59 + const errorCode = (err as any).code; 60 + if (errorCode) { 61 + const retryableCodes = [ 62 + 'ECONNRESET', 63 + 'ECONNREFUSED', 64 + 'ETIMEDOUT', 65 + 'ENOTFOUND', 66 + 'ENETUNREACH', 67 + 'EAI_AGAIN', 68 + 'EPIPE', 69 + 'ERR_SSL_TLSV1_ALERT_INTERNAL_ERROR', // SSL/TLS handshake failures 70 + 'ERR_SSL_WRONG_VERSION_NUMBER', 71 + 'UNABLE_TO_VERIFY_LEAF_SIGNATURE', 72 + ]; 73 + if (retryableCodes.includes(errorCode)) { 74 + return true; 75 + } 76 + } 77 + 78 + // Timeout errors 79 + if (err.name === 'AbortError' || err.message.includes('timeout')) { 80 + return true; 81 + } 82 + 83 + // Fetch failures (generic network errors) 84 + if (err.message.includes('fetch failed')) { 85 + return true; 86 + } 87 + 88 + return false; 89 + } 90 + 91 + /** 92 + * Sleep for a given number of milliseconds 93 + */ 94 + function sleep(ms: number): Promise<void> { 95 + return new Promise(resolve => setTimeout(resolve, ms)); 96 + } 97 + 98 + /** 99 + * Retry a function with exponential backoff 100 + */ 101 + async function withRetry<T>( 102 + fn: () => Promise<T>, 103 + options: { maxRetries?: number; initialDelay?: number; maxDelay?: number; context?: string } = {} 104 + ): Promise<T> { 105 + const maxRetries = options.maxRetries ?? MAX_RETRIES; 106 + const initialDelay = options.initialDelay ?? INITIAL_RETRY_DELAY; 107 + const maxDelay = options.maxDelay ?? MAX_RETRY_DELAY; 108 + const context = options.context ?? 'Request'; 109 + 110 + let lastError: unknown; 111 + 112 + for (let attempt = 0; attempt <= maxRetries; attempt++) { 113 + try { 114 + return await fn(); 115 + } catch (err) { 116 + lastError = err; 117 + 118 + // Don't retry if this is the last attempt or error is not retryable 119 + if (attempt === maxRetries || !isRetryableError(err)) { 120 + throw err; 121 + } 122 + 123 + // Calculate delay with exponential backoff 124 + const delay = Math.min(initialDelay * Math.pow(2, attempt), maxDelay); 125 + 126 + const errorCode = (err as any)?.code; 127 + const errorMsg = err instanceof Error ? err.message : String(err); 128 + console.warn( 129 + `${context} failed (attempt ${attempt + 1}/${maxRetries + 1}): ${errorMsg}${errorCode ? ` [${errorCode}]` : ''} - retrying in ${delay}ms` 130 + ); 131 + 132 + await sleep(delay); 133 + } 134 + } 135 + 136 + throw lastError; 137 + } 138 + 47 139 export async function safeFetch( 48 140 url: string, 49 - options?: RequestInit & { maxSize?: number; timeout?: number } 141 + options?: RequestInit & { maxSize?: number; timeout?: number; retry?: boolean } 50 142 ): Promise<Response> { 143 + const shouldRetry = options?.retry !== false; // Default to true 51 144 const timeoutMs = options?.timeout ?? FETCH_TIMEOUT; 52 145 const maxSize = options?.maxSize ?? MAX_RESPONSE_SIZE; 53 146 54 - // Parse and validate URL 147 + // Parse and validate URL (done once, outside retry loop) 55 148 let parsedUrl: URL; 56 149 try { 57 150 parsedUrl = new URL(url); ··· 68 161 throw new Error(`Blocked host: ${hostname}`); 69 162 } 70 163 71 - const controller = new AbortController(); 72 - const timeoutId = setTimeout(() => controller.abort(), timeoutMs); 164 + const fetchFn = async () => { 165 + const controller = new AbortController(); 166 + const timeoutId = setTimeout(() => controller.abort(), timeoutMs); 167 + 168 + try { 169 + const response = await fetch(url, { 170 + ...options, 171 + signal: controller.signal, 172 + redirect: 'follow', 173 + headers: { 174 + 'User-Agent': 'wisp-place hosting-service', 175 + ...(options?.headers || {}), 176 + }, 177 + }); 73 178 74 - try { 75 - const response = await fetch(url, { 76 - ...options, 77 - signal: controller.signal, 78 - redirect: 'follow', 79 - headers: { 80 - 'User-Agent': 'wisp-place hosting-service', 81 - ...(options?.headers || {}), 82 - }, 83 - }); 179 + const contentLength = response.headers.get('content-length'); 180 + if (contentLength && parseInt(contentLength, 10) > maxSize) { 181 + throw new Error(`Response too large: ${contentLength} bytes`); 182 + } 84 183 85 - const contentLength = response.headers.get('content-length'); 86 - if (contentLength && parseInt(contentLength, 10) > maxSize) { 87 - throw new Error(`Response too large: ${contentLength} bytes`); 184 + return response; 185 + } catch (err) { 186 + if (err instanceof Error && err.name === 'AbortError') { 187 + throw new Error(`Request timeout after ${timeoutMs}ms`); 188 + } 189 + throw err; 190 + } finally { 191 + clearTimeout(timeoutId); 88 192 } 193 + }; 89 194 90 - return response; 91 - } catch (err) { 92 - if (err instanceof Error && err.name === 'AbortError') { 93 - throw new Error(`Request timeout after ${timeoutMs}ms`); 94 - } 95 - throw err; 96 - } finally { 97 - clearTimeout(timeoutId); 195 + if (shouldRetry) { 196 + return withRetry(fetchFn, { context: `Fetch ${parsedUrl.hostname}` }); 197 + } else { 198 + return fetchFn(); 98 199 } 99 200 } 100 201 101 202 export async function safeFetchJson<T = any>( 102 203 url: string, 103 - options?: RequestInit & { maxSize?: number; timeout?: number } 204 + options?: RequestInit & { maxSize?: number; timeout?: number; retry?: boolean } 104 205 ): Promise<T> { 105 206 const maxJsonSize = options?.maxSize ?? MAX_JSON_SIZE; 106 207 const response = await safeFetch(url, { ...options, maxSize: maxJsonSize }); ··· 146 247 147 248 export async function safeFetchBlob( 148 249 url: string, 149 - options?: RequestInit & { maxSize?: number; timeout?: number } 250 + options?: RequestInit & { maxSize?: number; timeout?: number; retry?: boolean } 150 251 ): Promise<Uint8Array> { 151 252 const maxBlobSize = options?.maxSize ?? MAX_BLOB_SIZE; 152 253 const timeoutMs = options?.timeout ?? FETCH_TIMEOUT_BLOB;
+28
scripts/codegen.sh
··· 1 + #!/bin/bash 2 + set -e 3 + 4 + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 5 + ROOT_DIR="$(dirname "$SCRIPT_DIR")" 6 + 7 + # Parse arguments 8 + AUTO_ACCEPT="" 9 + if [[ "$1" == "-y" || "$1" == "--yes" ]]; then 10 + AUTO_ACCEPT="yes |" 11 + fi 12 + 13 + echo "=== Generating TypeScript lexicons ===" 14 + cd "$ROOT_DIR/packages/@wisp/lexicons" 15 + eval "$AUTO_ACCEPT npm run codegen" 16 + 17 + echo "=== Generating Rust lexicons ===" 18 + echo "Installing jacquard-lexgen..." 19 + cargo install jacquard-lexgen --version 0.9.5 2>/dev/null || true 20 + echo "Running jacquard-codegen..." 21 + echo " Input: $ROOT_DIR/lexicons" 22 + echo " Output: $ROOT_DIR/cli/crates/lexicons/src" 23 + jacquard-codegen -i "$ROOT_DIR/lexicons" -o "$ROOT_DIR/cli/crates/lexicons/src" 24 + 25 + # Add extern crate alloc for the macro to work 26 + sed -i '' '1s/^/extern crate alloc;\n\n/' "$ROOT_DIR/cli/crates/lexicons/src/lib.rs" 27 + 28 + echo "=== Done ==="
+1 -1
tsconfig.json
··· 33 33 // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 34 // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 35 "types": [ 36 - "bun-types" 36 + "bun" 37 37 ] /* Specify type package names to be included without being referenced in a source file. */, 38 38 // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 39 39 // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */