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

start enforcing limits on hosting service, clean up logging in cli

nekomimi.pet 804d61c0 66c7dfa2

verified
Changed files
+228 -59
apps
hosting-service
main-app
cli
packages
@wisp
atproto-utils
constants
src
lexicons
+4 -2
apps/hosting-service/package.json
··· 6 "dev": "tsx --env-file=.env src/index.ts", 7 "build": "bun run build.ts", 8 "start": "tsx src/index.ts", 9 "backfill": "tsx src/index.ts --backfill" 10 }, 11 "dependencies": { ··· 18 "@wisp/safe-fetch": "workspace:*", 19 "@atproto/api": "^0.17.4", 20 "@atproto/identity": "^0.4.9", 21 - "@atproto/lexicon": "^0.5.1", 22 "@atproto/sync": "^0.1.36", 23 "@atproto/xrpc": "^0.7.5", 24 "@hono/node-server": "^1.19.6", ··· 31 "@types/bun": "^1.3.1", 32 "@types/mime-types": "^2.1.4", 33 "@types/node": "^22.10.5", 34 - "tsx": "^4.19.2" 35 } 36 }
··· 6 "dev": "tsx --env-file=.env src/index.ts", 7 "build": "bun run build.ts", 8 "start": "tsx src/index.ts", 9 + "check": "tsc --noEmit", 10 "backfill": "tsx src/index.ts --backfill" 11 }, 12 "dependencies": { ··· 19 "@wisp/safe-fetch": "workspace:*", 20 "@atproto/api": "^0.17.4", 21 "@atproto/identity": "^0.4.9", 22 + "@atproto/lexicon": "^0.5.2", 23 "@atproto/sync": "^0.1.36", 24 "@atproto/xrpc": "^0.7.5", 25 "@hono/node-server": "^1.19.6", ··· 32 "@types/bun": "^1.3.1", 33 "@types/mime-types": "^2.1.4", 34 "@types/node": "^22.10.5", 35 + "tsx": "^4.19.2", 36 + "typescript": "^5.9.3" 37 } 38 }
+53 -3
apps/hosting-service/src/lib/utils.ts
··· 7 import { safeFetchJson, safeFetchBlob } from '@wisp/safe-fetch'; 8 import { CID } from 'multiformats'; 9 import { extractBlobCid } from '@wisp/atproto-utils'; 10 - import { sanitizePath, collectFileCidsFromEntries } from '@wisp/fs-utils'; 11 import { shouldCompressMimeType } from '@wisp/atproto-utils/compression'; 12 13 // Re-export shared utilities for local usage and tests 14 export { extractBlobCid, sanitizePath }; ··· 120 } 121 122 /** 123 * Extract all subfs URIs from a directory tree with their mount paths 124 */ 125 function extractSubfsUris(directory: Directory, currentPath: string = ''): Array<{ uri: string; path: string }> { ··· 300 // Expand subfs nodes before caching 301 const expandedRoot = await expandSubfsNodes(record.root, pdsEndpoint); 302 303 // Get existing cache metadata to check for incremental updates 304 const existingMetadata = await getCacheMetadata(did, rkey); 305 const existingFileCids = existingMetadata?.fileCids || {}; ··· 514 515 console.log(`[Cache] Fetching blob for file: ${filePath}, CID: ${cid}`); 516 517 - // Allow up to 500MB per file blob, with 5 minute timeout 518 - let content = await safeFetchBlob(blobUrl, { maxSize: 500 * 1024 * 1024, timeout: 300000 }); 519 520 // If content is base64-encoded, decode it back to raw binary (gzipped or not) 521 if (base64) {
··· 7 import { safeFetchJson, safeFetchBlob } from '@wisp/safe-fetch'; 8 import { CID } from 'multiformats'; 9 import { extractBlobCid } from '@wisp/atproto-utils'; 10 + import { sanitizePath, collectFileCidsFromEntries, countFilesInDirectory } from '@wisp/fs-utils'; 11 import { shouldCompressMimeType } from '@wisp/atproto-utils/compression'; 12 + import { MAX_BLOB_SIZE, MAX_FILE_COUNT, MAX_SITE_SIZE } from '@wisp/constants'; 13 14 // Re-export shared utilities for local usage and tests 15 export { extractBlobCid, sanitizePath }; ··· 121 } 122 123 /** 124 + * Calculate total size of all blobs in a directory tree from manifest metadata 125 + */ 126 + function calculateTotalBlobSize(directory: Directory): number { 127 + let totalSize = 0; 128 + 129 + function sumBlobSizes(entries: Entry[]) { 130 + for (const entry of entries) { 131 + const node = entry.node; 132 + 133 + if ('type' in node && node.type === 'directory' && 'entries' in node) { 134 + // Recursively sum subdirectories 135 + sumBlobSizes(node.entries); 136 + } else if ('type' in node && node.type === 'file' && 'blob' in node) { 137 + // Add blob size from manifest 138 + const fileNode = node as File; 139 + const blobSize = (fileNode.blob as any)?.size || 0; 140 + totalSize += blobSize; 141 + } 142 + } 143 + } 144 + 145 + sumBlobSizes(directory.entries); 146 + return totalSize; 147 + } 148 + 149 + /** 150 * Extract all subfs URIs from a directory tree with their mount paths 151 */ 152 function extractSubfsUris(directory: Directory, currentPath: string = ''): Array<{ uri: string; path: string }> { ··· 327 // Expand subfs nodes before caching 328 const expandedRoot = await expandSubfsNodes(record.root, pdsEndpoint); 329 330 + // Verify all subfs nodes were expanded (defensive check) 331 + const remainingSubfs = extractSubfsUris(expandedRoot); 332 + if (remainingSubfs.length > 0) { 333 + console.warn(`[Cache] Warning: ${remainingSubfs.length} subfs nodes remain unexpanded after expansion`, remainingSubfs); 334 + } 335 + 336 + // ===== VALIDATE LIMITS BEFORE DOWNLOADING ANY BLOBS ===== 337 + 338 + // 1. Validate file count limit 339 + const fileCount = countFilesInDirectory(expandedRoot); 340 + if (fileCount > MAX_FILE_COUNT) { 341 + throw new Error(`Site exceeds file count limit: ${fileCount} files (max ${MAX_FILE_COUNT})`); 342 + } 343 + console.log(`[Cache] File count validation passed: ${fileCount} files (limit: ${MAX_FILE_COUNT})`); 344 + 345 + // 2. Validate total size from blob metadata in manifest (before downloading) 346 + const totalBlobSize = calculateTotalBlobSize(expandedRoot); 347 + if (totalBlobSize > MAX_SITE_SIZE) { 348 + throw new Error(`Site exceeds size limit: ${(totalBlobSize / 1024 / 1024).toFixed(2)}MB (max ${(MAX_SITE_SIZE / 1024 / 1024).toFixed(0)}MB)`); 349 + } 350 + console.log(`[Cache] Size validation passed: ${(totalBlobSize / 1024 / 1024).toFixed(2)}MB (limit: ${(MAX_SITE_SIZE / 1024 / 1024).toFixed(0)}MB)`); 351 + 352 + // All validations passed, proceed with caching 353 // Get existing cache metadata to check for incremental updates 354 const existingMetadata = await getCacheMetadata(did, rkey); 355 const existingFileCids = existingMetadata?.fileCids || {}; ··· 564 565 console.log(`[Cache] Fetching blob for file: ${filePath}, CID: ${cid}`); 566 567 + // Allow up to MAX_BLOB_SIZE per file blob, with 5 minute timeout 568 + let content = await safeFetchBlob(blobUrl, { maxSize: MAX_BLOB_SIZE, timeout: 300000 }); 569 570 // If content is base64-encoded, decode it back to raw binary (gzipped or not) 571 if (base64) {
+3
apps/main-app/package.json
··· 7 "dev": "bun run --watch src/index.ts", 8 "start": "bun run src/index.ts", 9 "build": "bun run build.ts", 10 "screenshot": "bun run scripts/screenshot-sites.ts" 11 }, 12 "dependencies": { ··· 53 "zlib": "^1.0.5" 54 }, 55 "devDependencies": { 56 "@types/react": "^19.2.2", 57 "@types/react-dom": "^19.2.1", 58 "bun-types": "latest",
··· 7 "dev": "bun run --watch src/index.ts", 8 "start": "bun run src/index.ts", 9 "build": "bun run build.ts", 10 + "check": "tsc --noEmit", 11 "screenshot": "bun run scripts/screenshot-sites.ts" 12 }, 13 "dependencies": { ··· 54 "zlib": "^1.0.5" 55 }, 56 "devDependencies": { 57 + "@atproto-labs/handle-resolver": "^0.3.4", 58 + "@atproto/did": "^0.2.3", 59 "@types/react": "^19.2.2", 60 "@types/react-dom": "^19.2.1", 61 "bun-types": "latest",
+53 -22
bun.lock
··· 17 "dependencies": { 18 "@atproto/api": "^0.17.4", 19 "@atproto/identity": "^0.4.9", 20 - "@atproto/lexicon": "^0.5.1", 21 "@atproto/sync": "^0.1.36", 22 "@atproto/xrpc": "^0.7.5", 23 "@hono/node-server": "^1.19.6", ··· 38 "@types/mime-types": "^2.1.4", 39 "@types/node": "^22.10.5", 40 "tsx": "^4.19.2", 41 }, 42 }, 43 "apps/main-app": { ··· 87 "zlib": "^1.0.5", 88 }, 89 "devDependencies": { 90 "@types/react": "^19.2.2", 91 "@types/react-dom": "^19.2.1", 92 "bun-types": "latest", ··· 103 "@wisp/lexicons": "workspace:*", 104 "multiformats": "^13.3.1", 105 }, 106 }, 107 "packages/@wisp/constants": { 108 "name": "@wisp/constants", ··· 132 }, 133 "devDependencies": { 134 "@atproto/lex-cli": "^0.9.5", 135 }, 136 }, 137 "packages/@wisp/observability": { ··· 180 181 "@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" } }, ""], 182 183 - "@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" } }, ""], 184 185 "@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" } }, ""], 186 ··· 200 201 "@atproto/crypto": ["@atproto/crypto@0.4.4", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, ""], 202 203 - "@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 204 205 "@atproto/identity": ["@atproto/identity@0.4.10", "", { "dependencies": { "@atproto/common-web": "^0.4.4", "@atproto/crypto": "^0.4.4" } }, "sha512-nQbzDLXOhM8p/wo0cTh5DfMSOSHzj6jizpodX37LJ4S1TZzumSxAjHEZa5Rev3JaoD5uSWMVE0MmKEGWkPPvfQ=="], 206 ··· 230 231 "@atproto/sync": ["@atproto/sync@0.1.38", "", { "dependencies": { "@atproto/common": "^0.5.0", "@atproto/identity": "^0.4.10", "@atproto/lexicon": "^0.5.2", "@atproto/repo": "^0.8.11", "@atproto/syntax": "^0.4.1", "@atproto/xrpc-server": "^0.10.0", "multiformats": "^9.9.0", "p-queue": "^6.6.2", "ws": "^8.12.0" } }, "sha512-2rE0SM21Nk4hWw/XcIYFnzlWO6/gBg8mrzuWbOvDhD49sA/wW4zyjaHZ5t1gvk28/SLok2VZiIR8nYBdbf7F5Q=="], 232 233 - "@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 234 235 "@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=="], 236 237 - "@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""], 238 239 "@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" } }, ""], 240 ··· 656 657 "ee-first": ["ee-first@1.1.1", "", {}, ""], 658 659 - "elysia": ["elysia@1.4.17", "", { "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-GcR7tgxk0+NgMCEqmXMs/xgND4XpmIzUdSdwchcQbYFeFisBcw9cmsvSpI10i160idwtlVyaRXX9K9IZBqnA7Q=="], 660 661 "emoji-regex": ["emoji-regex@8.0.0", "", {}, ""], 662 ··· 1036 1037 "zod": ["zod@3.25.76", "", {}, ""], 1038 1039 "@atproto-labs/fetch-node/ipaddr.js": ["ipaddr.js@2.2.0", "", {}, ""], 1040 1041 "@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=="], 1042 1043 "@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.6.12", "", { "dependencies": { "@atproto/lexicon": "^0.4.10", "zod": "^3.23.8" } }, "sha512-Ut3iISNLujlmY9Gu8sNU+SPDJDvqlVzWddU8qUr0Yae5oD4SguaUFjjhireMGhQ3M5E0KljQgDbTmnBo1kIZ3w=="], 1044 ··· 1054 1055 "@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" } }, ""], 1056 1057 - "@atproto/lex-data/@atproto/syntax": ["@atproto/syntax@0.4.2", "", {}, "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA=="], 1058 1059 "@atproto/lex-data/multiformats": ["multiformats@9.9.0", "", {}, ""], 1060 1061 - "@atproto/lexicon/@atproto/syntax": ["@atproto/syntax@0.4.2", "", {}, "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA=="], 1062 1063 - "@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1064 1065 "@atproto/oauth-client/multiformats": ["multiformats@9.9.0", "", {}, ""], 1066 1067 "@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=="], 1068 ··· 1072 1073 "@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=="], 1074 1075 - "@atproto/sync/@atproto/syntax": ["@atproto/syntax@0.4.2", "", {}, "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA=="], 1076 - 1077 "@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=="], 1078 1079 "@atproto/sync/multiformats": ["multiformats@9.9.0", "", {}, ""], 1080 1081 "@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=="], 1082 1083 - "@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" } }, ""], 1084 - 1085 "@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" } }, ""], 1086 1087 "@ipld/dag-cbor/multiformats": ["multiformats@9.9.0", "", {}, ""], 1088 ··· 1140 1141 "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" } }, ""], 1142 1143 - "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" } }, ""], 1144 1145 "@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" } }, ""], 1146 1147 "@atproto/lex-cli/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1148 1149 - "@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=="], 1150 1151 - "@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=="], 1152 1153 "@atproto/ws-client/@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, ""], 1154 1155 "@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" } }, ""], 1156 1157 "@atproto/xrpc-server/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1158 1159 - "@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" } }, ""], 1160 - 1161 - "@atproto/xrpc/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1162 - 1163 "@tokenizer/inflate/debug/ms": ["ms@2.1.3", "", {}, ""], 1164 1165 "@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" } }, ""], 1166 1167 "@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" } }, ""], 1168 1169 "@wisp/main-app/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, ""], 1170 ··· 1228 1229 "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" } }, ""], 1230 1231 "wisp-hosting-service/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, ""], 1232 1233 - "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" } }, ""], 1234 1235 - "wisp-hosting-service/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1236 } 1237 }
··· 17 "dependencies": { 18 "@atproto/api": "^0.17.4", 19 "@atproto/identity": "^0.4.9", 20 + "@atproto/lexicon": "^0.5.2", 21 "@atproto/sync": "^0.1.36", 22 "@atproto/xrpc": "^0.7.5", 23 "@hono/node-server": "^1.19.6", ··· 38 "@types/mime-types": "^2.1.4", 39 "@types/node": "^22.10.5", 40 "tsx": "^4.19.2", 41 + "typescript": "^5.9.3", 42 }, 43 }, 44 "apps/main-app": { ··· 88 "zlib": "^1.0.5", 89 }, 90 "devDependencies": { 91 + "@atproto-labs/handle-resolver": "^0.3.4", 92 + "@atproto/did": "^0.2.3", 93 "@types/react": "^19.2.2", 94 "@types/react-dom": "^19.2.1", 95 "bun-types": "latest", ··· 106 "@wisp/lexicons": "workspace:*", 107 "multiformats": "^13.3.1", 108 }, 109 + "devDependencies": { 110 + "@atproto/lexicon": "^0.5.2", 111 + }, 112 }, 113 "packages/@wisp/constants": { 114 "name": "@wisp/constants", ··· 138 }, 139 "devDependencies": { 140 "@atproto/lex-cli": "^0.9.5", 141 + "multiformats": "^13.4.1", 142 }, 143 }, 144 "packages/@wisp/observability": { ··· 187 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" } }, ""], 189 190 + "@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 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" } }, ""], 193 ··· 207 208 "@atproto/crypto": ["@atproto/crypto@0.4.4", "", { "dependencies": { "@noble/curves": "^1.7.0", "@noble/hashes": "^1.6.1", "uint8arrays": "3.0.0" } }, ""], 209 210 + "@atproto/did": ["@atproto/did@0.2.3", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-VI8JJkSizvM2cHYJa37WlbzeCm5tWpojyc1/Zy8q8OOjyoy6X4S4BEfoP941oJcpxpMTObamibQIXQDo7tnIjg=="], 211 212 "@atproto/identity": ["@atproto/identity@0.4.10", "", { "dependencies": { "@atproto/common-web": "^0.4.4", "@atproto/crypto": "^0.4.4" } }, "sha512-nQbzDLXOhM8p/wo0cTh5DfMSOSHzj6jizpodX37LJ4S1TZzumSxAjHEZa5Rev3JaoD5uSWMVE0MmKEGWkPPvfQ=="], 213 ··· 237 238 "@atproto/sync": ["@atproto/sync@0.1.38", "", { "dependencies": { "@atproto/common": "^0.5.0", "@atproto/identity": "^0.4.10", "@atproto/lexicon": "^0.5.2", "@atproto/repo": "^0.8.11", "@atproto/syntax": "^0.4.1", "@atproto/xrpc-server": "^0.10.0", "multiformats": "^9.9.0", "p-queue": "^6.6.2", "ws": "^8.12.0" } }, "sha512-2rE0SM21Nk4hWw/XcIYFnzlWO6/gBg8mrzuWbOvDhD49sA/wW4zyjaHZ5t1gvk28/SLok2VZiIR8nYBdbf7F5Q=="], 239 240 + "@atproto/syntax": ["@atproto/syntax@0.4.2", "", {}, "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA=="], 241 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=="], 243 244 + "@atproto/xrpc": ["@atproto/xrpc@0.7.6", "", { "dependencies": { "@atproto/lexicon": "^0.5.2", "zod": "^3.23.8" } }, "sha512-RvCf4j0JnKYWuz3QzsYCntJi3VuiAAybQsMIUw2wLWcHhchO9F7UaBZINLL2z0qc/cYWPv5NSwcVydMseoCZLA=="], 245 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" } }, ""], 247 ··· 663 664 "ee-first": ["ee-first@1.1.1", "", {}, ""], 665 666 + "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 668 "emoji-regex": ["emoji-regex@8.0.0", "", {}, ""], 669 ··· 1043 1044 "zod": ["zod@3.25.76", "", {}, ""], 1045 1046 + "@atproto-labs/did-resolver/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1047 + 1048 "@atproto-labs/fetch-node/ipaddr.js": ["ipaddr.js@2.2.0", "", {}, ""], 1049 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" } }, ""], 1051 + 1052 + "@atproto-labs/handle-resolver-node/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1053 + 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" } }, ""], 1055 + 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=="], 1057 + 1058 + "@atproto/api/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1059 1060 "@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.6.12", "", { "dependencies": { "@atproto/lexicon": "^0.4.10", "zod": "^3.23.8" } }, "sha512-Ut3iISNLujlmY9Gu8sNU+SPDJDvqlVzWddU8qUr0Yae5oD4SguaUFjjhireMGhQ3M5E0KljQgDbTmnBo1kIZ3w=="], 1061 ··· 1071 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" } }, ""], 1073 1074 + "@atproto/lex-cli/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1075 1076 "@atproto/lex-data/multiformats": ["multiformats@9.9.0", "", {}, ""], 1077 1078 + "@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1079 + 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" } }, ""], 1081 1082 + "@atproto/oauth-client/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1083 + 1084 + "@atproto/oauth-client/@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""], 1085 1086 "@atproto/oauth-client/multiformats": ["multiformats@9.9.0", "", {}, ""], 1087 + 1088 + "@atproto/oauth-client-node/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1089 + 1090 + "@atproto/oauth-types/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1091 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=="], 1093 ··· 1097 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=="], 1099 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=="], 1101 1102 "@atproto/sync/multiformats": ["multiformats@9.9.0", "", {}, ""], 1103 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=="], 1105 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" } }, ""], 1107 + 1108 + "@atproto/xrpc-server/@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""], 1109 1110 "@ipld/dag-cbor/multiformats": ["multiformats@9.9.0", "", {}, ""], 1111 ··· 1163 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" } }, ""], 1165 1166 + "@atproto-labs/identity-resolver/@atproto-labs/handle-resolver/@atproto/did": ["@atproto/did@0.2.1", "", { "dependencies": { "zod": "^3.23.8" } }, ""], 1167 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" } }, ""], 1169 1170 "@atproto/lex-cli/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1171 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" } }, ""], 1173 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=="], 1175 1176 "@atproto/ws-client/@atproto/common/multiformats": ["multiformats@9.9.0", "", {}, ""], 1177 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" } }, ""], 1179 + 1180 + "@atproto/xrpc-server/@atproto/lexicon/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1181 1182 "@atproto/xrpc-server/@atproto/lexicon/multiformats": ["multiformats@9.9.0", "", {}, ""], 1183 1184 "@tokenizer/inflate/debug/ms": ["ms@2.1.3", "", {}, ""], 1185 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" } }, ""], 1187 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" } }, ""], 1189 + 1190 + "@wisp/main-app/@atproto/api/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1191 + 1192 + "@wisp/main-app/@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""], 1193 1194 "@wisp/main-app/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, ""], 1195 ··· 1253 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" } }, ""], 1255 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" } }, ""], 1257 + 1258 + "wisp-hosting-service/@atproto/api/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1259 + 1260 + "wisp-hosting-service/@atproto/api/@atproto/xrpc": ["@atproto/xrpc@0.7.5", "", { "dependencies": { "@atproto/lexicon": "^0.5.1", "zod": "^3.23.8" } }, ""], 1261 + 1262 "wisp-hosting-service/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, ""], 1263 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" } }, ""], 1265 1266 + "@atproto/oauth-client/@atproto/xrpc/@atproto/lexicon/@atproto/syntax": ["@atproto/syntax@0.4.1", "", {}, ""], 1267 } 1268 }
+54 -8
cli/Cargo.lock
··· 605 checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" 606 607 [[package]] 608 name = "const-oid" 609 version = "0.9.6" 610 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 957 "subtle", 958 "zeroize", 959 ] 960 961 [[package]] 962 name = "encoding_rs" ··· 1733 ] 1734 1735 [[package]] 1736 name = "indoc" 1737 version = "2.0.7" 1738 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1810 [[package]] 1811 name = "jacquard" 1812 version = "0.9.3" 1813 - source = "git+https://tangled.org/nekomimi.pet/jacquard#e1b90160d4026e036ab5b797e56ddd7ae5c5537e" 1814 dependencies = [ 1815 "bytes", 1816 "getrandom 0.2.16", ··· 1840 [[package]] 1841 name = "jacquard-api" 1842 version = "0.9.2" 1843 - source = "git+https://tangled.org/nekomimi.pet/jacquard#e1b90160d4026e036ab5b797e56ddd7ae5c5537e" 1844 dependencies = [ 1845 "bon", 1846 "bytes", ··· 1858 [[package]] 1859 name = "jacquard-common" 1860 version = "0.9.2" 1861 - source = "git+https://tangled.org/nekomimi.pet/jacquard#e1b90160d4026e036ab5b797e56ddd7ae5c5537e" 1862 dependencies = [ 1863 "base64 0.22.1", 1864 "bon", ··· 1900 [[package]] 1901 name = "jacquard-derive" 1902 version = "0.9.3" 1903 - source = "git+https://tangled.org/nekomimi.pet/jacquard#e1b90160d4026e036ab5b797e56ddd7ae5c5537e" 1904 dependencies = [ 1905 "heck 0.5.0", 1906 "jacquard-lexicon", ··· 1912 [[package]] 1913 name = "jacquard-identity" 1914 version = "0.9.2" 1915 - source = "git+https://tangled.org/nekomimi.pet/jacquard#e1b90160d4026e036ab5b797e56ddd7ae5c5537e" 1916 dependencies = [ 1917 "bon", 1918 "bytes", ··· 1938 [[package]] 1939 name = "jacquard-lexicon" 1940 version = "0.9.2" 1941 - source = "git+https://tangled.org/nekomimi.pet/jacquard#e1b90160d4026e036ab5b797e56ddd7ae5c5537e" 1942 dependencies = [ 1943 "cid", 1944 "dashmap", ··· 1964 [[package]] 1965 name = "jacquard-oauth" 1966 version = "0.9.2" 1967 - source = "git+https://tangled.org/nekomimi.pet/jacquard#e1b90160d4026e036ab5b797e56ddd7ae5c5537e" 1968 dependencies = [ 1969 "base64 0.22.1", 1970 "bytes", ··· 2289 [[package]] 2290 name = "mini-moka" 2291 version = "0.10.99" 2292 - source = "git+https://tangled.org/nekomimi.pet/jacquard#e1b90160d4026e036ab5b797e56ddd7ae5c5537e" 2293 dependencies = [ 2294 "crossbeam-channel", 2295 "crossbeam-utils", ··· 2513 ] 2514 2515 [[package]] 2516 name = "objc2" 2517 version = "0.6.3" 2518 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2756 "der", 2757 "spki", 2758 ] 2759 2760 [[package]] 2761 name = "potential_utf" ··· 4717 4718 [[package]] 4719 name = "windows-sys" 4720 version = "0.60.2" 4721 source = "registry+https://github.com/rust-lang/crates.io-index" 4722 checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" ··· 5008 "futures", 5009 "globset", 5010 "ignore", 5011 "jacquard", 5012 "jacquard-api", 5013 "jacquard-common",
··· 605 checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" 606 607 [[package]] 608 + name = "console" 609 + version = "0.15.11" 610 + source = "registry+https://github.com/rust-lang/crates.io-index" 611 + checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" 612 + dependencies = [ 613 + "encode_unicode", 614 + "libc", 615 + "once_cell", 616 + "unicode-width 0.2.2", 617 + "windows-sys 0.59.0", 618 + ] 619 + 620 + [[package]] 621 name = "const-oid" 622 version = "0.9.6" 623 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 970 "subtle", 971 "zeroize", 972 ] 973 + 974 + [[package]] 975 + name = "encode_unicode" 976 + version = "1.0.0" 977 + source = "registry+https://github.com/rust-lang/crates.io-index" 978 + checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" 979 980 [[package]] 981 name = "encoding_rs" ··· 1752 ] 1753 1754 [[package]] 1755 + name = "indicatif" 1756 + version = "0.17.11" 1757 + source = "registry+https://github.com/rust-lang/crates.io-index" 1758 + checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" 1759 + dependencies = [ 1760 + "console", 1761 + "number_prefix", 1762 + "portable-atomic", 1763 + "unicode-width 0.2.2", 1764 + "web-time", 1765 + ] 1766 + 1767 + [[package]] 1768 name = "indoc" 1769 version = "2.0.7" 1770 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1842 [[package]] 1843 name = "jacquard" 1844 version = "0.9.3" 1845 dependencies = [ 1846 "bytes", 1847 "getrandom 0.2.16", ··· 1871 [[package]] 1872 name = "jacquard-api" 1873 version = "0.9.2" 1874 dependencies = [ 1875 "bon", 1876 "bytes", ··· 1888 [[package]] 1889 name = "jacquard-common" 1890 version = "0.9.2" 1891 dependencies = [ 1892 "base64 0.22.1", 1893 "bon", ··· 1929 [[package]] 1930 name = "jacquard-derive" 1931 version = "0.9.3" 1932 dependencies = [ 1933 "heck 0.5.0", 1934 "jacquard-lexicon", ··· 1940 [[package]] 1941 name = "jacquard-identity" 1942 version = "0.9.2" 1943 dependencies = [ 1944 "bon", 1945 "bytes", ··· 1965 [[package]] 1966 name = "jacquard-lexicon" 1967 version = "0.9.2" 1968 dependencies = [ 1969 "cid", 1970 "dashmap", ··· 1990 [[package]] 1991 name = "jacquard-oauth" 1992 version = "0.9.2" 1993 dependencies = [ 1994 "base64 0.22.1", 1995 "bytes", ··· 2314 [[package]] 2315 name = "mini-moka" 2316 version = "0.10.99" 2317 dependencies = [ 2318 "crossbeam-channel", 2319 "crossbeam-utils", ··· 2537 ] 2538 2539 [[package]] 2540 + name = "number_prefix" 2541 + version = "0.4.0" 2542 + source = "registry+https://github.com/rust-lang/crates.io-index" 2543 + checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" 2544 + 2545 + [[package]] 2546 name = "objc2" 2547 version = "0.6.3" 2548 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2786 "der", 2787 "spki", 2788 ] 2789 + 2790 + [[package]] 2791 + name = "portable-atomic" 2792 + version = "1.11.1" 2793 + source = "registry+https://github.com/rust-lang/crates.io-index" 2794 + checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 2795 2796 [[package]] 2797 name = "potential_utf" ··· 4753 4754 [[package]] 4755 name = "windows-sys" 4756 + version = "0.59.0" 4757 + source = "registry+https://github.com/rust-lang/crates.io-index" 4758 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 4759 + dependencies = [ 4760 + "windows-targets 0.52.6", 4761 + ] 4762 + 4763 + [[package]] 4764 + name = "windows-sys" 4765 version = "0.60.2" 4766 source = "registry+https://github.com/rust-lang/crates.io-index" 4767 checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" ··· 5053 "futures", 5054 "globset", 5055 "ignore", 5056 + "indicatif", 5057 "jacquard", 5058 "jacquard-api", 5059 "jacquard-common",
+15 -7
cli/Cargo.toml
··· 8 place_wisp = [] 9 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 clap = { version = "4.5.51", features = ["derive"] } 19 tokio = { version = "1.48", features = ["full"] } 20 miette = { version = "7.6.0", features = ["fancy"] } ··· 42 regex = "1.11" 43 ignore = "0.4" 44 globset = "0.4"
··· 8 place_wisp = [] 9 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" } 25 clap = { version = "4.5.51", features = ["derive"] } 26 tokio = { version = "1.48", features = ["full"] } 27 miette = { version = "7.6.0", features = ["fancy"] } ··· 49 regex = "1.11" 50 ignore = "0.4" 51 globset = "0.4" 52 + indicatif = "0.17"
+39 -15
cli/src/main.rs
··· 26 use std::io::Write; 27 use base64::Engine; 28 use futures::stream::{self, StreamExt}; 29 30 use place_wisp::fs::*; 31 use place_wisp::settings::*; 32 33 #[derive(Parser, Debug)] 34 #[command(author, version, about = "wisp.place CLI tool")] ··· 326 327 // Build directory tree with ignore patterns 328 let ignore_matcher = ignore_patterns::IgnoreMatcher::new(&path)?; 329 - let (root_dir, total_files, reused_count) = build_directory(agent, &path, &existing_blob_map, String::new(), &ignore_matcher).await?; 330 let uploaded_count = total_files - reused_count; 331 332 // Check if we need to split into subfs records 333 const MAX_MANIFEST_SIZE: usize = 140 * 1024; // 140KB (PDS limit is 150KB) ··· 606 existing_blobs: &'a HashMap<String, (jacquard_common::types::blob::BlobRef<'static>, String)>, 607 current_path: String, 608 ignore_matcher: &'a ignore_patterns::IgnoreMatcher, 609 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = miette::Result<(Directory<'static>, usize, usize)>> + 'a>> 610 { 611 Box::pin(async move { ··· 653 } 654 } 655 656 - // Process files concurrently with a limit of 5 657 let file_results: Vec<(Entry<'static>, bool)> = stream::iter(file_tasks) 658 .map(|(name, path, full_path)| async move { 659 - let (file_node, reused) = process_file(agent, &path, &full_path, existing_blobs).await?; 660 let entry = Entry::new() 661 .name(CowStr::from(name)) 662 .node(EntryNode::File(Box::new(file_node))) 663 .build(); 664 Ok::<_, miette::Report>((entry, reused)) 665 }) 666 - .buffer_unordered(5) 667 .collect::<Vec<_>>() 668 .await 669 .into_iter() ··· 690 } else { 691 format!("{}/{}", current_path, name) 692 }; 693 - let (subdir, sub_total, sub_reused) = build_directory(agent, &path, existing_blobs, subdir_path, ignore_matcher).await?; 694 dir_entries.push(Entry::new() 695 .name(CowStr::from(name)) 696 .node(EntryNode::Directory(Box::new(subdir))) ··· 722 file_path: &Path, 723 file_path_key: &str, 724 existing_blobs: &HashMap<String, (jacquard_common::types::blob::BlobRef<'static>, String)>, 725 ) -> miette::Result<(File<'static>, bool)> 726 { 727 // Read file ··· 761 if let Some((existing_blob_ref, existing_cid)) = existing_blob { 762 if existing_cid == &file_cid { 763 // CIDs match - reuse existing blob 764 - println!(" ✓ Reusing blob for {} (CID: {})", file_path_key, file_cid); 765 let mut file_builder = File::new() 766 .r#type(CowStr::from("file")) 767 .blob(existing_blob_ref.clone()) ··· 775 } 776 777 return Ok((file_builder.build(), true)); 778 - } else { 779 - // CID mismatch - file changed 780 - println!(" → File changed: {} (old CID: {}, new CID: {})", file_path_key, existing_cid, file_cid); 781 - } 782 - } else { 783 - // File not in existing blob map 784 - if file_path_key.starts_with("imgs/") { 785 - println!(" → New file (not in blob map): {}", file_path_key); 786 } 787 } 788 ··· 793 MimeType::new_static("application/octet-stream") 794 }; 795 796 - println!(" ↑ Uploading {} ({} bytes, CID: {})", file_path_key, upload_bytes.len(), file_cid); 797 let blob = agent.upload_blob(upload_bytes, mime_type).await?; 798 799 let mut file_builder = File::new() 800 .r#type(CowStr::from("file"))
··· 26 use std::io::Write; 27 use base64::Engine; 28 use futures::stream::{self, StreamExt}; 29 + use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; 30 31 use place_wisp::fs::*; 32 use place_wisp::settings::*; 33 + 34 + /// Maximum number of concurrent file uploads to the PDS 35 + const MAX_CONCURRENT_UPLOADS: usize = 2; 36 37 #[derive(Parser, Debug)] 38 #[command(author, version, about = "wisp.place CLI tool")] ··· 330 331 // Build directory tree with ignore patterns 332 let ignore_matcher = ignore_patterns::IgnoreMatcher::new(&path)?; 333 + 334 + // Create progress tracking (spinner style since we don't know total count upfront) 335 + let multi_progress = MultiProgress::new(); 336 + let progress = multi_progress.add(ProgressBar::new_spinner()); 337 + progress.set_style( 338 + ProgressStyle::default_spinner() 339 + .template("[{elapsed_precise}] {spinner:.cyan} {pos} files {msg}") 340 + .into_diagnostic()? 341 + .tick_chars("⠁⠂⠄⡀⢀⠠⠐⠈ ") 342 + ); 343 + progress.set_message("Scanning files..."); 344 + progress.enable_steady_tick(std::time::Duration::from_millis(100)); 345 + 346 + let (root_dir, total_files, reused_count) = build_directory(agent, &path, &existing_blob_map, String::new(), &ignore_matcher, &progress).await?; 347 let uploaded_count = total_files - reused_count; 348 + 349 + progress.finish_with_message(format!("✓ {} files ({} uploaded, {} reused)", total_files, uploaded_count, reused_count)); 350 351 // Check if we need to split into subfs records 352 const MAX_MANIFEST_SIZE: usize = 140 * 1024; // 140KB (PDS limit is 150KB) ··· 625 existing_blobs: &'a HashMap<String, (jacquard_common::types::blob::BlobRef<'static>, String)>, 626 current_path: String, 627 ignore_matcher: &'a ignore_patterns::IgnoreMatcher, 628 + progress: &'a ProgressBar, 629 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = miette::Result<(Directory<'static>, usize, usize)>> + 'a>> 630 { 631 Box::pin(async move { ··· 673 } 674 } 675 676 + // Process files concurrently with a limit of 2 677 let file_results: Vec<(Entry<'static>, bool)> = stream::iter(file_tasks) 678 .map(|(name, path, full_path)| async move { 679 + let (file_node, reused) = process_file(agent, &path, &full_path, existing_blobs, progress).await?; 680 + progress.inc(1); 681 let entry = Entry::new() 682 .name(CowStr::from(name)) 683 .node(EntryNode::File(Box::new(file_node))) 684 .build(); 685 Ok::<_, miette::Report>((entry, reused)) 686 }) 687 + .buffer_unordered(MAX_CONCURRENT_UPLOADS) 688 .collect::<Vec<_>>() 689 .await 690 .into_iter() ··· 711 } else { 712 format!("{}/{}", current_path, name) 713 }; 714 + let (subdir, sub_total, sub_reused) = build_directory(agent, &path, existing_blobs, subdir_path, ignore_matcher, progress).await?; 715 dir_entries.push(Entry::new() 716 .name(CowStr::from(name)) 717 .node(EntryNode::Directory(Box::new(subdir))) ··· 743 file_path: &Path, 744 file_path_key: &str, 745 existing_blobs: &HashMap<String, (jacquard_common::types::blob::BlobRef<'static>, String)>, 746 + progress: &ProgressBar, 747 ) -> miette::Result<(File<'static>, bool)> 748 { 749 // Read file ··· 783 if let Some((existing_blob_ref, existing_cid)) = existing_blob { 784 if existing_cid == &file_cid { 785 // CIDs match - reuse existing blob 786 + progress.set_message(format!("✓ Reused {}", file_path_key)); 787 let mut file_builder = File::new() 788 .r#type(CowStr::from("file")) 789 .blob(existing_blob_ref.clone()) ··· 797 } 798 799 return Ok((file_builder.build(), true)); 800 } 801 } 802 ··· 807 MimeType::new_static("application/octet-stream") 808 }; 809 810 + // Format file size nicely 811 + let size_str = if upload_bytes.len() < 1024 { 812 + format!("{} B", upload_bytes.len()) 813 + } else if upload_bytes.len() < 1024 * 1024 { 814 + format!("{:.1} KB", upload_bytes.len() as f64 / 1024.0) 815 + } else { 816 + format!("{:.1} MB", upload_bytes.len() as f64 / (1024.0 * 1024.0)) 817 + }; 818 + 819 + progress.set_message(format!("↑ Uploading {} ({})", file_path_key, size_str)); 820 let blob = agent.upload_blob(upload_bytes, mime_type).await?; 821 + progress.set_message(format!("✓ Uploaded {}", file_path_key)); 822 823 let mut file_builder = File::new() 824 .r#type(CowStr::from("file"))
+1
package.json
··· 20 "build": "cd apps/main-app && bun run build.ts", 21 "build:hosting": "cd apps/hosting-service && npm run build", 22 "build:all": "bun run build && npm run build:hosting", 23 "screenshot": "bun run apps/main-app/scripts/screenshot-sites.ts", 24 "hosting:dev": "cd apps/hosting-service && npm run dev", 25 "hosting:start": "cd apps/hosting-service && npm run start"
··· 20 "build": "cd apps/main-app && bun run build.ts", 21 "build:hosting": "cd apps/hosting-service && npm run build", 22 "build:all": "bun run build && npm run build:hosting", 23 + "check": "cd apps/main-app && npm run check && cd ../hosting-service && npm run check", 24 "screenshot": "bun run apps/main-app/scripts/screenshot-sites.ts", 25 "hosting:dev": "cd apps/hosting-service && npm run dev", 26 "hosting:start": "cd apps/hosting-service && npm run start"
+3
packages/@wisp/atproto-utils/package.json
··· 27 "@atproto/api": "^0.14.1", 28 "@wisp/lexicons": "workspace:*", 29 "multiformats": "^13.3.1" 30 } 31 }
··· 27 "@atproto/api": "^0.14.1", 28 "@wisp/lexicons": "workspace:*", 29 "multiformats": "^13.3.1" 30 + }, 31 + "devDependencies": { 32 + "@atproto/lexicon": "^0.5.2" 33 } 34 }
+1 -1
packages/@wisp/constants/src/index.ts
··· 14 15 // File size limits 16 export const MAX_SITE_SIZE = 300 * 1024 * 1024; // 300MB 17 - export const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB 18 export const MAX_FILE_COUNT = 1000; 19 20 // Cache configuration
··· 14 15 // File size limits 16 export const MAX_SITE_SIZE = 300 * 1024 * 1024; // 300MB 17 + export const MAX_FILE_SIZE = 200 * 1024 * 1024; // 200MB 18 export const MAX_FILE_COUNT = 1000; 19 20 // Cache configuration
+2 -1
packages/@wisp/lexicons/package.json
··· 39 "@atproto/xrpc-server": "^0.9.5" 40 }, 41 "devDependencies": { 42 - "@atproto/lex-cli": "^0.9.5" 43 } 44 }
··· 39 "@atproto/xrpc-server": "^0.9.5" 40 }, 41 "devDependencies": { 42 + "@atproto/lex-cli": "^0.9.5", 43 + "multiformats": "^13.4.1" 44 } 45 }