+2
.tangled/workflows/deploy-wisp.yml
+2
.tangled/workflows/deploy-wisp.yml
+2
-1
.tangled/workflows/test.yml
+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
+9
-8
apps/hosting-service/package.json
+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",
+31
-3
apps/hosting-service/src/index.ts
+31
-3
apps/hosting-service/src/index.ts
···
5
5
import { mkdirSync, existsSync } from 'fs';
6
6
import { backfillCache } from './lib/backfill';
7
7
import { startDomainCacheCleanup, stopDomainCacheCleanup, setCacheOnlyMode, closeDatabase } from './lib/db';
8
+
import { storage, getStorageConfig } from './lib/storage';
8
9
9
10
// Initialize Grafana exporters if configured
10
11
initializeGrafanaExporters({
···
50
51
51
52
firehose.start();
52
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
+
53
69
// Run backfill if requested
54
70
if (backfillOnStartup) {
55
71
console.log('๐ Backfill requested, starting cache backfill...');
···
78
94
port: PORT,
79
95
});
80
96
97
+
// Get storage configuration for display
98
+
const storageConfig = getStorageConfig();
99
+
81
100
console.log(`
82
-
Wisp Hosting Service
101
+
Wisp Hosting Service with Tiered Storage
83
102
84
103
Server: http://localhost:${PORT}
85
104
Health: http://localhost:${PORT}/health
86
-
Cache: ${CACHE_DIR}
87
-
Firehose: Connected to Firehose
88
105
Cache-Only: ${CACHE_ONLY_MODE ? 'ENABLED (no DB writes)' : 'DISABLED'}
89
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...
90
118
`);
91
119
92
120
// Graceful shutdown
+1
-1
apps/hosting-service/src/lib/backfill.ts
+1
-1
apps/hosting-service/src/lib/backfill.ts
···
69
69
const processSite = async () => {
70
70
try {
71
71
// Check if already cached
72
-
if (skipExisting && isCached(site.did, site.rkey)) {
72
+
if (skipExisting && await isCached(site.did, site.rkey)) {
73
73
stats.skipped++;
74
74
processed++;
75
75
logger.debug(`Skipping already cached site`, { did: site.did, rkey: site.rkey });
+35
-43
apps/hosting-service/src/lib/cache.ts
+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,
+64
-76
apps/hosting-service/src/lib/file-serving.ts
+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
+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
+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
+1
-1
apps/hosting-service/src/lib/site-cache.ts
+270
apps/hosting-service/src/lib/storage.ts
+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
+
}
+69
-152
apps/hosting-service/src/lib/utils.ts
+69
-152
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 };
···
395
397
const existingMetadata = await getCacheMetadata(did, rkey);
396
398
const existingFileCids = existingMetadata?.fileCids || {};
397
399
398
-
// Use a temporary directory with timestamp to avoid collisions
399
-
const tempSuffix = `.tmp-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
400
-
const tempDir = `${CACHE_DIR}/${did}/${rkey}${tempSuffix}`;
401
-
const finalDir = `${CACHE_DIR}/${did}/${rkey}`;
402
-
403
-
try {
404
-
// Collect file CIDs from the new record (using expanded root)
405
-
const newFileCids: Record<string, string> = {};
406
-
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);
407
403
408
-
// Fetch site settings (optional)
409
-
const settings = await fetchSiteSettings(did, rkey);
404
+
// Fetch site settings (optional)
405
+
const settings = await fetchSiteSettings(did, rkey);
410
406
411
-
// Download/copy files to temporary directory (with incremental logic, using expanded root)
412
-
await cacheFiles(did, rkey, expandedRoot.entries, pdsEndpoint, '', tempSuffix, existingFileCids, finalDir);
413
-
await saveCacheMetadata(did, rkey, recordCid, tempSuffix, newFileCids, settings);
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);
414
410
415
-
// Atomically replace old cache with new cache
416
-
// On POSIX systems (Linux/macOS), rename is atomic
417
-
if (existsSync(finalDir)) {
418
-
// Rename old directory to backup
419
-
const backupDir = `${finalDir}.old-${Date.now()}`;
420
-
await rename(finalDir, backupDir);
421
-
422
-
try {
423
-
// Rename new directory to final location
424
-
await rename(tempDir, finalDir);
425
-
426
-
// Clean up old backup
427
-
rmSync(backupDir, { recursive: true, force: true });
428
-
} catch (err) {
429
-
// If rename failed, restore backup
430
-
if (existsSync(backupDir) && !existsSync(finalDir)) {
431
-
await rename(backupDir, finalDir);
432
-
}
433
-
throw err;
434
-
}
435
-
} else {
436
-
// No existing cache, just rename temp to final
437
-
await rename(tempDir, finalDir);
438
-
}
439
-
440
-
console.log('Successfully cached site atomically', did, rkey);
441
-
} catch (err) {
442
-
// Clean up temp directory on failure
443
-
if (existsSync(tempDir)) {
444
-
rmSync(tempDir, { recursive: true, force: true });
445
-
}
446
-
throw err;
447
-
}
411
+
console.log('Successfully cached site', did, rkey);
448
412
}
449
413
450
414
···
454
418
entries: Entry[],
455
419
pdsEndpoint: string,
456
420
pathPrefix: string,
457
-
dirSuffix: string = '',
458
-
existingFileCids: Record<string, string> = {},
459
-
existingCacheDir?: string
421
+
existingFileCids: Record<string, string> = {}
460
422
): Promise<void> {
461
-
// Collect file tasks, separating unchanged files from new/changed files
423
+
// Collect file download tasks (skip unchanged files)
462
424
const downloadTasks: Array<() => Promise<void>> = [];
463
-
const copyTasks: Array<() => Promise<void>> = [];
464
425
465
426
function collectFileTasks(
466
427
entries: Entry[],
···
477
438
const cid = extractBlobCid(fileNode.blob);
478
439
479
440
// Check if file is unchanged (same CID as existing cache)
480
-
if (cid && existingFileCids[currentPath] === cid && existingCacheDir) {
481
-
// File unchanged - copy from existing cache instead of downloading
482
-
copyTasks.push(() => copyExistingFile(
483
-
did,
484
-
site,
485
-
currentPath,
486
-
dirSuffix,
487
-
existingCacheDir
488
-
));
441
+
if (cid && existingFileCids[currentPath] === cid) {
442
+
// File unchanged - skip download (already in tiered storage)
443
+
console.log(`Skipping unchanged file: ${currentPath}`);
489
444
} else {
490
445
// File new or changed - download it
491
446
downloadTasks.push(() => cacheFileBlob(
···
496
451
pdsEndpoint,
497
452
fileNode.encoding,
498
453
fileNode.mimeType,
499
-
fileNode.base64,
500
-
dirSuffix
454
+
fileNode.base64
501
455
));
502
456
}
503
457
}
···
506
460
507
461
collectFileTasks(entries, pathPrefix);
508
462
509
-
console.log(`[Incremental Update] Files to copy: ${copyTasks.length}, Files to download: ${downloadTasks.length}`);
463
+
console.log(`[Incremental Update] Files to download: ${downloadTasks.length}`);
510
464
511
-
// Copy unchanged files in parallel (fast local operations) - increased limit for better performance
512
-
const copyLimit = 50;
513
-
for (let i = 0; i < copyTasks.length; i += copyLimit) {
514
-
const batch = copyTasks.slice(i, i + copyLimit);
515
-
await Promise.all(batch.map(task => task()));
516
-
if (copyTasks.length > copyLimit) {
517
-
console.log(`[Cache Progress] Copied ${Math.min(i + copyLimit, copyTasks.length)}/${copyTasks.length} unchanged files`);
518
-
}
519
-
}
520
-
521
-
// Download new/changed files concurrently - increased from 3 to 20 for much better performance
465
+
// Download new/changed files concurrently
522
466
const downloadLimit = 20;
523
467
let successCount = 0;
524
468
let failureCount = 0;
···
547
491
}
548
492
}
549
493
550
-
/**
551
-
* Copy an unchanged file from existing cache to new cache location
552
-
*/
553
-
async function copyExistingFile(
554
-
did: string,
555
-
site: string,
556
-
filePath: string,
557
-
dirSuffix: string,
558
-
existingCacheDir: string
559
-
): Promise<void> {
560
-
const { copyFile } = await import('fs/promises');
561
-
562
-
const sourceFile = `${existingCacheDir}/${filePath}`;
563
-
const destFile = `${CACHE_DIR}/${did}/${site}${dirSuffix}/${filePath}`;
564
-
const destDir = destFile.substring(0, destFile.lastIndexOf('/'));
565
-
566
-
// Create destination directory if needed
567
-
if (destDir && !existsSync(destDir)) {
568
-
mkdirSync(destDir, { recursive: true });
569
-
}
570
-
571
-
try {
572
-
// Copy the file
573
-
await copyFile(sourceFile, destFile);
574
-
575
-
// Copy metadata file if it exists
576
-
const sourceMetaFile = `${sourceFile}.meta`;
577
-
const destMetaFile = `${destFile}.meta`;
578
-
if (existsSync(sourceMetaFile)) {
579
-
await copyFile(sourceMetaFile, destMetaFile);
580
-
}
581
-
} catch (err) {
582
-
console.error(`Failed to copy cached file ${filePath}, will attempt download:`, err);
583
-
throw err;
584
-
}
585
-
}
586
-
587
494
async function cacheFileBlob(
588
495
did: string,
589
496
site: string,
···
592
499
pdsEndpoint: string,
593
500
encoding?: 'gzip',
594
501
mimeType?: string,
595
-
base64?: boolean,
596
-
dirSuffix: string = ''
502
+
base64?: boolean
597
503
): Promise<void> {
598
504
const cid = extractBlobCid(blobRef);
599
505
if (!cid) {
···
616
522
content = Buffer.from(base64String, 'base64');
617
523
}
618
524
619
-
const cacheFile = `${CACHE_DIR}/${did}/${site}${dirSuffix}/${filePath}`;
620
-
const fileDir = cacheFile.substring(0, cacheFile.lastIndexOf('/'));
621
-
622
-
if (fileDir && !existsSync(fileDir)) {
623
-
mkdirSync(fileDir, { recursive: true });
624
-
}
625
-
626
525
// Use the shared function to determine if this should remain compressed
627
526
const shouldStayCompressed = shouldCompressMimeType(mimeType);
628
527
···
640
539
}
641
540
}
642
541
643
-
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;
550
+
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
+
});
644
556
645
-
// Store metadata only if file is still compressed
557
+
// Log completion
646
558
if (encoding === 'gzip' && mimeType) {
647
-
const metaFile = `${cacheFile}.meta`;
648
-
await writeFile(metaFile, JSON.stringify({ encoding, mimeType }));
649
559
console.log('Cached file', filePath, content.length, 'bytes (gzipped,', mimeType + ')');
650
560
} else {
651
561
console.log('Cached file', filePath, content.length, 'bytes');
···
658
568
return `${CACHE_DIR}/${did}/${site}/${sanitizedPath}`;
659
569
}
660
570
661
-
export function isCached(did: string, site: string): boolean {
662
-
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);
663
580
}
664
581
665
-
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> {
666
583
const metadata: CacheMetadata = {
667
584
recordCid,
668
585
cachedAt: Date.now(),
···
672
589
settings: settings || undefined
673
590
};
674
591
675
-
const metadataPath = `${CACHE_DIR}/${did}/${rkey}${dirSuffix}/.metadata.json`;
676
-
const metadataDir = metadataPath.substring(0, metadataPath.lastIndexOf('/'));
677
-
678
-
if (!existsSync(metadataDir)) {
679
-
mkdirSync(metadataDir, { recursive: true });
680
-
}
681
-
682
-
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);
683
596
}
684
597
685
598
async function getCacheMetadata(did: string, rkey: string): Promise<CacheMetadata | null> {
686
599
try {
687
-
const metadataPath = `${CACHE_DIR}/${did}/${rkey}/.metadata.json`;
688
-
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);
689
603
690
-
const content = await readFile(metadataPath, 'utf-8');
691
-
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;
692
609
} catch (err) {
693
610
console.error('Failed to read cache metadata', err);
694
611
return null;
···
722
639
}
723
640
724
641
export async function updateCacheMetadataSettings(did: string, rkey: string, settings: WispSettings | null): Promise<void> {
725
-
const metadataPath = `${CACHE_DIR}/${did}/${rkey}/.metadata.json`;
726
-
727
-
if (!existsSync(metadataPath)) {
728
-
console.warn('Metadata file does not exist, cannot update settings', { did, rkey });
729
-
return;
730
-
}
731
-
732
642
try {
733
-
// Read existing metadata
734
-
const content = await readFile(metadataPath, 'utf-8');
735
-
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
+
}
736
650
737
651
// Update settings field
738
652
// Store null explicitly to cache "no settings" state and avoid repeated fetches
739
653
metadata.settings = settings ?? null;
740
654
741
-
// Write back to disk
742
-
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);
743
660
console.log('Updated metadata settings', { did, rkey, hasSettings: !!settings });
744
661
} catch (err) {
745
662
console.error('Failed to update metadata settings', err);
+4
-1
apps/hosting-service/src/server.ts
+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
+59
backup.nix
+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
+
}
+230
-5
bun.lock
+230
-5
bun.lock
···
36
36
"mime-types": "^2.1.35",
37
37
"multiformats": "^13.4.1",
38
38
"postgres": "^3.4.5",
39
+
"tiered-storage": "1.0.3",
39
40
},
40
41
"devDependencies": {
41
42
"@types/bun": "^1.3.1",
···
262
263
263
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=="],
264
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=="],
349
+
265
350
"@badrap/valita": ["@badrap/valita@0.4.6", "", {}, "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg=="],
266
351
267
352
"@borewit/text-codec": ["@borewit/text-codec@0.1.1", "", {}, "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA=="],
···
544
629
545
630
"@sinclair/typebox": ["@sinclair/typebox@0.34.41", "", {}, "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g=="],
546
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=="],
657
+
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=="],
659
+
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=="],
661
+
662
+
"@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ=="],
663
+
664
+
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
665
+
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=="],
667
+
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=="],
669
+
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=="],
671
+
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=="],
673
+
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=="],
675
+
676
+
"@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw=="],
677
+
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=="],
679
+
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=="],
681
+
682
+
"@smithy/property-provider": ["@smithy/property-provider@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
683
+
684
+
"@smithy/protocol-http": ["@smithy/protocol-http@5.3.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA=="],
685
+
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=="],
687
+
688
+
"@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w=="],
689
+
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=="],
691
+
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=="],
693
+
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=="],
695
+
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=="],
697
+
698
+
"@smithy/types": ["@smithy/types@4.11.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA=="],
699
+
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=="],
701
+
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=="],
703
+
704
+
"@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
705
+
706
+
"@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA=="],
707
+
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=="],
709
+
710
+
"@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
711
+
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=="],
713
+
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=="],
715
+
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=="],
717
+
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=="],
719
+
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=="],
721
+
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=="],
723
+
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=="],
725
+
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=="],
727
+
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=="],
733
+
547
734
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
548
735
549
736
"@tailwindcss/cli": ["@tailwindcss/cli@4.1.17", "", { "dependencies": { "@parcel/watcher": "^2.5.1", "@tailwindcss/node": "4.1.17", "@tailwindcss/oxide": "4.1.17", "enhanced-resolve": "^5.18.3", "mri": "^1.2.0", "picocolors": "^1.1.1", "tailwindcss": "4.1.17" }, "bin": { "tailwindcss": "dist/index.mjs" } }, "sha512-jUIxcyUNlCC2aNPnyPEWU/L2/ik3pB4fF3auKGXr8AvN3T3OFESVctFKOBoPZQaZJIeUpPn1uCLp0MRxuek8gg=="],
···
643
830
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
644
831
645
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=="],
833
+
834
+
"bowser": ["bowser@2.13.1", "", {}, "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw=="],
646
835
647
836
"brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
648
837
649
838
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
650
839
651
-
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
840
+
"buffer": ["buffer@5.6.0", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4" } }, "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw=="],
652
841
653
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=="],
654
843
···
750
939
751
940
"fast-redact": ["fast-redact@3.5.0", "", {}, "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A=="],
752
941
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=="],
943
+
753
944
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
754
945
755
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=="],
···
960
1151
961
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=="],
962
1153
963
-
"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=="],
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=="],
964
1155
965
1156
"real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
966
1157
···
1004
1195
1005
1196
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
1006
1197
1198
+
"stream-browserify": ["stream-browserify@3.0.0", "", { "dependencies": { "inherits": "~2.0.4", "readable-stream": "^3.5.0" } }, "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA=="],
1199
+
1007
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=="],
1008
1201
1009
1202
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
1010
1203
1011
1204
"strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
1205
+
1206
+
"strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="],
1012
1207
1013
1208
"strtok3": ["strtok3@10.3.4", "", { "dependencies": { "@tokenizer/token": "^0.3.0" } }, "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg=="],
1014
1209
···
1024
1219
1025
1220
"thread-stream": ["thread-stream@2.7.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw=="],
1026
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=="],
1223
+
1224
+
"tiny-lru": ["tiny-lru@11.4.5", "", {}, "sha512-hkcz3FjNJfKXjV4mjQ1OrXSLAehg8Hw+cEZclOVT+5c/cWQWImQ9wolzTjth+dmmDe++p3bme3fTxz6Q4Etsqw=="],
1225
+
1027
1226
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
1028
1227
1029
1228
"tlds": ["tlds@1.261.0", "", { "bin": { "tlds": "bin.js" } }, "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA=="],
···
1063
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=="],
1064
1263
1065
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=="],
1265
+
1266
+
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
1066
1267
1067
1268
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
1068
1269
···
1118
1319
1119
1320
"@atproto/sync/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="],
1120
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
+
1121
1328
"@ipld/dag-cbor/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="],
1122
1329
1123
1330
"@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="],
···
1274
1481
1275
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=="],
1276
1483
1484
+
"@wisp/observability/bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
1485
+
1277
1486
"express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
1278
1487
1279
1488
"iron-session/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
···
1282
1491
1283
1492
"node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
1284
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
+
1285
1496
"require-in-the-middle/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
1286
1497
1287
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=="],
···
1291
1502
"send/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
1292
1503
1293
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=="],
1505
+
1506
+
"tiered-storage/mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
1294
1507
1295
1508
"tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
1296
1509
···
1302
1515
1303
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=="],
1304
1517
1305
-
"wisp-hosting-service/@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="],
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=="],
1306
1519
1307
-
"@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=="],
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=="],
1521
+
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=="],
1523
+
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=="],
1308
1525
1309
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=="],
1310
1527
···
1342
1559
1343
1560
"@wisp/main-app/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="],
1344
1561
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=="],
1563
+
1345
1564
"require-in-the-middle/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
1346
1565
1347
1566
"serve-static/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
···
1351
1570
"serve-static/send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
1352
1571
1353
1572
"serve-static/send/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
1573
+
1574
+
"tiered-storage/mime-types/mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="],
1354
1575
1355
1576
"tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA=="],
1356
1577
···
1406
1627
1407
1628
"wisp-hosting-service/@atproto/api/multiformats": ["multiformats@9.9.0", "", {}, "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg=="],
1408
1629
1409
-
"wisp-hosting-service/@types/bun/bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="],
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=="],
1631
+
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=="],
1633
+
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=="],
1410
1635
}
1411
1636
}
+190
-262
cli/Cargo.lock
+190
-262
cli/Cargo.lock
···
126
126
127
127
[[package]]
128
128
name = "async-compression"
129
-
version = "0.4.34"
129
+
version = "0.4.36"
130
130
source = "registry+https://github.com/rust-lang/crates.io-index"
131
-
checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473"
131
+
checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37"
132
132
dependencies = [
133
133
"compression-codecs",
134
134
"compression-core",
···
145
145
dependencies = [
146
146
"proc-macro2",
147
147
"quote",
148
-
"syn 2.0.111",
148
+
"syn 2.0.113",
149
149
]
150
150
151
151
[[package]]
···
171
171
172
172
[[package]]
173
173
name = "axum"
174
-
version = "0.8.7"
174
+
version = "0.8.8"
175
175
source = "registry+https://github.com/rust-lang/crates.io-index"
176
-
checksum = "5b098575ebe77cb6d14fc7f32749631a6e44edbef6b796f89b020e99ba20d425"
176
+
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
177
177
dependencies = [
178
178
"axum-core",
179
179
"bytes",
···
204
204
205
205
[[package]]
206
206
name = "axum-core"
207
-
version = "0.5.5"
207
+
version = "0.5.6"
208
208
source = "registry+https://github.com/rust-lang/crates.io-index"
209
-
checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22"
209
+
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
210
210
dependencies = [
211
211
"bytes",
212
212
"futures-core",
···
233
233
"miniz_oxide",
234
234
"object",
235
235
"rustc-demangle",
236
-
"windows-link 0.2.1",
236
+
"windows-link",
237
237
]
238
238
239
239
[[package]]
···
281
281
282
282
[[package]]
283
283
name = "base64ct"
284
-
version = "1.8.0"
284
+
version = "1.8.2"
285
285
source = "registry+https://github.com/rust-lang/crates.io-index"
286
-
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
286
+
checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82"
287
287
288
288
[[package]]
289
289
name = "bitflags"
···
322
322
"proc-macro2",
323
323
"quote",
324
324
"rustversion",
325
-
"syn 2.0.111",
325
+
"syn 2.0.113",
326
326
]
327
327
328
328
[[package]]
329
329
name = "borsh"
330
-
version = "1.5.7"
330
+
version = "1.6.0"
331
331
source = "registry+https://github.com/rust-lang/crates.io-index"
332
-
checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce"
332
+
checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f"
333
333
dependencies = [
334
334
"cfg_aliases",
335
335
]
···
377
377
378
378
[[package]]
379
379
name = "bumpalo"
380
-
version = "3.19.0"
380
+
version = "3.19.1"
381
381
source = "registry+https://github.com/rust-lang/crates.io-index"
382
-
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
382
+
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
383
383
384
384
[[package]]
385
385
name = "byteorder"
···
407
407
408
408
[[package]]
409
409
name = "cc"
410
-
version = "1.2.47"
410
+
version = "1.2.51"
411
411
source = "registry+https://github.com/rust-lang/crates.io-index"
412
-
checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
412
+
checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
413
413
dependencies = [
414
414
"find-msvc-tools",
415
415
"shlex",
···
444
444
"num-traits",
445
445
"serde",
446
446
"wasm-bindgen",
447
-
"windows-link 0.2.1",
447
+
"windows-link",
448
448
]
449
449
450
450
[[package]]
···
496
496
497
497
[[package]]
498
498
name = "clap"
499
-
version = "4.5.53"
499
+
version = "4.5.54"
500
500
source = "registry+https://github.com/rust-lang/crates.io-index"
501
-
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
501
+
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
502
502
dependencies = [
503
503
"clap_builder",
504
504
"clap_derive",
···
506
506
507
507
[[package]]
508
508
name = "clap_builder"
509
-
version = "4.5.53"
509
+
version = "4.5.54"
510
510
source = "registry+https://github.com/rust-lang/crates.io-index"
511
-
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
511
+
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
512
512
dependencies = [
513
513
"anstream",
514
514
"anstyle",
···
525
525
"heck 0.5.0",
526
526
"proc-macro2",
527
527
"quote",
528
-
"syn 2.0.111",
528
+
"syn 2.0.113",
529
529
]
530
530
531
531
[[package]]
···
561
561
562
562
[[package]]
563
563
name = "compression-codecs"
564
-
version = "0.4.33"
564
+
version = "0.4.35"
565
565
source = "registry+https://github.com/rust-lang/crates.io-index"
566
-
checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad"
566
+
checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2"
567
567
dependencies = [
568
568
"compression-core",
569
569
"flate2",
···
753
753
"proc-macro2",
754
754
"quote",
755
755
"strsim",
756
-
"syn 2.0.111",
756
+
"syn 2.0.113",
757
757
]
758
758
759
759
[[package]]
···
764
764
dependencies = [
765
765
"darling_core",
766
766
"quote",
767
-
"syn 2.0.111",
767
+
"syn 2.0.113",
768
768
]
769
769
770
770
[[package]]
···
804
804
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
805
805
dependencies = [
806
806
"data-encoding",
807
-
"syn 2.0.111",
807
+
"syn 2.0.113",
808
808
]
809
809
810
810
[[package]]
···
854
854
dependencies = [
855
855
"proc-macro2",
856
856
"quote",
857
-
"syn 2.0.111",
857
+
"syn 2.0.113",
858
858
"unicode-xid",
859
859
]
860
860
···
905
905
dependencies = [
906
906
"proc-macro2",
907
907
"quote",
908
-
"syn 2.0.111",
908
+
"syn 2.0.113",
909
909
]
910
910
911
911
[[package]]
···
978
978
"heck 0.5.0",
979
979
"proc-macro2",
980
980
"quote",
981
-
"syn 2.0.111",
981
+
"syn 2.0.113",
982
982
]
983
983
984
984
[[package]]
···
1027
1027
1028
1028
[[package]]
1029
1029
name = "find-msvc-tools"
1030
-
version = "0.1.5"
1030
+
version = "0.1.6"
1031
1031
source = "registry+https://github.com/rust-lang/crates.io-index"
1032
-
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
1032
+
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
1033
1033
1034
1034
[[package]]
1035
1035
name = "flate2"
···
1154
1154
dependencies = [
1155
1155
"proc-macro2",
1156
1156
"quote",
1157
-
"syn 2.0.111",
1157
+
"syn 2.0.113",
1158
1158
]
1159
1159
1160
1160
[[package]]
···
1189
1189
1190
1190
[[package]]
1191
1191
name = "generator"
1192
-
version = "0.8.7"
1192
+
version = "0.8.8"
1193
1193
source = "registry+https://github.com/rust-lang/crates.io-index"
1194
-
checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2"
1194
+
checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9"
1195
1195
dependencies = [
1196
1196
"cc",
1197
1197
"cfg-if",
1198
1198
"libc",
1199
1199
"log",
1200
1200
"rustversion",
1201
-
"windows",
1201
+
"windows-link",
1202
+
"windows-result",
1202
1203
]
1203
1204
1204
1205
[[package]]
···
1308
1309
1309
1310
[[package]]
1310
1311
name = "h2"
1311
-
version = "0.4.12"
1312
+
version = "0.4.13"
1312
1313
source = "registry+https://github.com/rust-lang/crates.io-index"
1313
-
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
1314
+
checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54"
1314
1315
dependencies = [
1315
1316
"atomic-waker",
1316
1317
"bytes",
···
1471
1472
"markup5ever",
1472
1473
"proc-macro2",
1473
1474
"quote",
1474
-
"syn 2.0.111",
1475
+
"syn 2.0.113",
1475
1476
]
1476
1477
1477
1478
[[package]]
···
1567
1568
1568
1569
[[package]]
1569
1570
name = "hyper-util"
1570
-
version = "0.1.18"
1571
+
version = "0.1.19"
1571
1572
source = "registry+https://github.com/rust-lang/crates.io-index"
1572
-
checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
1573
+
checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f"
1573
1574
dependencies = [
1574
1575
"base64 0.22.1",
1575
1576
"bytes",
···
1603
1604
"js-sys",
1604
1605
"log",
1605
1606
"wasm-bindgen",
1606
-
"windows-core 0.62.2",
1607
+
"windows-core",
1607
1608
]
1608
1609
1609
1610
[[package]]
···
1663
1664
1664
1665
[[package]]
1665
1666
name = "icu_properties"
1666
-
version = "2.1.1"
1667
+
version = "2.1.2"
1667
1668
source = "registry+https://github.com/rust-lang/crates.io-index"
1668
-
checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
1669
+
checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec"
1669
1670
dependencies = [
1670
1671
"icu_collections",
1671
1672
"icu_locale_core",
···
1677
1678
1678
1679
[[package]]
1679
1680
name = "icu_properties_data"
1680
-
version = "2.1.1"
1681
+
version = "2.1.2"
1681
1682
source = "registry+https://github.com/rust-lang/crates.io-index"
1682
-
checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
1683
+
checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af"
1683
1684
1684
1685
[[package]]
1685
1686
name = "icu_provider"
···
1802
1803
1803
1804
[[package]]
1804
1805
name = "iri-string"
1805
-
version = "0.7.9"
1806
+
version = "0.7.10"
1806
1807
source = "registry+https://github.com/rust-lang/crates.io-index"
1807
-
checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397"
1808
+
checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a"
1808
1809
dependencies = [
1809
1810
"memchr",
1810
1811
"serde",
···
1824
1825
1825
1826
[[package]]
1826
1827
name = "itoa"
1827
-
version = "1.0.15"
1828
+
version = "1.0.17"
1828
1829
source = "registry+https://github.com/rust-lang/crates.io-index"
1829
-
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
1830
+
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
1830
1831
1831
1832
[[package]]
1832
1833
name = "jacquard"
···
1935
1936
"jacquard-lexicon",
1936
1937
"proc-macro2",
1937
1938
"quote",
1938
-
"syn 2.0.111",
1939
+
"syn 2.0.113",
1939
1940
]
1940
1941
1941
1942
[[package]]
···
1986
1987
"serde_repr",
1987
1988
"serde_with",
1988
1989
"sha2",
1989
-
"syn 2.0.111",
1990
+
"syn 2.0.113",
1990
1991
"thiserror 2.0.17",
1991
1992
"unicode-segmentation",
1992
1993
]
···
2082
2083
2083
2084
[[package]]
2084
2085
name = "js-sys"
2085
-
version = "0.3.82"
2086
+
version = "0.3.83"
2086
2087
source = "registry+https://github.com/rust-lang/crates.io-index"
2087
-
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
2088
+
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
2088
2089
dependencies = [
2089
2090
"once_cell",
2090
2091
"wasm-bindgen",
···
2113
2114
2114
2115
[[package]]
2115
2116
name = "libc"
2116
-
version = "0.2.177"
2117
+
version = "0.2.179"
2117
2118
source = "registry+https://github.com/rust-lang/crates.io-index"
2118
-
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
2119
+
checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
2119
2120
2120
2121
[[package]]
2121
2122
name = "libm"
···
2125
2126
2126
2127
[[package]]
2127
2128
name = "libredox"
2128
-
version = "0.1.10"
2129
+
version = "0.1.12"
2129
2130
source = "registry+https://github.com/rust-lang/crates.io-index"
2130
-
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
2131
+
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
2131
2132
dependencies = [
2132
2133
"bitflags",
2133
2134
"libc",
2134
-
"redox_syscall",
2135
+
"redox_syscall 0.7.0",
2135
2136
]
2136
2137
2137
2138
[[package]]
···
2163
2164
2164
2165
[[package]]
2165
2166
name = "log"
2166
-
version = "0.4.28"
2167
+
version = "0.4.29"
2167
2168
source = "registry+https://github.com/rust-lang/crates.io-index"
2168
-
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
2169
+
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
2169
2170
2170
2171
[[package]]
2171
2172
name = "loom"
···
2299
2300
dependencies = [
2300
2301
"proc-macro2",
2301
2302
"quote",
2302
-
"syn 2.0.111",
2303
+
"syn 2.0.113",
2303
2304
]
2304
2305
2305
2306
[[package]]
···
2344
2345
2345
2346
[[package]]
2346
2347
name = "mio"
2347
-
version = "1.1.0"
2348
+
version = "1.1.1"
2348
2349
source = "registry+https://github.com/rust-lang/crates.io-index"
2349
-
checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
2350
+
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
2350
2351
dependencies = [
2351
2352
"libc",
2352
2353
"wasi",
···
2588
2589
2589
2590
[[package]]
2590
2591
name = "openssl-probe"
2591
-
version = "0.1.6"
2592
+
version = "0.2.0"
2592
2593
source = "registry+https://github.com/rust-lang/crates.io-index"
2593
-
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
2594
+
checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391"
2594
2595
2595
2596
[[package]]
2596
2597
name = "option-ext"
···
2619
2620
"proc-macro2",
2620
2621
"proc-macro2-diagnostics",
2621
2622
"quote",
2622
-
"syn 2.0.111",
2623
+
"syn 2.0.113",
2623
2624
]
2624
2625
2625
2626
[[package]]
···
2683
2684
dependencies = [
2684
2685
"cfg-if",
2685
2686
"libc",
2686
-
"redox_syscall",
2687
+
"redox_syscall 0.5.18",
2687
2688
"smallvec",
2688
-
"windows-link 0.2.1",
2689
+
"windows-link",
2689
2690
]
2690
2691
2691
2692
[[package]]
···
2758
2759
dependencies = [
2759
2760
"proc-macro2",
2760
2761
"quote",
2761
-
"syn 2.0.111",
2762
+
"syn 2.0.113",
2762
2763
]
2763
2764
2764
2765
[[package]]
···
2796
2797
2797
2798
[[package]]
2798
2799
name = "portable-atomic"
2799
-
version = "1.11.1"
2800
+
version = "1.13.0"
2800
2801
source = "registry+https://github.com/rust-lang/crates.io-index"
2801
-
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
2802
+
checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950"
2802
2803
2803
2804
[[package]]
2804
2805
name = "postcard"
···
2850
2851
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
2851
2852
dependencies = [
2852
2853
"proc-macro2",
2853
-
"syn 2.0.111",
2854
+
"syn 2.0.113",
2854
2855
]
2855
2856
2856
2857
[[package]]
···
2864
2865
2865
2866
[[package]]
2866
2867
name = "proc-macro2"
2867
-
version = "1.0.103"
2868
+
version = "1.0.105"
2868
2869
source = "registry+https://github.com/rust-lang/crates.io-index"
2869
-
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
2870
+
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
2870
2871
dependencies = [
2871
2872
"unicode-ident",
2872
2873
]
···
2879
2880
dependencies = [
2880
2881
"proc-macro2",
2881
2882
"quote",
2882
-
"syn 2.0.111",
2883
+
"syn 2.0.113",
2883
2884
"version_check",
2884
2885
"yansi",
2885
2886
]
···
2947
2948
2948
2949
[[package]]
2949
2950
name = "quote"
2950
-
version = "1.0.42"
2951
+
version = "1.0.43"
2951
2952
source = "registry+https://github.com/rust-lang/crates.io-index"
2952
-
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
2953
+
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
2953
2954
dependencies = [
2954
2955
"proc-macro2",
2955
2956
]
···
3029
3030
]
3030
3031
3031
3032
[[package]]
3033
+
name = "redox_syscall"
3034
+
version = "0.7.0"
3035
+
source = "registry+https://github.com/rust-lang/crates.io-index"
3036
+
checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
3037
+
dependencies = [
3038
+
"bitflags",
3039
+
]
3040
+
3041
+
[[package]]
3032
3042
name = "redox_users"
3033
3043
version = "0.5.2"
3034
3044
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3076
3086
3077
3087
[[package]]
3078
3088
name = "reqwest"
3079
-
version = "0.12.24"
3089
+
version = "0.12.28"
3080
3090
source = "registry+https://github.com/rust-lang/crates.io-index"
3081
-
checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
3091
+
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
3082
3092
dependencies = [
3083
-
"async-compression",
3084
3093
"base64 0.22.1",
3085
3094
"bytes",
3086
3095
"encoding_rs",
···
3175
3184
3176
3185
[[package]]
3177
3186
name = "rsa"
3178
-
version = "0.9.9"
3187
+
version = "0.9.10"
3179
3188
source = "registry+https://github.com/rust-lang/crates.io-index"
3180
-
checksum = "40a0376c50d0358279d9d643e4bf7b7be212f1f4ff1da9070a7b54d22ef75c88"
3189
+
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
3181
3190
dependencies = [
3182
3191
"const-oid",
3183
3192
"digest",
···
3216
3225
3217
3226
[[package]]
3218
3227
name = "rustix"
3219
-
version = "1.1.2"
3228
+
version = "1.1.3"
3220
3229
source = "registry+https://github.com/rust-lang/crates.io-index"
3221
-
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
3230
+
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
3222
3231
dependencies = [
3223
3232
"bitflags",
3224
3233
"errno",
···
3229
3238
3230
3239
[[package]]
3231
3240
name = "rustls"
3232
-
version = "0.23.35"
3241
+
version = "0.23.36"
3233
3242
source = "registry+https://github.com/rust-lang/crates.io-index"
3234
-
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
3243
+
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
3235
3244
dependencies = [
3236
3245
"once_cell",
3237
3246
"ring",
···
3243
3252
3244
3253
[[package]]
3245
3254
name = "rustls-native-certs"
3246
-
version = "0.8.2"
3255
+
version = "0.8.3"
3247
3256
source = "registry+https://github.com/rust-lang/crates.io-index"
3248
-
checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923"
3257
+
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
3249
3258
dependencies = [
3250
3259
"openssl-probe",
3251
3260
"rustls-pki-types",
···
3255
3264
3256
3265
[[package]]
3257
3266
name = "rustls-pki-types"
3258
-
version = "1.13.0"
3267
+
version = "1.13.2"
3259
3268
source = "registry+https://github.com/rust-lang/crates.io-index"
3260
-
checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
3269
+
checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282"
3261
3270
dependencies = [
3262
3271
"web-time",
3263
3272
"zeroize",
···
3282
3291
3283
3292
[[package]]
3284
3293
name = "ryu"
3285
-
version = "1.0.20"
3294
+
version = "1.0.22"
3286
3295
source = "registry+https://github.com/rust-lang/crates.io-index"
3287
-
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
3296
+
checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
3288
3297
3289
3298
[[package]]
3290
3299
name = "safemem"
···
3408
3417
dependencies = [
3409
3418
"proc-macro2",
3410
3419
"quote",
3411
-
"syn 2.0.111",
3420
+
"syn 2.0.113",
3412
3421
]
3413
3422
3414
3423
[[package]]
···
3437
3446
3438
3447
[[package]]
3439
3448
name = "serde_json"
3440
-
version = "1.0.145"
3449
+
version = "1.0.149"
3441
3450
source = "registry+https://github.com/rust-lang/crates.io-index"
3442
-
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
3451
+
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
3443
3452
dependencies = [
3444
3453
"itoa",
3445
3454
"memchr",
3446
-
"ryu",
3447
3455
"serde",
3448
3456
"serde_core",
3457
+
"zmij",
3449
3458
]
3450
3459
3451
3460
[[package]]
···
3467
3476
dependencies = [
3468
3477
"proc-macro2",
3469
3478
"quote",
3470
-
"syn 2.0.111",
3479
+
"syn 2.0.113",
3471
3480
]
3472
3481
3473
3482
[[package]]
···
3484
3493
3485
3494
[[package]]
3486
3495
name = "serde_with"
3487
-
version = "3.16.0"
3496
+
version = "3.16.1"
3488
3497
source = "registry+https://github.com/rust-lang/crates.io-index"
3489
-
checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1"
3498
+
checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7"
3490
3499
dependencies = [
3491
3500
"base64 0.22.1",
3492
3501
"chrono",
···
3499
3508
3500
3509
[[package]]
3501
3510
name = "serde_with_macros"
3502
-
version = "3.16.0"
3511
+
version = "3.16.1"
3503
3512
source = "registry+https://github.com/rust-lang/crates.io-index"
3504
-
checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b"
3513
+
checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c"
3505
3514
dependencies = [
3506
3515
"darling",
3507
3516
"proc-macro2",
3508
3517
"quote",
3509
-
"syn 2.0.111",
3518
+
"syn 2.0.113",
3510
3519
]
3511
3520
3512
3521
[[package]]
···
3563
3572
3564
3573
[[package]]
3565
3574
name = "signal-hook-registry"
3566
-
version = "1.4.7"
3575
+
version = "1.4.8"
3567
3576
source = "registry+https://github.com/rust-lang/crates.io-index"
3568
-
checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad"
3577
+
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
3569
3578
dependencies = [
3579
+
"errno",
3570
3580
"libc",
3571
3581
]
3572
3582
···
3582
3592
3583
3593
[[package]]
3584
3594
name = "simd-adler32"
3585
-
version = "0.3.7"
3595
+
version = "0.3.8"
3586
3596
source = "registry+https://github.com/rust-lang/crates.io-index"
3587
-
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
3597
+
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
3588
3598
3589
3599
[[package]]
3590
3600
name = "siphasher"
···
3719
3729
3720
3730
[[package]]
3721
3731
name = "supports-hyperlinks"
3722
-
version = "3.1.0"
3732
+
version = "3.2.0"
3723
3733
source = "registry+https://github.com/rust-lang/crates.io-index"
3724
-
checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b"
3734
+
checksum = "e396b6523b11ccb83120b115a0b7366de372751aa6edf19844dfb13a6af97e91"
3725
3735
3726
3736
[[package]]
3727
3737
name = "supports-unicode"
···
3742
3752
3743
3753
[[package]]
3744
3754
name = "syn"
3745
-
version = "2.0.111"
3755
+
version = "2.0.113"
3746
3756
source = "registry+https://github.com/rust-lang/crates.io-index"
3747
-
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
3757
+
checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4"
3748
3758
dependencies = [
3749
3759
"proc-macro2",
3750
3760
"quote",
···
3768
3778
dependencies = [
3769
3779
"proc-macro2",
3770
3780
"quote",
3771
-
"syn 2.0.111",
3781
+
"syn 2.0.113",
3772
3782
]
3773
3783
3774
3784
[[package]]
···
3800
3810
3801
3811
[[package]]
3802
3812
name = "tempfile"
3803
-
version = "3.23.0"
3813
+
version = "3.24.0"
3804
3814
source = "registry+https://github.com/rust-lang/crates.io-index"
3805
-
checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
3815
+
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
3806
3816
dependencies = [
3807
3817
"fastrand",
3808
3818
"getrandom 0.3.4",
···
3868
3878
dependencies = [
3869
3879
"proc-macro2",
3870
3880
"quote",
3871
-
"syn 2.0.111",
3881
+
"syn 2.0.113",
3872
3882
]
3873
3883
3874
3884
[[package]]
···
3879
3889
dependencies = [
3880
3890
"proc-macro2",
3881
3891
"quote",
3882
-
"syn 2.0.111",
3892
+
"syn 2.0.113",
3883
3893
]
3884
3894
3885
3895
[[package]]
···
3960
3970
3961
3971
[[package]]
3962
3972
name = "tokio"
3963
-
version = "1.48.0"
3973
+
version = "1.49.0"
3964
3974
source = "registry+https://github.com/rust-lang/crates.io-index"
3965
-
checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
3975
+
checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
3966
3976
dependencies = [
3967
3977
"bytes",
3968
3978
"libc",
···
3983
3993
dependencies = [
3984
3994
"proc-macro2",
3985
3995
"quote",
3986
-
"syn 2.0.111",
3996
+
"syn 2.0.113",
3987
3997
]
3988
3998
3989
3999
[[package]]
···
4033
4043
4034
4044
[[package]]
4035
4045
name = "tokio-util"
4036
-
version = "0.7.17"
4046
+
version = "0.7.18"
4037
4047
source = "registry+https://github.com/rust-lang/crates.io-index"
4038
-
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
4048
+
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
4039
4049
dependencies = [
4040
4050
"bytes",
4041
4051
"futures-core",
···
4063
4073
4064
4074
[[package]]
4065
4075
name = "tower-http"
4066
-
version = "0.6.7"
4076
+
version = "0.6.8"
4067
4077
source = "registry+https://github.com/rust-lang/crates.io-index"
4068
-
checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456"
4078
+
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
4069
4079
dependencies = [
4070
4080
"async-compression",
4071
4081
"bitflags",
···
4104
4114
4105
4115
[[package]]
4106
4116
name = "tracing"
4107
-
version = "0.1.41"
4117
+
version = "0.1.44"
4108
4118
source = "registry+https://github.com/rust-lang/crates.io-index"
4109
-
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
4119
+
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
4110
4120
dependencies = [
4111
4121
"log",
4112
4122
"pin-project-lite",
···
4116
4126
4117
4127
[[package]]
4118
4128
name = "tracing-attributes"
4119
-
version = "0.1.30"
4129
+
version = "0.1.31"
4120
4130
source = "registry+https://github.com/rust-lang/crates.io-index"
4121
-
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
4131
+
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
4122
4132
dependencies = [
4123
4133
"proc-macro2",
4124
4134
"quote",
4125
-
"syn 2.0.111",
4135
+
"syn 2.0.113",
4126
4136
]
4127
4137
4128
4138
[[package]]
4129
4139
name = "tracing-core"
4130
-
version = "0.1.34"
4140
+
version = "0.1.36"
4131
4141
source = "registry+https://github.com/rust-lang/crates.io-index"
4132
-
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
4142
+
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
4133
4143
dependencies = [
4134
4144
"once_cell",
4135
4145
"valuable",
···
4148
4158
4149
4159
[[package]]
4150
4160
name = "tracing-subscriber"
4151
-
version = "0.3.20"
4161
+
version = "0.3.22"
4152
4162
source = "registry+https://github.com/rust-lang/crates.io-index"
4153
-
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
4163
+
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
4154
4164
dependencies = [
4155
4165
"matchers",
4156
4166
"nu-ansi-term",
···
4172
4182
dependencies = [
4173
4183
"proc-macro2",
4174
4184
"quote",
4175
-
"syn 2.0.111",
4185
+
"syn 2.0.113",
4176
4186
]
4177
4187
4178
4188
[[package]]
···
4224
4234
4225
4235
[[package]]
4226
4236
name = "unicase"
4227
-
version = "2.8.1"
4237
+
version = "2.9.0"
4228
4238
source = "registry+https://github.com/rust-lang/crates.io-index"
4229
-
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
4239
+
checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
4230
4240
4231
4241
[[package]]
4232
4242
name = "unicode-ident"
···
4278
4288
4279
4289
[[package]]
4280
4290
name = "url"
4281
-
version = "2.5.7"
4291
+
version = "2.5.8"
4282
4292
source = "registry+https://github.com/rust-lang/crates.io-index"
4283
-
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
4293
+
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
4284
4294
dependencies = [
4285
4295
"form_urlencoded",
4286
4296
"idna",
4287
4297
"percent-encoding",
4288
4298
"serde",
4299
+
"serde_derive",
4289
4300
]
4290
4301
4291
4302
[[package]]
···
4360
4371
4361
4372
[[package]]
4362
4373
name = "wasm-bindgen"
4363
-
version = "0.2.105"
4374
+
version = "0.2.106"
4364
4375
source = "registry+https://github.com/rust-lang/crates.io-index"
4365
-
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
4376
+
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
4366
4377
dependencies = [
4367
4378
"cfg-if",
4368
4379
"once_cell",
···
4373
4384
4374
4385
[[package]]
4375
4386
name = "wasm-bindgen-futures"
4376
-
version = "0.4.55"
4387
+
version = "0.4.56"
4377
4388
source = "registry+https://github.com/rust-lang/crates.io-index"
4378
-
checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0"
4389
+
checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c"
4379
4390
dependencies = [
4380
4391
"cfg-if",
4381
4392
"js-sys",
···
4386
4397
4387
4398
[[package]]
4388
4399
name = "wasm-bindgen-macro"
4389
-
version = "0.2.105"
4400
+
version = "0.2.106"
4390
4401
source = "registry+https://github.com/rust-lang/crates.io-index"
4391
-
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
4402
+
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
4392
4403
dependencies = [
4393
4404
"quote",
4394
4405
"wasm-bindgen-macro-support",
···
4396
4407
4397
4408
[[package]]
4398
4409
name = "wasm-bindgen-macro-support"
4399
-
version = "0.2.105"
4410
+
version = "0.2.106"
4400
4411
source = "registry+https://github.com/rust-lang/crates.io-index"
4401
-
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
4412
+
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
4402
4413
dependencies = [
4403
4414
"bumpalo",
4404
4415
"proc-macro2",
4405
4416
"quote",
4406
-
"syn 2.0.111",
4417
+
"syn 2.0.113",
4407
4418
"wasm-bindgen-shared",
4408
4419
]
4409
4420
4410
4421
[[package]]
4411
4422
name = "wasm-bindgen-shared"
4412
-
version = "0.2.105"
4423
+
version = "0.2.106"
4413
4424
source = "registry+https://github.com/rust-lang/crates.io-index"
4414
-
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
4425
+
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
4415
4426
dependencies = [
4416
4427
"unicode-ident",
4417
4428
]
···
4431
4442
4432
4443
[[package]]
4433
4444
name = "web-sys"
4434
-
version = "0.3.82"
4445
+
version = "0.3.83"
4435
4446
source = "registry+https://github.com/rust-lang/crates.io-index"
4436
-
checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1"
4447
+
checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac"
4437
4448
dependencies = [
4438
4449
"js-sys",
4439
4450
"wasm-bindgen",
···
4479
4490
4480
4491
[[package]]
4481
4492
name = "webpki-roots"
4482
-
version = "1.0.4"
4493
+
version = "1.0.5"
4483
4494
source = "registry+https://github.com/rust-lang/crates.io-index"
4484
-
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
4495
+
checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c"
4485
4496
dependencies = [
4486
4497
"rustls-pki-types",
4487
4498
]
···
4499
4510
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
4500
4511
dependencies = [
4501
4512
"windows-sys 0.61.2",
4502
-
]
4503
-
4504
-
[[package]]
4505
-
name = "windows"
4506
-
version = "0.61.3"
4507
-
source = "registry+https://github.com/rust-lang/crates.io-index"
4508
-
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
4509
-
dependencies = [
4510
-
"windows-collections",
4511
-
"windows-core 0.61.2",
4512
-
"windows-future",
4513
-
"windows-link 0.1.3",
4514
-
"windows-numerics",
4515
-
]
4516
-
4517
-
[[package]]
4518
-
name = "windows-collections"
4519
-
version = "0.2.0"
4520
-
source = "registry+https://github.com/rust-lang/crates.io-index"
4521
-
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
4522
-
dependencies = [
4523
-
"windows-core 0.61.2",
4524
-
]
4525
-
4526
-
[[package]]
4527
-
name = "windows-core"
4528
-
version = "0.61.2"
4529
-
source = "registry+https://github.com/rust-lang/crates.io-index"
4530
-
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
4531
-
dependencies = [
4532
-
"windows-implement",
4533
-
"windows-interface",
4534
-
"windows-link 0.1.3",
4535
-
"windows-result 0.3.4",
4536
-
"windows-strings 0.4.2",
4537
4513
]
4538
4514
4539
4515
[[package]]
···
4544
4520
dependencies = [
4545
4521
"windows-implement",
4546
4522
"windows-interface",
4547
-
"windows-link 0.2.1",
4548
-
"windows-result 0.4.1",
4549
-
"windows-strings 0.5.1",
4550
-
]
4551
-
4552
-
[[package]]
4553
-
name = "windows-future"
4554
-
version = "0.2.1"
4555
-
source = "registry+https://github.com/rust-lang/crates.io-index"
4556
-
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
4557
-
dependencies = [
4558
-
"windows-core 0.61.2",
4559
-
"windows-link 0.1.3",
4560
-
"windows-threading",
4523
+
"windows-link",
4524
+
"windows-result",
4525
+
"windows-strings",
4561
4526
]
4562
4527
4563
4528
[[package]]
···
4568
4533
dependencies = [
4569
4534
"proc-macro2",
4570
4535
"quote",
4571
-
"syn 2.0.111",
4536
+
"syn 2.0.113",
4572
4537
]
4573
4538
4574
4539
[[package]]
···
4579
4544
dependencies = [
4580
4545
"proc-macro2",
4581
4546
"quote",
4582
-
"syn 2.0.111",
4547
+
"syn 2.0.113",
4583
4548
]
4584
4549
4585
4550
[[package]]
4586
4551
name = "windows-link"
4587
-
version = "0.1.3"
4588
-
source = "registry+https://github.com/rust-lang/crates.io-index"
4589
-
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
4590
-
4591
-
[[package]]
4592
-
name = "windows-link"
4593
4552
version = "0.2.1"
4594
4553
source = "registry+https://github.com/rust-lang/crates.io-index"
4595
4554
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
4596
4555
4597
4556
[[package]]
4598
-
name = "windows-numerics"
4599
-
version = "0.2.0"
4600
-
source = "registry+https://github.com/rust-lang/crates.io-index"
4601
-
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
4602
-
dependencies = [
4603
-
"windows-core 0.61.2",
4604
-
"windows-link 0.1.3",
4605
-
]
4606
-
4607
-
[[package]]
4608
4557
name = "windows-registry"
4609
4558
version = "0.6.1"
4610
4559
source = "registry+https://github.com/rust-lang/crates.io-index"
4611
4560
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
4612
4561
dependencies = [
4613
-
"windows-link 0.2.1",
4614
-
"windows-result 0.4.1",
4615
-
"windows-strings 0.5.1",
4616
-
]
4617
-
4618
-
[[package]]
4619
-
name = "windows-result"
4620
-
version = "0.3.4"
4621
-
source = "registry+https://github.com/rust-lang/crates.io-index"
4622
-
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
4623
-
dependencies = [
4624
-
"windows-link 0.1.3",
4562
+
"windows-link",
4563
+
"windows-result",
4564
+
"windows-strings",
4625
4565
]
4626
4566
4627
4567
[[package]]
···
4630
4570
source = "registry+https://github.com/rust-lang/crates.io-index"
4631
4571
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
4632
4572
dependencies = [
4633
-
"windows-link 0.2.1",
4634
-
]
4635
-
4636
-
[[package]]
4637
-
name = "windows-strings"
4638
-
version = "0.4.2"
4639
-
source = "registry+https://github.com/rust-lang/crates.io-index"
4640
-
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
4641
-
dependencies = [
4642
-
"windows-link 0.1.3",
4573
+
"windows-link",
4643
4574
]
4644
4575
4645
4576
[[package]]
···
4648
4579
source = "registry+https://github.com/rust-lang/crates.io-index"
4649
4580
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
4650
4581
dependencies = [
4651
-
"windows-link 0.2.1",
4582
+
"windows-link",
4652
4583
]
4653
4584
4654
4585
[[package]]
···
4702
4633
source = "registry+https://github.com/rust-lang/crates.io-index"
4703
4634
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
4704
4635
dependencies = [
4705
-
"windows-link 0.2.1",
4636
+
"windows-link",
4706
4637
]
4707
4638
4708
4639
[[package]]
···
4757
4688
source = "registry+https://github.com/rust-lang/crates.io-index"
4758
4689
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
4759
4690
dependencies = [
4760
-
"windows-link 0.2.1",
4691
+
"windows-link",
4761
4692
"windows_aarch64_gnullvm 0.53.1",
4762
4693
"windows_aarch64_msvc 0.53.1",
4763
4694
"windows_i686_gnu 0.53.1",
···
4766
4697
"windows_x86_64_gnu 0.53.1",
4767
4698
"windows_x86_64_gnullvm 0.53.1",
4768
4699
"windows_x86_64_msvc 0.53.1",
4769
-
]
4770
-
4771
-
[[package]]
4772
-
name = "windows-threading"
4773
-
version = "0.1.0"
4774
-
source = "registry+https://github.com/rust-lang/crates.io-index"
4775
-
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
4776
-
dependencies = [
4777
-
"windows-link 0.1.3",
4778
4700
]
4779
4701
4780
4702
[[package]]
···
5067
4989
dependencies = [
5068
4990
"proc-macro2",
5069
4991
"quote",
5070
-
"syn 2.0.111",
4992
+
"syn 2.0.113",
5071
4993
"synstructure",
5072
4994
]
5073
4995
5074
4996
[[package]]
5075
4997
name = "zerocopy"
5076
-
version = "0.8.30"
4998
+
version = "0.8.32"
5077
4999
source = "registry+https://github.com/rust-lang/crates.io-index"
5078
-
checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c"
5000
+
checksum = "1fabae64378cb18147bb18bca364e63bdbe72a0ffe4adf0addfec8aa166b2c56"
5079
5001
dependencies = [
5080
5002
"zerocopy-derive",
5081
5003
]
5082
5004
5083
5005
[[package]]
5084
5006
name = "zerocopy-derive"
5085
-
version = "0.8.30"
5007
+
version = "0.8.32"
5086
5008
source = "registry+https://github.com/rust-lang/crates.io-index"
5087
-
checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5"
5009
+
checksum = "c9c2d862265a8bb4471d87e033e730f536e2a285cc7cb05dbce09a2a97075f90"
5088
5010
dependencies = [
5089
5011
"proc-macro2",
5090
5012
"quote",
5091
-
"syn 2.0.111",
5013
+
"syn 2.0.113",
5092
5014
]
5093
5015
5094
5016
[[package]]
···
5108
5030
dependencies = [
5109
5031
"proc-macro2",
5110
5032
"quote",
5111
-
"syn 2.0.111",
5033
+
"syn 2.0.113",
5112
5034
"synstructure",
5113
5035
]
5114
5036
···
5151
5073
dependencies = [
5152
5074
"proc-macro2",
5153
5075
"quote",
5154
-
"syn 2.0.111",
5076
+
"syn 2.0.113",
5155
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"
+72
-2
cli/README.md
+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
+16
cli/default.nix
+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
+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
+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
+
}
+15
-16
cli/src/main.rs
+15
-16
cli/src/main.rs
···
41
41
struct Args {
42
42
#[command(subcommand)]
43
43
command: Option<Commands>,
44
-
44
+
45
45
// Deploy arguments (when no subcommand is specified)
46
46
/// Handle (e.g., alice.bsky.social), DID, or PDS URL
47
-
#[arg(global = true, conflicts_with = "command")]
48
47
input: Option<CowStr<'static>>,
49
48
50
49
/// Path to the directory containing your static site
51
-
#[arg(short, long, global = true, conflicts_with = "command")]
50
+
#[arg(short, long)]
52
51
path: Option<PathBuf>,
53
52
54
53
/// Site name (defaults to directory name)
55
-
#[arg(short, long, global = true, conflicts_with = "command")]
54
+
#[arg(short, long)]
56
55
site: Option<String>,
57
56
58
57
/// Path to auth store file
59
-
#[arg(long, global = true, conflicts_with = "command")]
58
+
#[arg(long)]
60
59
store: Option<String>,
61
60
62
61
/// App Password for authentication
63
-
#[arg(long, global = true, conflicts_with = "command")]
62
+
#[arg(long)]
64
63
password: Option<CowStr<'static>>,
65
64
66
65
/// Enable directory listing mode for paths without index files
67
-
#[arg(long, global = true, conflicts_with = "command")]
66
+
#[arg(long)]
68
67
directory: bool,
69
68
70
69
/// Enable SPA mode (serve index.html for all routes)
71
-
#[arg(long, global = true, conflicts_with = "command")]
70
+
#[arg(long)]
72
71
spa: bool,
73
72
74
73
/// Skip confirmation prompts (automatically accept warnings)
75
-
#[arg(short = 'y', long, global = true, conflicts_with = "command")]
74
+
#[arg(short = 'y', long)]
76
75
yes: bool,
77
76
}
78
77
···
122
121
123
122
/// Output directory for the downloaded site
124
123
#[arg(short, long, default_value = ".")]
125
-
output: PathBuf,
124
+
path: PathBuf,
126
125
},
127
126
/// Serve a site locally with real-time firehose updates
128
127
Serve {
···
135
134
136
135
/// Output directory for the site files
137
136
#[arg(short, long, default_value = ".")]
138
-
output: PathBuf,
137
+
path: PathBuf,
139
138
140
139
/// Port to serve on
141
-
#[arg(short, long, default_value = "8080")]
140
+
#[arg(short = 'P', long, default_value = "8080")]
142
141
port: u16,
143
142
},
144
143
}
···
156
155
run_with_oauth(input, store, path, site, directory, spa, yes).await
157
156
}
158
157
}
159
-
Some(Commands::Pull { input, site, output }) => {
160
-
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
161
160
}
162
-
Some(Commands::Serve { input, site, output, port }) => {
163
-
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
164
163
}
165
164
None => {
166
165
// Legacy mode: if input is provided, assume deploy command
+21
docker-compose.yml
+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:
+6
-6
docs/src/content/docs/cli.md
+6
-6
docs/src/content/docs/cli.md
···
136
136
# Pull a site to a specific directory
137
137
wisp-cli pull your-handle.bsky.social \
138
138
--site my-site \
139
-
--output ./my-site
139
+
--path ./my-site
140
140
141
141
# Pull to current directory
142
142
wisp-cli pull your-handle.bsky.social \
···
233
233
### Pull Command
234
234
235
235
```bash
236
-
wisp-cli pull [OPTIONS] <INPUT>
236
+
wisp-cli pull [OPTIONS] --site <SITE> <INPUT>
237
237
238
238
Arguments:
239
239
<INPUT> Handle or DID
240
240
241
241
Options:
242
242
-s, --site <SITE> Site name to download
243
-
-o, --output <OUTPUT> Output directory [default: .]
243
+
-p, --path <PATH> Output directory [default: .]
244
244
-h, --help Print help
245
245
```
246
246
247
247
### Serve Command
248
248
249
249
```bash
250
-
wisp-cli serve [OPTIONS] <INPUT>
250
+
wisp-cli serve [OPTIONS] --site <SITE> <INPUT>
251
251
252
252
Arguments:
253
253
<INPUT> Handle or DID
254
254
255
255
Options:
256
256
-s, --site <SITE> Site name to serve
257
-
-o, --output <OUTPUT> Site files directory [default: .]
258
-
-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]
259
259
--spa Enable SPA mode (serve index.html for all routes)
260
260
--directory Enable directory listing mode for paths without index files
261
261
-h, --help Print help
-318
flake.lock
-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
-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
-
}
+2
-2
packages/@wisp/observability/src/exporters.ts
+2
-2
packages/@wisp/observability/src/exporters.ts
···
3
3
* Integrates with Grafana Loki for logs and Prometheus/OTLP for metrics
4
4
*/
5
5
6
-
import { LogEntry, ErrorEntry, MetricEntry } from './core'
7
-
import { metrics, MeterProvider } from '@opentelemetry/api'
6
+
import type { LogEntry, ErrorEntry, MetricEntry } from './core'
7
+
import { metrics, type MeterProvider } from '@opentelemetry/api'
8
8
import { MeterProvider as SdkMeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics'
9
9
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http'
10
10
import { Resource } from '@opentelemetry/resources'