@superblocksteam/workspace-sync 0.0.0-stage → 2.0.178-next.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.
- package/LICENSE.txt +87 -0
- package/README.md +43 -2
- package/dist/atomic-write.d.ts +7 -0
- package/dist/atomic-write.d.ts.map +1 -0
- package/dist/atomic-write.js +33 -0
- package/dist/atomic-write.js.map +1 -0
- package/dist/branch-write-guard.d.ts +32 -0
- package/dist/branch-write-guard.d.ts.map +1 -0
- package/dist/branch-write-guard.js +38 -0
- package/dist/branch-write-guard.js.map +1 -0
- package/dist/control.d.ts +237 -0
- package/dist/control.d.ts.map +1 -0
- package/dist/control.js +242 -0
- package/dist/control.js.map +1 -0
- package/dist/dbfs-transport.d.ts +52 -0
- package/dist/dbfs-transport.d.ts.map +1 -0
- package/dist/dbfs-transport.js +21 -0
- package/dist/dbfs-transport.js.map +1 -0
- package/dist/download.d.ts +54 -0
- package/dist/download.d.ts.map +1 -0
- package/dist/download.js +475 -0
- package/dist/download.js.map +1 -0
- package/dist/draft-helpers.d.ts +114 -0
- package/dist/draft-helpers.d.ts.map +1 -0
- package/dist/draft-helpers.js +760 -0
- package/dist/draft-helpers.js.map +1 -0
- package/dist/draft-journal-store.d.ts +30 -0
- package/dist/draft-journal-store.d.ts.map +1 -0
- package/dist/draft-journal-store.js +7 -0
- package/dist/draft-journal-store.js.map +1 -0
- package/dist/draft-state-report-metrics.d.ts +5 -0
- package/dist/draft-state-report-metrics.d.ts.map +1 -0
- package/dist/draft-state-report-metrics.js +36 -0
- package/dist/draft-state-report-metrics.js.map +1 -0
- package/dist/file-draft-journal-store.d.ts +26 -0
- package/dist/file-draft-journal-store.d.ts.map +1 -0
- package/dist/file-draft-journal-store.js +123 -0
- package/dist/file-draft-journal-store.js.map +1 -0
- package/dist/flush-scheduler.d.ts +24 -0
- package/dist/flush-scheduler.d.ts.map +1 -0
- package/dist/flush-scheduler.js +36 -0
- package/dist/flush-scheduler.js.map +1 -0
- package/dist/hash-cache.d.ts +16 -0
- package/dist/hash-cache.d.ts.map +1 -0
- package/dist/hash-cache.js +62 -0
- package/dist/hash-cache.js.map +1 -0
- package/dist/hash-dir-tree.d.ts +10 -0
- package/dist/hash-dir-tree.d.ts.map +1 -0
- package/dist/hash-dir-tree.js +53 -0
- package/dist/hash-dir-tree.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/list-dir.d.ts +31 -0
- package/dist/list-dir.d.ts.map +1 -0
- package/dist/list-dir.js +153 -0
- package/dist/list-dir.js.map +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +16 -0
- package/dist/logger.js.map +1 -0
- package/dist/native-draft-session.d.ts +28 -0
- package/dist/native-draft-session.d.ts.map +1 -0
- package/dist/native-draft-session.js +154 -0
- package/dist/native-draft-session.js.map +1 -0
- package/dist/rpc-client.d.ts +7 -0
- package/dist/rpc-client.d.ts.map +1 -0
- package/dist/rpc-client.js +2 -0
- package/dist/rpc-client.js.map +1 -0
- package/dist/safe-directory-reader.d.ts +28 -0
- package/dist/safe-directory-reader.d.ts.map +1 -0
- package/dist/safe-directory-reader.js +136 -0
- package/dist/safe-directory-reader.js.map +1 -0
- package/dist/server-tree.d.ts +56 -0
- package/dist/server-tree.d.ts.map +1 -0
- package/dist/server-tree.js +179 -0
- package/dist/server-tree.js.map +1 -0
- package/dist/session-draft-journal-store.d.ts +42 -0
- package/dist/session-draft-journal-store.d.ts.map +1 -0
- package/dist/session-draft-journal-store.js +177 -0
- package/dist/session-draft-journal-store.js.map +1 -0
- package/dist/snapshot/take-snapshot.d.ts +41 -0
- package/dist/snapshot/take-snapshot.d.ts.map +1 -0
- package/dist/snapshot/take-snapshot.js +244 -0
- package/dist/snapshot/take-snapshot.js.map +1 -0
- package/dist/snapshot/types.d.ts +25 -0
- package/dist/snapshot/types.d.ts.map +1 -0
- package/dist/snapshot/types.js +3 -0
- package/dist/snapshot/types.js.map +1 -0
- package/dist/snapshot/upload-snapshot.d.ts +33 -0
- package/dist/snapshot/upload-snapshot.d.ts.map +1 -0
- package/dist/snapshot/upload-snapshot.js +113 -0
- package/dist/snapshot/upload-snapshot.js.map +1 -0
- package/dist/socket-dbfs-transport.d.ts +32 -0
- package/dist/socket-dbfs-transport.d.ts.map +1 -0
- package/dist/socket-dbfs-transport.js +126 -0
- package/dist/socket-dbfs-transport.js.map +1 -0
- package/dist/sync-credential-provider.d.ts +27 -0
- package/dist/sync-credential-provider.d.ts.map +1 -0
- package/dist/sync-credential-provider.js +22 -0
- package/dist/sync-credential-provider.js.map +1 -0
- package/dist/sync-engine.d.ts +119 -0
- package/dist/sync-engine.d.ts.map +1 -0
- package/dist/sync-engine.js +358 -0
- package/dist/sync-engine.js.map +1 -0
- package/dist/sync-paths.d.ts +61 -0
- package/dist/sync-paths.d.ts.map +1 -0
- package/dist/sync-paths.js +107 -0
- package/dist/sync-paths.js.map +1 -0
- package/dist/sync-telemetry.d.ts +12 -0
- package/dist/sync-telemetry.d.ts.map +1 -0
- package/dist/sync-telemetry.js +33 -0
- package/dist/sync-telemetry.js.map +1 -0
- package/dist/testing.d.ts +66 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +180 -0
- package/dist/testing.js.map +1 -0
- package/dist/workspace-state-store.d.ts +52 -0
- package/dist/workspace-state-store.d.ts.map +1 -0
- package/dist/workspace-state-store.js +45 -0
- package/dist/workspace-state-store.js.map +1 -0
- package/package.json +59 -3
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import * as fsp from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { hashDirectoryContents, hashFileContents, } from "@superblocksteam/shared";
|
|
4
|
+
import { unreachable } from "@superblocksteam/util";
|
|
5
|
+
import { listLocalDirectory } from "../list-dir.js";
|
|
6
|
+
/**
|
|
7
|
+
* Takes a snapshot of a local directory. The current implementation stores the snapshot in memory.
|
|
8
|
+
* This is not scalable for large directories and is subject to change.
|
|
9
|
+
*
|
|
10
|
+
* This function now captures the DRAFT state (current filesystem state).
|
|
11
|
+
* For committed state, use snapshotLocalDirectoryCommittedState.
|
|
12
|
+
*
|
|
13
|
+
* @param localDirectoryPath - The path to the local directory to snapshot.
|
|
14
|
+
* @returns A promise that resolves to a snapshot of the local directory.
|
|
15
|
+
*/
|
|
16
|
+
export async function snapshotLocalDirectory(localDirectoryPath, rootDirectoryPath = localDirectoryPath) {
|
|
17
|
+
const directoryContents = [];
|
|
18
|
+
const localDirListing = listLocalDirectory(localDirectoryPath, rootDirectoryPath);
|
|
19
|
+
for await (const localDirEntry of localDirListing) {
|
|
20
|
+
let entry;
|
|
21
|
+
switch (localDirEntry.type) {
|
|
22
|
+
case "-": {
|
|
23
|
+
const hash = await hashFileContents(localDirEntry.contents);
|
|
24
|
+
entry = {
|
|
25
|
+
type: "-",
|
|
26
|
+
name: localDirEntry.name,
|
|
27
|
+
executable: localDirEntry.executable,
|
|
28
|
+
hash,
|
|
29
|
+
contents: localDirEntry.contents,
|
|
30
|
+
};
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case "d": {
|
|
34
|
+
const subdirectoryContents = await snapshotLocalDirectory(localDirEntry.localPath, rootDirectoryPath);
|
|
35
|
+
entry = {
|
|
36
|
+
type: "d",
|
|
37
|
+
name: localDirEntry.name,
|
|
38
|
+
hash: subdirectoryContents.hash,
|
|
39
|
+
contents: subdirectoryContents.contents,
|
|
40
|
+
};
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
case "l": {
|
|
44
|
+
entry = {
|
|
45
|
+
type: "l",
|
|
46
|
+
target: localDirEntry.target,
|
|
47
|
+
name: localDirEntry.name,
|
|
48
|
+
};
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
default:
|
|
52
|
+
unreachable(localDirEntry);
|
|
53
|
+
}
|
|
54
|
+
directoryContents.push(entry);
|
|
55
|
+
}
|
|
56
|
+
const directoryHash = await hashDirectoryContents(directoryContents);
|
|
57
|
+
return {
|
|
58
|
+
hash: directoryHash,
|
|
59
|
+
contents: directoryContents,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Takes a snapshot of a local directory showing the COMMITTED state.
|
|
64
|
+
* This function:
|
|
65
|
+
* - Uses .sbswap.edit content from the drafts directory instead of current files (for edited files)
|
|
66
|
+
* - Excludes files that have .sbswap.add markers in the drafts directory (draft additions)
|
|
67
|
+
* - Excludes .sbswap.edit and .sbswap.add marker files themselves
|
|
68
|
+
*
|
|
69
|
+
* @param localDirectoryPath - The path to the local directory to snapshot.
|
|
70
|
+
* @param appRootPath - The app root path (defaults to localDirectoryPath for top-level calls).
|
|
71
|
+
* @param editMarkerMap - Map of edit markers (for internal recursive use).
|
|
72
|
+
* @param addMarkerSet - Set of add markers (for internal recursive use).
|
|
73
|
+
* @returns A promise that resolves to a snapshot of the committed state.
|
|
74
|
+
*/
|
|
75
|
+
export async function snapshotLocalDirectoryCommittedState(localDirectoryPath, appRootPath, editMarkerMap, addMarkerSet) {
|
|
76
|
+
const directoryContents = [];
|
|
77
|
+
// Use the provided appRootPath, or default to localDirectoryPath for top-level calls
|
|
78
|
+
const rootPath = appRootPath || localDirectoryPath;
|
|
79
|
+
const draftsDir = path.join(rootPath, ".superblocks", "drafts");
|
|
80
|
+
// Step 1: Collect all physical entries in the directory
|
|
81
|
+
const allPhysicalEntries = [];
|
|
82
|
+
const localDirListing = listLocalDirectory(localDirectoryPath, rootPath);
|
|
83
|
+
for await (const entry of localDirListing) {
|
|
84
|
+
allPhysicalEntries.push(entry);
|
|
85
|
+
}
|
|
86
|
+
// Step 2: Create maps for marker files from the drafts directory (only for top-level calls)
|
|
87
|
+
let finalEditMarkerMap;
|
|
88
|
+
let finalAddMarkerSet;
|
|
89
|
+
if (!editMarkerMap || !addMarkerSet) {
|
|
90
|
+
// This is the top-level call, scan the drafts directory
|
|
91
|
+
finalEditMarkerMap = new Map();
|
|
92
|
+
finalAddMarkerSet = new Set();
|
|
93
|
+
try {
|
|
94
|
+
await fsp.access(draftsDir);
|
|
95
|
+
await scanDraftsDirectory(draftsDir, rootPath, finalEditMarkerMap, finalAddMarkerSet);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Drafts directory doesn't exist, no drafts to consider
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// Use the provided maps from recursive calls
|
|
103
|
+
finalEditMarkerMap = editMarkerMap;
|
|
104
|
+
finalAddMarkerSet = addMarkerSet;
|
|
105
|
+
}
|
|
106
|
+
// Step 3: Process entries for committed state
|
|
107
|
+
for (const localDirEntry of allPhysicalEntries) {
|
|
108
|
+
const relativePath = path.relative(rootPath, localDirEntry.localPath);
|
|
109
|
+
// Skip files that are draft additions
|
|
110
|
+
if (finalAddMarkerSet.has(relativePath)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
let entry;
|
|
114
|
+
switch (localDirEntry.type) {
|
|
115
|
+
case "-": {
|
|
116
|
+
// Regular file
|
|
117
|
+
let fileContentsToUse;
|
|
118
|
+
let executableToUse;
|
|
119
|
+
if (finalEditMarkerMap.has(relativePath)) {
|
|
120
|
+
// Use the original content from .sbswap.edit file in drafts directory
|
|
121
|
+
fileContentsToUse = finalEditMarkerMap.get(relativePath);
|
|
122
|
+
executableToUse = localDirEntry.executable; // Use current executable flag
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// No edit marker, use current file content (this is committed content)
|
|
126
|
+
fileContentsToUse = localDirEntry.contents;
|
|
127
|
+
executableToUse = localDirEntry.executable;
|
|
128
|
+
}
|
|
129
|
+
const hash = await hashFileContents(fileContentsToUse);
|
|
130
|
+
entry = {
|
|
131
|
+
type: "-",
|
|
132
|
+
name: localDirEntry.name,
|
|
133
|
+
executable: executableToUse,
|
|
134
|
+
hash,
|
|
135
|
+
contents: fileContentsToUse,
|
|
136
|
+
};
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case "d": {
|
|
140
|
+
// Directory
|
|
141
|
+
// Recursive call uses the same committed-state snapshotting, passing the app root and marker maps
|
|
142
|
+
const subdirectorySnapshot = await snapshotLocalDirectoryCommittedState(localDirEntry.localPath, rootPath, // Pass the app root path to recursive calls
|
|
143
|
+
finalEditMarkerMap, // Pass the edit marker map
|
|
144
|
+
finalAddMarkerSet);
|
|
145
|
+
entry = {
|
|
146
|
+
type: "d",
|
|
147
|
+
name: localDirEntry.name,
|
|
148
|
+
hash: subdirectorySnapshot.hash,
|
|
149
|
+
contents: subdirectorySnapshot.contents,
|
|
150
|
+
};
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
case "l": {
|
|
154
|
+
// Symlink
|
|
155
|
+
entry = {
|
|
156
|
+
type: "l",
|
|
157
|
+
target: localDirEntry.target,
|
|
158
|
+
name: localDirEntry.name,
|
|
159
|
+
};
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
default:
|
|
163
|
+
unreachable(localDirEntry);
|
|
164
|
+
}
|
|
165
|
+
directoryContents.push(entry);
|
|
166
|
+
}
|
|
167
|
+
const directoryHash = await hashDirectoryContents(directoryContents);
|
|
168
|
+
return {
|
|
169
|
+
hash: directoryHash,
|
|
170
|
+
contents: directoryContents,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Recursively scans the drafts directory to build maps of edit markers and add markers.
|
|
175
|
+
*
|
|
176
|
+
* @param currentDraftsPath - The current path being scanned in the drafts directory.
|
|
177
|
+
* @param appRootPath - The app root directory path.
|
|
178
|
+
* @param editMarkerMap - Map to store edit marker file contents.
|
|
179
|
+
* @param addMarkerSet - Set to store paths of files that are draft additions.
|
|
180
|
+
*/
|
|
181
|
+
async function scanDraftsDirectory(currentDraftsPath, appRootPath, editMarkerMap, addMarkerSet) {
|
|
182
|
+
try {
|
|
183
|
+
const entries = await fsp.readdir(currentDraftsPath, {
|
|
184
|
+
withFileTypes: true,
|
|
185
|
+
});
|
|
186
|
+
const draftsDir = path.join(appRootPath, ".superblocks", "drafts");
|
|
187
|
+
for (const entry of entries) {
|
|
188
|
+
const fullPath = path.join(currentDraftsPath, entry.name);
|
|
189
|
+
if (entry.isFile()) {
|
|
190
|
+
if (entry.name.endsWith(".sbswap.edit")) {
|
|
191
|
+
// This is the original content for an edited file
|
|
192
|
+
const markerRelativePath = path.relative(draftsDir, fullPath);
|
|
193
|
+
const originalRelativePath = markerRelativePath.slice(0, -".sbswap.edit".length);
|
|
194
|
+
const originalContent = await fsp.readFile(fullPath);
|
|
195
|
+
editMarkerMap.set(originalRelativePath, originalContent);
|
|
196
|
+
}
|
|
197
|
+
else if (entry.name.endsWith(".sbswap.add")) {
|
|
198
|
+
// This marks a file as a draft addition - exclude the corresponding file
|
|
199
|
+
const markerRelativePath = path.relative(draftsDir, fullPath);
|
|
200
|
+
const addedFileRelativePath = markerRelativePath.slice(0, -".sbswap.add".length);
|
|
201
|
+
addMarkerSet.add(addedFileRelativePath);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (entry.isDirectory()) {
|
|
205
|
+
await scanDraftsDirectory(fullPath, appRootPath, editMarkerMap, addMarkerSet);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
// Ignore errors reading directories
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Takes a snapshot of a local directory, prioritizing .sbswap files for content.
|
|
215
|
+
* .sbswap files themselves are not included in the snapshot output.
|
|
216
|
+
*
|
|
217
|
+
* @param localDirectoryPath - The path to the local directory to snapshot.
|
|
218
|
+
* @returns A promise that resolves to a snapshot of the local directory, reflecting draft states.
|
|
219
|
+
*/
|
|
220
|
+
export async function snapshotLocalDirectoryWithDrafts(localDirectoryPath) {
|
|
221
|
+
return snapshotLocalDirectory(localDirectoryPath);
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Flattens a snapshot into relative path -> content hash. A symlink maps to its
|
|
225
|
+
* target, so retargeting it counts as a change. Directories are left out; their
|
|
226
|
+
* files carry the change.
|
|
227
|
+
*/
|
|
228
|
+
export function snapshotPathHashes(snapshot) {
|
|
229
|
+
const hashes = new Map();
|
|
230
|
+
const walk = (entries, prefix) => {
|
|
231
|
+
for (const entry of entries) {
|
|
232
|
+
const entryPath = prefix + entry.name;
|
|
233
|
+
if (entry.type === "d") {
|
|
234
|
+
walk(entry.contents, `${entryPath}/`);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
hashes.set(entryPath, entry.type === "l" ? `symlink:${entry.target}` : entry.hash);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
walk(snapshot.contents, "");
|
|
242
|
+
return hashes;
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=take-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"take-snapshot.js","sourceRoot":"","sources":["../../src/snapshot/take-snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DirectoryEntryDir, DirectoryEntryRegFile, DirectoryEntrySymlink } from "@superblocksteam/shared";
|
|
2
|
+
interface SnapshotDirEntryRegFile extends DirectoryEntryRegFile {
|
|
3
|
+
contents: Buffer;
|
|
4
|
+
}
|
|
5
|
+
interface SnapshotDirEntryDir extends DirectoryEntryDir {
|
|
6
|
+
contents: SnapshotDirEntry[];
|
|
7
|
+
}
|
|
8
|
+
/** This is like `DirectoryEntry` but includes a complete snapshot of the entire directory tree, including all subdirectories and files. */
|
|
9
|
+
export type SnapshotDirEntry = SnapshotDirEntryRegFile | SnapshotDirEntryDir | DirectoryEntrySymlink;
|
|
10
|
+
export interface DirectorySnapshot {
|
|
11
|
+
hash: string;
|
|
12
|
+
contents: SnapshotDirEntry[];
|
|
13
|
+
}
|
|
14
|
+
/** This is like `DirectoryEntry` but includes the recursive contents of all subdirectories in the tree. */
|
|
15
|
+
export interface DirectoryEntryDirWithContents extends DirectoryEntryDir {
|
|
16
|
+
contents: DirectoryEntryTree[];
|
|
17
|
+
}
|
|
18
|
+
export type DirectoryEntryTree = DirectoryEntryRegFile | DirectoryEntryDirWithContents | DirectoryEntrySymlink;
|
|
19
|
+
/** The state of a directory, i.e. its hash and recursively the hashes of all the hashes of all the files and directories within it. */
|
|
20
|
+
export interface DirectoryState {
|
|
21
|
+
hash: string;
|
|
22
|
+
contents: DirectoryEntryTree[];
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/snapshot/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,UAAU,uBAAwB,SAAQ,qBAAqB;IAC7D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,mBAAoB,SAAQ,iBAAiB;IACrD,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,2IAA2I;AAC3I,MAAM,MAAM,gBAAgB,GACxB,uBAAuB,GACvB,mBAAmB,GAGnB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,2GAA2G;AAC3G,MAAM,WAAW,6BAA8B,SAAQ,iBAAiB;IACtE,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED,MAAM,MAAM,kBAAkB,GAC1B,qBAAqB,GACrB,6BAA6B,GAC7B,qBAAqB,CAAC;AAE1B,uIAAuI;AACvI,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,kBAAkB,EAAE,CAAC;CAChC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/snapshot/types.ts"],"names":[],"mappings":"AAAA,6EAA6E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PutManyRequestPayload } from "@superblocksteam/shared";
|
|
2
|
+
import type { HashCache } from "../hash-cache.js";
|
|
3
|
+
import type { StdISocketRPCClient } from "../rpc-client.js";
|
|
4
|
+
import type { DirectorySnapshot } from "./types.js";
|
|
5
|
+
export type PutManyItems = PutManyRequestPayload["items"];
|
|
6
|
+
/** The two DBFS writes a snapshot upload needs. */
|
|
7
|
+
export interface SnapshotUploadTarget {
|
|
8
|
+
/**
|
|
9
|
+
* Checks directory listings before anything is written. Optional: a server
|
|
10
|
+
* without the method is uploaded to without validation.
|
|
11
|
+
*/
|
|
12
|
+
validateMany?: (items: PutManyItems) => Promise<void>;
|
|
13
|
+
putMany: (items: PutManyItems) => Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The putMany items that bring the server up to `directorySnapshot`, children
|
|
17
|
+
* before the listings that reference them. Hashes the cache already knows are
|
|
18
|
+
* left out.
|
|
19
|
+
*/
|
|
20
|
+
export declare function collectSnapshotUploadItems(directorySnapshot: DirectorySnapshot, hashCache: HashCache): PutManyItems;
|
|
21
|
+
/**
|
|
22
|
+
* Validates every directory listing, then writes every item, in batches of 64.
|
|
23
|
+
* Returns the number of items written.
|
|
24
|
+
*/
|
|
25
|
+
export declare function uploadSnapshotItems(target: SnapshotUploadTarget, itemsToUpload: PutManyItems): Promise<number>;
|
|
26
|
+
export declare function uploadDirectorySnapshot(rpcClient: StdISocketRPCClient, directorySnapshot: DirectorySnapshot, hashCache: HashCache): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* {@link SnapshotUploadTarget} over the socket RPC client. Validation is
|
|
29
|
+
* skipped when the client lacks the method, and once the server reports it
|
|
30
|
+
* as unsupported.
|
|
31
|
+
*/
|
|
32
|
+
export declare function socketSnapshotUploadTarget(rpcClient: StdISocketRPCClient): SnapshotUploadTarget;
|
|
33
|
+
//# sourceMappingURL=upload-snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-snapshot.d.ts","sourceRoot":"","sources":["../../src/snapshot/upload-snapshot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,qBAAqB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAIpD,MAAM,MAAM,YAAY,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAE1D,mDAAmD;AACnD,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAWD;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,iBAAiB,EAAE,iBAAiB,EACpC,SAAS,EAAE,SAAS,GACnB,YAAY,CAwDd;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,oBAAoB,EAC5B,aAAa,EAAE,YAAY,GAC1B,OAAO,CAAC,MAAM,CAAC,CAajB;AAED,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,mBAAmB,EAC9B,iBAAiB,EAAE,iBAAiB,EACpC,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,SAAS,EAAE,mBAAmB,GAC7B,oBAAoB,CAiBtB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { unwrapResponseDto } from "@superblocksteam/shared";
|
|
2
|
+
const BATCH_SIZE = 64;
|
|
3
|
+
function isUnsupportedMethod(error) {
|
|
4
|
+
return (typeof error === "object" &&
|
|
5
|
+
error !== null &&
|
|
6
|
+
"code" in error &&
|
|
7
|
+
error.code === 2);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The putMany items that bring the server up to `directorySnapshot`, children
|
|
11
|
+
* before the listings that reference them. Hashes the cache already knows are
|
|
12
|
+
* left out.
|
|
13
|
+
*/
|
|
14
|
+
export function collectSnapshotUploadItems(directorySnapshot, hashCache) {
|
|
15
|
+
const itemsToUpload = [];
|
|
16
|
+
const processRecursively = (directorySnapshot) => {
|
|
17
|
+
if (hashCache.check_directory(directorySnapshot.hash)) {
|
|
18
|
+
// The directory already exists on the server, we don't need to upload anything.
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const directoryContents = [];
|
|
22
|
+
for (const snapshotDirEntry of directorySnapshot.contents) {
|
|
23
|
+
const name = snapshotDirEntry.name;
|
|
24
|
+
let entry;
|
|
25
|
+
switch (snapshotDirEntry.type) {
|
|
26
|
+
case "-": {
|
|
27
|
+
if (!hashCache.check_file_content(snapshotDirEntry.hash)) {
|
|
28
|
+
// We are not sure if the file exists on the server, so we need to upload it
|
|
29
|
+
itemsToUpload.push({
|
|
30
|
+
type: "fileContents",
|
|
31
|
+
contents: snapshotDirEntry.contents.toString("base64"),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
entry = {
|
|
35
|
+
type: "-",
|
|
36
|
+
hash: snapshotDirEntry.hash,
|
|
37
|
+
executable: snapshotDirEntry.executable,
|
|
38
|
+
name,
|
|
39
|
+
};
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case "d": {
|
|
43
|
+
if (!hashCache.check_directory(snapshotDirEntry.hash)) {
|
|
44
|
+
processRecursively({
|
|
45
|
+
hash: snapshotDirEntry.hash,
|
|
46
|
+
contents: snapshotDirEntry.contents,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
entry = { type: "d", name, hash: snapshotDirEntry.hash };
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case "l": {
|
|
53
|
+
entry = { type: "l", target: snapshotDirEntry.target, name };
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
default:
|
|
57
|
+
throw new Error(`Unknown entry type: ${snapshotDirEntry}`);
|
|
58
|
+
}
|
|
59
|
+
directoryContents.push(entry);
|
|
60
|
+
}
|
|
61
|
+
itemsToUpload.push({
|
|
62
|
+
type: "directoryContents",
|
|
63
|
+
contents: directoryContents,
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
processRecursively(directorySnapshot);
|
|
67
|
+
return itemsToUpload;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Validates every directory listing, then writes every item, in batches of 64.
|
|
71
|
+
* Returns the number of items written.
|
|
72
|
+
*/
|
|
73
|
+
export async function uploadSnapshotItems(target, itemsToUpload) {
|
|
74
|
+
const itemsToValidate = itemsToUpload.filter((item) => item.type === "directoryContents");
|
|
75
|
+
if (target.validateMany) {
|
|
76
|
+
for (let idx = 0; idx < itemsToValidate.length; idx += BATCH_SIZE) {
|
|
77
|
+
await target.validateMany(itemsToValidate.slice(idx, idx + BATCH_SIZE));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
for (let idx = 0; idx < itemsToUpload.length; idx += BATCH_SIZE) {
|
|
81
|
+
await target.putMany(itemsToUpload.slice(idx, idx + BATCH_SIZE));
|
|
82
|
+
}
|
|
83
|
+
return itemsToUpload.length;
|
|
84
|
+
}
|
|
85
|
+
export async function uploadDirectorySnapshot(rpcClient, directorySnapshot, hashCache) {
|
|
86
|
+
await uploadSnapshotItems(socketSnapshotUploadTarget(rpcClient), collectSnapshotUploadItems(directorySnapshot, hashCache));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* {@link SnapshotUploadTarget} over the socket RPC client. Validation is
|
|
90
|
+
* skipped when the client lacks the method, and once the server reports it
|
|
91
|
+
* as unsupported.
|
|
92
|
+
*/
|
|
93
|
+
export function socketSnapshotUploadTarget(rpcClient) {
|
|
94
|
+
let validationSupported = typeof rpcClient.call.v1.dbfs.validateMany === "function";
|
|
95
|
+
return {
|
|
96
|
+
validateMany: async (items) => {
|
|
97
|
+
if (!validationSupported)
|
|
98
|
+
return;
|
|
99
|
+
try {
|
|
100
|
+
await unwrapResponseDto(rpcClient.call.v1.dbfs.validateMany({ items }));
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (!isUnsupportedMethod(error))
|
|
104
|
+
throw error;
|
|
105
|
+
validationSupported = false;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
putMany: async (items) => {
|
|
109
|
+
await unwrapResponseDto(rpcClient.call.v1.dbfs.putMany({ items }));
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=upload-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-snapshot.js","sourceRoot":"","sources":["../../src/snapshot/upload-snapshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { DirectoryEntry } from "@superblocksteam/shared";
|
|
2
|
+
import type { DbfsFileRef, DbfsTransport, SetHashRequest, SetHashResult } from "./dbfs-transport.js";
|
|
3
|
+
import type { StdISocketRPCClient } from "./rpc-client.js";
|
|
4
|
+
import { type PutManyItems } from "./snapshot/upload-snapshot.js";
|
|
5
|
+
export interface SocketDbfsTransportOptions {
|
|
6
|
+
/**
|
|
7
|
+
* The server client. A function is called for every request, so a host
|
|
8
|
+
* that reconnects with a refreshed credential hands out the new client.
|
|
9
|
+
*/
|
|
10
|
+
rpcClient: StdISocketRPCClient | (() => StdISocketRPCClient);
|
|
11
|
+
applicationId: string;
|
|
12
|
+
branchName: string | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* {@link DbfsTransport} over the server socket, using the same RPC methods the
|
|
16
|
+
* live-edit sync service calls.
|
|
17
|
+
*/
|
|
18
|
+
export declare class SocketDbfsTransport implements DbfsTransport {
|
|
19
|
+
private readonly options;
|
|
20
|
+
constructor(options: SocketDbfsTransportOptions);
|
|
21
|
+
private get client();
|
|
22
|
+
validateMany(items: PutManyItems): Promise<void>;
|
|
23
|
+
putMany(items: PutManyItems): Promise<string[]>;
|
|
24
|
+
getDirectory(hash: string): Promise<DirectoryEntry[]>;
|
|
25
|
+
getContents(rootHash: string, files: DbfsFileRef[]): Promise<Buffer[]>;
|
|
26
|
+
getBlob(hash: string): Promise<Buffer>;
|
|
27
|
+
getServerHash(): Promise<string>;
|
|
28
|
+
setHash({ hash, source }: SetHashRequest): Promise<SetHashResult>;
|
|
29
|
+
setDraftState(hasDraft: boolean): Promise<void>;
|
|
30
|
+
private getManyBatch;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=socket-dbfs-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-dbfs-transport.d.ts","sourceRoot":"","sources":["../src/socket-dbfs-transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACd,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EACL,KAAK,YAAY,EAElB,MAAM,+BAA+B,CAAC;AAKvC,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,SAAS,EAAE,mBAAmB,GAAG,CAAC,MAAM,mBAAmB,CAAC,CAAC;IAC7D,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAApC,YAA6B,OAAO,EAAE,0BAA0B,EAAI;IAEpE,OAAO,KAAK,MAAM,GAGjB;IAEK,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;IAEK,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAKpD;IAEK,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAK1D;IAEK,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA2B3E;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK3C;IAEK,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAQrC;IAEK,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAgBtE;IAEK,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpD;YAEa,YAAY;CAoB3B"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { unwrapResponseDto } from "@superblocksteam/shared";
|
|
2
|
+
import { getDraftOrLiveDirectoryHash } from "./download.js";
|
|
3
|
+
import { getSyncLogger } from "./logger.js";
|
|
4
|
+
import { socketSnapshotUploadTarget, } from "./snapshot/upload-snapshot.js";
|
|
5
|
+
const GET_MANY_BATCH = 256;
|
|
6
|
+
const BLOB_BATCH = 50;
|
|
7
|
+
/**
|
|
8
|
+
* {@link DbfsTransport} over the server socket, using the same RPC methods the
|
|
9
|
+
* live-edit sync service calls.
|
|
10
|
+
*/
|
|
11
|
+
export class SocketDbfsTransport {
|
|
12
|
+
options;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.options = options;
|
|
15
|
+
}
|
|
16
|
+
get client() {
|
|
17
|
+
const { rpcClient } = this.options;
|
|
18
|
+
return typeof rpcClient === "function" ? rpcClient() : rpcClient;
|
|
19
|
+
}
|
|
20
|
+
async validateMany(items) {
|
|
21
|
+
await socketSnapshotUploadTarget(this.client).validateMany?.(items);
|
|
22
|
+
}
|
|
23
|
+
async putMany(items) {
|
|
24
|
+
const response = await unwrapResponseDto(this.client.call.v1.dbfs.putMany({ items }));
|
|
25
|
+
return response.items.map((item) => item.hash);
|
|
26
|
+
}
|
|
27
|
+
async getDirectory(hash) {
|
|
28
|
+
const response = await unwrapResponseDto(this.client.call.v1.dbfs.directoryContents.get({ hash }));
|
|
29
|
+
return response.contents;
|
|
30
|
+
}
|
|
31
|
+
async getContents(rootHash, files) {
|
|
32
|
+
const results = [];
|
|
33
|
+
let bulk = typeof this.client.call.v1.dbfs.getMany === "function";
|
|
34
|
+
for (let idx = 0; idx < files.length;) {
|
|
35
|
+
if (bulk) {
|
|
36
|
+
const batch = files.slice(idx, idx + GET_MANY_BATCH);
|
|
37
|
+
try {
|
|
38
|
+
results.push(...(await this.getManyBatch(rootHash, batch)));
|
|
39
|
+
idx += batch.length;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (!isGetManyFallbackError(error))
|
|
44
|
+
throw error;
|
|
45
|
+
getSyncLogger().warn(`DBFS getMany unavailable, invalid, or exceeded bulk limits (${errorMessage(error)}), falling back to per-file download`);
|
|
46
|
+
bulk = false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const batch = files.slice(idx, idx + BLOB_BATCH);
|
|
50
|
+
results.push(...(await Promise.all(batch.map((file) => this.getBlob(file.hash)))));
|
|
51
|
+
idx += batch.length;
|
|
52
|
+
}
|
|
53
|
+
return results;
|
|
54
|
+
}
|
|
55
|
+
async getBlob(hash) {
|
|
56
|
+
const response = await unwrapResponseDto(this.client.call.v1.dbfs.fileContents.get({ hash }));
|
|
57
|
+
return Buffer.from(response.contents, "base64");
|
|
58
|
+
}
|
|
59
|
+
async getServerHash() {
|
|
60
|
+
const { hash } = await getDraftOrLiveDirectoryHash(this.client, this.options.applicationId, this.options.branchName);
|
|
61
|
+
if (!hash)
|
|
62
|
+
throw new Error("No live edit hash found");
|
|
63
|
+
return hash;
|
|
64
|
+
}
|
|
65
|
+
async setHash({ hash, source }) {
|
|
66
|
+
// `baseHash` is not sent: the server has no compare-and-set until the
|
|
67
|
+
// branching work (#22956) lands it. Until then the BranchWriteGuard
|
|
68
|
+
// enforces the base hash on the client before this call.
|
|
69
|
+
const response = await unwrapResponseDto(this.client.call.v3.application.liveEditDirectoryContents.set({
|
|
70
|
+
applicationId: this.options.applicationId,
|
|
71
|
+
branchName: this.options.branchName,
|
|
72
|
+
hash,
|
|
73
|
+
source,
|
|
74
|
+
}));
|
|
75
|
+
return {
|
|
76
|
+
hash: response.hash ?? hash,
|
|
77
|
+
...(response.commitId ? { commitId: response.commitId } : {}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async setDraftState(hasDraft) {
|
|
81
|
+
await unwrapResponseDto(this.client.call.v3.application.draftState.set({
|
|
82
|
+
applicationId: this.options.applicationId,
|
|
83
|
+
branchName: this.options.branchName,
|
|
84
|
+
hasDraft,
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
async getManyBatch(rootHash, batch) {
|
|
88
|
+
const response = await unwrapResponseDto(this.client.call.v1.dbfs.getMany({
|
|
89
|
+
rootHash,
|
|
90
|
+
paths: batch.map((file) => file.path),
|
|
91
|
+
}));
|
|
92
|
+
const byPath = new Map(response.items.map((item) => [item.path, item]));
|
|
93
|
+
return batch.map((file) => {
|
|
94
|
+
const item = byPath.get(file.path);
|
|
95
|
+
if (item?.type !== "fileContents" || item.hash !== file.hash)
|
|
96
|
+
throw new InvalidGetManyResponseError(`DBFS getMany did not return the expected contents for ${file.path}`);
|
|
97
|
+
return Buffer.from(item.contents, "base64");
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
class InvalidGetManyResponseError extends Error {
|
|
102
|
+
}
|
|
103
|
+
function errorMessage(error) {
|
|
104
|
+
if (typeof error === "object" &&
|
|
105
|
+
error !== null &&
|
|
106
|
+
"message" in error &&
|
|
107
|
+
typeof error.message === "string")
|
|
108
|
+
return error.message;
|
|
109
|
+
return String(error);
|
|
110
|
+
}
|
|
111
|
+
/** Mirrors the fallback rules of the live-edit download. */
|
|
112
|
+
function isGetManyFallbackError(error) {
|
|
113
|
+
if (error instanceof InvalidGetManyResponseError)
|
|
114
|
+
return true;
|
|
115
|
+
if (typeof error === "object" &&
|
|
116
|
+
error !== null &&
|
|
117
|
+
"code" in error &&
|
|
118
|
+
error.code === 2)
|
|
119
|
+
return true;
|
|
120
|
+
const message = errorMessage(error);
|
|
121
|
+
return (message.includes("is not implemented") ||
|
|
122
|
+
message.includes("maxObjects") ||
|
|
123
|
+
message.includes("maxBytes") ||
|
|
124
|
+
message.includes("bulk read"));
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=socket-dbfs-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-dbfs-transport.js","sourceRoot":"","sources":["../src/socket-dbfs-transport.ts"],"names":[],"mappings":"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"}
|