pushwork 1.0.4 → 1.0.7
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/README.md +87 -328
- package/dist/.pushwork/automerge/3P/Dm3ekE2pmjGnWvDaG3vSR7ww98/snapshot/aa2349c94955ea561f698720142f9d884a6872d9f82dc332d578c216beb0df0e +0 -0
- package/dist/.pushwork/automerge/st/orage-adapter-id +1 -0
- package/dist/.pushwork/config.json +15 -0
- package/dist/.pushwork/snapshot.json +7 -0
- package/dist/cli.js +231 -170
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +51 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +799 -0
- package/dist/commands.js.map +1 -0
- package/dist/core/change-detection.d.ts +6 -19
- package/dist/core/change-detection.d.ts.map +1 -1
- package/dist/core/change-detection.js +101 -80
- package/dist/core/change-detection.js.map +1 -1
- package/dist/{config/index.d.ts → core/config.d.ts} +13 -3
- package/dist/core/config.d.ts.map +1 -0
- package/dist/{config/index.js → core/config.js} +55 -73
- package/dist/core/config.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/move-detection.d.ts +12 -50
- package/dist/core/move-detection.d.ts.map +1 -1
- package/dist/core/move-detection.js +58 -139
- package/dist/core/move-detection.js.map +1 -1
- package/dist/core/snapshot.d.ts +0 -4
- package/dist/core/snapshot.d.ts.map +1 -1
- package/dist/core/snapshot.js +2 -11
- package/dist/core/snapshot.js.map +1 -1
- package/dist/core/sync-engine.d.ts +5 -11
- package/dist/core/sync-engine.d.ts.map +1 -1
- package/dist/core/sync-engine.js +220 -362
- package/dist/core/sync-engine.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -6
- package/dist/index.js.map +1 -1
- package/dist/types/config.d.ts +43 -67
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +6 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/documents.d.ts +15 -3
- package/dist/types/documents.d.ts.map +1 -1
- package/dist/types/documents.js.map +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +1 -1
- package/dist/types/snapshot.d.ts +3 -21
- package/dist/types/snapshot.d.ts.map +1 -1
- package/dist/types/snapshot.js +0 -14
- package/dist/types/snapshot.js.map +1 -1
- package/dist/utils/content.d.ts.map +1 -1
- package/dist/utils/content.js +2 -6
- package/dist/utils/content.js.map +1 -1
- package/dist/utils/directory.d.ts +10 -0
- package/dist/utils/directory.d.ts.map +1 -0
- package/dist/utils/directory.js +37 -0
- package/dist/utils/directory.js.map +1 -0
- package/dist/utils/fs.d.ts +15 -2
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +63 -53
- package/dist/utils/fs.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -4
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/mime-types.d.ts.map +1 -1
- package/dist/utils/mime-types.js +11 -4
- package/dist/utils/mime-types.js.map +1 -1
- package/dist/utils/network-sync.d.ts +0 -6
- package/dist/utils/network-sync.d.ts.map +1 -1
- package/dist/utils/network-sync.js +55 -99
- package/dist/utils/network-sync.js.map +1 -1
- package/dist/utils/output.d.ts +129 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +375 -0
- package/dist/utils/output.js.map +1 -0
- package/dist/utils/repo-factory.d.ts +2 -6
- package/dist/utils/repo-factory.d.ts.map +1 -1
- package/dist/utils/repo-factory.js +8 -22
- package/dist/utils/repo-factory.js.map +1 -1
- package/dist/utils/string-similarity.d.ts +14 -0
- package/dist/utils/string-similarity.d.ts.map +1 -0
- package/dist/utils/string-similarity.js +43 -0
- package/dist/utils/string-similarity.js.map +1 -0
- package/dist/utils/trace.d.ts +19 -0
- package/dist/utils/trace.d.ts.map +1 -0
- package/dist/utils/trace.js +68 -0
- package/dist/utils/trace.js.map +1 -0
- package/package.json +17 -12
- package/src/cli.ts +326 -252
- package/src/commands.ts +988 -0
- package/src/core/change-detection.ts +199 -162
- package/src/{config/index.ts → core/config.ts} +65 -82
- package/src/core/index.ts +1 -1
- package/src/core/move-detection.ts +74 -180
- package/src/core/snapshot.ts +2 -12
- package/src/core/sync-engine.ts +248 -499
- package/src/index.ts +0 -10
- package/src/types/config.ts +50 -72
- package/src/types/documents.ts +16 -3
- package/src/types/index.ts +0 -5
- package/src/types/snapshot.ts +1 -23
- package/src/utils/content.ts +2 -6
- package/src/utils/directory.ts +50 -0
- package/src/utils/fs.ts +67 -56
- package/src/utils/index.ts +1 -6
- package/src/utils/mime-types.ts +12 -4
- package/src/utils/network-sync.ts +79 -137
- package/src/utils/output.ts +450 -0
- package/src/utils/repo-factory.ts +13 -31
- package/src/utils/string-similarity.ts +54 -0
- package/src/utils/trace.ts +70 -0
- package/test/integration/exclude-patterns.test.ts +6 -15
- package/test/integration/fuzzer.test.ts +308 -391
- package/test/integration/init-sync.test.ts +89 -0
- package/test/integration/sync-deletion.test.ts +2 -61
- package/test/integration/sync-flow.test.ts +4 -24
- package/test/jest.setup.ts +34 -0
- package/test/unit/deletion-behavior.test.ts +3 -14
- package/test/unit/enhanced-mime-detection.test.ts +0 -22
- package/test/unit/snapshot.test.ts +2 -29
- package/test/unit/sync-convergence.test.ts +3 -198
- package/test/unit/sync-timing.test.ts +0 -44
- package/test/unit/utils.test.ts +0 -2
- package/tsconfig.json +3 -3
- package/dist/browser/browser-sync-engine.d.ts +0 -64
- package/dist/browser/browser-sync-engine.d.ts.map +0 -1
- package/dist/browser/browser-sync-engine.js +0 -303
- package/dist/browser/browser-sync-engine.js.map +0 -1
- package/dist/browser/filesystem-adapter.d.ts +0 -84
- package/dist/browser/filesystem-adapter.d.ts.map +0 -1
- package/dist/browser/filesystem-adapter.js +0 -413
- package/dist/browser/filesystem-adapter.js.map +0 -1
- package/dist/browser/index.d.ts +0 -36
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js +0 -90
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/types.d.ts +0 -70
- package/dist/browser/types.d.ts.map +0 -1
- package/dist/browser/types.js +0 -6
- package/dist/browser/types.js.map +0 -1
- package/dist/cli/commands.d.ts +0 -77
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -904
- package/dist/cli/commands.js.map +0 -1
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -19
- package/dist/cli/index.js.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js.map +0 -1
- package/dist/core/isomorphic-snapshot.d.ts +0 -58
- package/dist/core/isomorphic-snapshot.d.ts.map +0 -1
- package/dist/core/isomorphic-snapshot.js +0 -204
- package/dist/core/isomorphic-snapshot.js.map +0 -1
- package/dist/platform/browser-filesystem.d.ts +0 -26
- package/dist/platform/browser-filesystem.d.ts.map +0 -1
- package/dist/platform/browser-filesystem.js +0 -91
- package/dist/platform/browser-filesystem.js.map +0 -1
- package/dist/platform/filesystem.d.ts +0 -29
- package/dist/platform/filesystem.d.ts.map +0 -1
- package/dist/platform/filesystem.js +0 -65
- package/dist/platform/filesystem.js.map +0 -1
- package/dist/platform/node-filesystem.d.ts +0 -21
- package/dist/platform/node-filesystem.d.ts.map +0 -1
- package/dist/platform/node-filesystem.js +0 -93
- package/dist/platform/node-filesystem.js.map +0 -1
- package/dist/utils/content-similarity.d.ts +0 -53
- package/dist/utils/content-similarity.d.ts.map +0 -1
- package/dist/utils/content-similarity.js +0 -155
- package/dist/utils/content-similarity.js.map +0 -1
- package/dist/utils/fs-browser.d.ts +0 -57
- package/dist/utils/fs-browser.d.ts.map +0 -1
- package/dist/utils/fs-browser.js +0 -311
- package/dist/utils/fs-browser.js.map +0 -1
- package/dist/utils/fs-node.d.ts +0 -53
- package/dist/utils/fs-node.d.ts.map +0 -1
- package/dist/utils/fs-node.js +0 -220
- package/dist/utils/fs-node.js.map +0 -1
- package/dist/utils/isomorphic.d.ts +0 -29
- package/dist/utils/isomorphic.d.ts.map +0 -1
- package/dist/utils/isomorphic.js +0 -139
- package/dist/utils/isomorphic.js.map +0 -1
- package/dist/utils/pure.d.ts +0 -25
- package/dist/utils/pure.d.ts.map +0 -1
- package/dist/utils/pure.js +0 -112
- package/dist/utils/pure.js.map +0 -1
- package/src/cli/commands.ts +0 -1207
- package/src/cli/index.ts +0 -2
- package/src/utils/content-similarity.ts +0 -194
- package/test/README-TESTING-GAPS.md +0 -174
- package/test/unit/content-similarity.test.ts +0 -236
|
@@ -4,34 +4,19 @@ import {
|
|
|
4
4
|
ChangeType,
|
|
5
5
|
FileType,
|
|
6
6
|
SyncSnapshot,
|
|
7
|
-
SnapshotFileEntry,
|
|
8
|
-
SnapshotDirectoryEntry,
|
|
9
7
|
FileDocument,
|
|
10
8
|
DirectoryDocument,
|
|
9
|
+
DetectedChange,
|
|
11
10
|
} from "../types";
|
|
12
11
|
import {
|
|
13
12
|
readFileContent,
|
|
14
|
-
getFileSystemEntry,
|
|
15
13
|
listDirectory,
|
|
16
14
|
getRelativePath,
|
|
17
|
-
|
|
15
|
+
findFileInDirectoryHierarchy,
|
|
16
|
+
joinAndNormalizePath,
|
|
18
17
|
} from "../utils";
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export { ChangeType } from "../types";
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Represents a detected change
|
|
25
|
-
*/
|
|
26
|
-
export interface DetectedChange {
|
|
27
|
-
path: string;
|
|
28
|
-
changeType: ChangeType;
|
|
29
|
-
fileType: FileType;
|
|
30
|
-
localContent: string | Uint8Array | null;
|
|
31
|
-
remoteContent: string | Uint8Array | null;
|
|
32
|
-
localHead?: UrlHeads;
|
|
33
|
-
remoteHead?: UrlHeads;
|
|
34
|
-
}
|
|
18
|
+
import { isContentEqual } from "../utils/content";
|
|
19
|
+
import { out } from "../utils/output";
|
|
35
20
|
|
|
36
21
|
/**
|
|
37
22
|
* Change detection engine
|
|
@@ -76,36 +61,82 @@ export class ChangeDetector {
|
|
|
76
61
|
): Promise<DetectedChange[]> {
|
|
77
62
|
const changes: DetectedChange[] = [];
|
|
78
63
|
|
|
79
|
-
// Check for new and modified files
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
64
|
+
// Check for new and modified files in parallel for better performance
|
|
65
|
+
await Promise.all(
|
|
66
|
+
Array.from(currentFiles.entries()).map(
|
|
67
|
+
async ([relativePath, fileInfo]) => {
|
|
68
|
+
const snapshotEntry = snapshot.files.get(relativePath);
|
|
69
|
+
|
|
70
|
+
if (!snapshotEntry) {
|
|
71
|
+
// New file
|
|
72
|
+
changes.push({
|
|
73
|
+
path: relativePath,
|
|
74
|
+
changeType: ChangeType.LOCAL_ONLY,
|
|
75
|
+
fileType: fileInfo.type,
|
|
76
|
+
localContent: fileInfo.content,
|
|
77
|
+
remoteContent: null,
|
|
78
|
+
});
|
|
79
|
+
} else {
|
|
80
|
+
// Check if content changed
|
|
81
|
+
const lastKnownContent = await this.getContentAtHead(
|
|
82
|
+
snapshotEntry.url,
|
|
83
|
+
snapshotEntry.head
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const contentChanged = !isContentEqual(
|
|
87
|
+
fileInfo.content,
|
|
88
|
+
lastKnownContent
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (contentChanged) {
|
|
92
|
+
// Check remote state too
|
|
93
|
+
const currentRemoteContent = await this.getCurrentRemoteContent(
|
|
94
|
+
snapshotEntry.url
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const remoteChanged = !isContentEqual(
|
|
98
|
+
lastKnownContent,
|
|
99
|
+
currentRemoteContent
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const changeType = remoteChanged
|
|
103
|
+
? ChangeType.BOTH_CHANGED
|
|
104
|
+
: ChangeType.LOCAL_ONLY;
|
|
105
|
+
|
|
106
|
+
const remoteHead = await this.getCurrentRemoteHead(
|
|
107
|
+
snapshotEntry.url
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
changes.push({
|
|
111
|
+
path: relativePath,
|
|
112
|
+
changeType,
|
|
113
|
+
fileType: fileInfo.type,
|
|
114
|
+
localContent: fileInfo.content,
|
|
115
|
+
remoteContent: currentRemoteContent,
|
|
116
|
+
localHead: snapshotEntry.head,
|
|
117
|
+
remoteHead,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Check for deleted files in parallel
|
|
126
|
+
await Promise.all(
|
|
127
|
+
Array.from(snapshot.files.entries())
|
|
128
|
+
.filter(([relativePath]) => !currentFiles.has(relativePath))
|
|
129
|
+
.map(async ([relativePath, snapshotEntry]) => {
|
|
130
|
+
// File was deleted locally
|
|
105
131
|
const currentRemoteContent = await this.getCurrentRemoteContent(
|
|
106
132
|
snapshotEntry.url
|
|
107
133
|
);
|
|
108
|
-
const
|
|
134
|
+
const lastKnownContent = await this.getContentAtHead(
|
|
135
|
+
snapshotEntry.url,
|
|
136
|
+
snapshotEntry.head
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const remoteChanged = !isContentEqual(
|
|
109
140
|
lastKnownContent,
|
|
110
141
|
currentRemoteContent
|
|
111
142
|
);
|
|
@@ -117,47 +148,14 @@ export class ChangeDetector {
|
|
|
117
148
|
changes.push({
|
|
118
149
|
path: relativePath,
|
|
119
150
|
changeType,
|
|
120
|
-
fileType:
|
|
121
|
-
localContent:
|
|
151
|
+
fileType: FileType.TEXT, // Will be determined from document
|
|
152
|
+
localContent: null,
|
|
122
153
|
remoteContent: currentRemoteContent,
|
|
123
154
|
localHead: snapshotEntry.head,
|
|
124
155
|
remoteHead: await this.getCurrentRemoteHead(snapshotEntry.url),
|
|
125
156
|
});
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Check for deleted files
|
|
131
|
-
for (const [relativePath, snapshotEntry] of snapshot.files.entries()) {
|
|
132
|
-
if (!currentFiles.has(relativePath)) {
|
|
133
|
-
// File was deleted locally
|
|
134
|
-
const currentRemoteContent = await this.getCurrentRemoteContent(
|
|
135
|
-
snapshotEntry.url
|
|
136
|
-
);
|
|
137
|
-
const lastKnownContent = await this.getContentAtHead(
|
|
138
|
-
snapshotEntry.url,
|
|
139
|
-
snapshotEntry.head
|
|
140
|
-
);
|
|
141
|
-
const remoteChanged = !this.isContentEqual(
|
|
142
|
-
lastKnownContent,
|
|
143
|
-
currentRemoteContent
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
const changeType = remoteChanged
|
|
147
|
-
? ChangeType.BOTH_CHANGED
|
|
148
|
-
: ChangeType.LOCAL_ONLY;
|
|
149
|
-
|
|
150
|
-
changes.push({
|
|
151
|
-
path: relativePath,
|
|
152
|
-
changeType,
|
|
153
|
-
fileType: FileType.TEXT, // Will be determined from document
|
|
154
|
-
localContent: null,
|
|
155
|
-
remoteContent: currentRemoteContent,
|
|
156
|
-
localHead: snapshotEntry.head,
|
|
157
|
-
remoteHead: await this.getCurrentRemoteHead(snapshotEntry.url),
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
}
|
|
157
|
+
})
|
|
158
|
+
);
|
|
161
159
|
|
|
162
160
|
return changes;
|
|
163
161
|
}
|
|
@@ -170,41 +168,72 @@ export class ChangeDetector {
|
|
|
170
168
|
): Promise<DetectedChange[]> {
|
|
171
169
|
const changes: DetectedChange[] = [];
|
|
172
170
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
snapshotEntry
|
|
176
|
-
|
|
171
|
+
await Promise.all(
|
|
172
|
+
Array.from(snapshot.files.entries()).map(
|
|
173
|
+
async ([relativePath, snapshotEntry]) => {
|
|
174
|
+
// CRITICAL FIX: Check if file still exists in remote directory listing
|
|
175
|
+
// Files can be removed from the directory without their document heads changing
|
|
176
|
+
const stillExistsInDirectory = await this.fileExistsInRemoteDirectory(
|
|
177
|
+
snapshot.rootDirectoryUrl,
|
|
178
|
+
relativePath
|
|
179
|
+
);
|
|
177
180
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
181
|
+
if (!stillExistsInDirectory) {
|
|
182
|
+
// File was removed from remote directory listing
|
|
183
|
+
const localContent = await this.getLocalContent(relativePath);
|
|
184
|
+
|
|
185
|
+
// Only report as deleted if local file still exists
|
|
186
|
+
// (if local file is also deleted, detectLocalChanges handles it)
|
|
187
|
+
if (localContent !== null) {
|
|
188
|
+
changes.push({
|
|
189
|
+
path: relativePath,
|
|
190
|
+
changeType: ChangeType.REMOTE_ONLY,
|
|
191
|
+
fileType: FileType.TEXT,
|
|
192
|
+
localContent,
|
|
193
|
+
remoteContent: null, // File deleted remotely
|
|
194
|
+
localHead: snapshotEntry.head,
|
|
195
|
+
remoteHead: snapshotEntry.head,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const currentRemoteHead = await this.getCurrentRemoteHead(
|
|
202
|
+
snapshotEntry.url
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
if (!A.equals(currentRemoteHead, snapshotEntry.head)) {
|
|
206
|
+
// Remote document has changed
|
|
207
|
+
const currentRemoteContent = await this.getCurrentRemoteContent(
|
|
208
|
+
snapshotEntry.url
|
|
209
|
+
);
|
|
210
|
+
const localContent = await this.getLocalContent(relativePath);
|
|
211
|
+
const lastKnownContent = await this.getContentAtHead(
|
|
212
|
+
snapshotEntry.url,
|
|
213
|
+
snapshotEntry.head
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const localChanged = localContent
|
|
217
|
+
? !isContentEqual(localContent, lastKnownContent)
|
|
218
|
+
: false;
|
|
219
|
+
|
|
220
|
+
const changeType = localChanged
|
|
221
|
+
? ChangeType.BOTH_CHANGED
|
|
222
|
+
: ChangeType.REMOTE_ONLY;
|
|
223
|
+
|
|
224
|
+
changes.push({
|
|
225
|
+
path: relativePath,
|
|
226
|
+
changeType,
|
|
227
|
+
fileType: await this.getFileTypeFromContent(currentRemoteContent),
|
|
228
|
+
localContent,
|
|
229
|
+
remoteContent: currentRemoteContent,
|
|
230
|
+
localHead: snapshotEntry.head,
|
|
231
|
+
remoteHead: currentRemoteHead,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
);
|
|
208
237
|
|
|
209
238
|
return changes;
|
|
210
239
|
}
|
|
@@ -232,7 +261,7 @@ export class ChangeDetector {
|
|
|
232
261
|
changes
|
|
233
262
|
);
|
|
234
263
|
} catch (error) {
|
|
235
|
-
|
|
264
|
+
out.taskLine(`Failed to discover remote documents: ${error}`, true);
|
|
236
265
|
}
|
|
237
266
|
|
|
238
267
|
return changes;
|
|
@@ -314,7 +343,7 @@ export class ChangeDetector {
|
|
|
314
343
|
}
|
|
315
344
|
}
|
|
316
345
|
} catch (error) {
|
|
317
|
-
|
|
346
|
+
out.taskLine(`Failed to process directory: ${error}`, true);
|
|
318
347
|
}
|
|
319
348
|
}
|
|
320
349
|
|
|
@@ -336,8 +365,12 @@ export class ChangeDetector {
|
|
|
336
365
|
this.excludePatterns
|
|
337
366
|
);
|
|
338
367
|
|
|
339
|
-
|
|
340
|
-
|
|
368
|
+
const fileEntries = entries.filter(
|
|
369
|
+
(entry) => entry.type !== FileType.DIRECTORY
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
await Promise.all(
|
|
373
|
+
fileEntries.map(async (entry) => {
|
|
341
374
|
const relativePath = getRelativePath(this.rootPath, entry.path);
|
|
342
375
|
const content = await readFileContent(entry.path);
|
|
343
376
|
|
|
@@ -345,10 +378,17 @@ export class ChangeDetector {
|
|
|
345
378
|
content,
|
|
346
379
|
type: entry.type,
|
|
347
380
|
});
|
|
381
|
+
})
|
|
382
|
+
);
|
|
383
|
+
} catch (error) {
|
|
384
|
+
out.taskLine(`Failed to scan filesystem: ${error}`, true);
|
|
385
|
+
// Log more details about the error
|
|
386
|
+
if (error instanceof Error) {
|
|
387
|
+
out.taskLine(`Error details: ${error.message}`, true);
|
|
388
|
+
if (error.stack) {
|
|
389
|
+
out.taskLine(`Stack: ${error.stack}`, true);
|
|
348
390
|
}
|
|
349
391
|
}
|
|
350
|
-
} catch (error) {
|
|
351
|
-
console.warn(`Failed to scan filesystem: ${error}`);
|
|
352
392
|
}
|
|
353
393
|
|
|
354
394
|
return fileMap;
|
|
@@ -361,7 +401,7 @@ export class ChangeDetector {
|
|
|
361
401
|
relativePath: string
|
|
362
402
|
): Promise<string | Uint8Array | null> {
|
|
363
403
|
try {
|
|
364
|
-
const fullPath =
|
|
404
|
+
const fullPath = joinAndNormalizePath(this.rootPath, relativePath);
|
|
365
405
|
return await readFileContent(fullPath);
|
|
366
406
|
} catch {
|
|
367
407
|
return null;
|
|
@@ -377,7 +417,13 @@ export class ChangeDetector {
|
|
|
377
417
|
): Promise<string | Uint8Array | null> {
|
|
378
418
|
const handle = await this.repo.find<FileDocument>(url);
|
|
379
419
|
const doc = await handle.view(heads).doc();
|
|
380
|
-
|
|
420
|
+
|
|
421
|
+
const content = (doc as FileDocument | undefined)?.content;
|
|
422
|
+
// Convert ImmutableString to regular string
|
|
423
|
+
if (A.isImmutableString(content)) {
|
|
424
|
+
return content.toString();
|
|
425
|
+
}
|
|
426
|
+
return content as string | Uint8Array;
|
|
381
427
|
}
|
|
382
428
|
|
|
383
429
|
/**
|
|
@@ -393,11 +439,14 @@ export class ChangeDetector {
|
|
|
393
439
|
if (!doc) return null;
|
|
394
440
|
|
|
395
441
|
const fileDoc = doc as FileDocument;
|
|
396
|
-
|
|
442
|
+
const content = fileDoc.content;
|
|
443
|
+
// Convert ImmutableString to regular string
|
|
444
|
+
if (A.isImmutableString(content)) {
|
|
445
|
+
return content.toString();
|
|
446
|
+
}
|
|
447
|
+
return content as string | Uint8Array;
|
|
397
448
|
} catch (error) {
|
|
398
|
-
|
|
399
|
-
`❌ Failed to get current remote content for ${url}: ${error}`
|
|
400
|
-
);
|
|
449
|
+
out.taskLine(`Failed to get remote content: ${error}`, true);
|
|
401
450
|
return null;
|
|
402
451
|
}
|
|
403
452
|
}
|
|
@@ -406,7 +455,8 @@ export class ChangeDetector {
|
|
|
406
455
|
* Get current head of Automerge document
|
|
407
456
|
*/
|
|
408
457
|
private async getCurrentRemoteHead(url: AutomergeUrl): Promise<UrlHeads> {
|
|
409
|
-
|
|
458
|
+
const handle = await this.repo.find<FileDocument>(url);
|
|
459
|
+
return handle.heads();
|
|
410
460
|
}
|
|
411
461
|
|
|
412
462
|
/**
|
|
@@ -424,35 +474,6 @@ export class ChangeDetector {
|
|
|
424
474
|
}
|
|
425
475
|
}
|
|
426
476
|
|
|
427
|
-
/**
|
|
428
|
-
* Compare two content pieces for equality
|
|
429
|
-
*/
|
|
430
|
-
private isContentEqual(
|
|
431
|
-
content1: string | Uint8Array | null,
|
|
432
|
-
content2: string | Uint8Array | null
|
|
433
|
-
): boolean {
|
|
434
|
-
if (content1 === content2) return true;
|
|
435
|
-
if (!content1 || !content2) return false;
|
|
436
|
-
|
|
437
|
-
if (typeof content1 !== typeof content2) return false;
|
|
438
|
-
|
|
439
|
-
if (typeof content1 === "string") {
|
|
440
|
-
return content1 === content2;
|
|
441
|
-
} else {
|
|
442
|
-
// Compare Uint8Array
|
|
443
|
-
const buf1 = content1 as Uint8Array;
|
|
444
|
-
const buf2 = content2 as Uint8Array;
|
|
445
|
-
|
|
446
|
-
if (buf1.length !== buf2.length) return false;
|
|
447
|
-
|
|
448
|
-
for (let i = 0; i < buf1.length; i++) {
|
|
449
|
-
if (buf1[i] !== buf2[i]) return false;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
return true;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
477
|
/**
|
|
457
478
|
* Classify change type for a path
|
|
458
479
|
*/
|
|
@@ -477,9 +498,9 @@ export class ChangeDetector {
|
|
|
477
498
|
);
|
|
478
499
|
|
|
479
500
|
const localChanged = localContent
|
|
480
|
-
? !
|
|
501
|
+
? !isContentEqual(localContent, lastKnownContent)
|
|
481
502
|
: true;
|
|
482
|
-
const remoteChanged = !
|
|
503
|
+
const remoteChanged = !isContentEqual(
|
|
483
504
|
lastKnownContent,
|
|
484
505
|
currentRemoteContent
|
|
485
506
|
);
|
|
@@ -494,4 +515,20 @@ export class ChangeDetector {
|
|
|
494
515
|
return ChangeType.BOTH_CHANGED;
|
|
495
516
|
}
|
|
496
517
|
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Check if a file exists in the remote directory hierarchy
|
|
521
|
+
*/
|
|
522
|
+
private async fileExistsInRemoteDirectory(
|
|
523
|
+
rootDirectoryUrl: AutomergeUrl | undefined,
|
|
524
|
+
filePath: string
|
|
525
|
+
): Promise<boolean> {
|
|
526
|
+
if (!rootDirectoryUrl) return false;
|
|
527
|
+
const entry = await findFileInDirectoryHierarchy(
|
|
528
|
+
this.repo,
|
|
529
|
+
rootDirectoryUrl,
|
|
530
|
+
filePath
|
|
531
|
+
);
|
|
532
|
+
return entry !== null;
|
|
533
|
+
}
|
|
497
534
|
}
|