pushwork 2.0.0-a.sub.1 → 2.0.0-preview.2
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 +20 -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 +245 -270
- 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 +35 -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 +129 -0
- package/dist/pushwork.d.ts.map +1 -0
- package/dist/pushwork.js +1062 -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 +38 -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 +92 -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/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +93 -0
- package/dist/version.js.map +1 -0
- package/package.json +19 -48
- package/patches/@automerge__automerge-repo@2.6.0-subduction.15.patch +26 -0
- package/.prettierrc +0 -9
- 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 -157
- package/dist/core/sync-engine.d.ts.map +0 -1
- package/dist/core/sync-engine.js +0 -1379
- 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/cli.ts +0 -442
- 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 -1795
- package/src/index.ts +0 -4
- 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
- package/tsconfig.json +0 -23
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs/promises";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import { tmpdir } from "os";
|
|
4
|
-
|
|
5
|
-
describe("Sync Timing Analysis", () => {
|
|
6
|
-
let testDir: string;
|
|
7
|
-
|
|
8
|
-
beforeEach(async () => {
|
|
9
|
-
testDir = await fs.mkdtemp(path.join(tmpdir(), "sync-timing-"));
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
afterEach(async () => {
|
|
13
|
-
await fs.rm(testDir, { recursive: true, force: true });
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
describe("File Operation Timing", () => {
|
|
17
|
-
it("should measure rapid file operations timing", async () => {
|
|
18
|
-
const startTime = Date.now();
|
|
19
|
-
|
|
20
|
-
// Simulate rapid file operations similar to sync
|
|
21
|
-
const promises: Promise<void>[] = [];
|
|
22
|
-
for (let i = 0; i < 10; i++) {
|
|
23
|
-
promises.push(
|
|
24
|
-
fs.writeFile(path.join(testDir, `file${i}.txt`), `content${i}`)
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
await Promise.all(promises);
|
|
29
|
-
const totalTime = Date.now() - startTime;
|
|
30
|
-
|
|
31
|
-
// Verify all files exist
|
|
32
|
-
const files = await fs.readdir(testDir);
|
|
33
|
-
expect(files).toHaveLength(10);
|
|
34
|
-
|
|
35
|
-
// This test shows us baseline file operation timing
|
|
36
|
-
expect(totalTime).toBeLessThan(1000); // Should be fast for local operations
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("should test file operation atomicity", async () => {
|
|
40
|
-
const filePath = path.join(testDir, "test.txt");
|
|
41
|
-
|
|
42
|
-
// Write initial content
|
|
43
|
-
await fs.writeFile(filePath, "initial content");
|
|
44
|
-
|
|
45
|
-
// Rapid successive writes to same file
|
|
46
|
-
const writes: Promise<void>[] = [];
|
|
47
|
-
for (let i = 0; i < 5; i++) {
|
|
48
|
-
writes.push(fs.writeFile(filePath, `updated content ${i}`));
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
await Promise.all(writes);
|
|
52
|
-
|
|
53
|
-
// Check final content (should be one of the updates)
|
|
54
|
-
const finalContent = await fs.readFile(filePath, "utf8");
|
|
55
|
-
expect(finalContent).toMatch(/updated content \d/);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe("Sync Completion Scenarios", () => {
|
|
60
|
-
it("should simulate the need for sync completion detection", async () => {
|
|
61
|
-
// This test simulates what might happen with network sync
|
|
62
|
-
// where we need to wait for operations to complete
|
|
63
|
-
|
|
64
|
-
const results: { operation: string; time: number }[] = [];
|
|
65
|
-
|
|
66
|
-
// Simulate "local" operations (fast)
|
|
67
|
-
const localStart = Date.now();
|
|
68
|
-
await fs.writeFile(path.join(testDir, "local.txt"), "local content");
|
|
69
|
-
const localTime = Date.now() - localStart;
|
|
70
|
-
results.push({ operation: "local write", time: localTime });
|
|
71
|
-
|
|
72
|
-
// Simulate "network" operations (slower with artificial delay)
|
|
73
|
-
const networkStart = Date.now();
|
|
74
|
-
await new Promise((resolve) => setTimeout(resolve, 50)); // 50ms delay
|
|
75
|
-
await fs.writeFile(path.join(testDir, "network.txt"), "network content");
|
|
76
|
-
const networkTime = Date.now() - networkStart;
|
|
77
|
-
results.push({ operation: "network write", time: networkTime });
|
|
78
|
-
|
|
79
|
-
// This demonstrates why we might need to wait for slower operations
|
|
80
|
-
expect(networkTime).toBeGreaterThan(localTime);
|
|
81
|
-
expect(networkTime).toBeGreaterThan(40); // Should include our delay
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it("should test what happens without proper completion waiting", async () => {
|
|
85
|
-
// Simulate starting an operation but not waiting for it
|
|
86
|
-
const promises: Promise<void>[] = [];
|
|
87
|
-
|
|
88
|
-
// Start operations without awaiting
|
|
89
|
-
for (let i = 0; i < 3; i++) {
|
|
90
|
-
promises.push(
|
|
91
|
-
(async () => {
|
|
92
|
-
await new Promise((resolve) => setTimeout(resolve, 10 * i)); // Varying delays
|
|
93
|
-
await fs.writeFile(
|
|
94
|
-
path.join(testDir, `async${i}.txt`),
|
|
95
|
-
`content${i}`
|
|
96
|
-
);
|
|
97
|
-
})()
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Check immediately (before operations complete)
|
|
102
|
-
const filesImmediate = await fs.readdir(testDir);
|
|
103
|
-
|
|
104
|
-
// Now wait for operations to complete
|
|
105
|
-
await Promise.all(promises);
|
|
106
|
-
|
|
107
|
-
// Check after completion
|
|
108
|
-
const filesAfter = await fs.readdir(testDir);
|
|
109
|
-
|
|
110
|
-
// This shows the difference between checking immediately vs waiting
|
|
111
|
-
expect(filesAfter.length).toBeGreaterThanOrEqual(filesImmediate.length);
|
|
112
|
-
expect(filesAfter).toHaveLength(3);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe("Potential Race Conditions", () => {
|
|
117
|
-
it("should test for potential race conditions in file operations", async () => {
|
|
118
|
-
const sharedFile = path.join(testDir, "shared.txt");
|
|
119
|
-
|
|
120
|
-
// Multiple operations on the same file
|
|
121
|
-
const operations = [
|
|
122
|
-
fs.writeFile(sharedFile, "operation1"),
|
|
123
|
-
fs.writeFile(sharedFile, "operation2"),
|
|
124
|
-
fs.writeFile(sharedFile, "operation3"),
|
|
125
|
-
];
|
|
126
|
-
|
|
127
|
-
await Promise.all(operations);
|
|
128
|
-
|
|
129
|
-
// Only one operation should "win"
|
|
130
|
-
const content = await fs.readFile(sharedFile, "utf8");
|
|
131
|
-
expect(["operation1", "operation2", "operation3"]).toContain(content);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
});
|
package/test/unit/utils.test.ts
DELETED
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs/promises";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import * as tmp from "tmp";
|
|
4
|
-
import {
|
|
5
|
-
pathExists,
|
|
6
|
-
getFileSystemEntry,
|
|
7
|
-
isTextFile,
|
|
8
|
-
readFileContent,
|
|
9
|
-
writeFileContent,
|
|
10
|
-
ensureDirectoryExists,
|
|
11
|
-
removePath,
|
|
12
|
-
listDirectory,
|
|
13
|
-
calculateContentHash,
|
|
14
|
-
getMimeType,
|
|
15
|
-
getFileExtension,
|
|
16
|
-
normalizePath,
|
|
17
|
-
getRelativePath,
|
|
18
|
-
} from "../../src/utils/fs";
|
|
19
|
-
import { FileType } from "../../src/types";
|
|
20
|
-
|
|
21
|
-
describe("File System Utilities", () => {
|
|
22
|
-
let tmpDir: string;
|
|
23
|
-
let cleanup: () => void;
|
|
24
|
-
|
|
25
|
-
beforeEach(() => {
|
|
26
|
-
const tmpObj = tmp.dirSync({ unsafeCleanup: true });
|
|
27
|
-
tmpDir = tmpObj.name;
|
|
28
|
-
cleanup = tmpObj.removeCallback;
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
cleanup();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe("pathExists", () => {
|
|
36
|
-
it("should return true for existing files", async () => {
|
|
37
|
-
const filePath = path.join(tmpDir, "test.txt");
|
|
38
|
-
await fs.writeFile(filePath, "test content");
|
|
39
|
-
|
|
40
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("should return false for non-existing files", async () => {
|
|
44
|
-
const filePath = path.join(tmpDir, "nonexistent.txt");
|
|
45
|
-
|
|
46
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("should return true for existing directories", async () => {
|
|
50
|
-
expect(await pathExists(tmpDir)).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
describe("getFileSystemEntry", () => {
|
|
55
|
-
it("should return metadata for files", async () => {
|
|
56
|
-
const filePath = path.join(tmpDir, "test.txt");
|
|
57
|
-
await fs.writeFile(filePath, "test content");
|
|
58
|
-
|
|
59
|
-
const entry = await getFileSystemEntry(filePath);
|
|
60
|
-
|
|
61
|
-
expect(entry).not.toBeNull();
|
|
62
|
-
expect(entry?.path).toBe(filePath);
|
|
63
|
-
expect(entry?.type).toBe(FileType.TEXT);
|
|
64
|
-
expect(entry?.size).toBe(12); // 'test content'.length
|
|
65
|
-
expect(entry?.mtime).toBeDefined();
|
|
66
|
-
expect(entry?.mtime.getTime()).toBeGreaterThan(0);
|
|
67
|
-
expect(typeof entry?.mtime.getTime()).toBe("number");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("should return metadata for directories", async () => {
|
|
71
|
-
const dirPath = path.join(tmpDir, "subdir");
|
|
72
|
-
await fs.mkdir(dirPath);
|
|
73
|
-
|
|
74
|
-
const entry = await getFileSystemEntry(dirPath);
|
|
75
|
-
|
|
76
|
-
expect(entry).not.toBeNull();
|
|
77
|
-
expect(entry?.path).toBe(dirPath);
|
|
78
|
-
expect(entry?.type).toBe(FileType.DIRECTORY);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("should return null for non-existing paths", async () => {
|
|
82
|
-
const entry = await getFileSystemEntry(path.join(tmpDir, "nonexistent"));
|
|
83
|
-
expect(entry).toBeNull();
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe("isTextFile", () => {
|
|
88
|
-
it("should detect text files by extension", async () => {
|
|
89
|
-
const filePath = path.join(tmpDir, "test.txt");
|
|
90
|
-
await fs.writeFile(filePath, "text content");
|
|
91
|
-
|
|
92
|
-
expect(await isTextFile(filePath)).toBe(true);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("should detect JSON files as text", async () => {
|
|
96
|
-
const filePath = path.join(tmpDir, "test.json");
|
|
97
|
-
await fs.writeFile(filePath, '{"key": "value"}');
|
|
98
|
-
|
|
99
|
-
expect(await isTextFile(filePath)).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it("should detect binary files by content", async () => {
|
|
103
|
-
const filePath = path.join(tmpDir, "test.bin");
|
|
104
|
-
const binaryContent = Buffer.from([0x00, 0x01, 0x02, 0x03]);
|
|
105
|
-
await fs.writeFile(filePath, binaryContent);
|
|
106
|
-
|
|
107
|
-
expect(await isTextFile(filePath)).toBe(false);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe("readFileContent", () => {
|
|
112
|
-
it("should read text files as strings", async () => {
|
|
113
|
-
const filePath = path.join(tmpDir, "test.txt");
|
|
114
|
-
const content = "Hello, world!";
|
|
115
|
-
await fs.writeFile(filePath, content);
|
|
116
|
-
|
|
117
|
-
const result = await readFileContent(filePath);
|
|
118
|
-
|
|
119
|
-
expect(typeof result).toBe("string");
|
|
120
|
-
expect(result).toBe(content);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it("should read TypeScript files as strings", async () => {
|
|
124
|
-
const filePath = path.join(tmpDir, "component.ts");
|
|
125
|
-
const content = "interface User { name: string; age: number; }";
|
|
126
|
-
await fs.writeFile(filePath, content);
|
|
127
|
-
|
|
128
|
-
const result = await readFileContent(filePath);
|
|
129
|
-
|
|
130
|
-
expect(typeof result).toBe("string");
|
|
131
|
-
expect(result).toBe(content);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("should read TSX files as strings", async () => {
|
|
135
|
-
const filePath = path.join(tmpDir, "Component.tsx");
|
|
136
|
-
const content = "export const App = () => <div>Hello World</div>;";
|
|
137
|
-
await fs.writeFile(filePath, content);
|
|
138
|
-
|
|
139
|
-
const result = await readFileContent(filePath);
|
|
140
|
-
|
|
141
|
-
expect(typeof result).toBe("string");
|
|
142
|
-
expect(result).toBe(content);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it("should read Vue files as strings", async () => {
|
|
146
|
-
const filePath = path.join(tmpDir, "App.vue");
|
|
147
|
-
const content = "<template><div>{{ message }}</div></template>";
|
|
148
|
-
await fs.writeFile(filePath, content);
|
|
149
|
-
|
|
150
|
-
const result = await readFileContent(filePath);
|
|
151
|
-
|
|
152
|
-
expect(typeof result).toBe("string");
|
|
153
|
-
expect(result).toBe(content);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("should read SCSS files as strings", async () => {
|
|
157
|
-
const filePath = path.join(tmpDir, "styles.scss");
|
|
158
|
-
const content = "$primary: #007bff; .btn { color: $primary; }";
|
|
159
|
-
await fs.writeFile(filePath, content);
|
|
160
|
-
|
|
161
|
-
const result = await readFileContent(filePath);
|
|
162
|
-
|
|
163
|
-
expect(typeof result).toBe("string");
|
|
164
|
-
expect(result).toBe(content);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it("should read binary files as Uint8Array", async () => {
|
|
168
|
-
const filePath = path.join(tmpDir, "test.bin");
|
|
169
|
-
const binaryContent = Buffer.from([0x00, 0x01, 0x02, 0x03]);
|
|
170
|
-
await fs.writeFile(filePath, binaryContent);
|
|
171
|
-
|
|
172
|
-
const result = await readFileContent(filePath);
|
|
173
|
-
|
|
174
|
-
expect(result).toBeInstanceOf(Uint8Array);
|
|
175
|
-
expect(Array.from(result as Uint8Array)).toEqual([
|
|
176
|
-
0x00, 0x01, 0x02, 0x03,
|
|
177
|
-
]);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe("writeFileContent", () => {
|
|
182
|
-
it("should write string content to files", async () => {
|
|
183
|
-
const filePath = path.join(tmpDir, "output.txt");
|
|
184
|
-
const content = "Test content";
|
|
185
|
-
|
|
186
|
-
await writeFileContent(filePath, content);
|
|
187
|
-
|
|
188
|
-
const written = await fs.readFile(filePath, "utf8");
|
|
189
|
-
expect(written).toBe(content);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it("should write binary content to files", async () => {
|
|
193
|
-
const filePath = path.join(tmpDir, "output.bin");
|
|
194
|
-
const content = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
|
|
195
|
-
|
|
196
|
-
await writeFileContent(filePath, content);
|
|
197
|
-
|
|
198
|
-
const written = await fs.readFile(filePath);
|
|
199
|
-
expect(Array.from(written)).toEqual([0x00, 0x01, 0x02, 0x03]);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("should create directories if they don't exist", async () => {
|
|
203
|
-
const filePath = path.join(tmpDir, "nested", "deep", "file.txt");
|
|
204
|
-
|
|
205
|
-
await writeFileContent(filePath, "content");
|
|
206
|
-
|
|
207
|
-
expect(await pathExists(filePath)).toBe(true);
|
|
208
|
-
expect(await fs.readFile(filePath, "utf8")).toBe("content");
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
describe("ensureDirectoryExists", () => {
|
|
213
|
-
it("should create directories recursively", async () => {
|
|
214
|
-
const dirPath = path.join(tmpDir, "nested", "deep", "directory");
|
|
215
|
-
|
|
216
|
-
await ensureDirectoryExists(dirPath);
|
|
217
|
-
|
|
218
|
-
expect(await pathExists(dirPath)).toBe(true);
|
|
219
|
-
const stats = await fs.stat(dirPath);
|
|
220
|
-
expect(stats.isDirectory()).toBe(true);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it("should not fail if directory already exists", async () => {
|
|
224
|
-
await ensureDirectoryExists(tmpDir);
|
|
225
|
-
await ensureDirectoryExists(tmpDir); // Should not throw
|
|
226
|
-
|
|
227
|
-
expect(await pathExists(tmpDir)).toBe(true);
|
|
228
|
-
});
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
describe("removePath", () => {
|
|
232
|
-
it("should remove files", async () => {
|
|
233
|
-
const filePath = path.join(tmpDir, "toremove.txt");
|
|
234
|
-
await fs.writeFile(filePath, "content");
|
|
235
|
-
|
|
236
|
-
await removePath(filePath);
|
|
237
|
-
|
|
238
|
-
expect(await pathExists(filePath)).toBe(false);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it("should remove directories recursively", async () => {
|
|
242
|
-
const dirPath = path.join(tmpDir, "toremove");
|
|
243
|
-
const filePath = path.join(dirPath, "file.txt");
|
|
244
|
-
await fs.mkdir(dirPath);
|
|
245
|
-
await fs.writeFile(filePath, "content");
|
|
246
|
-
|
|
247
|
-
await removePath(dirPath);
|
|
248
|
-
|
|
249
|
-
expect(await pathExists(dirPath)).toBe(false);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
it("should not fail if path doesn't exist", async () => {
|
|
253
|
-
const nonExistentPath = path.join(tmpDir, "nonexistent");
|
|
254
|
-
|
|
255
|
-
await removePath(nonExistentPath); // Should not throw
|
|
256
|
-
|
|
257
|
-
expect(await pathExists(nonExistentPath)).toBe(false);
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
describe("listDirectory", () => {
|
|
262
|
-
beforeEach(async () => {
|
|
263
|
-
// Create test directory structure
|
|
264
|
-
await fs.mkdir(path.join(tmpDir, "subdir"));
|
|
265
|
-
await fs.writeFile(path.join(tmpDir, "file1.txt"), "content1");
|
|
266
|
-
await fs.writeFile(path.join(tmpDir, "file2.txt"), "content2");
|
|
267
|
-
await fs.writeFile(path.join(tmpDir, "subdir", "file3.txt"), "content3");
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it("should list directory contents non-recursively", async () => {
|
|
271
|
-
const entries = await listDirectory(tmpDir, false);
|
|
272
|
-
|
|
273
|
-
const names = entries.map((e) => path.basename(e.path)).sort();
|
|
274
|
-
expect(names).toEqual(["file1.txt", "file2.txt", "subdir"]);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
it("should list directory contents recursively", async () => {
|
|
278
|
-
const entries = await listDirectory(tmpDir, true);
|
|
279
|
-
|
|
280
|
-
const relativePaths = entries
|
|
281
|
-
.map((e) => path.relative(tmpDir, e.path))
|
|
282
|
-
.sort();
|
|
283
|
-
|
|
284
|
-
expect(relativePaths).toContain("file1.txt");
|
|
285
|
-
expect(relativePaths).toContain("file2.txt");
|
|
286
|
-
expect(relativePaths).toContain("subdir");
|
|
287
|
-
expect(relativePaths).toContain(path.join("subdir", "file3.txt"));
|
|
288
|
-
});
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
describe("calculateContentHash", () => {
|
|
292
|
-
it("should generate consistent hashes for string content", async () => {
|
|
293
|
-
const content = "test content";
|
|
294
|
-
|
|
295
|
-
const hash1 = await calculateContentHash(content);
|
|
296
|
-
const hash2 = await calculateContentHash(content);
|
|
297
|
-
|
|
298
|
-
expect(hash1).toBe(hash2);
|
|
299
|
-
expect(hash1).toHaveLength(64); // SHA-256 hex string
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
it("should generate consistent hashes for binary content", async () => {
|
|
303
|
-
const content = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
|
|
304
|
-
|
|
305
|
-
const hash1 = await calculateContentHash(content);
|
|
306
|
-
const hash2 = await calculateContentHash(content);
|
|
307
|
-
|
|
308
|
-
expect(hash1).toBe(hash2);
|
|
309
|
-
expect(hash1).toHaveLength(64);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it("should generate different hashes for different content", async () => {
|
|
313
|
-
const content1 = "content1";
|
|
314
|
-
const content2 = "content2";
|
|
315
|
-
|
|
316
|
-
const hash1 = await calculateContentHash(content1);
|
|
317
|
-
const hash2 = await calculateContentHash(content2);
|
|
318
|
-
|
|
319
|
-
expect(hash1).not.toBe(hash2);
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
describe("getMimeType", () => {
|
|
324
|
-
it("should return correct MIME type for text files", () => {
|
|
325
|
-
expect(getMimeType("test.txt")).toBe("text/plain");
|
|
326
|
-
expect(getMimeType("test.json")).toBe("application/json");
|
|
327
|
-
expect(getMimeType("test.html")).toBe("text/html");
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it("should return default MIME type for unknown extensions", () => {
|
|
331
|
-
expect(getMimeType("test.unknown")).toBe("application/octet-stream");
|
|
332
|
-
});
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
describe("getFileExtension", () => {
|
|
336
|
-
it("should extract file extensions", () => {
|
|
337
|
-
expect(getFileExtension("test.txt")).toBe("txt");
|
|
338
|
-
expect(getFileExtension("archive.tar.gz")).toBe("gz");
|
|
339
|
-
expect(getFileExtension("noextension")).toBe("");
|
|
340
|
-
});
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
describe("normalizePath", () => {
|
|
344
|
-
it("should normalize path separators", () => {
|
|
345
|
-
expect(normalizePath("path\\to\\file")).toBe("path/to/file");
|
|
346
|
-
expect(normalizePath("path/to/file")).toBe("path/to/file");
|
|
347
|
-
expect(normalizePath("path//to//file")).toBe("path/to/file");
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
describe("getRelativePath", () => {
|
|
352
|
-
it("should return relative paths", () => {
|
|
353
|
-
const base = "/home/user/project";
|
|
354
|
-
const target = "/home/user/project/src/file.txt";
|
|
355
|
-
|
|
356
|
-
expect(getRelativePath(base, target)).toBe("src/file.txt");
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
it("should handle same directory", () => {
|
|
360
|
-
const base = "/home/user/project";
|
|
361
|
-
const target = "/home/user/project";
|
|
362
|
-
|
|
363
|
-
expect(getRelativePath(base, target)).toBe(".");
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "nodenext",
|
|
5
|
-
"moduleResolution": "nodenext",
|
|
6
|
-
"lib": ["ES2020"],
|
|
7
|
-
"outDir": "./dist",
|
|
8
|
-
"rootDir": "./src",
|
|
9
|
-
"strict": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"declaration": true,
|
|
14
|
-
"declarationMap": true,
|
|
15
|
-
"sourceMap": true,
|
|
16
|
-
"resolveJsonModule": true,
|
|
17
|
-
"types": ["node", "jest"],
|
|
18
|
-
"noUnusedLocals": true,
|
|
19
|
-
"noUnusedParameters": true
|
|
20
|
-
},
|
|
21
|
-
"include": ["src/**/*"],
|
|
22
|
-
"exclude": ["node_modules", "dist", "test"]
|
|
23
|
-
}
|