pushwork 2.0.0-a.sub.0 → 2.0.0-preview
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/dist/branches.d.ts +19 -0
- package/dist/branches.d.ts.map +1 -0
- package/dist/branches.js +111 -0
- package/dist/branches.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +238 -272
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +84 -0
- package/dist/config.js.map +1 -0
- package/dist/fs-tree.d.ts +6 -0
- package/dist/fs-tree.d.ts.map +1 -0
- package/dist/fs-tree.js +99 -0
- package/dist/fs-tree.js.map +1 -0
- package/dist/ignore.d.ts +6 -0
- package/dist/ignore.d.ts.map +1 -0
- package/dist/ignore.js +74 -0
- package/dist/ignore.js.map +1 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -4
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +3 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +14 -0
- package/dist/log.js.map +1 -0
- package/dist/pushwork.d.ts +115 -0
- package/dist/pushwork.d.ts.map +1 -0
- package/dist/pushwork.js +918 -0
- package/dist/pushwork.js.map +1 -0
- package/dist/repo.d.ts +14 -0
- package/dist/repo.d.ts.map +1 -0
- package/dist/repo.js +60 -0
- package/dist/repo.js.map +1 -0
- package/dist/shapes/custom.d.ts +3 -0
- package/dist/shapes/custom.d.ts.map +1 -0
- package/dist/shapes/custom.js +57 -0
- package/dist/shapes/custom.js.map +1 -0
- package/dist/shapes/file.d.ts +20 -0
- package/dist/shapes/file.d.ts.map +1 -0
- package/dist/shapes/file.js +140 -0
- package/dist/shapes/file.js.map +1 -0
- package/dist/shapes/index.d.ts +10 -0
- package/dist/shapes/index.d.ts.map +1 -0
- package/dist/shapes/index.js +35 -0
- package/dist/shapes/index.js.map +1 -0
- package/dist/shapes/patchwork-folder.d.ts +3 -0
- package/dist/shapes/patchwork-folder.d.ts.map +1 -0
- package/dist/shapes/patchwork-folder.js +160 -0
- package/dist/shapes/patchwork-folder.js.map +1 -0
- package/dist/shapes/types.d.ts +37 -0
- package/dist/shapes/types.d.ts.map +1 -0
- package/dist/shapes/types.js +52 -0
- package/dist/shapes/types.js.map +1 -0
- package/dist/shapes/vfs.d.ts +3 -0
- package/dist/shapes/vfs.d.ts.map +1 -0
- package/dist/shapes/vfs.js +88 -0
- package/dist/shapes/vfs.js.map +1 -0
- package/dist/stash.d.ts +23 -0
- package/dist/stash.d.ts.map +1 -0
- package/dist/stash.js +118 -0
- package/dist/stash.js.map +1 -0
- package/flake.lock +128 -0
- package/flake.nix +66 -0
- package/package.json +15 -48
- package/patches/@automerge__automerge-repo@2.6.0-subduction.15.patch +26 -0
- package/pnpm-workspace.yaml +5 -0
- package/src/branches.ts +93 -0
- package/src/cli.ts +258 -408
- package/src/config.ts +64 -0
- package/src/fs-tree.ts +70 -0
- package/src/ignore.ts +33 -0
- package/src/index.ts +38 -4
- package/src/log.ts +8 -0
- package/src/pushwork.ts +1055 -0
- package/src/repo.ts +76 -0
- package/src/shapes/custom.ts +29 -0
- package/src/shapes/file.ts +115 -0
- package/src/shapes/index.ts +19 -0
- package/src/shapes/patchwork-folder.ts +156 -0
- package/src/shapes/types.ts +79 -0
- package/src/shapes/vfs.ts +93 -0
- package/src/stash.ts +106 -0
- package/test/integration/branches.test.ts +389 -0
- package/test/integration/pushwork.test.ts +547 -0
- package/test/setup.ts +29 -0
- package/test/unit/doc-shape.test.ts +612 -0
- package/tsconfig.json +2 -3
- package/vitest.config.ts +14 -0
- package/ARCHITECTURE-ACCORDING-TO-CLAUDE.md +0 -248
- package/CLAUDE.md +0 -141
- package/README.md +0 -221
- package/babel.config.js +0 -5
- package/dist/cli/commands.d.ts +0 -71
- 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/commands.d.ts +0 -61
- package/dist/commands.d.ts.map +0 -1
- package/dist/commands.js +0 -861
- package/dist/commands.js.map +0 -1
- package/dist/config/index.d.ts +0 -71
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -314
- package/dist/config/index.js.map +0 -1
- package/dist/core/change-detection.d.ts +0 -80
- package/dist/core/change-detection.d.ts.map +0 -1
- package/dist/core/change-detection.js +0 -523
- package/dist/core/change-detection.js.map +0 -1
- package/dist/core/config.d.ts +0 -81
- package/dist/core/config.d.ts.map +0 -1
- package/dist/core/config.js +0 -258
- package/dist/core/config.js.map +0 -1
- package/dist/core/index.d.ts +0 -6
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -6
- package/dist/core/index.js.map +0 -1
- package/dist/core/move-detection.d.ts +0 -34
- package/dist/core/move-detection.d.ts.map +0 -1
- package/dist/core/move-detection.js +0 -121
- package/dist/core/move-detection.js.map +0 -1
- package/dist/core/snapshot.d.ts +0 -105
- package/dist/core/snapshot.d.ts.map +0 -1
- package/dist/core/snapshot.js +0 -217
- package/dist/core/snapshot.js.map +0 -1
- package/dist/core/sync-engine.d.ts +0 -151
- package/dist/core/sync-engine.d.ts.map +0 -1
- package/dist/core/sync-engine.js +0 -1346
- package/dist/core/sync-engine.js.map +0 -1
- package/dist/types/config.d.ts +0 -99
- package/dist/types/config.d.ts.map +0 -1
- package/dist/types/config.js +0 -5
- package/dist/types/config.js.map +0 -1
- package/dist/types/documents.d.ts +0 -88
- package/dist/types/documents.d.ts.map +0 -1
- package/dist/types/documents.js +0 -20
- package/dist/types/documents.js.map +0 -1
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -4
- package/dist/types/index.js.map +0 -1
- package/dist/types/snapshot.d.ts +0 -64
- package/dist/types/snapshot.d.ts.map +0 -1
- package/dist/types/snapshot.js +0 -2
- package/dist/types/snapshot.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/content.d.ts +0 -10
- package/dist/utils/content.d.ts.map +0 -1
- package/dist/utils/content.js +0 -31
- package/dist/utils/content.js.map +0 -1
- package/dist/utils/directory.d.ts +0 -24
- package/dist/utils/directory.d.ts.map +0 -1
- package/dist/utils/directory.js +0 -52
- package/dist/utils/directory.js.map +0 -1
- package/dist/utils/fs.d.ts +0 -74
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/fs.js +0 -248
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -5
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/mime-types.d.ts +0 -13
- package/dist/utils/mime-types.d.ts.map +0 -1
- package/dist/utils/mime-types.js +0 -209
- package/dist/utils/mime-types.js.map +0 -1
- package/dist/utils/network-sync.d.ts +0 -36
- package/dist/utils/network-sync.d.ts.map +0 -1
- package/dist/utils/network-sync.js +0 -250
- package/dist/utils/network-sync.js.map +0 -1
- package/dist/utils/node-polyfills.d.ts +0 -9
- package/dist/utils/node-polyfills.d.ts.map +0 -1
- package/dist/utils/node-polyfills.js +0 -9
- package/dist/utils/node-polyfills.js.map +0 -1
- package/dist/utils/output.d.ts +0 -129
- package/dist/utils/output.d.ts.map +0 -1
- package/dist/utils/output.js +0 -368
- package/dist/utils/output.js.map +0 -1
- package/dist/utils/repo-factory.d.ts +0 -13
- package/dist/utils/repo-factory.d.ts.map +0 -1
- package/dist/utils/repo-factory.js +0 -46
- package/dist/utils/repo-factory.js.map +0 -1
- package/dist/utils/string-similarity.d.ts +0 -14
- package/dist/utils/string-similarity.d.ts.map +0 -1
- package/dist/utils/string-similarity.js +0 -39
- package/dist/utils/string-similarity.js.map +0 -1
- package/dist/utils/text-diff.d.ts +0 -37
- package/dist/utils/text-diff.d.ts.map +0 -1
- package/dist/utils/text-diff.js +0 -93
- package/dist/utils/text-diff.js.map +0 -1
- package/dist/utils/trace.d.ts +0 -19
- package/dist/utils/trace.d.ts.map +0 -1
- package/dist/utils/trace.js +0 -63
- package/dist/utils/trace.js.map +0 -1
- package/src/commands.ts +0 -1134
- package/src/core/change-detection.ts +0 -712
- package/src/core/config.ts +0 -313
- package/src/core/index.ts +0 -5
- package/src/core/move-detection.ts +0 -169
- package/src/core/snapshot.ts +0 -275
- package/src/core/sync-engine.ts +0 -1758
- package/src/types/config.ts +0 -111
- package/src/types/documents.ts +0 -91
- package/src/types/index.ts +0 -3
- package/src/types/snapshot.ts +0 -67
- package/src/utils/content.ts +0 -34
- package/src/utils/directory.ts +0 -73
- package/src/utils/fs.ts +0 -297
- package/src/utils/index.ts +0 -4
- package/src/utils/mime-types.ts +0 -244
- package/src/utils/network-sync.ts +0 -319
- package/src/utils/node-polyfills.ts +0 -8
- package/src/utils/output.ts +0 -450
- package/src/utils/repo-factory.ts +0 -73
- package/src/utils/string-similarity.ts +0 -54
- package/src/utils/text-diff.ts +0 -101
- package/src/utils/trace.ts +0 -70
- package/test/integration/README.md +0 -328
- package/test/integration/clone-test.sh +0 -310
- package/test/integration/conflict-resolution-test.sh +0 -309
- package/test/integration/debug-both-nested.sh +0 -74
- package/test/integration/debug-concurrent-nested.sh +0 -87
- package/test/integration/debug-nested.sh +0 -73
- package/test/integration/deletion-behavior-test.sh +0 -487
- package/test/integration/deletion-sync-test-simple.sh +0 -193
- package/test/integration/deletion-sync-test.sh +0 -297
- package/test/integration/exclude-patterns.test.ts +0 -144
- package/test/integration/full-integration-test.sh +0 -363
- package/test/integration/fuzzer.test.ts +0 -818
- package/test/integration/in-memory-sync.test.ts +0 -830
- package/test/integration/init-sync.test.ts +0 -89
- package/test/integration/manual-sync-test.sh +0 -84
- package/test/integration/sync-deletion.test.ts +0 -280
- package/test/integration/sync-flow.test.ts +0 -291
- package/test/jest.setup.ts +0 -34
- package/test/run-tests.sh +0 -225
- package/test/unit/deletion-behavior.test.ts +0 -249
- package/test/unit/enhanced-mime-detection.test.ts +0 -244
- package/test/unit/snapshot.test.ts +0 -404
- package/test/unit/sync-convergence.test.ts +0 -298
- package/test/unit/sync-timing.test.ts +0 -134
- package/test/unit/utils.test.ts +0 -366
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs/promises";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { tmpdir } from "os";
|
|
4
|
-
import {
|
|
5
|
-
readFileContent,
|
|
6
|
-
writeFileContent,
|
|
7
|
-
removePath,
|
|
8
|
-
pathExists,
|
|
9
|
-
} from "../../src/utils";
|
|
10
|
-
import { SnapshotManager } from "../../src/core/snapshot";
|
|
11
|
-
import { ChangeDetector } from "../../src/core/change-detection";
|
|
12
|
-
|
|
13
|
-
describe("File Deletion Behavior", () => {
|
|
14
|
-
let testDir: string;
|
|
15
|
-
let snapshotManager: SnapshotManager;
|
|
16
|
-
|
|
17
|
-
beforeEach(async () => {
|
|
18
|
-
testDir = await fs.mkdtemp(path.join(tmpdir(), "deletion-test-"));
|
|
19
|
-
snapshotManager = new SnapshotManager(testDir);
|
|
20
|
-
// Create a minimal change detector for testing (without Automerge repo)
|
|
21
|
-
new ChangeDetector(null as any, testDir, []);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
afterEach(async () => {
|
|
25
|
-
await fs.rm(testDir, { recursive: true, force: true });
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe("Basic File Deletion", () => {
|
|
29
|
-
it("should read TypeScript files correctly before deletion", async () => {
|
|
30
|
-
const tsFile = path.join(testDir, "component.ts");
|
|
31
|
-
const content = "interface User { name: string; }";
|
|
32
|
-
await writeFileContent(tsFile, content);
|
|
33
|
-
|
|
34
|
-
const result = await readFileContent(tsFile);
|
|
35
|
-
expect(typeof result).toBe("string");
|
|
36
|
-
expect(result).toBe(content);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("should handle file deletion through removePath", async () => {
|
|
40
|
-
const filePath = path.join(testDir, "test.txt");
|
|
41
|
-
await writeFileContent(filePath, "test content");
|
|
42
|
-
|
|
43
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
44
|
-
|
|
45
|
-
await removePath(filePath);
|
|
46
|
-
|
|
47
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("should handle directory deletion through removePath", async () => {
|
|
51
|
-
const dirPath = path.join(testDir, "subdir");
|
|
52
|
-
const filePath = path.join(dirPath, "file.txt");
|
|
53
|
-
|
|
54
|
-
await fs.mkdir(dirPath);
|
|
55
|
-
await writeFileContent(filePath, "content");
|
|
56
|
-
|
|
57
|
-
expect(await pathExists(dirPath)).toBe(true);
|
|
58
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
59
|
-
|
|
60
|
-
await removePath(dirPath);
|
|
61
|
-
|
|
62
|
-
expect(await pathExists(dirPath)).toBe(false);
|
|
63
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe("Snapshot Deletion Behavior", () => {
|
|
68
|
-
it("should properly remove files from snapshot", () => {
|
|
69
|
-
const snapshot = snapshotManager.createEmpty();
|
|
70
|
-
|
|
71
|
-
// Add a file to snapshot
|
|
72
|
-
snapshotManager.updateFileEntry(snapshot, "test.txt", {
|
|
73
|
-
path: path.join(testDir, "test.txt"),
|
|
74
|
-
url: "automerge:test-url" as any,
|
|
75
|
-
head: ["test-head"] as any,
|
|
76
|
-
extension: "txt",
|
|
77
|
-
mimeType: "text/plain",
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
expect(snapshot.files.has("test.txt")).toBe(true);
|
|
81
|
-
|
|
82
|
-
// Remove file from snapshot
|
|
83
|
-
snapshotManager.removeFileEntry(snapshot, "test.txt");
|
|
84
|
-
|
|
85
|
-
expect(snapshot.files.has("test.txt")).toBe(false);
|
|
86
|
-
expect(snapshot.files.size).toBe(0);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("should handle removing non-existent files gracefully", () => {
|
|
90
|
-
const snapshot = snapshotManager.createEmpty();
|
|
91
|
-
|
|
92
|
-
// Should not throw when removing non-existent file
|
|
93
|
-
expect(() => {
|
|
94
|
-
snapshotManager.removeFileEntry(snapshot, "nonexistent.txt");
|
|
95
|
-
}).not.toThrow();
|
|
96
|
-
|
|
97
|
-
expect(snapshot.files.size).toBe(0);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
describe("Deletion Scenario Simulation", () => {
|
|
102
|
-
it("should simulate local file deletion scenario", async () => {
|
|
103
|
-
// Create a file
|
|
104
|
-
const filePath = path.join(testDir, "deleteme.txt");
|
|
105
|
-
const content = "This file will be deleted";
|
|
106
|
-
await writeFileContent(filePath, content);
|
|
107
|
-
|
|
108
|
-
// Verify file exists
|
|
109
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
110
|
-
const readContent = await readFileContent(filePath);
|
|
111
|
-
expect(readContent).toBe(content);
|
|
112
|
-
|
|
113
|
-
// Create snapshot with this file
|
|
114
|
-
const snapshot = snapshotManager.createEmpty();
|
|
115
|
-
snapshotManager.updateFileEntry(snapshot, "deleteme.txt", {
|
|
116
|
-
path: filePath,
|
|
117
|
-
url: "automerge:delete-test" as any,
|
|
118
|
-
head: ["initial-head"] as any,
|
|
119
|
-
extension: "txt",
|
|
120
|
-
mimeType: "text/plain",
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// Simulate local deletion (user deletes file)
|
|
124
|
-
await removePath(filePath);
|
|
125
|
-
|
|
126
|
-
// Verify file is gone
|
|
127
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
128
|
-
|
|
129
|
-
// Snapshot should still have the file entry (until sync processes the deletion)
|
|
130
|
-
expect(snapshot.files.has("deleteme.txt")).toBe(true);
|
|
131
|
-
|
|
132
|
-
// Simulate sync engine processing the deletion
|
|
133
|
-
snapshotManager.removeFileEntry(snapshot, "deleteme.txt");
|
|
134
|
-
|
|
135
|
-
// Now snapshot should not have the file
|
|
136
|
-
expect(snapshot.files.has("deleteme.txt")).toBe(false);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("should handle rapid create-delete cycles", async () => {
|
|
140
|
-
const filePath = path.join(testDir, "rapid.txt");
|
|
141
|
-
|
|
142
|
-
// Rapid create-delete cycle
|
|
143
|
-
for (let i = 0; i < 5; i++) {
|
|
144
|
-
await writeFileContent(filePath, `content ${i}`);
|
|
145
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
146
|
-
|
|
147
|
-
await removePath(filePath);
|
|
148
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
it("should handle deletion of different file types", async () => {
|
|
153
|
-
const testFiles = [
|
|
154
|
-
{ name: "text.txt", content: "text content" },
|
|
155
|
-
{ name: "code.ts", content: "interface Test { x: number; }" },
|
|
156
|
-
{ name: "config.json", content: '{"key": "value"}' },
|
|
157
|
-
{ name: "binary.bin", content: new Uint8Array([0x00, 0x01, 0x02]) },
|
|
158
|
-
];
|
|
159
|
-
|
|
160
|
-
// Create all files
|
|
161
|
-
for (const file of testFiles) {
|
|
162
|
-
const filePath = path.join(testDir, file.name);
|
|
163
|
-
await writeFileContent(filePath, file.content);
|
|
164
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Delete all files
|
|
168
|
-
for (const file of testFiles) {
|
|
169
|
-
const filePath = path.join(testDir, file.name);
|
|
170
|
-
await removePath(filePath);
|
|
171
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe("Edge Cases and Error Conditions", () => {
|
|
177
|
-
it("should handle deletion of files with special characters", async () => {
|
|
178
|
-
const specialFiles = [
|
|
179
|
-
"file with spaces.txt",
|
|
180
|
-
"file-with-dashes.txt",
|
|
181
|
-
"file_with_underscores.txt",
|
|
182
|
-
"file.with.multiple.dots.txt",
|
|
183
|
-
];
|
|
184
|
-
|
|
185
|
-
for (const fileName of specialFiles) {
|
|
186
|
-
const filePath = path.join(testDir, fileName);
|
|
187
|
-
await writeFileContent(filePath, "test content");
|
|
188
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
189
|
-
|
|
190
|
-
await removePath(filePath);
|
|
191
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it("should handle deletion of nested directory structures", async () => {
|
|
196
|
-
// Create nested structure
|
|
197
|
-
const nestedPath = path.join(testDir, "level1", "level2", "level3");
|
|
198
|
-
const filePath = path.join(nestedPath, "deep.txt");
|
|
199
|
-
|
|
200
|
-
await fs.mkdir(nestedPath, { recursive: true });
|
|
201
|
-
await writeFileContent(filePath, "deep content");
|
|
202
|
-
|
|
203
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
204
|
-
|
|
205
|
-
// Delete entire structure from top level
|
|
206
|
-
await removePath(path.join(testDir, "level1"));
|
|
207
|
-
|
|
208
|
-
expect(await pathExists(path.join(testDir, "level1"))).toBe(false);
|
|
209
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it("should handle concurrent deletion attempts", async () => {
|
|
213
|
-
const filePath = path.join(testDir, "concurrent.txt");
|
|
214
|
-
await writeFileContent(filePath, "content");
|
|
215
|
-
|
|
216
|
-
// Multiple deletion attempts (should not cause errors)
|
|
217
|
-
const deletions = [
|
|
218
|
-
removePath(filePath),
|
|
219
|
-
removePath(filePath),
|
|
220
|
-
removePath(filePath),
|
|
221
|
-
];
|
|
222
|
-
|
|
223
|
-
await Promise.all(deletions);
|
|
224
|
-
|
|
225
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
describe("Debug Information", () => {
|
|
230
|
-
it("should provide detailed info about deletion behavior", async () => {
|
|
231
|
-
const filePath = path.join(testDir, "debug.txt");
|
|
232
|
-
const content = "Debug test content";
|
|
233
|
-
|
|
234
|
-
// Create file
|
|
235
|
-
await writeFileContent(filePath, content);
|
|
236
|
-
|
|
237
|
-
// Verify file content
|
|
238
|
-
const readBack = await readFileContent(filePath);
|
|
239
|
-
expect(readBack).toBe(content);
|
|
240
|
-
|
|
241
|
-
// Delete file
|
|
242
|
-
await removePath(filePath);
|
|
243
|
-
|
|
244
|
-
// Verify deletion
|
|
245
|
-
const exists = await pathExists(filePath);
|
|
246
|
-
expect(exists).toBe(false);
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
});
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs/promises";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { tmpdir } from "os";
|
|
4
|
-
import {
|
|
5
|
-
getEnhancedMimeType,
|
|
6
|
-
isEnhancedTextFile,
|
|
7
|
-
shouldForceAsText,
|
|
8
|
-
getMimeType,
|
|
9
|
-
} from "../../src/utils";
|
|
10
|
-
|
|
11
|
-
describe("Enhanced MIME Detection", () => {
|
|
12
|
-
let testDir: string;
|
|
13
|
-
|
|
14
|
-
beforeEach(async () => {
|
|
15
|
-
testDir = await fs.mkdtemp(path.join(tmpdir(), "enhanced-mime-test-"));
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
afterEach(async () => {
|
|
19
|
-
await fs.rm(testDir, { recursive: true, force: true });
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe("Enhanced vs Standard MIME Detection", () => {
|
|
23
|
-
it("should fix TypeScript file MIME detection", async () => {
|
|
24
|
-
const tsFile = path.join(testDir, "test.ts");
|
|
25
|
-
await fs.writeFile(tsFile, "interface User { name: string; }");
|
|
26
|
-
|
|
27
|
-
// Standard MIME detection (broken)
|
|
28
|
-
const standardMime = getMimeType(tsFile);
|
|
29
|
-
|
|
30
|
-
// Enhanced MIME detection (fixed)
|
|
31
|
-
const enhancedMime = getEnhancedMimeType(tsFile);
|
|
32
|
-
const enhancedIsText = await isEnhancedTextFile(tsFile);
|
|
33
|
-
const shouldForce = shouldForceAsText(tsFile);
|
|
34
|
-
|
|
35
|
-
// Verify the fix
|
|
36
|
-
expect(enhancedMime).toBe("text/typescript"); // Fixed!
|
|
37
|
-
expect(enhancedIsText).toBe(true); // Fixed!
|
|
38
|
-
expect(shouldForce).toBe(true); // Force TypeScript as text
|
|
39
|
-
|
|
40
|
-
// Show the original problem still exists with standard detection
|
|
41
|
-
expect(standardMime).toBe("video/mp2t"); // The original problem
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("should fix TSX file MIME detection", async () => {
|
|
45
|
-
const tsxFile = path.join(testDir, "Component.tsx");
|
|
46
|
-
await fs.writeFile(tsxFile, "export const App = () => <div>Hello</div>;");
|
|
47
|
-
|
|
48
|
-
const standardMime = getMimeType(tsxFile);
|
|
49
|
-
const enhancedMime = getEnhancedMimeType(tsxFile);
|
|
50
|
-
const enhancedIsText = await isEnhancedTextFile(tsxFile);
|
|
51
|
-
|
|
52
|
-
expect(enhancedMime).toBe("text/tsx"); // Fixed!
|
|
53
|
-
expect(enhancedIsText).toBe(true); // Fixed!
|
|
54
|
-
expect(standardMime).toBe("application/octet-stream"); // Original problem
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("should handle Vue.js single file components", async () => {
|
|
58
|
-
const vueFile = path.join(testDir, "App.vue");
|
|
59
|
-
await fs.writeFile(
|
|
60
|
-
vueFile,
|
|
61
|
-
`
|
|
62
|
-
<template>
|
|
63
|
-
<div>{{ message }}</div>
|
|
64
|
-
</template>
|
|
65
|
-
|
|
66
|
-
<script>
|
|
67
|
-
export default {
|
|
68
|
-
data() {
|
|
69
|
-
return { message: 'Hello Vue!' }
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
</script>
|
|
73
|
-
|
|
74
|
-
<style scoped>
|
|
75
|
-
div { color: blue; }
|
|
76
|
-
</style>
|
|
77
|
-
`
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const enhancedMime = getEnhancedMimeType(vueFile);
|
|
81
|
-
const enhancedIsText = await isEnhancedTextFile(vueFile);
|
|
82
|
-
|
|
83
|
-
expect(enhancedMime).toBe("text/vue");
|
|
84
|
-
expect(enhancedIsText).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("should handle modern CSS preprocessors correctly", async () => {
|
|
88
|
-
const testCases = [
|
|
89
|
-
{ file: "styles.scss", expectedMime: "text/scss" },
|
|
90
|
-
{ file: "styles.sass", expectedMime: "text/sass" },
|
|
91
|
-
{ file: "styles.less", expectedMime: "text/less" },
|
|
92
|
-
{ file: "styles.styl", expectedMime: "text/stylus" },
|
|
93
|
-
];
|
|
94
|
-
|
|
95
|
-
for (const testCase of testCases) {
|
|
96
|
-
const filePath = path.join(testDir, testCase.file);
|
|
97
|
-
await fs.writeFile(
|
|
98
|
-
filePath,
|
|
99
|
-
"$primary-color: #007bff;\n.button { color: $primary-color; }"
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
const enhancedMime = getEnhancedMimeType(filePath);
|
|
103
|
-
const enhancedIsText = await isEnhancedTextFile(filePath);
|
|
104
|
-
|
|
105
|
-
expect(enhancedMime).toBe(testCase.expectedMime);
|
|
106
|
-
expect(enhancedIsText).toBe(true);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("should handle configuration files by filename", async () => {
|
|
111
|
-
const configFiles = [
|
|
112
|
-
{ filename: "Dockerfile", expectedMime: "text/plain" },
|
|
113
|
-
{ filename: "package.json", expectedMime: "application/json" },
|
|
114
|
-
{ filename: "tsconfig.json", expectedMime: "application/json" },
|
|
115
|
-
{
|
|
116
|
-
filename: "webpack.config.js",
|
|
117
|
-
expectedMime: "application/javascript",
|
|
118
|
-
},
|
|
119
|
-
];
|
|
120
|
-
|
|
121
|
-
for (const config of configFiles) {
|
|
122
|
-
const filePath = path.join(testDir, config.filename);
|
|
123
|
-
await fs.writeFile(filePath, "# Configuration content");
|
|
124
|
-
|
|
125
|
-
const enhancedMime = getEnhancedMimeType(filePath);
|
|
126
|
-
const enhancedIsText = await isEnhancedTextFile(filePath);
|
|
127
|
-
|
|
128
|
-
expect(enhancedMime).toBe(config.expectedMime);
|
|
129
|
-
expect(enhancedIsText).toBe(true);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
describe("Comprehensive Developer File Support", () => {
|
|
135
|
-
it("should correctly handle all common developer file types", async () => {
|
|
136
|
-
const developerFiles = [
|
|
137
|
-
// JavaScript/TypeScript ecosystem
|
|
138
|
-
{ name: "app.js", mime: "application/javascript", text: true },
|
|
139
|
-
{ name: "app.ts", mime: "text/typescript", text: true },
|
|
140
|
-
{ name: "Component.tsx", mime: "text/tsx", text: true },
|
|
141
|
-
{ name: "Component.jsx", mime: "text/jsx", text: true },
|
|
142
|
-
{ name: "types.d.ts", mime: "text/typescript", text: true },
|
|
143
|
-
{ name: "bundle.mjs", mime: "application/javascript", text: true },
|
|
144
|
-
{ name: "server.cjs", mime: "application/javascript", text: true },
|
|
145
|
-
|
|
146
|
-
// Frontend frameworks
|
|
147
|
-
{ name: "App.vue", mime: "text/vue", text: true },
|
|
148
|
-
{ name: "Button.svelte", mime: "text/svelte", text: true },
|
|
149
|
-
|
|
150
|
-
// Stylesheets
|
|
151
|
-
{ name: "styles.scss", mime: "text/scss", text: true },
|
|
152
|
-
{ name: "main.css", mime: "text/css", text: true },
|
|
153
|
-
|
|
154
|
-
// Documentation
|
|
155
|
-
{ name: "README.md", mime: "text/markdown", text: true },
|
|
156
|
-
{ name: "docs.mdx", mime: "text/markdown", text: true },
|
|
157
|
-
|
|
158
|
-
// Config files
|
|
159
|
-
{ name: ".env", mime: "text/plain", text: true },
|
|
160
|
-
{ name: ".gitignore", mime: "text/plain", text: true },
|
|
161
|
-
{ name: "config.toml", mime: "application/toml", text: true },
|
|
162
|
-
|
|
163
|
-
// Source maps and build artifacts
|
|
164
|
-
{ name: "app.js.map", mime: "application/json", text: true },
|
|
165
|
-
|
|
166
|
-
// Binary files (should not be forced as text)
|
|
167
|
-
{ name: "image.png", mime: "image/png", text: false },
|
|
168
|
-
{ name: "font.woff2", mime: "font/woff2", text: false },
|
|
169
|
-
];
|
|
170
|
-
|
|
171
|
-
for (const file of developerFiles) {
|
|
172
|
-
const filePath = path.join(testDir, file.name);
|
|
173
|
-
|
|
174
|
-
// Create appropriate content
|
|
175
|
-
const content = file.text
|
|
176
|
-
? `// Content for ${file.name}\nconst test = true;`
|
|
177
|
-
: Buffer.from([0x89, 0x50, 0x4e, 0x47]); // PNG-like header
|
|
178
|
-
|
|
179
|
-
await fs.writeFile(filePath, content);
|
|
180
|
-
|
|
181
|
-
const enhancedMime = getEnhancedMimeType(filePath);
|
|
182
|
-
const enhancedIsText = await isEnhancedTextFile(filePath);
|
|
183
|
-
|
|
184
|
-
expect(enhancedMime).toBe(file.mime);
|
|
185
|
-
expect(enhancedIsText).toBe(file.text);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
describe("Edge Cases and Fallbacks", () => {
|
|
191
|
-
it("should handle files without extensions", async () => {
|
|
192
|
-
const noExtFile = path.join(testDir, "README");
|
|
193
|
-
await fs.writeFile(noExtFile, "# This is a README file");
|
|
194
|
-
|
|
195
|
-
const enhancedMime = getEnhancedMimeType(noExtFile);
|
|
196
|
-
const enhancedIsText = await isEnhancedTextFile(noExtFile);
|
|
197
|
-
|
|
198
|
-
// Should fall back to content-based detection
|
|
199
|
-
expect(enhancedMime).toBe("application/octet-stream");
|
|
200
|
-
expect(enhancedIsText).toBe(true); // Detected as text by content
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it("should prioritize custom definitions over standard library", async () => {
|
|
204
|
-
// .ts files are wrongly detected as video/mp2t by standard library
|
|
205
|
-
const tsFile = path.join(testDir, "test.ts");
|
|
206
|
-
await fs.writeFile(tsFile, "const x: string = 'test';");
|
|
207
|
-
|
|
208
|
-
const standardMime = getMimeType(tsFile);
|
|
209
|
-
const enhancedMime = getEnhancedMimeType(tsFile);
|
|
210
|
-
|
|
211
|
-
expect(standardMime).toBe("video/mp2t"); // Wrong
|
|
212
|
-
expect(enhancedMime).toBe("text/typescript"); // Corrected by our custom definitions
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
it("should handle empty files correctly", async () => {
|
|
216
|
-
const emptyFile = path.join(testDir, "empty.ts");
|
|
217
|
-
await fs.writeFile(emptyFile, "");
|
|
218
|
-
|
|
219
|
-
const enhancedMime = getEnhancedMimeType(emptyFile);
|
|
220
|
-
const enhancedIsText = await isEnhancedTextFile(emptyFile);
|
|
221
|
-
|
|
222
|
-
expect(enhancedMime).toBe("text/typescript");
|
|
223
|
-
expect(enhancedIsText).toBe(true); // Empty files should be treated as text
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it("should ensure TypeScript files are read as strings (integration test)", async () => {
|
|
227
|
-
const tsFile = path.join(testDir, "integration.ts");
|
|
228
|
-
const tsContent = "interface Config { apiUrl: string; timeout: number; }";
|
|
229
|
-
await fs.writeFile(tsFile, tsContent);
|
|
230
|
-
|
|
231
|
-
// Import readFileContent here to test integration
|
|
232
|
-
const { readFileContent } = await import("../../src/utils");
|
|
233
|
-
|
|
234
|
-
const result = await readFileContent(tsFile);
|
|
235
|
-
|
|
236
|
-
// Critical: TypeScript files MUST be read as strings
|
|
237
|
-
expect(typeof result).toBe("string");
|
|
238
|
-
expect(result).toBe(tsContent);
|
|
239
|
-
|
|
240
|
-
// This test would have FAILED before our fix when readFileContent
|
|
241
|
-
// used isTextFile() instead of isEnhancedTextFile()
|
|
242
|
-
});
|
|
243
|
-
});
|
|
244
|
-
});
|