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

dockerfile work

nekomimi.pet 75f9efeb ec6efeba

verified
Changed files
+104 -67
apps
hosting-service
main-app
public
editor
tabs
src
routes
binaries
docs
src
content
docs
packages
@wisp
fs-utils
+9 -11
Dockerfile
··· 4 # Set working directory 5 WORKDIR /app 6 7 - # Copy package files 8 - COPY package.json ./ 9 10 - # Copy Bun configuration 11 - COPY bunfig.toml ./ 12 13 - COPY tsconfig.json ./ 14 15 - # Install dependencies 16 RUN bun install 17 18 - # Copy source code 19 - COPY src ./src 20 - COPY public ./public 21 - 22 ENV PORT=8000 23 ENV NODE_ENV=production 24 25 EXPOSE 8000 26 27 - CMD ["bun", "start"]
··· 4 # Set working directory 5 WORKDIR /app 6 7 + # Copy workspace configuration 8 + COPY package.json bunfig.toml tsconfig.json ./ 9 10 + # Copy workspace packages 11 + COPY packages ./packages 12 13 + # Copy both apps (needed for workspace resolution) 14 + COPY apps/main-app ./apps/main-app 15 + COPY apps/hosting-service/package.json ./apps/hosting-service/package.json 16 17 + # Install all dependencies (including workspaces) 18 RUN bun install 19 20 ENV PORT=8000 21 ENV NODE_ENV=production 22 23 EXPOSE 8000 24 25 + CMD ["bun", "run", "start"]
+20 -9
apps/hosting-service/Dockerfile
··· 1 - # Use official Node.js Alpine image 2 FROM node:alpine AS base 3 4 # Set working directory 5 WORKDIR /app 6 7 - # Copy package files 8 - COPY package.json ./ 9 10 - # Install dependencies 11 - RUN npm install 12 13 - # Copy source code 14 - COPY src ./src 15 16 # Create cache directory 17 RUN mkdir -p ./cache/sites ··· 27 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ 28 CMD node -e "fetch('http://localhost:3001/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))" 29 30 - # Start the application (can override with 'npm run backfill' in compose) 31 - CMD ["npm", "run", "start"]
··· 1 + # Use official Node.js Alpine image with pnpm 2 + # Build from monorepo root: docker build -f apps/hosting-service/Dockerfile . 3 FROM node:alpine AS base 4 5 + # Install pnpm globally (supports workspace: protocol) 6 + RUN npm install -g pnpm 7 + 8 # Set working directory 9 WORKDIR /app 10 11 + # Copy workspace configuration 12 + COPY package.json tsconfig.json ./ 13 + 14 + # Copy all workspace packages 15 + COPY packages ./packages 16 + 17 + # Copy hosting-service and main-app package.json (for workspace resolution) 18 + COPY apps/hosting-service ./apps/hosting-service 19 + COPY apps/main-app/package.json ./apps/main-app/package.json 20 21 + # Install all dependencies (including workspaces) 22 + RUN pnpm install --frozen-lockfile || pnpm install 23 24 + # Set working directory to hosting-service 25 + WORKDIR /app/apps/hosting-service 26 27 # Create cache directory 28 RUN mkdir -p ./cache/sites ··· 38 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ 39 CMD node -e "fetch('http://localhost:3001/health').then(r => r.ok ? process.exit(0) : process.exit(1)).catch(() => process.exit(1))" 40 41 + # Start the application (can override with 'pnpm run backfill' in compose) 42 + CMD ["pnpm", "run", "start"]
+4 -3
apps/hosting-service/src/lib/firehose.ts
··· 6 } from './utils' 7 import { upsertSite, tryAcquireLock, releaseLock } from './db' 8 import { safeFetch } from '@wisp/safe-fetch' 9 - import { isRecord, validateRecord } from '@wisp/lexicons/types/place/wisp/fs' 10 import { Firehose } from '@atproto/sync' 11 import { IdResolver } from '@atproto/identity' 12 import { invalidateSiteCache, markSiteAsBeingCached, unmarkSiteAsBeingCached } from './cache' ··· 65 // If the write is a valid place.wisp.fs record 66 if ( 67 evt.collection === 'place.wisp.fs' && 68 - isRecord(record) && 69 - validateRecord(record).success 70 ) { 71 this.log('Received place.wisp.fs event', { 72 did: evt.did,
··· 6 } from './utils' 7 import { upsertSite, tryAcquireLock, releaseLock } from './db' 8 import { safeFetch } from '@wisp/safe-fetch' 9 + // import { isRecord, validateRecord } from '@wisp/lexicons/types/place/wisp/fs' 10 + import { isRecord } from '@wisp/lexicons/types/place/wisp/fs' 11 import { Firehose } from '@atproto/sync' 12 import { IdResolver } from '@atproto/identity' 13 import { invalidateSiteCache, markSiteAsBeingCached, unmarkSiteAsBeingCached } from './cache' ··· 66 // If the write is a valid place.wisp.fs record 67 if ( 68 evt.collection === 'place.wisp.fs' && 69 + isRecord(record) 70 + // && validateRecord(record).success 71 ) { 72 this.log('Received place.wisp.fs event', { 73 did: evt.did,
+4 -2
apps/main-app/package.json
··· 17 "@wisp/database": "workspace:*", 18 "@wisp/fs-utils": "workspace:*", 19 "@atproto/api": "^0.17.3", 20 "@atproto/lex-cli": "^0.9.5", 21 "@atproto/oauth-client-node": "^0.3.9", 22 "@atproto/xrpc-server": "^0.9.5", ··· 47 "tailwindcss": "4", 48 "tw-animate-css": "^1.4.0", 49 "typescript": "^5.9.3", 50 - "zlib": "^1.0.5" 51 }, 52 "devDependencies": { 53 "@types/react": "^19.2.2", 54 "@types/react-dom": "^19.2.1", 55 - "bun-plugin-tailwind": "^0.1.2", 56 "bun-types": "latest", 57 "esbuild": "0.26.0", 58 "playwright": "^1.49.0"
··· 17 "@wisp/database": "workspace:*", 18 "@wisp/fs-utils": "workspace:*", 19 "@atproto/api": "^0.17.3", 20 + "@atproto/common-web": "^0.4.5", 21 + "@atproto/jwk-jose": "^0.1.11", 22 "@atproto/lex-cli": "^0.9.5", 23 "@atproto/oauth-client-node": "^0.3.9", 24 "@atproto/xrpc-server": "^0.9.5", ··· 49 "tailwindcss": "4", 50 "tw-animate-css": "^1.4.0", 51 "typescript": "^5.9.3", 52 + "zlib": "^1.0.5", 53 + "bun-plugin-tailwind": "^0.1.2" 54 }, 55 "devDependencies": { 56 "@types/react": "^19.2.2", 57 "@types/react-dom": "^19.2.1", 58 "bun-types": "latest", 59 "esbuild": "0.26.0", 60 "playwright": "^1.49.0"
+12 -2
apps/main-app/public/editor/tabs/CLITab.tsx
··· 17 <div className="flex items-center gap-2 mb-2"> 18 <CardTitle>Wisp CLI Tool</CardTitle> 19 <Badge variant="secondary" className="text-xs"> 20 - v0.2.0 21 </Badge> 22 <Badge variant="outline" className="text-xs"> 23 Alpha ··· 55 </div> 56 57 <div className="space-y-3"> 58 - <h3 className="text-sm font-semibold">Download v0.2.0</h3> 59 <div className="grid gap-2"> 60 <div className="p-3 bg-muted/50 hover:bg-muted rounded-lg transition-colors border border-border"> 61 <a ··· 183 wisp-cli serve your-handle.bsky.social \\ 184 --site my-site \\ 185 --port 3000 186 187 # Downloads site, serves it, and watches firehose for live updates!`} 188 language="bash"
··· 17 <div className="flex items-center gap-2 mb-2"> 18 <CardTitle>Wisp CLI Tool</CardTitle> 19 <Badge variant="secondary" className="text-xs"> 20 + v0.4.0 21 </Badge> 22 <Badge variant="outline" className="text-xs"> 23 Alpha ··· 55 </div> 56 57 <div className="space-y-3"> 58 + <h3 className="text-sm font-semibold">Download v0.4.0</h3> 59 <div className="grid gap-2"> 60 <div className="p-3 bg-muted/50 hover:bg-muted rounded-lg transition-colors border border-border"> 61 <a ··· 183 wisp-cli serve your-handle.bsky.social \\ 184 --site my-site \\ 185 --port 3000 186 + 187 + # Enable SPA mode (serve index.html for all routes) 188 + wisp-cli serve your-handle.bsky.social \\ 189 + --site my-site \\ 190 + --spa 191 + 192 + # Enable directory listing for paths without index files 193 + wisp-cli serve your-handle.bsky.social \\ 194 + --site my-site \\ 195 + --directory 196 197 # Downloads site, serves it, and watches firehose for live updates!`} 198 language="bash"
+19 -18
apps/main-app/src/routes/wisp.ts
··· 22 import { createManifest } from '@wisp/fs-utils' 23 import { upsertSite } from '../lib/db' 24 import { createLogger } from '@wisp/observability' 25 - import { validateRecord, type Directory } from '@wisp/lexicons/types/place/wisp/fs' 26 - import { validateRecord as validateSubfsRecord } from '@wisp/lexicons/types/place/wisp/subfs' 27 import { MAX_SITE_SIZE, MAX_FILE_SIZE, MAX_FILE_COUNT } from '@wisp/constants' 28 import { 29 createUploadJob, ··· 373 createdAt: new Date().toISOString() 374 }; 375 376 - const validationResult = validateRecord(emptyManifest); 377 - if (!validationResult.success) { 378 - throw new Error(`Invalid manifest: ${validationResult.error?.message || 'Validation failed'}`); 379 - } 380 381 const rkey = siteName; 382 updateJobProgress(jobId, { phase: 'finalizing' }); ··· 735 }; 736 737 // Validate subfs record 738 - const subfsValidation = validateSubfsRecord(subfsManifest); 739 - if (!subfsValidation.success) { 740 - throw new Error(`Invalid subfs manifest: ${subfsValidation.error?.message || 'Validation failed'}`); 741 - } 742 743 // Upload subfs record to PDS 744 const subfsRecord = await agent.com.atproto.repo.putRecord({ ··· 789 }; 790 791 // Validate subfs record 792 - const subfsValidation = validateSubfsRecord(subfsManifest); 793 - if (!subfsValidation.success) { 794 - throw new Error(`Invalid subfs manifest: ${subfsValidation.error?.message || 'Validation failed'}`); 795 - } 796 797 // Upload subfs record to PDS 798 const subfsRecord = await agent.com.atproto.repo.putRecord({ ··· 1070 createdAt: new Date().toISOString() 1071 }; 1072 1073 - const validationResult = validateRecord(emptyManifest); 1074 - if (!validationResult.success) { 1075 - throw new Error(`Invalid manifest: ${validationResult.error?.message || 'Validation failed'}`); 1076 - } 1077 1078 const rkey = siteName; 1079
··· 22 import { createManifest } from '@wisp/fs-utils' 23 import { upsertSite } from '../lib/db' 24 import { createLogger } from '@wisp/observability' 25 + // import { validateRecord, type Directory } from '@wisp/lexicons/types/place/wisp/fs' 26 + import { type Directory } from '@wisp/lexicons/types/place/wisp/fs' 27 + // import { validateRecord as validateSubfsRecord } from '@wisp/lexicons/types/place/wisp/subfs' 28 import { MAX_SITE_SIZE, MAX_FILE_SIZE, MAX_FILE_COUNT } from '@wisp/constants' 29 import { 30 createUploadJob, ··· 374 createdAt: new Date().toISOString() 375 }; 376 377 + // const validationResult = validateRecord(emptyManifest); 378 + // if (!validationResult.success) { 379 + // throw new Error(`Invalid manifest: ${validationResult.error?.message || 'Validation failed'}`); 380 + // } 381 382 const rkey = siteName; 383 updateJobProgress(jobId, { phase: 'finalizing' }); ··· 736 }; 737 738 // Validate subfs record 739 + // const subfsValidation = validateSubfsRecord(subfsManifest); 740 + // if (!subfsValidation.success) { 741 + // throw new Error(`Invalid subfs manifest: ${subfsValidation.error?.message || 'Validation failed'}`); 742 + // } 743 744 // Upload subfs record to PDS 745 const subfsRecord = await agent.com.atproto.repo.putRecord({ ··· 790 }; 791 792 // Validate subfs record 793 + // const subfsValidation = validateSubfsRecord(subfsManifest); 794 + // if (!subfsValidation.success) { 795 + // throw new Error(`Invalid subfs manifest: ${subfsValidation.error?.message || 'Validation failed'}`); 796 + // } 797 798 // Upload subfs record to PDS 799 const subfsRecord = await agent.com.atproto.repo.putRecord({ ··· 1071 createdAt: new Date().toISOString() 1072 }; 1073 1074 + // const validationResult = validateRecord(emptyManifest); 1075 + // if (!validationResult.success) { 1076 + // throw new Error(`Invalid manifest: ${validationResult.error?.message || 'Validation failed'}`); 1077 + // } 1078 1079 const rkey = siteName; 1080
+1 -1
binaries/index.html
··· 234 </head> 235 <body> 236 <div class="container"> 237 - <h1>Wisp CLI <span style="font-size: 1.5rem; color: var(--demo-text-secondary);">v0.2.0</span></h1> 238 <p class="subtitle">Deploy static sites to the AT Protocol</p> 239 240 <div class="description">
··· 234 </head> 235 <body> 236 <div class="container"> 237 + <h1>Wisp CLI <span style="font-size: 1.5rem; color: var(--demo-text-secondary);">v0.4.0</span></h1> 238 <p class="subtitle">Deploy static sites to the AT Protocol</p> 239 240 <div class="description">
+24 -14
bun.lock
··· 4 "workspaces": { 5 "": { 6 "name": "elysia-static", 7 }, 8 "apps/hosting-service": { 9 "name": "wisp-hosting-service", ··· 39 "version": "1.0.50", 40 "dependencies": { 41 "@atproto/api": "^0.17.3", 42 "@atproto/lex-cli": "^0.9.5", 43 "@atproto/oauth-client-node": "^0.3.9", 44 "@atproto/xrpc-server": "^0.9.5", ··· 62 "@wisp/observability": "workspace:*", 63 "actor-typeahead": "^0.1.1", 64 "atproto-ui": "^0.11.3", 65 "class-variance-authority": "^0.7.1", 66 "clsx": "^2.1.1", 67 "elysia": "latest", ··· 80 "devDependencies": { 81 "@types/react": "^19.2.2", 82 "@types/react-dom": "^19.2.1", 83 - "bun-plugin-tailwind": "^0.1.2", 84 "bun-types": "latest", 85 "esbuild": "0.26.0", 86 "playwright": "^1.49.0", ··· 191 192 "@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=="], 193 194 - "@atproto/common-web": ["@atproto/common-web@0.4.3", "", { "dependencies": { "graphemer": "^1.4.0", "multiformats": "^9.9.0", "uint8arrays": "3.0.0", "zod": "^3.23.8" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 195 196 "@atproto/crypto": ["@atproto/crypto@0.4.4", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, "sha512-Yq9+crJ7WQl7sxStVpHgie5Z51R05etaK9DLWYG/7bR5T4bhdcIgF6IfklLShtZwLYdVVj+K15s0BqW9a8PSDA=="], 197 ··· 939 940 "@atproto-labs/fetch-node/ipaddr.js": ["ipaddr.js@2.2.0", "", {}, "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="], 941 942 "@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=="], 943 944 "@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.6.12", "", { "dependencies": { "@atproto/lexicon": "^0.4.10", "zod": "^3.23.8" } }, "sha512-Ut3iISNLujlmY9Gu8sNU+SPDJDvqlVzWddU8qUr0Yae5oD4SguaUFjjhireMGhQ3M5E0KljQgDbTmnBo1kIZ3w=="], 945 946 "@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 947 948 - "@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 949 - 950 - "@atproto/common-web/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 951 952 - "@atproto/identity/@atproto/common-web": ["@atproto/common-web@0.4.5", "", { "dependencies": { "@atproto/lex-data": "0.0.1", "@atproto/lex-json": "0.0.1", "zod": "^3.23.8" } }, "sha512-Tx0xUafLm3vRvOQpbBl5eb9V8xlC7TaRXs6dAulHRkDG3Kb+P9qn3pkDteq+aeMshbVXbVa1rm3Ok4vFyuoyYA=="], 953 954 "@atproto/jwk/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 955 ··· 959 960 "@atproto/lex-data/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 961 962 - "@atproto/lexicon/@atproto/common-web": ["@atproto/common-web@0.4.5", "", { "dependencies": { "@atproto/lex-data": "0.0.1", "@atproto/lex-json": "0.0.1", "zod": "^3.23.8" } }, "sha512-Tx0xUafLm3vRvOQpbBl5eb9V8xlC7TaRXs6dAulHRkDG3Kb+P9qn3pkDteq+aeMshbVXbVa1rm3Ok4vFyuoyYA=="], 963 - 964 "@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 965 966 "@atproto/oauth-client/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 967 968 "@atproto/repo/@atproto/common": ["@atproto/common@0.5.1", "", { "dependencies": { "@atproto/common-web": "^0.4.5", "@atproto/lex-cbor": "0.0.1", "@atproto/lex-data": "0.0.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, "sha512-0S57sjzw4r9OLc5srJFi6uAz/aTKYl6btz3x36tSnGriL716m6h0x2IVtgd+FhUfIQfisevrqcqw8SfaGk8VTw=="], 969 - 970 - "@atproto/repo/@atproto/common-web": ["@atproto/common-web@0.4.5", "", { "dependencies": { "@atproto/lex-data": "0.0.1", "@atproto/lex-json": "0.0.1", "zod": "^3.23.8" } }, "sha512-Tx0xUafLm3vRvOQpbBl5eb9V8xlC7TaRXs6dAulHRkDG3Kb+P9qn3pkDteq+aeMshbVXbVa1rm3Ok4vFyuoyYA=="], 971 972 "@atproto/repo/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 973 ··· 1021 1022 "wisp-hosting-service/@atproto/lexicon": ["@atproto/lexicon@0.5.1", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/syntax": "^0.4.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A=="], 1023 1024 - "@atproto/lex-cli/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1025 1026 - "@atproto/sync/@atproto/common/@atproto/common-web": ["@atproto/common-web@0.4.5", "", { "dependencies": { "@atproto/lex-data": "0.0.1", "@atproto/lex-json": "0.0.1", "zod": "^3.23.8" } }, "sha512-Tx0xUafLm3vRvOQpbBl5eb9V8xlC7TaRXs6dAulHRkDG3Kb+P9qn3pkDteq+aeMshbVXbVa1rm3Ok4vFyuoyYA=="], 1027 1028 "@atproto/sync/@atproto/xrpc-server/@atproto/xrpc": ["@atproto/xrpc@0.7.6", "", { "dependencies": { "@atproto/lexicon": "^0.5.2", "zod": "^3.23.8" } }, "sha512-RvCf4j0JnKYWuz3QzsYCntJi3VuiAAybQsMIUw2wLWcHhchO9F7UaBZINLL2z0qc/cYWPv5NSwcVydMseoCZLA=="], 1029 1030 - "@atproto/ws-client/@atproto/common/@atproto/common-web": ["@atproto/common-web@0.4.5", "", { "dependencies": { "@atproto/lex-data": "0.0.1", "@atproto/lex-json": "0.0.1", "zod": "^3.23.8" } }, "sha512-Tx0xUafLm3vRvOQpbBl5eb9V8xlC7TaRXs6dAulHRkDG3Kb+P9qn3pkDteq+aeMshbVXbVa1rm3Ok4vFyuoyYA=="], 1031 - 1032 "@atproto/ws-client/@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1033 1034 "@atproto/xrpc-server/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1035 1036 "@atproto/xrpc/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1037 1038 "@tokenizer/inflate/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 1039 1040 "@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" } }, "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A=="], 1041 ··· 1099 1100 "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], 1101 1102 "wisp-hosting-service/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1103 1104 "wisp-hosting-service/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1105 }
··· 4 "workspaces": { 5 "": { 6 "name": "elysia-static", 7 + "dependencies": { 8 + "bun-plugin-tailwind": "^0.1.2", 9 + "tailwindcss": "4", 10 + }, 11 }, 12 "apps/hosting-service": { 13 "name": "wisp-hosting-service", ··· 43 "version": "1.0.50", 44 "dependencies": { 45 "@atproto/api": "^0.17.3", 46 + "@atproto/common-web": "^0.4.5", 47 + "@atproto/jwk-jose": "^0.1.11", 48 "@atproto/lex-cli": "^0.9.5", 49 "@atproto/oauth-client-node": "^0.3.9", 50 "@atproto/xrpc-server": "^0.9.5", ··· 68 "@wisp/observability": "workspace:*", 69 "actor-typeahead": "^0.1.1", 70 "atproto-ui": "^0.11.3", 71 + "bun-plugin-tailwind": "^0.1.2", 72 "class-variance-authority": "^0.7.1", 73 "clsx": "^2.1.1", 74 "elysia": "latest", ··· 87 "devDependencies": { 88 "@types/react": "^19.2.2", 89 "@types/react-dom": "^19.2.1", 90 "bun-types": "latest", 91 "esbuild": "0.26.0", 92 "playwright": "^1.49.0", ··· 197 198 "@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=="], 199 200 + "@atproto/common-web": ["@atproto/common-web@0.4.5", "", { "dependencies": { "@atproto/lex-data": "0.0.1", "@atproto/lex-json": "0.0.1", "zod": "^3.23.8" } }, "sha512-Tx0xUafLm3vRvOQpbBl5eb9V8xlC7TaRXs6dAulHRkDG3Kb+P9qn3pkDteq+aeMshbVXbVa1rm3Ok4vFyuoyYA=="], 201 202 "@atproto/crypto": ["@atproto/crypto@0.4.4", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, "sha512-Yq9+crJ7WQl7sxStVpHgie5Z51R05etaK9DLWYG/7bR5T4bhdcIgF6IfklLShtZwLYdVVj+K15s0BqW9a8PSDA=="], 203 ··· 945 946 "@atproto-labs/fetch-node/ipaddr.js": ["ipaddr.js@2.2.0", "", {}, "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="], 947 948 + "@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" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 949 + 950 "@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=="], 951 952 "@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.6.12", "", { "dependencies": { "@atproto/lexicon": "^0.4.10", "zod": "^3.23.8" } }, "sha512-Ut3iISNLujlmY9Gu8sNU+SPDJDvqlVzWddU8qUr0Yae5oD4SguaUFjjhireMGhQ3M5E0KljQgDbTmnBo1kIZ3w=="], 953 954 "@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 955 956 + "@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" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 957 958 + "@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 959 960 "@atproto/jwk/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 961 ··· 965 966 "@atproto/lex-data/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 967 968 "@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 969 970 "@atproto/oauth-client/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 971 972 "@atproto/repo/@atproto/common": ["@atproto/common@0.5.1", "", { "dependencies": { "@atproto/common-web": "^0.4.5", "@atproto/lex-cbor": "0.0.1", "@atproto/lex-data": "0.0.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "pino": "^8.21.0" } }, "sha512-0S57sjzw4r9OLc5srJFi6uAz/aTKYl6btz3x36tSnGriL716m6h0x2IVtgd+FhUfIQfisevrqcqw8SfaGk8VTw=="], 973 974 "@atproto/repo/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 975 ··· 1023 1024 "wisp-hosting-service/@atproto/lexicon": ["@atproto/lexicon@0.5.1", "", { "dependencies": { "@atproto/common-web": "^0.4.3", "@atproto/syntax": "^0.4.1", "iso-datestring-validator": "^2.2.2", "multiformats": "^9.9.0", "zod": "^3.23.8" } }, "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A=="], 1025 1026 + "@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" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 1027 1028 + "@atproto/lex-cli/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1029 1030 "@atproto/sync/@atproto/xrpc-server/@atproto/xrpc": ["@atproto/xrpc@0.7.6", "", { "dependencies": { "@atproto/lexicon": "^0.5.2", "zod": "^3.23.8" } }, "sha512-RvCf4j0JnKYWuz3QzsYCntJi3VuiAAybQsMIUw2wLWcHhchO9F7UaBZINLL2z0qc/cYWPv5NSwcVydMseoCZLA=="], 1031 1032 "@atproto/ws-client/@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1033 1034 + "@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" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 1035 + 1036 "@atproto/xrpc-server/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1037 + 1038 + "@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" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 1039 1040 "@atproto/xrpc/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1041 1042 "@tokenizer/inflate/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 1043 + 1044 + "@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" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 1045 1046 "@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" } }, "sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A=="], 1047 ··· 1105 1106 "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], 1107 1108 + "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" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 1109 + 1110 "wisp-hosting-service/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1111 + 1112 + "wisp-hosting-service/@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" } }, "sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg=="], 1113 1114 "wisp-hosting-service/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="], 1115 }
+2 -2
docs/src/content/docs/cli.md
··· 1 --- 2 - title: Wisp CLI 0.2.0 (alpha) 3 description: Command-line tool for deploying static sites to the AT Protocol 4 --- 5 ··· 19 20 <div class="downloads"> 21 22 - <h2>Download v0.3.0</h2> 23 24 <a href="https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-aarch64-darwin" class="download-link" download=""> 25
··· 1 --- 2 + title: Wisp CLI 0.4.0 (alpha) 3 description: Command-line tool for deploying static sites to the AT Protocol 4 --- 5 ··· 19 20 <div class="downloads"> 21 22 + <h2>Download v0.4.0</h2> 23 24 <a href="https://sites.wisp.place/nekomimi.pet/wisp-cli-binaries/wisp-cli-aarch64-darwin" class="download-link" download=""> 25
+4
package.json
··· 7 "apps/main-app", 8 "apps/hosting-service" 9 ], 10 "scripts": { 11 "test": "bun test", 12 "dev": "bun run --watch apps/main-app/src/index.ts",
··· 7 "apps/main-app", 8 "apps/hosting-service" 9 ], 10 + "dependencies": { 11 + "bun-plugin-tailwind": "^0.1.2", 12 + "tailwindcss": "4" 13 + }, 14 "scripts": { 15 "test": "bun test", 16 "dev": "bun run --watch apps/main-app/src/index.ts",
+5 -5
packages/@wisp/fs-utils/src/manifest.ts
··· 1 import type { Record, Directory } from "@wisp/lexicons/types/place/wisp/fs"; 2 - import { validateRecord } from "@wisp/lexicons/types/place/wisp/fs"; 3 4 /** 5 * Create the manifest record for a site ··· 18 }; 19 20 // Validate the manifest before returning 21 - const validationResult = validateRecord(manifest); 22 - if (!validationResult.success) { 23 - throw new Error(`Invalid manifest: ${validationResult.error?.message || 'Validation failed'}`); 24 - } 25 26 return manifest; 27 }
··· 1 import type { Record, Directory } from "@wisp/lexicons/types/place/wisp/fs"; 2 + // import { validateRecord } from "@wisp/lexicons/types/place/wisp/fs"; 3 4 /** 5 * Create the manifest record for a site ··· 18 }; 19 20 // Validate the manifest before returning 21 + // const validationResult = validateRecord(manifest); 22 + // if (!validationResult.success) { 23 + // throw new Error(`Invalid manifest: ${validationResult.error?.message || 'Validation failed'}`); 24 + // } 25 26 return manifest; 27 }