@superblocksteam/workspace-sync 0.0.0-stage → 2.0.0-SNAPSHOT.e3d0ac5dd38ba62e
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 +51 -2
- package/dist/atomic-write.d.ts +10 -0
- package/dist/atomic-write.d.ts.map +1 -0
- package/dist/atomic-write.js +35 -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 +54 -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 +127 -0
- package/dist/draft-helpers.d.ts.map +1 -0
- package/dist/draft-helpers.js +840 -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 +129 -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 +141 -0
- package/dist/sync-engine.d.ts.map +1 -0
- package/dist/sync-engine.js +447 -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 +81 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +192 -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 +60 -3
package/dist/download.js
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import * as fsp from "node:fs/promises";
|
|
2
|
+
import { NotFoundError, unwrapResponseDto } from "@superblocksteam/shared";
|
|
3
|
+
import { codeModeDownloadSkippedDirectories, codeModeSyncExcludedDirectories, ExportViewMode, } from "@superblocksteam/shared";
|
|
4
|
+
import { cleanUpDraftsDirectory, getDraftInfo, writeDraftMarker, } from "./draft-helpers.js";
|
|
5
|
+
import { hashLocalDirectory } from "./hash-dir-tree.js";
|
|
6
|
+
import { getSyncLogger } from "./logger.js";
|
|
7
|
+
import { isSnapshotExcludedName } from "./sync-paths.js";
|
|
8
|
+
const DOWNLOAD_BATCH_SIZE = 50;
|
|
9
|
+
// TODO(code-mode): this is re-implemented in the sdk package
|
|
10
|
+
// because SDK depends on vite-plugin-file-sync, but can't be circular
|
|
11
|
+
async function getApplicationDirectoryHash(rpcClient, applicationId, branch, localDirectoryPath) {
|
|
12
|
+
const [draftOrLiveEditHash, localDirectoryStatePreDownload] = await Promise.all([
|
|
13
|
+
getDraftOrLiveDirectoryHash(rpcClient, applicationId, branch),
|
|
14
|
+
hashLocalDirectory(localDirectoryPath),
|
|
15
|
+
]);
|
|
16
|
+
const directoryHash = draftOrLiveEditHash.hash;
|
|
17
|
+
if (!directoryHash) {
|
|
18
|
+
throw new Error("No live edit hash found");
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
serverDirectoryHash: directoryHash,
|
|
22
|
+
latestNonDraftCommitId: draftOrLiveEditHash.latestNonDraftCommitId,
|
|
23
|
+
localDirectoryStatePreDownload,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export async function downloadApplicationDirectory({ rpcClient, applicationId, branch, localDirectoryPath, hashCache, }) {
|
|
27
|
+
const { serverDirectoryHash, latestNonDraftCommitId, localDirectoryStatePreDownload, } = await getApplicationDirectoryHash(rpcClient, applicationId, branch, localDirectoryPath);
|
|
28
|
+
const directoryState = await doDownloadApplicationDirectory(rpcClient, serverDirectoryHash, localDirectoryStatePreDownload, localDirectoryPath);
|
|
29
|
+
// Now that the directory has been downloaded, we know that all of its hashes are in the server,
|
|
30
|
+
// so we can add them to the hash cache
|
|
31
|
+
hashCache.add_directory_recursive(directoryState);
|
|
32
|
+
return { directoryHash: serverDirectoryHash, latestNonDraftCommitId };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Incrementally download the directory contents, skipping any files that are already
|
|
36
|
+
* present locally, based on the old local directory state.
|
|
37
|
+
* Returns the new local directory state.
|
|
38
|
+
*
|
|
39
|
+
* @param rpcClient - The RPC client to use to get the directory contents from the server.
|
|
40
|
+
* @param directoryHash - The hash of the directory to download.
|
|
41
|
+
* @param oldLocalDirState - The old local directory state.
|
|
42
|
+
* @param localDirectoryPath - The path to the local directory.
|
|
43
|
+
* @returns The new local directory state.
|
|
44
|
+
*/
|
|
45
|
+
export async function doDownloadApplicationDirectory(rpcClient, directoryHash, oldLocalDirState, localDirectoryPath) {
|
|
46
|
+
const filesToGet = [];
|
|
47
|
+
const doRecursive = async (rpcClient, directoryHash, oldLocalDirState, localDirectoryPath, dbfsPath, filesToGet) => {
|
|
48
|
+
if (oldLocalDirState?.hash === directoryHash) {
|
|
49
|
+
// The directory is already up to date, we don't need to download anything and we can return the old local directory state
|
|
50
|
+
return oldLocalDirState;
|
|
51
|
+
}
|
|
52
|
+
const newStateContents = [];
|
|
53
|
+
// FIXME: @joeyagreco - this is slow!
|
|
54
|
+
// FIXME: @joeyagreco - look into having this be a single call to the server beforehand or implement pagination of some sort
|
|
55
|
+
const directoryContentsResponse = await unwrapResponseDto(rpcClient.call.v1.dbfs.directoryContents.get({ hash: directoryHash }));
|
|
56
|
+
// Create a map of the local directory contents
|
|
57
|
+
// As we process the directory entries, we will be removing them from the map so that the entries left will
|
|
58
|
+
// correspond to files that have been deleted
|
|
59
|
+
const localDirMap = new Map(oldLocalDirState?.contents.map((entry) => [entry.name, entry]) ?? []);
|
|
60
|
+
for (const entry of directoryContentsResponse.contents) {
|
|
61
|
+
const entryDbfsPath = dbfsPath === "" ? entry.name : `${dbfsPath}/${entry.name}`;
|
|
62
|
+
if (codeModeDownloadSkippedDirectories.includes(entry.name) ||
|
|
63
|
+
((entry.name !== ".superblocks" || entry.type !== "d") &&
|
|
64
|
+
isSnapshotExcludedName(entry.name))) {
|
|
65
|
+
getSyncLogger().debug(`[sync-service] dbfs download: skipping download-excluded entry "${entry.name}" (type ${entry.type}) at ${entryDbfsPath}`);
|
|
66
|
+
if (entry.type !== "d") {
|
|
67
|
+
getSyncLogger().warn(`[sync-service] dbfs download: skipping non-directory entry "${entry.name}" (type ${entry.type}) with download-excluded name at ${entryDbfsPath}`);
|
|
68
|
+
}
|
|
69
|
+
localDirMap.delete(entry.name);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const localFilePath = `${localDirectoryPath}/${entry.name}`;
|
|
73
|
+
// The entry has been matched, remove it from the map, if it exists
|
|
74
|
+
let localEntry = localDirMap.get(entry.name);
|
|
75
|
+
localDirMap.delete(entry.name);
|
|
76
|
+
if (localEntry !== undefined && localEntry.type !== entry.type) {
|
|
77
|
+
// The file type has changed, the file is a regular file but the local file is not, so we need to delete the local file
|
|
78
|
+
// because we will not be able to overwrite it otherwise
|
|
79
|
+
await fsp.rm(localFilePath, { recursive: true });
|
|
80
|
+
localEntry = undefined;
|
|
81
|
+
}
|
|
82
|
+
// At this point, if `localEntry` is defined, it is guaranteed to have the same type as `entry`.
|
|
83
|
+
// TypeScript will not be able to infer this, so we need to cast it to the correct type in each case.
|
|
84
|
+
switch (entry.type) {
|
|
85
|
+
case "-": {
|
|
86
|
+
newStateContents.push(entry);
|
|
87
|
+
const localEntryTyped = localEntry;
|
|
88
|
+
if (localEntryTyped?.hash !== entry.hash) {
|
|
89
|
+
filesToGet.push({
|
|
90
|
+
dbfsPath: entryDbfsPath,
|
|
91
|
+
hash: entry.hash,
|
|
92
|
+
localFilePath,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case "l": {
|
|
98
|
+
newStateContents.push(entry);
|
|
99
|
+
const localEntryTyped = localEntry;
|
|
100
|
+
if (localEntryTyped?.target !== entry.target) {
|
|
101
|
+
await fsp.symlink(entry.target, localFilePath);
|
|
102
|
+
}
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case "d": {
|
|
106
|
+
const localEntryTyped = localEntry;
|
|
107
|
+
if (localEntryTyped === undefined) {
|
|
108
|
+
await fsp.mkdir(localFilePath, { recursive: true }); // don't fail if directory exists
|
|
109
|
+
}
|
|
110
|
+
const localSubDirState = localEntryTyped === undefined
|
|
111
|
+
? undefined
|
|
112
|
+
: {
|
|
113
|
+
hash: localEntryTyped.hash,
|
|
114
|
+
contents: localEntryTyped.contents,
|
|
115
|
+
};
|
|
116
|
+
const newSubDirState = await doRecursive(rpcClient, entry.hash, localSubDirState, localFilePath, entryDbfsPath, filesToGet);
|
|
117
|
+
newStateContents.push({
|
|
118
|
+
...entry,
|
|
119
|
+
contents: newSubDirState.contents,
|
|
120
|
+
});
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
default: {
|
|
124
|
+
throw new Error(`Unknown entry type: ${entry}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Any remaining entries in the map are files/directories that have been deleted
|
|
129
|
+
for (const localEntry of localDirMap.values()) {
|
|
130
|
+
// Never delete sync-excluded directories (e.g. `.superblocks`) during
|
|
131
|
+
// download reconciliation. They are upload-excluded (see
|
|
132
|
+
// `codeModeSyncExcludedDirectories` / `listLocalDirectory`), so the
|
|
133
|
+
// server manifest never lists them — treating that absence as a
|
|
134
|
+
// server-side deletion would `rm -rf` app-scoped local state such as
|
|
135
|
+
// `.superblocks/superblocks.json`, bricking the live-edit workspace
|
|
136
|
+
// (APPS-4488). Upload and download must honor the same exclusions.
|
|
137
|
+
if (isSnapshotExcludedName(localEntry.name) ||
|
|
138
|
+
(localEntry.type === "d" &&
|
|
139
|
+
codeModeSyncExcludedDirectories.includes(localEntry.name))) {
|
|
140
|
+
getSyncLogger().debug(`[sync-service] dbfs download: keeping upload-excluded directory "${localEntry.name}" at ${localDirectoryPath} — skipping deletion`);
|
|
141
|
+
// Not pushed to newStateContents: excluded dirs are not server-tracked
|
|
142
|
+
// and must not appear in the returned DirectoryState (used only for
|
|
143
|
+
// upload-hash caching).
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const localFilePath = `${localDirectoryPath}/${localEntry.name}`;
|
|
147
|
+
getSyncLogger().debug(`[sync-service] dbfs download: deleting local entry "${localEntry.name}" at ${localFilePath}`);
|
|
148
|
+
await fsp.rm(localFilePath, { recursive: true });
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
hash: directoryHash,
|
|
152
|
+
contents: newStateContents,
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
const newLocalDirState = await doRecursive(rpcClient, directoryHash, oldLocalDirState, localDirectoryPath, "", filesToGet);
|
|
156
|
+
const downloadedWithGetMany = await tryDownloadFilesByPath(rpcClient, directoryHash, filesToGet);
|
|
157
|
+
if (downloadedWithGetMany) {
|
|
158
|
+
return newLocalDirState;
|
|
159
|
+
}
|
|
160
|
+
for (let i = 0; i < filesToGet.length; i += DOWNLOAD_BATCH_SIZE) {
|
|
161
|
+
const batch = filesToGet.slice(i, i + DOWNLOAD_BATCH_SIZE);
|
|
162
|
+
await Promise.all(batch.map(async ({ hash, localFilePath }) => {
|
|
163
|
+
const fileContentsResponse = await unwrapResponseDto(rpcClient.call.v1.dbfs.fileContents.get({ hash }));
|
|
164
|
+
const fileContents = Buffer.from(fileContentsResponse.contents, "base64");
|
|
165
|
+
await fsp.writeFile(localFilePath, fileContents);
|
|
166
|
+
}));
|
|
167
|
+
}
|
|
168
|
+
return newLocalDirState;
|
|
169
|
+
}
|
|
170
|
+
export class DraftRestoreError extends Error {
|
|
171
|
+
phase;
|
|
172
|
+
constructor(phase, cause) {
|
|
173
|
+
super(`Draft restore failed during ${phase}`, { cause });
|
|
174
|
+
this.phase = phase;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function inPhase(phase, run) {
|
|
178
|
+
try {
|
|
179
|
+
return await run();
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
throw new DraftRestoreError(phase, e);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Rebuilds the `.sbswap` markers that a replaced pod lost: every file that
|
|
187
|
+
* differs from the last checkpoint becomes a draft, so Accept and Reject work.
|
|
188
|
+
* The markers do not change the upload hash, because snapshots skip them.
|
|
189
|
+
*/
|
|
190
|
+
export async function restoreDraftMarkersFromCheckpoint({ rpcClient, applicationId, branch, appRootDirPath, }) {
|
|
191
|
+
const existing = await getDraftInfo(appRootDirPath);
|
|
192
|
+
if (existing.edits.length +
|
|
193
|
+
existing.additions.length +
|
|
194
|
+
existing.deletions.length >
|
|
195
|
+
0) {
|
|
196
|
+
return {
|
|
197
|
+
outcome: "markers_present",
|
|
198
|
+
edits: existing.edits.length,
|
|
199
|
+
additions: existing.additions.length,
|
|
200
|
+
deletions: existing.deletions.length,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const checkpointHash = await inPhase("checkpoint_fetch", async () => {
|
|
204
|
+
try {
|
|
205
|
+
// EXPORT_LIVE is the latest non-draft commit, the same commit the server
|
|
206
|
+
// compares an upload with to decide whether to create a checkpoint.
|
|
207
|
+
const checkpoint = await unwrapResponseDto(rpcClient.call.v3.application.directoryContents.get({
|
|
208
|
+
applicationId,
|
|
209
|
+
branchName: branch,
|
|
210
|
+
viewMode: ExportViewMode.EXPORT_LIVE,
|
|
211
|
+
}));
|
|
212
|
+
return checkpoint.hash;
|
|
213
|
+
}
|
|
214
|
+
catch (e) {
|
|
215
|
+
if (e instanceof NotFoundError) {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
throw e;
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
if (checkpointHash === undefined) {
|
|
222
|
+
return { outcome: "no_checkpoint", edits: 0, additions: 0, deletions: 0 };
|
|
223
|
+
}
|
|
224
|
+
const markers = [];
|
|
225
|
+
await inPhase("tree_diff", async () => diffWithCheckpoint(rpcClient, checkpointHash, await hashLocalDirectory(appRootDirPath), "", markers));
|
|
226
|
+
if (markers.length === 0) {
|
|
227
|
+
return { outcome: "no_changes", edits: 0, additions: 0, deletions: 0 };
|
|
228
|
+
}
|
|
229
|
+
// Fetch every original before writing any marker, so a failed fetch leaves no partial draft.
|
|
230
|
+
const originals = await inPhase("content_fetch", async () => {
|
|
231
|
+
const contents = [];
|
|
232
|
+
for (let i = 0; i < markers.length; i += DOWNLOAD_BATCH_SIZE) {
|
|
233
|
+
const batch = markers.slice(i, i + DOWNLOAD_BATCH_SIZE);
|
|
234
|
+
contents.push(...(await Promise.all(batch.map(async ({ checkpointHash: hash }) => {
|
|
235
|
+
if (hash === undefined) {
|
|
236
|
+
return undefined;
|
|
237
|
+
}
|
|
238
|
+
const response = await unwrapResponseDto(rpcClient.call.v1.dbfs.fileContents.get({ hash }));
|
|
239
|
+
return Buffer.from(response.contents, "base64");
|
|
240
|
+
}))));
|
|
241
|
+
}
|
|
242
|
+
return contents;
|
|
243
|
+
});
|
|
244
|
+
await inPhase("marker_write", async () => {
|
|
245
|
+
try {
|
|
246
|
+
for (const [i, marker] of markers.entries()) {
|
|
247
|
+
await writeDraftMarker(marker.path, appRootDirPath, marker.kind, originals[i]);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
// No marker existed before this loop, so removing the drafts directory undoes a partial write.
|
|
252
|
+
await cleanUpDraftsDirectory(appRootDirPath);
|
|
253
|
+
throw e;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
const count = (kind) => markers.filter((marker) => marker.kind === kind).length;
|
|
257
|
+
return {
|
|
258
|
+
outcome: "restored",
|
|
259
|
+
edits: count("edit"),
|
|
260
|
+
additions: count("add"),
|
|
261
|
+
deletions: count("delete"),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
async function diffWithCheckpoint(rpcClient, checkpointHash, local, prefix, markers) {
|
|
265
|
+
if (local?.hash === checkpointHash) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
const { contents } = await unwrapResponseDto(rpcClient.call.v1.dbfs.directoryContents.get({ hash: checkpointHash }));
|
|
269
|
+
const localByName = new Map(local?.contents.map((entry) => [entry.name, entry]) ?? []);
|
|
270
|
+
for (const entry of contents) {
|
|
271
|
+
const localEntry = localByName.get(entry.name);
|
|
272
|
+
localByName.delete(entry.name);
|
|
273
|
+
if (codeModeSyncExcludedDirectories.includes(entry.name) ||
|
|
274
|
+
isSnapshotExcludedName(entry.name)) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
// A local symlink is not part of the restored draft, so Reject never writes through it.
|
|
278
|
+
if (localEntry?.type === "l") {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
const entryPath = prefix + entry.name;
|
|
282
|
+
if (entry.type === "d") {
|
|
283
|
+
await diffWithCheckpoint(rpcClient, entry.hash, localEntry?.type === "d" ? localEntry : undefined, `${entryPath}/`, markers);
|
|
284
|
+
if (localEntry?.type === "-") {
|
|
285
|
+
markers.push({ kind: "add", path: entryPath });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else if (entry.type === "-") {
|
|
289
|
+
if (localEntry?.type !== "-") {
|
|
290
|
+
markers.push({
|
|
291
|
+
kind: "delete",
|
|
292
|
+
path: entryPath,
|
|
293
|
+
checkpointHash: entry.hash,
|
|
294
|
+
});
|
|
295
|
+
if (localEntry?.type === "d") {
|
|
296
|
+
collectLocalAdditions(localEntry.contents, `${entryPath}/`, markers);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
else if (localEntry.hash !== entry.hash) {
|
|
300
|
+
markers.push({
|
|
301
|
+
kind: "edit",
|
|
302
|
+
path: entryPath,
|
|
303
|
+
checkpointHash: entry.hash,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
collectLocalAdditions([...localByName.values()], prefix, markers);
|
|
309
|
+
}
|
|
310
|
+
function collectLocalAdditions(entries, prefix, markers) {
|
|
311
|
+
for (const entry of entries) {
|
|
312
|
+
if (codeModeSyncExcludedDirectories.includes(entry.name) ||
|
|
313
|
+
isSnapshotExcludedName(entry.name)) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (entry.type === "-") {
|
|
317
|
+
markers.push({ kind: "add", path: prefix + entry.name });
|
|
318
|
+
}
|
|
319
|
+
else if (entry.type === "d") {
|
|
320
|
+
collectLocalAdditions(entry.contents, `${prefix}${entry.name}/`, markers);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
async function tryDownloadFilesByPath(rpcClient, rootHash, filesToGet) {
|
|
325
|
+
if (filesToGet.length === 0) {
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
const getMany = rpcClient.call.v1.dbfs.getMany;
|
|
329
|
+
if (getMany === undefined) {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
try {
|
|
333
|
+
const response = await unwrapResponseDto(getMany({
|
|
334
|
+
rootHash,
|
|
335
|
+
paths: filesToGet.map((file) => file.dbfsPath),
|
|
336
|
+
}));
|
|
337
|
+
const itemByPath = new Map(response.items.map((item) => [item.path, item]));
|
|
338
|
+
if (response.items.length !== filesToGet.length) {
|
|
339
|
+
throw new InvalidGetManyResponseError(`DBFS getMany returned ${response.items.length} items for ${filesToGet.length} requested files`);
|
|
340
|
+
}
|
|
341
|
+
const filesWithContents = filesToGet.map((file) => {
|
|
342
|
+
const item = itemByPath.get(file.dbfsPath);
|
|
343
|
+
if (item?.type !== "fileContents") {
|
|
344
|
+
throw new InvalidGetManyResponseError(`DBFS getMany did not return file contents for ${file.dbfsPath}`);
|
|
345
|
+
}
|
|
346
|
+
if (item.hash !== file.hash) {
|
|
347
|
+
throw new InvalidGetManyResponseError(`DBFS getMany returned hash ${item.hash} for ${file.dbfsPath}, expected ${file.hash}`);
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
file,
|
|
351
|
+
contents: Buffer.from(item.contents, "base64"),
|
|
352
|
+
};
|
|
353
|
+
});
|
|
354
|
+
// Validate every returned item before changing the local filesystem. A
|
|
355
|
+
// malformed partial response can then safely fall back to per-file reads.
|
|
356
|
+
for (const { file, contents } of filesWithContents) {
|
|
357
|
+
await fsp.writeFile(file.localFilePath, contents);
|
|
358
|
+
}
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
catch (e) {
|
|
362
|
+
if (e instanceof InvalidGetManyResponseError ||
|
|
363
|
+
isGetManyUnsupported(e) ||
|
|
364
|
+
isBulkReadLimitError(e)) {
|
|
365
|
+
getSyncLogger().warn(`DBFS getMany unavailable, invalid, or exceeded bulk limits (${getErrorMessage(e)}), falling back to per-file download`);
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
throw e;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
class InvalidGetManyResponseError extends Error {
|
|
372
|
+
}
|
|
373
|
+
function isSocketError(e) {
|
|
374
|
+
return (typeof e === "object" &&
|
|
375
|
+
e !== null &&
|
|
376
|
+
"code" in e &&
|
|
377
|
+
typeof e.code === "number" &&
|
|
378
|
+
"message" in e &&
|
|
379
|
+
typeof e.message === "string");
|
|
380
|
+
}
|
|
381
|
+
function getErrorMessage(e) {
|
|
382
|
+
if (e instanceof Error || isSocketError(e)) {
|
|
383
|
+
return e.message;
|
|
384
|
+
}
|
|
385
|
+
return String(e);
|
|
386
|
+
}
|
|
387
|
+
function isBulkReadLimitError(e) {
|
|
388
|
+
const message = getErrorMessage(e);
|
|
389
|
+
return (message.includes("maxObjects") ||
|
|
390
|
+
message.includes("maxBytes") ||
|
|
391
|
+
message.includes("bulk read"));
|
|
392
|
+
}
|
|
393
|
+
function isGetManyUnsupported(e) {
|
|
394
|
+
return ((isSocketError(e) && e.code === 2) ||
|
|
395
|
+
getErrorMessage(e).includes("is not implemented"));
|
|
396
|
+
}
|
|
397
|
+
export async function getDraftOrLiveDirectoryHash(rpcClient, applicationId, branch) {
|
|
398
|
+
// Prefer the live-edit working copy. Clark's in-progress autosaves advance the
|
|
399
|
+
// working-copy pointer (application_live_edit.directoryContentsHash) without
|
|
400
|
+
// creating a commit, so the commit-based draft/live views can lag behind it.
|
|
401
|
+
// Restoring an older commit on a cold/recycled pod is what made in-progress
|
|
402
|
+
// work appear lost (APPS-4873).
|
|
403
|
+
try {
|
|
404
|
+
const response = await unwrapResponseDto(rpcClient.call.v3.application.directoryContents.get({
|
|
405
|
+
applicationId,
|
|
406
|
+
branchName: branch,
|
|
407
|
+
viewMode: ExportViewMode.EXPORT_LIVE_EDIT,
|
|
408
|
+
}));
|
|
409
|
+
const directoryHash = response;
|
|
410
|
+
return {
|
|
411
|
+
hash: directoryHash.hash,
|
|
412
|
+
latestNonDraftCommitId: directoryHash.latestNonDraftCommitId,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
catch (e) {
|
|
416
|
+
// Fall back to the commit-based draft/live views only when this app has no
|
|
417
|
+
// live-edit working copy (NotFoundError) or when the server predates the
|
|
418
|
+
// EXPORT_LIVE_EDIT view mode (deploy skew: an old server reports it as not
|
|
419
|
+
// implemented). Any other error (auth, transport) surfaces, matching the
|
|
420
|
+
// draft/live behavior below — a cold load that can resolve no hash should
|
|
421
|
+
// fail loudly rather than silently restore a stale tree.
|
|
422
|
+
if (!isMissingLiveEditOrUnsupportedViewMode(e)) {
|
|
423
|
+
throw e;
|
|
424
|
+
}
|
|
425
|
+
getSyncLogger().warn(`Live-edit working copy unavailable (${e instanceof Error ? e.message : String(e)}), falling back to draft/live views`);
|
|
426
|
+
}
|
|
427
|
+
return getDraftOrLiveCommitDirectoryHash(rpcClient, applicationId, branch);
|
|
428
|
+
}
|
|
429
|
+
// The legacy restore path: the latest draft commit, falling back to the latest
|
|
430
|
+
// non-draft (live) commit. Used when no live-edit working copy is available, or
|
|
431
|
+
// against a server that predates the live-edit view mode.
|
|
432
|
+
async function getDraftOrLiveCommitDirectoryHash(rpcClient, applicationId, branch) {
|
|
433
|
+
try {
|
|
434
|
+
const response = await unwrapResponseDto(rpcClient.call.v3.application.directoryContents.get({
|
|
435
|
+
applicationId,
|
|
436
|
+
branchName: branch,
|
|
437
|
+
viewMode: ExportViewMode.EXPORT_DRAFT,
|
|
438
|
+
}));
|
|
439
|
+
const directoryHash = response;
|
|
440
|
+
return {
|
|
441
|
+
hash: directoryHash.hash,
|
|
442
|
+
latestNonDraftCommitId: directoryHash.latestNonDraftCommitId,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
catch (e) {
|
|
446
|
+
if (e instanceof NotFoundError) {
|
|
447
|
+
const response = await unwrapResponseDto(rpcClient.call.v3.application.directoryContents.get({
|
|
448
|
+
applicationId,
|
|
449
|
+
branchName: branch,
|
|
450
|
+
viewMode: ExportViewMode.EXPORT_LIVE,
|
|
451
|
+
}));
|
|
452
|
+
// This is the latest live (non-draft) commit, i.e. a snapshot -- not the
|
|
453
|
+
// live-edit working copy. The working copy is restored earlier via
|
|
454
|
+
// EXPORT_LIVE_EDIT; this path only runs when that is unavailable.
|
|
455
|
+
getSyncLogger().warn(`Draft commit not found, falling back to the latest live commit: ${response.hash}`);
|
|
456
|
+
const directoryHash = response;
|
|
457
|
+
return {
|
|
458
|
+
hash: directoryHash.hash,
|
|
459
|
+
latestNonDraftCommitId: directoryHash.latestNonDraftCommitId,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
throw e;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
// True when the live-edit view should yield to the legacy draft/live path:
|
|
466
|
+
// either this app has no live-edit working copy (NotFoundError), or the server
|
|
467
|
+
// is too old to know the view mode and reports it as not implemented.
|
|
468
|
+
function isMissingLiveEditOrUnsupportedViewMode(e) {
|
|
469
|
+
if (e instanceof NotFoundError) {
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
473
|
+
return message.includes("is not implemented");
|
|
474
|
+
}
|
|
475
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../src/download.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
declare const MARKER_EXT: {
|
|
2
|
+
readonly add: ".sbswap.add";
|
|
3
|
+
readonly delete: ".sbswap.delete";
|
|
4
|
+
readonly edit: ".sbswap.edit";
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Creates a draft edit or addition marker file only.
|
|
8
|
+
*
|
|
9
|
+
* @param filePath - The absolute path to the file for which to create the respective marker.
|
|
10
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createDraftMarkerOnly(filePath: string, appRootDirPath: string): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Creates a draft edit or addition based on the presence of an existing file.
|
|
15
|
+
* This makes the draft content visible to the build system while preserving the original.
|
|
16
|
+
*
|
|
17
|
+
* @param filePath - The absolute path to the file to edit or add.
|
|
18
|
+
* @param draftContent - The content of the draft edit or addition.
|
|
19
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
20
|
+
* @returns A promise that resolves to "edit" if the file already exists, or "add" if the file does not exist.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createDraft(filePath: string, draftContent: string | Buffer, appRootDirPath: string): Promise<"edit" | "add">;
|
|
23
|
+
/**
|
|
24
|
+
* Creates a draft edit by copying the original file to the drafts directory and writing draft content to the original location.
|
|
25
|
+
* This makes the draft content visible to the build system while preserving the original.
|
|
26
|
+
* If a draft edit already exists, this will just update the draft content.
|
|
27
|
+
*
|
|
28
|
+
* @param filePath - The absolute path to the file to edit.
|
|
29
|
+
* @param draftContent - The content of the draft edit.
|
|
30
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
31
|
+
* @returns A promise that resolves when the draft edit is created.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createDraftEdit(filePath: string, draftContent: string | Buffer, appRootDirPath: string): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Creates a draft addition by writing new file content and creating a .sbswap.add marker.
|
|
36
|
+
* This makes the new file visible to the build system while marking it as a draft addition.
|
|
37
|
+
* If a draft addition already exists, this will just update the content.
|
|
38
|
+
*
|
|
39
|
+
* @param filePath - The absolute path where the new file should be created.
|
|
40
|
+
* @param content - The content of the new file.
|
|
41
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
42
|
+
* @returns A promise that resolves when the draft addition is created.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createDraftAdd(filePath: string, content: string | Buffer, appRootDirPath: string): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Writes a marker for a file that already holds its draft content, leaving the
|
|
47
|
+
* file untouched. `original` is the pre-draft content of an edit or a delete.
|
|
48
|
+
*/
|
|
49
|
+
export declare function writeDraftMarker(relativePath: string, appRootDirPath: string, kind: keyof typeof MARKER_EXT, original?: Buffer): Promise<void>;
|
|
50
|
+
export declare class DraftMarkers {
|
|
51
|
+
private readonly root;
|
|
52
|
+
constructor(root: string);
|
|
53
|
+
prepare(): Promise<void>;
|
|
54
|
+
originals(targets: string[], before: Map<string, Buffer>, maxBytes: number, maxFiles: number): Promise<Map<string, Buffer>>;
|
|
55
|
+
private assertSafePath;
|
|
56
|
+
private markerFiles;
|
|
57
|
+
reconcile(relative: string, original: Buffer | undefined, current: Buffer | undefined): Promise<void>;
|
|
58
|
+
private withinTargets;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Creates a draft deletion by backing up the file content and removing it from the workspace.
|
|
62
|
+
*
|
|
63
|
+
* @param filePath - The absolute path to the file to delete.
|
|
64
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
65
|
+
* @returns A promise that resolves when the draft deletion is created.
|
|
66
|
+
*/
|
|
67
|
+
export declare function createDraftDelete(filePath: string, appRootDirPath: string): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Recursively checks if any draft files exist in the drafts directory.
|
|
70
|
+
* Looks for .sbswap.edit, .sbswap.add, and .sbswap.delete marker files.
|
|
71
|
+
*
|
|
72
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
73
|
+
* @returns A promise that resolves to true if any draft files are found, false otherwise.
|
|
74
|
+
*/
|
|
75
|
+
export declare function hasSwapFiles(appRootDirPath: string): Promise<boolean>;
|
|
76
|
+
/**
|
|
77
|
+
* {@link hasSwapFiles} for decisions that clear the server's draft flag: only
|
|
78
|
+
* a missing drafts directory means no drafts. A directory or entry that
|
|
79
|
+
* cannot be read counts as a draft, so an unreadable tree never reads as
|
|
80
|
+
* reviewed.
|
|
81
|
+
*/
|
|
82
|
+
export declare function hasDraftsFailClosed(appRootDirPath: string): Promise<boolean>;
|
|
83
|
+
/**
|
|
84
|
+
* Throws unless the drafts directory is gone. Accept, reject and clear all
|
|
85
|
+
* end by removing it, and their helpers swallow removal and read errors, so
|
|
86
|
+
* this is the check that they finished.
|
|
87
|
+
*/
|
|
88
|
+
export declare function assertDraftsSettled(appRootDirPath: string, action: string): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Discards all draft changes by removing draft additions, then restoring the
|
|
91
|
+
* originals of edits and deletions. Each marker is retired once its step
|
|
92
|
+
* succeeds, so a retry after a failed restore resumes where it stopped.
|
|
93
|
+
*
|
|
94
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
95
|
+
* @returns A promise that resolves when all drafts have been discarded.
|
|
96
|
+
*/
|
|
97
|
+
export declare function discardAllDrafts(appRootDirPath: string): Promise<void>;
|
|
98
|
+
export declare function cleanUpDraftsDirectory(appRootDirPath: string): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Commits all draft changes by removing .sbswap.edit and .sbswap.add markers.
|
|
101
|
+
* This leaves the current filesystem state as the committed state.
|
|
102
|
+
*
|
|
103
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
104
|
+
* @returns A promise that resolves when all drafts have been committed.
|
|
105
|
+
*/
|
|
106
|
+
export declare function commitAllDrafts(appRootDirPath: string): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Gets information about all draft files in the drafts directory.
|
|
109
|
+
*
|
|
110
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
111
|
+
* @returns A promise that resolves to information about draft files.
|
|
112
|
+
*/
|
|
113
|
+
export declare function getDraftInfo(appRootDirPath: string): Promise<{
|
|
114
|
+
edits: string[];
|
|
115
|
+
additions: string[];
|
|
116
|
+
deletions: string[];
|
|
117
|
+
}>;
|
|
118
|
+
/**
|
|
119
|
+
* Generates a git-like diff showing all draft changes.
|
|
120
|
+
*
|
|
121
|
+
* @param appRootDirPath - The absolute path to the app root directory.
|
|
122
|
+
* @param specificPaths - An optional array of relative paths to limit the diff to.
|
|
123
|
+
* @returns A promise that resolves to a git-like diff string.
|
|
124
|
+
*/
|
|
125
|
+
export declare function generateDraftDiff(appRootDirPath: string, specificPaths?: string[]): Promise<string>;
|
|
126
|
+
export {};
|
|
127
|
+
//# sourceMappingURL=draft-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-helpers.d.ts","sourceRoot":"","sources":["../src/draft-helpers.ts"],"names":[],"mappings":"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"}
|