pushwork 1.0.5 → 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 -335
- 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 +208 -213
- 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 +2 -23
- package/dist/core/change-detection.d.ts.map +1 -1
- package/dist/core/change-detection.js +73 -115
- 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 +4 -3
- package/dist/core/move-detection.d.ts.map +1 -1
- package/dist/core/move-detection.js +8 -7
- 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 +211 -308
- 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 +24 -88
- 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 -2
- 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 +0 -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 +54 -20
- package/dist/utils/fs.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -3
- 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 -31
- package/dist/utils/repo-factory.js.map +1 -1
- package/dist/utils/string-similarity.js +2 -2
- package/dist/utils/string-similarity.js.map +1 -1
- 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 +11 -11
- package/src/cli.ts +276 -308
- package/src/commands.ts +988 -0
- package/src/core/change-detection.ts +182 -240
- package/src/{config/index.ts → core/config.ts} +65 -82
- package/src/core/index.ts +1 -1
- package/src/core/move-detection.ts +10 -8
- package/src/core/snapshot.ts +2 -12
- package/src/core/sync-engine.ts +237 -427
- package/src/index.ts +0 -10
- package/src/types/config.ts +28 -93
- package/src/types/documents.ts +16 -2
- package/src/types/index.ts +0 -5
- package/src/types/snapshot.ts +0 -23
- package/src/utils/content.ts +2 -6
- package/src/utils/directory.ts +50 -0
- package/src/utils/fs.ts +58 -23
- package/src/utils/index.ts +1 -5
- 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 -44
- package/src/utils/string-similarity.ts +2 -2
- 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/bench/filesystem.bench.ts +0 -78
- package/bench/hashing.bench.ts +0 -60
- package/bench/move-detection.bench.ts +0 -130
- package/bench/runner.ts +0 -49
- 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 -67
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -794
- 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/cli/output.d.ts +0 -75
- package/dist/cli/output.d.ts.map +0 -1
- package/dist/cli/output.js +0 -182
- package/dist/cli/output.js.map +0 -1
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js.map +0 -1
- package/dist/config/remote-manager.d.ts +0 -65
- package/dist/config/remote-manager.d.ts.map +0 -1
- package/dist/config/remote-manager.js +0 -243
- package/dist/config/remote-manager.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 -1030
- package/src/cli/index.ts +0 -2
- package/src/cli/output.ts +0 -244
- package/test/README-TESTING-GAPS.md +0 -174
package/src/index.ts
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
// Core sync functionality
|
|
2
1
|
export * from "./core";
|
|
3
|
-
|
|
4
|
-
// Utilities
|
|
5
2
|
export * from "./utils";
|
|
6
|
-
|
|
7
|
-
// Configuration
|
|
8
|
-
export * from "./config";
|
|
9
|
-
|
|
10
|
-
// Types
|
|
11
3
|
export * from "./types";
|
|
12
|
-
|
|
13
|
-
// CLI commands (for programmatic use)
|
|
14
4
|
export * from "./cli";
|
package/src/types/config.ts
CHANGED
|
@@ -1,85 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
* Global configuration options
|
|
3
|
-
*/
|
|
4
|
-
export interface GlobalConfig {
|
|
5
|
-
sync_server?: string;
|
|
6
|
-
sync_server_storage_id?: string;
|
|
7
|
-
exclude_patterns?: string[];
|
|
8
|
-
large_file_threshold?: string;
|
|
9
|
-
diff?: {
|
|
10
|
-
external_tool?: string;
|
|
11
|
-
show_binary?: boolean;
|
|
12
|
-
};
|
|
13
|
-
sync?: {
|
|
14
|
-
move_detection_threshold?: number;
|
|
15
|
-
prompt_threshold?: number;
|
|
16
|
-
auto_sync?: boolean;
|
|
17
|
-
parallel_operations?: number;
|
|
18
|
-
};
|
|
19
|
-
}
|
|
1
|
+
import { StorageId } from "@automerge/automerge-repo";
|
|
20
2
|
|
|
21
3
|
/**
|
|
22
|
-
* Default
|
|
4
|
+
* Default sync server configuration
|
|
23
5
|
*/
|
|
24
|
-
export
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
large_file_threshold: string;
|
|
28
|
-
}
|
|
6
|
+
export const DEFAULT_SYNC_SERVER = "wss://sync3.automerge.org";
|
|
7
|
+
export const DEFAULT_SYNC_SERVER_STORAGE_ID =
|
|
8
|
+
"3760df37-a4c6-4f66-9ecd-732039a9385d" as StorageId;
|
|
29
9
|
|
|
30
10
|
/**
|
|
31
|
-
*
|
|
32
|
-
*/
|
|
33
|
-
export interface DiffSettings {
|
|
34
|
-
external_tool?: string;
|
|
35
|
-
show_binary: boolean;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Sync behavior settings
|
|
11
|
+
* Global configuration options
|
|
40
12
|
*/
|
|
41
|
-
export interface
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
13
|
+
export interface GlobalConfig {
|
|
14
|
+
sync_server?: string;
|
|
15
|
+
sync_server_storage_id?: StorageId;
|
|
16
|
+
exclude_patterns: string[];
|
|
17
|
+
sync: {
|
|
18
|
+
move_detection_threshold: number;
|
|
19
|
+
};
|
|
46
20
|
}
|
|
47
21
|
|
|
48
22
|
/**
|
|
49
23
|
* Per-directory configuration
|
|
50
24
|
*/
|
|
51
|
-
export interface DirectoryConfig {
|
|
52
|
-
|
|
53
|
-
sync_server_storage_id?: string;
|
|
25
|
+
export interface DirectoryConfig extends GlobalConfig {
|
|
26
|
+
root_directory_url?: string;
|
|
54
27
|
sync_enabled: boolean;
|
|
55
|
-
root_directory_url?: string; // AutomergeUrl of the root directory document
|
|
56
|
-
defaults: {
|
|
57
|
-
exclude_patterns: string[];
|
|
58
|
-
large_file_threshold: string;
|
|
59
|
-
};
|
|
60
|
-
diff: {
|
|
61
|
-
external_tool?: string;
|
|
62
|
-
show_binary: boolean;
|
|
63
|
-
};
|
|
64
|
-
sync: {
|
|
65
|
-
move_detection_threshold: number;
|
|
66
|
-
prompt_threshold: number;
|
|
67
|
-
auto_sync: boolean;
|
|
68
|
-
parallel_operations: number;
|
|
69
|
-
};
|
|
70
28
|
}
|
|
71
29
|
|
|
72
30
|
/**
|
|
73
31
|
* CLI command options
|
|
74
32
|
*/
|
|
75
33
|
export interface CommandOptions {
|
|
76
|
-
dryRun?: boolean;
|
|
77
34
|
verbose?: boolean;
|
|
78
|
-
debug?: boolean;
|
|
79
|
-
tool?: string;
|
|
80
|
-
nameOnly?: boolean;
|
|
81
|
-
oneline?: boolean;
|
|
82
|
-
remote?: string;
|
|
83
35
|
}
|
|
84
36
|
|
|
85
37
|
/**
|
|
@@ -88,22 +40,21 @@ export interface CommandOptions {
|
|
|
88
40
|
export interface CloneOptions extends CommandOptions {
|
|
89
41
|
force?: boolean; // Overwrite existing directory
|
|
90
42
|
syncServer?: string; // Custom sync server URL
|
|
91
|
-
syncServerStorageId?:
|
|
43
|
+
syncServerStorageId?: StorageId; // Custom sync server storage ID
|
|
92
44
|
}
|
|
93
45
|
|
|
94
46
|
/**
|
|
95
47
|
* Sync command specific options
|
|
96
48
|
*/
|
|
97
49
|
export interface SyncOptions extends CommandOptions {
|
|
98
|
-
dryRun: boolean;
|
|
99
50
|
force?: boolean;
|
|
51
|
+
dryRun?: boolean;
|
|
100
52
|
}
|
|
101
53
|
|
|
102
54
|
/**
|
|
103
55
|
* Diff command specific options
|
|
104
56
|
*/
|
|
105
57
|
export interface DiffOptions extends CommandOptions {
|
|
106
|
-
tool?: string;
|
|
107
58
|
nameOnly: boolean;
|
|
108
59
|
}
|
|
109
60
|
|
|
@@ -128,31 +79,7 @@ export interface CheckoutOptions extends CommandOptions {
|
|
|
128
79
|
*/
|
|
129
80
|
export interface InitOptions extends CommandOptions {
|
|
130
81
|
syncServer?: string;
|
|
131
|
-
syncServerStorageId?:
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Commit command specific options
|
|
136
|
-
*/
|
|
137
|
-
export interface CommitOptions extends CommandOptions {
|
|
138
|
-
dryRun?: boolean;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Status command specific options
|
|
143
|
-
*/
|
|
144
|
-
export interface StatusOptions extends CommandOptions {}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* URL command specific options
|
|
148
|
-
*/
|
|
149
|
-
export interface UrlOptions extends CommandOptions {}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* List (ls) command specific options
|
|
153
|
-
*/
|
|
154
|
-
export interface ListOptions extends CommandOptions {
|
|
155
|
-
long?: boolean;
|
|
82
|
+
syncServerStorageId?: StorageId;
|
|
156
83
|
}
|
|
157
84
|
|
|
158
85
|
/**
|
|
@@ -166,8 +93,16 @@ export interface ConfigOptions extends CommandOptions {
|
|
|
166
93
|
}
|
|
167
94
|
|
|
168
95
|
/**
|
|
169
|
-
*
|
|
96
|
+
* Status command specific options
|
|
170
97
|
*/
|
|
171
|
-
export interface
|
|
98
|
+
export interface StatusOptions extends CommandOptions {
|
|
172
99
|
verbose?: boolean;
|
|
173
100
|
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Watch command specific options
|
|
104
|
+
*/
|
|
105
|
+
export interface WatchOptions extends CommandOptions {
|
|
106
|
+
script?: string; // Script to run before syncing
|
|
107
|
+
watchDir?: string; // Directory to watch (relative to working dir)
|
|
108
|
+
}
|
package/src/types/documents.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { AutomergeUrl } from "@automerge/automerge-repo";
|
|
1
|
+
import { AutomergeUrl, UrlHeads } from "@automerge/automerge-repo";
|
|
2
|
+
import * as A from "@automerge/automerge";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Entry in a directory document
|
|
@@ -26,7 +27,7 @@ export interface FileDocument {
|
|
|
26
27
|
name: string;
|
|
27
28
|
extension: string;
|
|
28
29
|
mimeType: string;
|
|
29
|
-
content:
|
|
30
|
+
content: A.ImmutableString | Uint8Array;
|
|
30
31
|
metadata: {
|
|
31
32
|
permissions: number;
|
|
32
33
|
};
|
|
@@ -71,3 +72,16 @@ export interface MoveCandidate {
|
|
|
71
72
|
similarity: number;
|
|
72
73
|
newContent?: string | Uint8Array; // Content at destination (may differ from source if modified during move)
|
|
73
74
|
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Represents a detected change
|
|
78
|
+
*/
|
|
79
|
+
export interface DetectedChange {
|
|
80
|
+
path: string;
|
|
81
|
+
changeType: ChangeType;
|
|
82
|
+
fileType: FileType;
|
|
83
|
+
localContent: string | Uint8Array | null;
|
|
84
|
+
remoteContent: string | Uint8Array | null;
|
|
85
|
+
localHead?: UrlHeads;
|
|
86
|
+
remoteHead?: UrlHeads;
|
|
87
|
+
}
|
package/src/types/index.ts
CHANGED
package/src/types/snapshot.ts
CHANGED
|
@@ -64,26 +64,3 @@ export interface SyncError {
|
|
|
64
64
|
error: Error;
|
|
65
65
|
recoverable: boolean;
|
|
66
66
|
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Sync operation type
|
|
70
|
-
*/
|
|
71
|
-
export enum SyncOperation {
|
|
72
|
-
CREATE_FILE = "create_file",
|
|
73
|
-
UPDATE_FILE = "update_file",
|
|
74
|
-
DELETE_FILE = "delete_file",
|
|
75
|
-
MOVE_FILE = "move_file",
|
|
76
|
-
CREATE_DIRECTORY = "create_directory",
|
|
77
|
-
DELETE_DIRECTORY = "delete_directory",
|
|
78
|
-
MOVE_DIRECTORY = "move_directory",
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Pending sync operation
|
|
83
|
-
*/
|
|
84
|
-
export interface PendingSyncOperation {
|
|
85
|
-
operation: SyncOperation;
|
|
86
|
-
path: string;
|
|
87
|
-
newPath?: string;
|
|
88
|
-
priority: number;
|
|
89
|
-
}
|
package/src/utils/content.ts
CHANGED
|
@@ -13,16 +13,12 @@ export function isContentEqual(
|
|
|
13
13
|
if (typeof content1 === "string") {
|
|
14
14
|
return content1 === content2;
|
|
15
15
|
} else {
|
|
16
|
-
// Compare Uint8Array
|
|
16
|
+
// Compare Uint8Array using native Buffer.equals() for better performance
|
|
17
17
|
const buf1 = content1 as Uint8Array;
|
|
18
18
|
const buf2 = content2 as Uint8Array;
|
|
19
19
|
|
|
20
20
|
if (buf1.length !== buf2.length) return false;
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
if (buf1[i] !== buf2[i]) return false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return true;
|
|
22
|
+
return Buffer.from(buf1).equals(Buffer.from(buf2));
|
|
27
23
|
}
|
|
28
24
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AutomergeUrl, Repo } from "@automerge/automerge-repo";
|
|
2
|
+
import { DirectoryDocument } from "../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Find a file in the directory hierarchy by path
|
|
6
|
+
*/
|
|
7
|
+
export async function findFileInDirectoryHierarchy(
|
|
8
|
+
repo: Repo,
|
|
9
|
+
directoryUrl: AutomergeUrl,
|
|
10
|
+
filePath: string
|
|
11
|
+
): Promise<{ name: string; type: string; url: AutomergeUrl } | null> {
|
|
12
|
+
try {
|
|
13
|
+
const pathParts = filePath.split("/");
|
|
14
|
+
let currentDirUrl = directoryUrl;
|
|
15
|
+
|
|
16
|
+
// Navigate through directories to find the parent directory
|
|
17
|
+
for (let i = 0; i < pathParts.length - 1; i++) {
|
|
18
|
+
const dirName = pathParts[i];
|
|
19
|
+
const dirHandle = await repo.find<DirectoryDocument>(currentDirUrl);
|
|
20
|
+
const dirDoc = await dirHandle.doc();
|
|
21
|
+
|
|
22
|
+
if (!dirDoc) return null;
|
|
23
|
+
|
|
24
|
+
const subDirEntry = dirDoc.docs.find(
|
|
25
|
+
(entry: { name: string; type: string; url: AutomergeUrl }) =>
|
|
26
|
+
entry.name === dirName && entry.type === "folder"
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (!subDirEntry) return null;
|
|
30
|
+
currentDirUrl = subDirEntry.url;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Now look for the file in the final directory
|
|
34
|
+
const fileName = pathParts[pathParts.length - 1];
|
|
35
|
+
const finalDirHandle = await repo.find<DirectoryDocument>(currentDirUrl);
|
|
36
|
+
const finalDirDoc = await finalDirHandle.doc();
|
|
37
|
+
|
|
38
|
+
if (!finalDirDoc) return null;
|
|
39
|
+
|
|
40
|
+
const fileEntry = finalDirDoc.docs.find(
|
|
41
|
+
(entry: { name: string; type: string; url: AutomergeUrl }) =>
|
|
42
|
+
entry.name === fileName && entry.type === "file"
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return fileEntry || null;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// Failed to find file in hierarchy
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/utils/fs.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as crypto from "crypto";
|
|
|
4
4
|
import { glob } from "glob";
|
|
5
5
|
import * as mimeTypes from "mime-types";
|
|
6
6
|
import * as ignore from "ignore";
|
|
7
|
+
import * as A from "@automerge/automerge";
|
|
7
8
|
import { FileSystemEntry, FileType } from "../types";
|
|
8
9
|
import { isEnhancedTextFile } from "./mime-types";
|
|
9
10
|
|
|
@@ -95,12 +96,15 @@ export async function readFileContent(
|
|
|
95
96
|
*/
|
|
96
97
|
export async function writeFileContent(
|
|
97
98
|
filePath: string,
|
|
98
|
-
content: string | Uint8Array
|
|
99
|
+
content: string | A.ImmutableString | Uint8Array
|
|
99
100
|
): Promise<void> {
|
|
100
101
|
await ensureDirectoryExists(path.dirname(filePath));
|
|
101
102
|
|
|
102
103
|
if (typeof content === "string") {
|
|
103
104
|
await fs.writeFile(filePath, content, "utf8");
|
|
105
|
+
} else if (A.isImmutableString(content)) {
|
|
106
|
+
// Convert ImmutableString to regular string for filesystem operations
|
|
107
|
+
await fs.writeFile(filePath, content.toString(), "utf8");
|
|
104
108
|
} else {
|
|
105
109
|
await fs.writeFile(filePath, content);
|
|
106
110
|
}
|
|
@@ -126,7 +130,7 @@ export async function removePath(filePath: string): Promise<void> {
|
|
|
126
130
|
try {
|
|
127
131
|
const stats = await fs.stat(filePath);
|
|
128
132
|
if (stats.isDirectory()) {
|
|
129
|
-
await fs.
|
|
133
|
+
await fs.rm(filePath, { recursive: true });
|
|
130
134
|
} else {
|
|
131
135
|
await fs.unlink(filePath);
|
|
132
136
|
}
|
|
@@ -168,33 +172,34 @@ export async function listDirectory(
|
|
|
168
172
|
const entries: FileSystemEntry[] = [];
|
|
169
173
|
|
|
170
174
|
try {
|
|
175
|
+
// Construct pattern using path.join for proper cross-platform handling
|
|
171
176
|
const pattern = recursive
|
|
172
177
|
? path.join(dirPath, "**/*")
|
|
173
178
|
: path.join(dirPath, "*");
|
|
179
|
+
|
|
180
|
+
// CRITICAL: glob expects forward slashes, even on Windows
|
|
181
|
+
// Convert backslashes to forward slashes for glob pattern
|
|
182
|
+
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
174
183
|
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
// Directory patterns
|
|
179
|
-
return `${pattern}/**`;
|
|
180
|
-
}
|
|
181
|
-
return pattern;
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const paths = await glob(pattern, {
|
|
184
|
+
// Use glob to get all paths (with dot files)
|
|
185
|
+
// Note: We don't use glob's ignore option because it doesn't support gitignore semantics
|
|
186
|
+
const paths = await glob(normalizedPattern, {
|
|
185
187
|
dot: true,
|
|
186
|
-
ignore: ignorePatterns,
|
|
187
188
|
});
|
|
188
189
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
|
|
190
|
+
// Parallelize all stat calls for better performance
|
|
191
|
+
const allEntries = await Promise.all(
|
|
192
|
+
paths.map(async (filePath) => {
|
|
193
|
+
// Filter using proper gitignore semantics from the ignore library
|
|
194
|
+
if (isExcluded(filePath, dirPath, excludePatterns)) {
|
|
195
|
+
return null;
|
|
195
196
|
}
|
|
196
|
-
|
|
197
|
-
|
|
197
|
+
return await getFileSystemEntry(filePath);
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Filter out null entries (excluded files or files that couldn't be read)
|
|
202
|
+
entries.push(...allEntries.filter((e): e is FileSystemEntry => e !== null));
|
|
198
203
|
} catch {
|
|
199
204
|
// Return empty array if directory doesn't exist or can't be read
|
|
200
205
|
}
|
|
@@ -232,10 +237,14 @@ export async function movePath(
|
|
|
232
237
|
* Calculate content hash for change detection
|
|
233
238
|
*/
|
|
234
239
|
export async function calculateContentHash(
|
|
235
|
-
content: string | Uint8Array
|
|
240
|
+
content: string | A.ImmutableString | Uint8Array
|
|
236
241
|
): Promise<string> {
|
|
237
242
|
const hash = crypto.createHash("sha256");
|
|
238
|
-
|
|
243
|
+
if (A.isImmutableString(content)) {
|
|
244
|
+
hash.update(content.toString());
|
|
245
|
+
} else {
|
|
246
|
+
hash.update(content);
|
|
247
|
+
}
|
|
239
248
|
return hash.digest("hex");
|
|
240
249
|
}
|
|
241
250
|
|
|
@@ -256,14 +265,40 @@ export function getFileExtension(filePath: string): string {
|
|
|
256
265
|
|
|
257
266
|
/**
|
|
258
267
|
* Normalize path separators for cross-platform compatibility
|
|
268
|
+
* Converts all path separators to forward slashes for consistent storage
|
|
259
269
|
*/
|
|
260
270
|
export function normalizePath(filePath: string): string {
|
|
261
271
|
return path.posix.normalize(filePath.replace(/\\/g, "/"));
|
|
262
272
|
}
|
|
263
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Join paths and normalize separators for cross-platform compatibility
|
|
276
|
+
* Use this instead of string concatenation to ensure proper path handling on Windows
|
|
277
|
+
*/
|
|
278
|
+
export function joinAndNormalizePath(...paths: string[]): string {
|
|
279
|
+
// Use path.join to properly handle path construction (handles Windows drive letters, etc.)
|
|
280
|
+
const joined = path.join(...paths);
|
|
281
|
+
// Then normalize to forward slashes for consistent storage/comparison
|
|
282
|
+
return normalizePath(joined);
|
|
283
|
+
}
|
|
284
|
+
|
|
264
285
|
/**
|
|
265
286
|
* Get relative path from base directory
|
|
266
287
|
*/
|
|
267
288
|
export function getRelativePath(basePath: string, filePath: string): string {
|
|
268
289
|
return normalizePath(path.relative(basePath, filePath));
|
|
269
290
|
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Format a path as a relative path with proper prefix
|
|
294
|
+
* Ensures paths like "src" become "./src" for clarity
|
|
295
|
+
* Leaves absolute paths and paths already starting with . or .. unchanged
|
|
296
|
+
*/
|
|
297
|
+
export function formatRelativePath(filePath: string): string {
|
|
298
|
+
// Already starts with . or / - leave as-is
|
|
299
|
+
if (filePath.startsWith(".") || filePath.startsWith("/")) {
|
|
300
|
+
return filePath;
|
|
301
|
+
}
|
|
302
|
+
// Add ./ prefix for clarity
|
|
303
|
+
return `./${filePath}`;
|
|
304
|
+
}
|
package/src/utils/index.ts
CHANGED
package/src/utils/mime-types.ts
CHANGED
|
@@ -109,8 +109,9 @@ const FORCE_TEXT_EXTENSIONS = new Set([
|
|
|
109
109
|
* Get enhanced MIME type for file with custom dev file support
|
|
110
110
|
*/
|
|
111
111
|
export function getEnhancedMimeType(filePath: string): string {
|
|
112
|
-
const
|
|
113
|
-
const
|
|
112
|
+
const normalized = normalizePathSeparators(filePath);
|
|
113
|
+
const filename = normalized.split("/").pop() || "";
|
|
114
|
+
const extension = getFileExtension(normalized);
|
|
114
115
|
|
|
115
116
|
// Check custom definitions first (by extension)
|
|
116
117
|
if (extension && CUSTOM_MIME_TYPES[extension]) {
|
|
@@ -123,7 +124,7 @@ export function getEnhancedMimeType(filePath: string): string {
|
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
// Fall back to standard mime-types library
|
|
126
|
-
const standardMime = mimeTypes.lookup(
|
|
127
|
+
const standardMime = mimeTypes.lookup(normalized);
|
|
127
128
|
if (standardMime) {
|
|
128
129
|
return standardMime;
|
|
129
130
|
}
|
|
@@ -145,7 +146,14 @@ export function shouldForceAsText(filePath: string): boolean {
|
|
|
145
146
|
*/
|
|
146
147
|
function getFileExtension(filePath: string): string {
|
|
147
148
|
const match = filePath.match(/\.[^.]*$/);
|
|
148
|
-
return match ? match[0] : "";
|
|
149
|
+
return match ? match[0].toLowerCase() : "";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Normalize path separators to forward slashes for cross-platform consistency
|
|
154
|
+
*/
|
|
155
|
+
function normalizePathSeparators(p: string): string {
|
|
156
|
+
return p.replace(/\\/g, "/");
|
|
149
157
|
}
|
|
150
158
|
|
|
151
159
|
/**
|