+8
-3
hosting-service/src/lexicon/lexicons.ts
+8
-3
hosting-service/src/lexicon/lexicons.ts
···
118
type: 'string',
119
format: 'at-uri',
120
description:
121
-
'AT-URI pointing to a place.wisp.subfs record containing this subtree',
122
},
123
},
124
},
···
131
main: {
132
type: 'record',
133
description:
134
-
'Virtual filesystem manifest within a place.wisp.fs record',
135
record: {
136
type: 'object',
137
required: ['root', 'createdAt'],
···
230
type: 'string',
231
format: 'at-uri',
232
description:
233
-
'AT-URI pointing to another place.wisp.subfs record for nested subtrees',
234
},
235
},
236
},
···
118
type: 'string',
119
format: 'at-uri',
120
description:
121
+
'AT-URI pointing to a place.wisp.subfs record containing this subtree.',
122
+
},
123
+
flat: {
124
+
type: 'boolean',
125
+
description:
126
+
"If true, the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false (default), the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure.",
127
},
128
},
129
},
···
136
main: {
137
type: 'record',
138
description:
139
+
'Virtual filesystem subtree referenced by place.wisp.fs records. When a subfs entry is expanded, its root entries are merged (flattened) into the parent directory, allowing large directories to be split across multiple records while maintaining a flat structure.',
140
record: {
141
type: 'object',
142
required: ['root', 'createdAt'],
···
235
type: 'string',
236
format: 'at-uri',
237
description:
238
+
"AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures.",
239
},
240
},
241
},
+3
-1
hosting-service/src/lexicon/types/place/wisp/fs.ts
+3
-1
hosting-service/src/lexicon/types/place/wisp/fs.ts
···
93
export interface Subfs {
94
$type?: 'place.wisp.fs#subfs'
95
type: 'subfs'
96
+
/** AT-URI pointing to a place.wisp.subfs record containing this subtree. */
97
subject: string
98
+
/** If true, the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false (default), the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure. */
99
+
flat?: boolean
100
}
101
102
const hashSubfs = 'subfs'
+1
-1
hosting-service/src/lexicon/types/place/wisp/subfs.ts
+1
-1
hosting-service/src/lexicon/types/place/wisp/subfs.ts
···
92
export interface Subfs {
93
$type?: 'place.wisp.subfs#subfs'
94
type: 'subfs'
95
+
/** AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures. */
96
subject: string
97
}
98
+1
hosting-service/src/lib/firehose.ts
+1
hosting-service/src/lib/firehose.ts
+43
-7
hosting-service/src/lib/utils.ts
+43
-7
hosting-service/src/lib/utils.ts
···
295
const node = entry.node;
296
297
if ('type' in node && node.type === 'subfs') {
298
-
// Merge subfs entries into parent directory
299
const subfsEntries = subfsMap.get(fullPath);
300
if (subfsEntries) {
301
-
console.log(`Merging subfs node at ${fullPath} (${subfsEntries.length} entries)`);
302
-
// Recursively process the merged entries in case they contain nested subfs
303
-
const processedEntries = replaceSubfsInEntries(subfsEntries, currentPath);
304
-
result.push(...processedEntries);
305
} else {
306
// If fetch failed, skip this entry
307
console.warn(`Failed to fetch subfs at ${fullPath}, skipping`);
···
491
492
// Download new/changed files concurrently - increased from 3 to 20 for much better performance
493
const downloadLimit = 20;
494
for (let i = 0; i < downloadTasks.length; i += downloadLimit) {
495
const batch = downloadTasks.slice(i, i + downloadLimit);
496
-
await Promise.all(batch.map(task => task()));
497
if (downloadTasks.length > downloadLimit) {
498
-
console.log(`[Cache Progress] Downloaded ${Math.min(i + downloadLimit, downloadTasks.length)}/${downloadTasks.length} files`);
499
}
500
}
501
}
502
···
555
}
556
557
const blobUrl = `${pdsEndpoint}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}`;
558
559
// Allow up to 500MB per file blob, with 5 minute timeout
560
let content = await safeFetchBlob(blobUrl, { maxSize: 500 * 1024 * 1024, timeout: 300000 });
···
295
const node = entry.node;
296
297
if ('type' in node && node.type === 'subfs') {
298
+
// Check if this is a flat merge or subdirectory merge (default to flat if not specified)
299
+
const subfsNode = node as any;
300
+
const isFlat = subfsNode.flat !== false; // Default to true
301
const subfsEntries = subfsMap.get(fullPath);
302
+
303
if (subfsEntries) {
304
+
console.log(`Merging subfs node at ${fullPath} (${subfsEntries.length} entries, flat: ${isFlat})`);
305
+
306
+
if (isFlat) {
307
+
// Flat merge: hoist entries directly into parent directory
308
+
const processedEntries = replaceSubfsInEntries(subfsEntries, currentPath);
309
+
result.push(...processedEntries);
310
+
} else {
311
+
// Subdirectory merge: create a directory with the subfs node's name
312
+
const processedEntries = replaceSubfsInEntries(subfsEntries, fullPath);
313
+
result.push({
314
+
name: entry.name,
315
+
node: {
316
+
type: 'directory',
317
+
entries: processedEntries
318
+
}
319
+
});
320
+
}
321
} else {
322
// If fetch failed, skip this entry
323
console.warn(`Failed to fetch subfs at ${fullPath}, skipping`);
···
507
508
// Download new/changed files concurrently - increased from 3 to 20 for much better performance
509
const downloadLimit = 20;
510
+
let successCount = 0;
511
+
let failureCount = 0;
512
+
513
for (let i = 0; i < downloadTasks.length; i += downloadLimit) {
514
const batch = downloadTasks.slice(i, i + downloadLimit);
515
+
const results = await Promise.allSettled(batch.map(task => task()));
516
+
517
+
// Count successes and failures
518
+
results.forEach((result, index) => {
519
+
if (result.status === 'fulfilled') {
520
+
successCount++;
521
+
} else {
522
+
failureCount++;
523
+
console.error(`[Cache] Failed to download file (continuing with others):`, result.reason);
524
+
}
525
+
});
526
+
527
if (downloadTasks.length > downloadLimit) {
528
+
console.log(`[Cache Progress] Downloaded ${Math.min(i + downloadLimit, downloadTasks.length)}/${downloadTasks.length} files (${failureCount} failed)`);
529
}
530
+
}
531
+
532
+
if (failureCount > 0) {
533
+
console.warn(`[Cache] Completed with ${successCount} successful and ${failureCount} failed file downloads`);
534
}
535
}
536
···
589
}
590
591
const blobUrl = `${pdsEndpoint}/xrpc/com.atproto.sync.getBlob?did=${encodeURIComponent(did)}&cid=${encodeURIComponent(cid)}`;
592
+
593
+
console.log(`[Cache] Fetching blob for file: ${filePath}, CID: ${cid}`);
594
595
// Allow up to 500MB per file blob, with 5 minute timeout
596
let content = await safeFetchBlob(blobUrl, { maxSize: 500 * 1024 * 1024, timeout: 300000 });
+2
-1
lexicons/fs.json
+2
-1
lexicons/fs.json
···
51
"required": ["type", "subject"],
52
"properties": {
53
"type": { "type": "string", "const": "subfs" },
54
-
"subject": { "type": "string", "format": "at-uri", "description": "AT-URI pointing to a place.wisp.subfs record containing this subtree. When expanded, the subfs record's root entries are merged (flattened) into the parent directory - the subfs entry itself is removed and replaced by all entries from the referenced record's root. This allows splitting large directories across multiple records while maintaining a flat structure." }
55
}
56
}
57
}
···
51
"required": ["type", "subject"],
52
"properties": {
53
"type": { "type": "string", "const": "subfs" },
54
+
"subject": { "type": "string", "format": "at-uri", "description": "AT-URI pointing to a place.wisp.subfs record containing this subtree." },
55
+
"flat": { "type": "boolean", "description": "If true, the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false (default), the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure." }
56
}
57
}
58
}
+8
-3
src/lexicons/lexicons.ts
+8
-3
src/lexicons/lexicons.ts
···
118
type: 'string',
119
format: 'at-uri',
120
description:
121
-
'AT-URI pointing to a place.wisp.subfs record containing this subtree',
122
},
123
},
124
},
···
131
main: {
132
type: 'record',
133
description:
134
-
'Virtual filesystem manifest within a place.wisp.fs record',
135
record: {
136
type: 'object',
137
required: ['root', 'createdAt'],
···
230
type: 'string',
231
format: 'at-uri',
232
description:
233
-
'AT-URI pointing to another place.wisp.subfs record for nested subtrees',
234
},
235
},
236
},
···
118
type: 'string',
119
format: 'at-uri',
120
description:
121
+
'AT-URI pointing to a place.wisp.subfs record containing this subtree.',
122
+
},
123
+
flat: {
124
+
type: 'boolean',
125
+
description:
126
+
"If true, the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false (default), the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure.",
127
},
128
},
129
},
···
136
main: {
137
type: 'record',
138
description:
139
+
'Virtual filesystem subtree referenced by place.wisp.fs records. When a subfs entry is expanded, its root entries are merged (flattened) into the parent directory, allowing large directories to be split across multiple records while maintaining a flat structure.',
140
record: {
141
type: 'object',
142
required: ['root', 'createdAt'],
···
235
type: 'string',
236
format: 'at-uri',
237
description:
238
+
"AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures.",
239
},
240
},
241
},
+3
-1
src/lexicons/types/place/wisp/fs.ts
+3
-1
src/lexicons/types/place/wisp/fs.ts
···
93
export interface Subfs {
94
$type?: 'place.wisp.fs#subfs'
95
type: 'subfs'
96
+
/** AT-URI pointing to a place.wisp.subfs record containing this subtree. */
97
subject: string
98
+
/** If true, the subfs record's root entries are merged (flattened) into the parent directory, replacing the subfs entry. If false (default), the subfs entries are placed in a subdirectory with the subfs entry's name. Flat merging is useful for splitting large directories across multiple records while maintaining a flat structure. */
99
+
flat?: boolean
100
}
101
102
const hashSubfs = 'subfs'
+1
-1
src/lexicons/types/place/wisp/subfs.ts
+1
-1
src/lexicons/types/place/wisp/subfs.ts
···
92
export interface Subfs {
93
$type?: 'place.wisp.subfs#subfs'
94
type: 'subfs'
95
+
/** AT-URI pointing to another place.wisp.subfs record for nested subtrees. When expanded, the referenced record's root entries are merged (flattened) into the parent directory, allowing recursive splitting of large directory structures. */
96
subject: string
97
}
98
+2
-1
src/lib/wisp-utils.ts
+2
-1
src/lib/wisp-utils.ts
+2
-1
src/routes/wisp.ts
+2
-1
src/routes/wisp.ts