forked from
nekomimi.pet/wisp.place-monorepo
Monorepo for wisp.place. A static site hosting service built on top of the AT Protocol.
1import type { Directory } from "@wispplace/lexicons/types/place/wisp/fs";
2
3/**
4 * Estimate the JSON size of a directory tree
5 */
6export function estimateDirectorySize(directory: Directory): number {
7 return JSON.stringify(directory).length;
8}
9
10/**
11 * Count files in a directory tree
12 */
13export function countFilesInDirectory(directory: Directory): number {
14 let count = 0;
15 for (const entry of directory.entries) {
16 if ('type' in entry.node && entry.node.type === 'file') {
17 count++;
18 } else if ('type' in entry.node && entry.node.type === 'directory') {
19 count += countFilesInDirectory(entry.node as Directory);
20 }
21 }
22 return count;
23}
24
25/**
26 * Find all directories in a tree with their paths and sizes
27 */
28export function findLargeDirectories(directory: Directory, currentPath: string = ''): Array<{
29 path: string;
30 directory: Directory;
31 size: number;
32 fileCount: number;
33}> {
34 const result: Array<{ path: string; directory: Directory; size: number; fileCount: number }> = [];
35
36 for (const entry of directory.entries) {
37 if ('type' in entry.node && entry.node.type === 'directory') {
38 const dirPath = currentPath ? `${currentPath}/${entry.name}` : entry.name;
39 const dir = entry.node as Directory;
40 const size = estimateDirectorySize(dir);
41 const fileCount = countFilesInDirectory(dir);
42
43 result.push({ path: dirPath, directory: dir, size, fileCount });
44
45 // Recursively find subdirectories
46 const subdirs = findLargeDirectories(dir, dirPath);
47 result.push(...subdirs);
48 }
49 }
50
51 return result;
52}
53
54/**
55 * Replace a directory with a subfs node in the tree
56 */
57export function replaceDirectoryWithSubfs(
58 directory: Directory,
59 targetPath: string,
60 subfsUri: string
61): Directory {
62 const pathParts = targetPath.split('/');
63 const targetName = pathParts[pathParts.length - 1];
64 const parentPath = pathParts.slice(0, -1).join('/');
65
66 // If this is a root-level directory
67 if (pathParts.length === 1) {
68 const newEntries = directory.entries.map(entry => {
69 if (entry.name === targetName && 'type' in entry.node && entry.node.type === 'directory') {
70 return {
71 name: entry.name,
72 node: {
73 $type: 'place.wisp.fs#subfs' as const,
74 type: 'subfs' as const,
75 subject: subfsUri,
76 flat: false // Preserve directory structure
77 }
78 };
79 }
80 return entry;
81 });
82
83 return {
84 $type: 'place.wisp.fs#directory' as const,
85 type: 'directory' as const,
86 entries: newEntries
87 };
88 }
89
90 // Recursively navigate to parent directory
91 const newEntries = directory.entries.map(entry => {
92 if ('type' in entry.node && entry.node.type === 'directory') {
93 const entryPath = entry.name;
94 if (parentPath.startsWith(entryPath) || parentPath === entry.name) {
95 const remainingPath = pathParts.slice(1).join('/');
96 return {
97 name: entry.name,
98 node: {
99 ...replaceDirectoryWithSubfs(entry.node as Directory, remainingPath, subfsUri),
100 $type: 'place.wisp.fs#directory' as const
101 }
102 };
103 }
104 }
105 return entry;
106 });
107
108 return {
109 $type: 'place.wisp.fs#directory' as const,
110 type: 'directory' as const,
111 entries: newEntries
112 };
113}
114
115/**
116 * Split a large directory into multiple smaller chunks that each fit within maxSize
117 * Used when a single directory is too large for one subfs record
118 */
119export function splitDirectoryIntoChunks(directory: Directory, maxSize: number): Directory[] {
120 const chunks: Directory[] = [];
121 let currentChunkEntries: Directory['entries'] = [];
122 let currentChunkSize = 100; // Base size for directory structure overhead
123
124 for (const entry of directory.entries) {
125 const entrySize = JSON.stringify(entry).length;
126
127 // If adding this entry would exceed max size, start a new chunk
128 if (currentChunkEntries.length > 0 && currentChunkSize + entrySize > maxSize) {
129 chunks.push({
130 $type: 'place.wisp.fs#directory' as const,
131 type: 'directory' as const,
132 entries: currentChunkEntries
133 });
134 currentChunkEntries = [];
135 currentChunkSize = 100;
136 }
137
138 currentChunkEntries.push(entry);
139 currentChunkSize += entrySize;
140 }
141
142 // Add the last chunk if it has entries
143 if (currentChunkEntries.length > 0) {
144 chunks.push({
145 $type: 'place.wisp.fs#directory' as const,
146 type: 'directory' as const,
147 entries: currentChunkEntries
148 });
149 }
150
151 return chunks;
152}