querysub 0.591.0 → 0.593.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.claude/settings.local.json +5 -1
  2. package/bin/stop-machine.js +4 -0
  3. package/package.json +6 -3
  4. package/spec.txt +12 -0
  5. package/src/-a-archives/archiveCache.ts +28 -7
  6. package/src/-a-archives/archiveCache2.ts +4 -1
  7. package/src/-a-archives/archives.ts +2 -63
  8. package/src/-a-archives/archives2.ts +21 -17
  9. package/src/-a-archives/archivesDisk.ts +2 -13
  10. package/src/-a-archives/archivesMemoryCache.ts +27 -17
  11. package/src/-a-archives/archivesMemoryCache2.ts +23 -0
  12. package/src/-d-trust/NetworkTrust2.ts +3 -3
  13. package/src/-e-certs/certAuthority.ts +2 -2
  14. package/src/-f-node-discovery/NodeDiscovery.ts +3 -3
  15. package/src/0-path-value-core/AuthorityLookup.ts +0 -4
  16. package/src/0-path-value-core/PathRouter.ts +17 -16
  17. package/src/0-path-value-core/ShardPrefixes.ts +2 -4
  18. package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +133 -38
  19. package/src/0-path-value-core/archiveLocks/archiveSnapshots.ts +0 -1
  20. package/src/0-path-value-core/pathValueArchives.ts +9 -5
  21. package/src/2-proxy/archiveMoveHarness.ts +1 -1
  22. package/src/4-deploy/edgeBootstrap.ts +50 -11
  23. package/src/4-deploy/edgeNodes.ts +7 -13
  24. package/src/4-querysub/Querysub.ts +2 -2
  25. package/src/archiveapps/archiveJoinEntry.ts +1 -1
  26. package/src/config2.ts +1 -7
  27. package/src/deployManager/components/ServicesListPage.tsx +27 -8
  28. package/src/deployManager/machineDaemonShared.ts +2 -0
  29. package/src/deployManager/machineSchema.ts +5 -6
  30. package/src/deployManager/setupMachineMain.ts +1 -2
  31. package/src/deployManager/stopMachineMain.ts +45 -0
  32. package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +34 -21
  33. package/src/diagnostics/logs/IndexedLogs/MCPIndexedLogs.ts +3 -3
  34. package/src/diagnostics/logs/IndexedLogs/TimeFileTree.ts +11 -2
  35. package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +3 -4
  36. package/src/diagnostics/logs/errorTickets/tickets.ts +3 -4
  37. package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +2 -4
  38. package/src/storageSetup.ts +1 -1
  39. package/src/-a-archives/archivesBackBlaze.ts +0 -156
  40. package/src/-a-archives/archivesCborT.ts +0 -52
  41. package/src/-a-archives/archivesLimitedCache.ts +0 -307
  42. package/src/-a-archives/archivesPrivateFileSystem.ts +0 -326
  43. package/src/-a-archives/copyLocalToBackblaze.ts +0 -24
  44. package/src/-b-authorities/cdnAuthority.ts +0 -53
@@ -1,307 +0,0 @@
1
- import { formatNumber, formatTime } from "socket-function/src/formatting/format";
2
- import { Archives } from "./archives";
3
- import { cache } from "socket-function/src/caching";
4
- import { measureFnc } from "socket-function/src/profiling/measure";
5
- import { batchFunction, runInfinitePoll } from "socket-function/src/batching";
6
- import { timeInHour } from "socket-function/src/misc";
7
-
8
- interface FileInfo {
9
- writeTime: number;
10
- accessTime: number;
11
- size: number;
12
- }
13
-
14
- interface IndexData {
15
- files: Record<string, FileInfo>;
16
- }
17
-
18
- class ArchivesLimitedCache {
19
- private baseArchives: Archives;
20
- private maxFiles: number;
21
- private maxSize: number;
22
- private cache = new Map<string, FileInfo>();
23
- private initialized = false;
24
- private readonly indexPath = ".cache-index.json";
25
-
26
- constructor(baseArchives: Archives, config: { maxFiles: number; maxSize: number }) {
27
- this.baseArchives = baseArchives;
28
- this.maxFiles = config.maxFiles;
29
- this.maxSize = config.maxSize;
30
- this.initOptionalMethods();
31
- this.initPeriodicSync();
32
- }
33
-
34
- public getDebugName(): string {
35
- return `limitedCache(${this.maxFiles}files,${Math.round(this.maxSize / (1024 * 1024))}MB)/${this.baseArchives.getDebugName()}`;
36
- }
37
-
38
- @measureFnc
39
- private async rebuildCacheFromFiles(): Promise<void> {
40
- const allFiles = await this.baseArchives.findInfo("", { type: "files" });
41
- const newCache = new Map<string, FileInfo>();
42
-
43
- // Build map of actual files (excluding index file)
44
- for (const file of allFiles) {
45
- if (file.path === this.indexPath) continue;
46
-
47
- newCache.set(file.path, {
48
- writeTime: file.createTime,
49
- accessTime: this.cache.get(file.path)?.accessTime ?? file.createTime,
50
- size: file.size,
51
- });
52
- }
53
-
54
- // Update cache with actual files
55
- this.cache.clear();
56
- for (const [path, info] of newCache.entries()) {
57
- this.cache.set(path, info);
58
- }
59
- }
60
-
61
- @measureFnc
62
- private async ensureInitialized(): Promise<void> {
63
- if (this.initialized) return;
64
-
65
- // First try to load from index file
66
- await this.loadIndex();
67
-
68
- // If index is empty or missing, fall back to scanning all files
69
- if (this.cache.size === 0) {
70
- console.log("Index file missing or empty, scanning all files...");
71
- await this.rebuildCacheFromFiles();
72
- // Save the newly built index
73
- await this.saveIndex();
74
- }
75
-
76
- this.initialized = true;
77
-
78
- // Cleanup if we're already over limits
79
- await this.applyLimits();
80
- }
81
-
82
- private async applyLimits(): Promise<void> {
83
- const files = Array.from(this.cache.entries()).map(([path, info]) => ({
84
- path,
85
- ...info,
86
- }));
87
-
88
- const totalSize = files.reduce((sum, file) => sum + file.size, 0);
89
- const totalFiles = files.length;
90
-
91
- if (totalFiles <= this.maxFiles && totalSize <= this.maxSize) {
92
- return; // No cleanup needed
93
- }
94
-
95
- // Sort by access time (oldest first)
96
- files.sort((a, b) => a.accessTime - b.accessTime);
97
-
98
- let currentSize = totalSize;
99
- let currentFiles = totalFiles;
100
-
101
- // Remove files until we're under both limits
102
- for (const file of files) {
103
- if (currentFiles <= this.maxFiles && currentSize <= this.maxSize) {
104
- break;
105
- }
106
-
107
- console.warn(`Deleting file ${file.path} to maintain limits. Currently have ${formatNumber(currentFiles)} files and ${formatNumber(currentSize)}B`);
108
- try {
109
- await this.baseArchives.del(file.path);
110
- this.cache.delete(file.path);
111
- currentSize -= file.size;
112
- currentFiles--;
113
- } catch (error) {
114
- // File might already be deleted, continue
115
- console.warn(`Failed to delete file ${file.path}:`, error);
116
- this.cache.delete(file.path); // Remove from cache anyway
117
- currentSize -= file.size;
118
- currentFiles--;
119
- }
120
- }
121
-
122
- // Trigger index flush if any files were deleted
123
- if (currentFiles < totalFiles) {
124
- this.triggerIndexFlush();
125
- }
126
- }
127
-
128
- private updateAccessTime(path: string): void {
129
- const info = this.cache.get(path);
130
- if (info) {
131
- info.accessTime = Date.now();
132
- this.triggerIndexFlush();
133
- }
134
- }
135
-
136
- public async get(path: string, config?: { range?: { start: number; end: number; }; retryCount?: number }): Promise<Buffer | undefined> {
137
- await this.ensureInitialized();
138
- this.updateAccessTime(path);
139
- return this.baseArchives.get(path, config);
140
- }
141
-
142
- public async set(path: string, data: Buffer): Promise<void> {
143
- await this.ensureInitialized();
144
- // Limit before, not after, so we don't remove the file we just added
145
- await this.applyLimits();
146
- await this.baseArchives.set(path, data);
147
- const now = Date.now();
148
- this.cache.set(path, {
149
- writeTime: now,
150
- accessTime: now,
151
- size: data.length,
152
- });
153
- this.triggerIndexFlush();
154
- }
155
-
156
- public async append(path: string, data: Buffer): Promise<void> {
157
- await this.ensureInitialized();
158
- await this.baseArchives.append(path, data);
159
- // Update the cache info - increment size and update times
160
- const info = this.cache.get(path);
161
- const now = Date.now();
162
- if (info) {
163
- info.size += data.length;
164
- info.writeTime = now;
165
- info.accessTime = now;
166
- } else {
167
- // File didn't exist in cache, add it
168
- this.cache.set(path, {
169
- writeTime: now,
170
- accessTime: now,
171
- size: data.length,
172
- });
173
- }
174
- this.triggerIndexFlush();
175
- }
176
-
177
- public async del(path: string): Promise<void> {
178
- await this.ensureInitialized();
179
- await this.baseArchives.del(path);
180
- this.cache.delete(path);
181
- this.triggerIndexFlush();
182
- }
183
-
184
- public async getInfo(path: string): Promise<{ writeTime: number; size: number; } | undefined> {
185
- await this.ensureInitialized();
186
- this.updateAccessTime(path);
187
- return this.baseArchives.getInfo(path);
188
- }
189
-
190
- public async setLargeFile(config: { path: string; getNextData(): Promise<Buffer | undefined>; }): Promise<void> {
191
- throw new Error(`setLargeFile in archivesLimitedCache is not supported`);
192
- }
193
-
194
- public async find(prefix: string, config?: { shallow?: boolean; type: "files" | "folders" }): Promise<string[]> {
195
- await this.ensureInitialized();
196
- return this.baseArchives.find(prefix, config);
197
- }
198
-
199
- public async findInfo(prefix: string, config?: { shallow?: boolean; type: "files" | "folders" }): Promise<{ path: string; createTime: number; size: number; }[]> {
200
- await this.ensureInitialized();
201
- return this.baseArchives.findInfo(prefix, config);
202
- }
203
-
204
- public async move(config: { path: string; target: Archives; targetPath: string }): Promise<void> {
205
- throw new Error(`Move in archivesLimitedCache is not supported`);
206
- }
207
-
208
- public async copy(config: { path: string; target: Archives; targetPath: string }): Promise<void> {
209
- throw new Error(`Copy in archivesLimitedCache is not supported`);
210
- }
211
-
212
- public enableLogging(): void {
213
- this.baseArchives.enableLogging();
214
- }
215
-
216
- public async assertPathValid(path: string): Promise<void> {
217
- await this.baseArchives.assertPathValid(path);
218
- }
219
-
220
- public getBaseArchives?: () => { parentPath: string; archives: Archives; } | undefined = () => {
221
- const base = this.baseArchives.getBaseArchives?.();
222
- if (base) {
223
- return base;
224
- }
225
- return { parentPath: "", archives: this.baseArchives };
226
- };
227
-
228
- public getURL?: (path: string) => Promise<string>;
229
- public getDownloadAuthorization?: (config: { validDurationInSeconds: number }) => Promise<{ bucketId: string; fileNamePrefix: string; authorizationToken: string; }>;
230
-
231
- private batchedFlushIndex = batchFunction(
232
- { delay: 2000 }, // Flush index changes every 2 seconds
233
- async () => {
234
- // We don't actually need the batched values, just flush the index
235
- await this.saveIndex();
236
- }
237
- );
238
-
239
- private async loadIndex(): Promise<void> {
240
- try {
241
- const indexBuffer = await this.baseArchives.get(this.indexPath);
242
- if (!indexBuffer) {
243
- // No existing index, will need to build it
244
- return;
245
- }
246
-
247
- const indexData: IndexData = JSON.parse(indexBuffer.toString("utf8"));
248
-
249
- // Load the cache from the index
250
- this.cache.clear();
251
- for (const [path, info] of Object.entries(indexData.files)) {
252
- this.cache.set(path, info);
253
- }
254
-
255
- console.log(`Loaded index with ${this.cache.size} files`);
256
- } catch (error) {
257
- console.warn(`Failed to load index file:`, error);
258
- // Continue without index, will rebuild
259
- }
260
- }
261
-
262
- private async saveIndex(): Promise<void> {
263
- try {
264
- const indexData: IndexData = {
265
- files: Object.fromEntries(this.cache.entries())
266
- };
267
-
268
- const indexBuffer = Buffer.from(JSON.stringify(indexData), "utf8");
269
- await this.baseArchives.set(this.indexPath, indexBuffer);
270
- } catch (error) {
271
- console.warn(`Failed to save index file:`, error);
272
- }
273
- }
274
-
275
- private triggerIndexFlush(): void {
276
- void this.batchedFlushIndex(undefined);
277
- }
278
-
279
- private initPeriodicSync(): void {
280
- // Sync index with actual files every hour to prevent drift
281
- runInfinitePoll(timeInHour, async () => {
282
- await this.syncIndexWithFiles();
283
- });
284
- }
285
-
286
- private async syncIndexWithFiles(): Promise<void> {
287
- try {
288
- console.log("Syncing index with actual files...");
289
- await this.rebuildCacheFromFiles();
290
-
291
- // Save updated index
292
- await this.saveIndex();
293
- console.log(`Index synced with ${this.cache.size} actual files`);
294
- } catch (error) {
295
- console.warn("Failed to sync index with files:", error);
296
- }
297
- }
298
-
299
- private initOptionalMethods(): void {
300
- this.getURL = this.baseArchives.getURL;
301
- this.getDownloadAuthorization = this.baseArchives.getDownloadAuthorization;
302
- }
303
- }
304
-
305
- export function createArchivesLimitedCache(baseArchives: Archives, config: { maxFiles: number; maxSize: number }): Archives {
306
- return new ArchivesLimitedCache(baseArchives, config);
307
- }
@@ -1,326 +0,0 @@
1
- /// <reference path="../src.d.ts" />
2
-
3
- import { measureFnc } from "socket-function/src/profiling/measure";
4
- import { blue } from "socket-function/src/formatting/logColors";
5
- import { Archives } from "./archives";
6
- import { cache, lazy } from "socket-function/src/caching";
7
- import { delay } from "socket-function/src/batching";
8
- import { isDefined } from "../misc";
9
- import { formatTime } from "socket-function/src/formatting/format";
10
-
11
- let getRootDirectory = lazy(async () => {
12
- await navigator.storage.persist();
13
- return navigator.storage.getDirectory();
14
- });
15
-
16
- class ArchivesPrivateFileSystem {
17
- public LAG = 0;
18
- private rootDir: string;
19
-
20
- private logging = false;
21
- public enableLogging() {
22
- this.logging = true;
23
- }
24
- private log(text: string) {
25
- if (!this.logging) return;
26
- console.log(text);
27
- }
28
-
29
- constructor(rootDir: string) {
30
- this.rootDir = rootDir;
31
- // Ensure rootDir ends with "/" for consistency
32
- if (!this.rootDir.endsWith("/")) {
33
- this.rootDir = this.rootDir + "/";
34
- }
35
- }
36
-
37
- public getDebugName() {
38
- return "privateFS/" + this.rootDir;
39
- }
40
-
41
- private async getOrCreateDirectory(path: string): Promise<FileSystemDirectoryHandle> {
42
- const root = await getRootDirectory();
43
- const pathParts = (this.rootDir + path).split("/").filter(part => part.length > 0);
44
-
45
- let currentDir = root;
46
- for (const part of pathParts) {
47
- currentDir = await currentDir.getDirectoryHandle(part, { create: true });
48
- }
49
- return currentDir;
50
- }
51
-
52
- private async ensureDirsExist(path: string) {
53
- const pathParts = path.split("/");
54
- // Create directories up to the parent of the file (don't include the file itself)
55
- if (pathParts.length > 1) {
56
- const dirPath = pathParts.slice(0, -1).join("/");
57
- await this.getOrCreateDirectory(dirPath);
58
- }
59
- }
60
-
61
- @measureFnc
62
- public async set(fileName: string, data: Buffer): Promise<void> {
63
- this.log(blue(`Setting file ${fileName} = ${data.length} bytes`));
64
-
65
- await this.ensureDirsExist(fileName);
66
-
67
- const pathParts = fileName.split("/");
68
- const filename = pathParts[pathParts.length - 1];
69
- const dirPath = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
70
-
71
- const directory = await this.getOrCreateDirectory(dirPath);
72
- const fileHandle = await directory.getFileHandle(filename, { create: true });
73
- const writable = await fileHandle.createWritable();
74
- await writable.write(data);
75
- await writable.close();
76
- }
77
-
78
- @measureFnc
79
- public async append(fileName: string, data: Buffer): Promise<void> {
80
- this.log(blue(`Appending to file ${fileName} += ${data.length} bytes`));
81
-
82
- await this.ensureDirsExist(fileName);
83
-
84
- const pathParts = fileName.split("/");
85
- const filename = pathParts[pathParts.length - 1];
86
- const dirPath = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
87
-
88
- const directory = await this.getOrCreateDirectory(dirPath);
89
- const fileHandle = await directory.getFileHandle(filename, { create: true });
90
-
91
- // Get current file to find the size for appending at the end
92
- const file = await fileHandle.getFile();
93
- const currentSize = file.size;
94
-
95
- const writable = await fileHandle.createWritable({ keepExistingData: true });
96
- await writable.write({ type: "write", position: currentSize, data });
97
- await writable.close();
98
- }
99
-
100
- @measureFnc
101
- public async del(fileName: string): Promise<void> {
102
- this.log(blue(`Deleting file ${fileName}`));
103
-
104
- const pathParts = fileName.split("/");
105
- const filename = pathParts[pathParts.length - 1];
106
- const dirPath = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
107
-
108
- try {
109
- const directory = await this.getOrCreateDirectory(dirPath);
110
- await directory.removeEntry(filename);
111
- } catch (e: any) {
112
- // File might not exist, which is fine for delete operations
113
- if (e.name !== "NotFoundError") {
114
- throw e;
115
- }
116
- }
117
- }
118
-
119
- public async setLargeFile(config: { path: string; getNextData(): Promise<Buffer | undefined>; }): Promise<void> {
120
- let { path } = config;
121
- await this.ensureDirsExist(path);
122
-
123
- const pathParts = path.split("/");
124
- const filename = pathParts[pathParts.length - 1];
125
- const dirPath = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
126
-
127
- const directory = await this.getOrCreateDirectory(dirPath);
128
- const fileHandle = await directory.getFileHandle(filename, { create: true });
129
- const writable = await fileHandle.createWritable();
130
-
131
- try {
132
- while (true) {
133
- let data = await config.getNextData();
134
- if (!data?.length) break;
135
- await writable.write(data);
136
- }
137
- } finally {
138
- await writable.close();
139
- }
140
- }
141
-
142
- @measureFnc
143
- public async get(fileName: string, config?: { range?: { start: number; end: number; }; retryCount?: number }): Promise<Buffer | undefined> {
144
- this.log(blue(`Start read file ${fileName}`));
145
-
146
- const pathParts = fileName.split("/");
147
- const filename = pathParts[pathParts.length - 1];
148
- const dirPath = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
149
-
150
- try {
151
- const directory = await this.getOrCreateDirectory(dirPath);
152
- const fileHandle = await directory.getFileHandle(filename, { create: false });
153
- const file = await fileHandle.getFile();
154
-
155
- if (config?.range) {
156
- const start = config.range.start;
157
- const end = config.range.end;
158
- const slice = file.slice(start, end);
159
- const arrayBuffer = await slice.arrayBuffer();
160
- return Buffer.from(arrayBuffer);
161
- } else {
162
- const arrayBuffer = await file.arrayBuffer();
163
- return Buffer.from(arrayBuffer);
164
- }
165
- } catch (e: any) {
166
- if (e.name === "NotFoundError") {
167
- return undefined;
168
- }
169
- throw e;
170
- }
171
- }
172
-
173
- @measureFnc
174
- public async find(prefix: string, config?: { shallow?: boolean; type: "files" | "folders" }): Promise<string[]> {
175
- this.log(blue(`findFileNames ${prefix}`));
176
-
177
- let fileNames: string[] = [];
178
- let folderNames: string[] = [];
179
-
180
- async function readDir(directory: FileSystemDirectoryHandle, currentPath: string) {
181
- for await (const [name, handle] of directory) {
182
- const fullPath = currentPath + (currentPath ? "/" : "") + name;
183
-
184
- if (handle.kind === "directory") {
185
- folderNames.push(fullPath);
186
- if (!config?.shallow) {
187
- await readDir(handle, fullPath);
188
- }
189
- } else {
190
- fileNames.push(fullPath);
191
- }
192
- }
193
- }
194
-
195
- try {
196
- let readDirTime = Date.now();
197
- // Start from the root directory of our namespace
198
- const rootDirectory = await this.getOrCreateDirectory("");
199
- await readDir(rootDirectory, "");
200
- console.log(`readDir took ${formatTime(Date.now() - readDirTime)}`);
201
-
202
- let results = config?.type === "folders" ? folderNames : fileNames;
203
- results = results.filter(name => name.startsWith(prefix));
204
-
205
- if (config?.shallow) {
206
- let targetDepth = prefix.split("/").length;
207
- results = results.filter(name => name.split("/").length === targetDepth);
208
- }
209
-
210
- return results;
211
- } catch (e: any) {
212
- if (e.name === "NotFoundError") {
213
- return [];
214
- }
215
- throw e;
216
- }
217
- }
218
-
219
- @measureFnc
220
- public async findInfo(prefix: string, config?: { shallow?: boolean; type: "files" | "folders" }): Promise<{ path: string; createTime: number; size: number; }[]> {
221
- let files = await this.find(prefix, config);
222
- return (await Promise.all(files.map(async file => {
223
- try {
224
- const info = await this.getInfo(file);
225
- if (!info) return undefined;
226
- return { path: file, createTime: info.writeTime, size: info.size };
227
- } catch {
228
- return undefined;
229
- }
230
- }))).filter(isDefined);
231
- }
232
-
233
- @measureFnc
234
- public async getInfo(pathInput: string): Promise<{
235
- writeTime: number;
236
- size: number;
237
- } | undefined> {
238
- const pathParts = pathInput.split("/");
239
- const filename = pathParts[pathParts.length - 1];
240
- const dirPath = pathParts.length > 1 ? pathParts.slice(0, -1).join("/") : "";
241
-
242
- try {
243
- const directory = await this.getOrCreateDirectory(dirPath);
244
- const fileHandle = await directory.getFileHandle(filename, { create: false });
245
- const file = await fileHandle.getFile();
246
-
247
- return {
248
- writeTime: file.lastModified,
249
- size: file.size,
250
- };
251
- } catch (e: any) {
252
- if (e.name === "NotFoundError") {
253
- return undefined;
254
- }
255
- throw e;
256
- }
257
- }
258
-
259
- public async assertPathValid(path: string) {
260
- // Private file system has fewer restrictions than regular file system
261
- // Most characters are allowed, so we just check for obviously problematic patterns
262
- if (path.includes("..")) {
263
- throw new Error(`Invalid path contains '..': ${path}`);
264
- }
265
- if (path.startsWith("/")) {
266
- throw new Error(`Invalid path starts with '/': ${path}`);
267
- }
268
- }
269
-
270
- @measureFnc
271
- public async move(config: {
272
- path: string;
273
- target: Archives;
274
- targetPath: string;
275
- }) {
276
- let { path, target, targetPath } = config;
277
-
278
- // Unwrap any nested archives
279
- while (true) {
280
- let targetUnwrapped = target.getBaseArchives?.();
281
- if (!targetUnwrapped) break;
282
- target = targetUnwrapped.archives;
283
- targetPath = targetUnwrapped.parentPath + targetPath;
284
- }
285
-
286
- // For moves, we can optimize if both source and target are private file system
287
- if (target instanceof ArchivesPrivateFileSystem) {
288
- // Private File System API doesn't have a native move operation
289
- // So we fall back to copy + delete
290
- await this.copy({ path, target, targetPath });
291
- await this.del(path);
292
- } else {
293
- // Moving to a different archive type - copy then delete
294
- let data = await this.get(path);
295
- if (!data) throw new Error(`File not found to move: ${path}`);
296
- await target.set(targetPath, data);
297
- await this.del(path);
298
- }
299
- }
300
-
301
- @measureFnc
302
- public async copy(config: {
303
- path: string;
304
- target: Archives;
305
- targetPath: string;
306
- }) {
307
- let { path, target, targetPath } = config;
308
-
309
- // Unwrap any nested archives
310
- while (true) {
311
- let targetUnwrapped = target.getBaseArchives?.();
312
- if (!targetUnwrapped) break;
313
- target = targetUnwrapped.archives;
314
- targetPath = targetUnwrapped.parentPath + targetPath;
315
- }
316
-
317
- // Get the data and set it in the target
318
- let data = await this.get(path);
319
- if (!data) throw new Error(`File not found to copy: ${path}`);
320
- await target.set(targetPath, data);
321
- }
322
- }
323
-
324
- export const getArchivesPrivateFileSystem = cache((rootDir: string): Archives => {
325
- return new ArchivesPrivateFileSystem(rootDir);
326
- });
@@ -1,24 +0,0 @@
1
- import { formatNumber } from "socket-function/src/formatting/format";
2
- import { getDomain } from "../config";
3
- import { getArchivesBackblaze } from "./archivesBackBlaze";
4
- import { getArchivesLocal } from "./archivesDisk";
5
-
6
- async function main() {
7
- const domain = getDomain();
8
- let local = getArchivesLocal(domain);
9
- let remote = getArchivesBackblaze(domain);
10
-
11
- let files = await local.find("");
12
- let allRemoteFiles = new Set(await remote.find(""));
13
- for (let file of files) {
14
- if (allRemoteFiles.has(file)) continue;
15
- let buffer = await local.get(file);
16
- if (!buffer) continue;
17
- console.log(`Copying ${file} size ${formatNumber(buffer.length)}B`);
18
- await remote.set(file, buffer);
19
- }
20
- }
21
- main().catch(console.error).finally(() => process.exit(0));
22
-
23
- // archives = getArchivesLocal(domain);
24
- // archives = getArchivesBackblaze(domain);