pushwork 2.0.0-a.sub.1 → 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.
Files changed (251) hide show
  1. package/dist/branches.d.ts +19 -0
  2. package/dist/branches.d.ts.map +1 -0
  3. package/dist/branches.js +111 -0
  4. package/dist/branches.js.map +1 -0
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +238 -272
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config.d.ts +17 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +84 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/fs-tree.d.ts +6 -0
  14. package/dist/fs-tree.d.ts.map +1 -0
  15. package/dist/fs-tree.js +99 -0
  16. package/dist/fs-tree.js.map +1 -0
  17. package/dist/ignore.d.ts +6 -0
  18. package/dist/ignore.d.ts.map +1 -0
  19. package/dist/ignore.js +74 -0
  20. package/dist/ignore.js.map +1 -0
  21. package/dist/index.d.ts +8 -4
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +34 -4
  24. package/dist/index.js.map +1 -1
  25. package/dist/log.d.ts +3 -0
  26. package/dist/log.d.ts.map +1 -0
  27. package/dist/log.js +14 -0
  28. package/dist/log.js.map +1 -0
  29. package/dist/pushwork.d.ts +115 -0
  30. package/dist/pushwork.d.ts.map +1 -0
  31. package/dist/pushwork.js +918 -0
  32. package/dist/pushwork.js.map +1 -0
  33. package/dist/repo.d.ts +14 -0
  34. package/dist/repo.d.ts.map +1 -0
  35. package/dist/repo.js +60 -0
  36. package/dist/repo.js.map +1 -0
  37. package/dist/shapes/custom.d.ts +3 -0
  38. package/dist/shapes/custom.d.ts.map +1 -0
  39. package/dist/shapes/custom.js +57 -0
  40. package/dist/shapes/custom.js.map +1 -0
  41. package/dist/shapes/file.d.ts +20 -0
  42. package/dist/shapes/file.d.ts.map +1 -0
  43. package/dist/shapes/file.js +140 -0
  44. package/dist/shapes/file.js.map +1 -0
  45. package/dist/shapes/index.d.ts +10 -0
  46. package/dist/shapes/index.d.ts.map +1 -0
  47. package/dist/shapes/index.js +35 -0
  48. package/dist/shapes/index.js.map +1 -0
  49. package/dist/shapes/patchwork-folder.d.ts +3 -0
  50. package/dist/shapes/patchwork-folder.d.ts.map +1 -0
  51. package/dist/shapes/patchwork-folder.js +160 -0
  52. package/dist/shapes/patchwork-folder.js.map +1 -0
  53. package/dist/shapes/types.d.ts +37 -0
  54. package/dist/shapes/types.d.ts.map +1 -0
  55. package/dist/shapes/types.js +52 -0
  56. package/dist/shapes/types.js.map +1 -0
  57. package/dist/shapes/vfs.d.ts +3 -0
  58. package/dist/shapes/vfs.d.ts.map +1 -0
  59. package/dist/shapes/vfs.js +88 -0
  60. package/dist/shapes/vfs.js.map +1 -0
  61. package/dist/stash.d.ts +23 -0
  62. package/dist/stash.d.ts.map +1 -0
  63. package/dist/stash.js +118 -0
  64. package/dist/stash.js.map +1 -0
  65. package/flake.lock +128 -0
  66. package/flake.nix +66 -0
  67. package/package.json +15 -48
  68. package/patches/@automerge__automerge-repo@2.6.0-subduction.15.patch +26 -0
  69. package/pnpm-workspace.yaml +5 -0
  70. package/src/branches.ts +93 -0
  71. package/src/cli.ts +258 -408
  72. package/src/config.ts +64 -0
  73. package/src/fs-tree.ts +70 -0
  74. package/src/ignore.ts +33 -0
  75. package/src/index.ts +38 -4
  76. package/src/log.ts +8 -0
  77. package/src/pushwork.ts +1055 -0
  78. package/src/repo.ts +76 -0
  79. package/src/shapes/custom.ts +29 -0
  80. package/src/shapes/file.ts +115 -0
  81. package/src/shapes/index.ts +19 -0
  82. package/src/shapes/patchwork-folder.ts +156 -0
  83. package/src/shapes/types.ts +79 -0
  84. package/src/shapes/vfs.ts +93 -0
  85. package/src/stash.ts +106 -0
  86. package/test/integration/branches.test.ts +389 -0
  87. package/test/integration/pushwork.test.ts +547 -0
  88. package/test/setup.ts +29 -0
  89. package/test/unit/doc-shape.test.ts +612 -0
  90. package/tsconfig.json +2 -3
  91. package/vitest.config.ts +14 -0
  92. package/ARCHITECTURE-ACCORDING-TO-CLAUDE.md +0 -248
  93. package/CLAUDE.md +0 -141
  94. package/README.md +0 -221
  95. package/babel.config.js +0 -5
  96. package/dist/cli/commands.d.ts +0 -71
  97. package/dist/cli/commands.d.ts.map +0 -1
  98. package/dist/cli/commands.js +0 -794
  99. package/dist/cli/commands.js.map +0 -1
  100. package/dist/cli/index.d.ts +0 -2
  101. package/dist/cli/index.d.ts.map +0 -1
  102. package/dist/cli/index.js +0 -19
  103. package/dist/cli/index.js.map +0 -1
  104. package/dist/commands.d.ts +0 -61
  105. package/dist/commands.d.ts.map +0 -1
  106. package/dist/commands.js +0 -861
  107. package/dist/commands.js.map +0 -1
  108. package/dist/config/index.d.ts +0 -71
  109. package/dist/config/index.d.ts.map +0 -1
  110. package/dist/config/index.js +0 -314
  111. package/dist/config/index.js.map +0 -1
  112. package/dist/core/change-detection.d.ts +0 -80
  113. package/dist/core/change-detection.d.ts.map +0 -1
  114. package/dist/core/change-detection.js +0 -523
  115. package/dist/core/change-detection.js.map +0 -1
  116. package/dist/core/config.d.ts +0 -81
  117. package/dist/core/config.d.ts.map +0 -1
  118. package/dist/core/config.js +0 -258
  119. package/dist/core/config.js.map +0 -1
  120. package/dist/core/index.d.ts +0 -6
  121. package/dist/core/index.d.ts.map +0 -1
  122. package/dist/core/index.js +0 -6
  123. package/dist/core/index.js.map +0 -1
  124. package/dist/core/move-detection.d.ts +0 -34
  125. package/dist/core/move-detection.d.ts.map +0 -1
  126. package/dist/core/move-detection.js +0 -121
  127. package/dist/core/move-detection.js.map +0 -1
  128. package/dist/core/snapshot.d.ts +0 -105
  129. package/dist/core/snapshot.d.ts.map +0 -1
  130. package/dist/core/snapshot.js +0 -217
  131. package/dist/core/snapshot.js.map +0 -1
  132. package/dist/core/sync-engine.d.ts +0 -157
  133. package/dist/core/sync-engine.d.ts.map +0 -1
  134. package/dist/core/sync-engine.js +0 -1379
  135. package/dist/core/sync-engine.js.map +0 -1
  136. package/dist/types/config.d.ts +0 -99
  137. package/dist/types/config.d.ts.map +0 -1
  138. package/dist/types/config.js +0 -5
  139. package/dist/types/config.js.map +0 -1
  140. package/dist/types/documents.d.ts +0 -88
  141. package/dist/types/documents.d.ts.map +0 -1
  142. package/dist/types/documents.js +0 -20
  143. package/dist/types/documents.js.map +0 -1
  144. package/dist/types/index.d.ts +0 -4
  145. package/dist/types/index.d.ts.map +0 -1
  146. package/dist/types/index.js +0 -4
  147. package/dist/types/index.js.map +0 -1
  148. package/dist/types/snapshot.d.ts +0 -64
  149. package/dist/types/snapshot.d.ts.map +0 -1
  150. package/dist/types/snapshot.js +0 -2
  151. package/dist/types/snapshot.js.map +0 -1
  152. package/dist/utils/content-similarity.d.ts +0 -53
  153. package/dist/utils/content-similarity.d.ts.map +0 -1
  154. package/dist/utils/content-similarity.js +0 -155
  155. package/dist/utils/content-similarity.js.map +0 -1
  156. package/dist/utils/content.d.ts +0 -10
  157. package/dist/utils/content.d.ts.map +0 -1
  158. package/dist/utils/content.js +0 -31
  159. package/dist/utils/content.js.map +0 -1
  160. package/dist/utils/directory.d.ts +0 -24
  161. package/dist/utils/directory.d.ts.map +0 -1
  162. package/dist/utils/directory.js +0 -52
  163. package/dist/utils/directory.js.map +0 -1
  164. package/dist/utils/fs.d.ts +0 -74
  165. package/dist/utils/fs.d.ts.map +0 -1
  166. package/dist/utils/fs.js +0 -248
  167. package/dist/utils/fs.js.map +0 -1
  168. package/dist/utils/index.d.ts +0 -5
  169. package/dist/utils/index.d.ts.map +0 -1
  170. package/dist/utils/index.js +0 -5
  171. package/dist/utils/index.js.map +0 -1
  172. package/dist/utils/mime-types.d.ts +0 -13
  173. package/dist/utils/mime-types.d.ts.map +0 -1
  174. package/dist/utils/mime-types.js +0 -209
  175. package/dist/utils/mime-types.js.map +0 -1
  176. package/dist/utils/network-sync.d.ts +0 -36
  177. package/dist/utils/network-sync.d.ts.map +0 -1
  178. package/dist/utils/network-sync.js +0 -250
  179. package/dist/utils/network-sync.js.map +0 -1
  180. package/dist/utils/node-polyfills.d.ts +0 -9
  181. package/dist/utils/node-polyfills.d.ts.map +0 -1
  182. package/dist/utils/node-polyfills.js +0 -9
  183. package/dist/utils/node-polyfills.js.map +0 -1
  184. package/dist/utils/output.d.ts +0 -129
  185. package/dist/utils/output.d.ts.map +0 -1
  186. package/dist/utils/output.js +0 -368
  187. package/dist/utils/output.js.map +0 -1
  188. package/dist/utils/repo-factory.d.ts +0 -13
  189. package/dist/utils/repo-factory.d.ts.map +0 -1
  190. package/dist/utils/repo-factory.js +0 -46
  191. package/dist/utils/repo-factory.js.map +0 -1
  192. package/dist/utils/string-similarity.d.ts +0 -14
  193. package/dist/utils/string-similarity.d.ts.map +0 -1
  194. package/dist/utils/string-similarity.js +0 -39
  195. package/dist/utils/string-similarity.js.map +0 -1
  196. package/dist/utils/text-diff.d.ts +0 -37
  197. package/dist/utils/text-diff.d.ts.map +0 -1
  198. package/dist/utils/text-diff.js +0 -93
  199. package/dist/utils/text-diff.js.map +0 -1
  200. package/dist/utils/trace.d.ts +0 -19
  201. package/dist/utils/trace.d.ts.map +0 -1
  202. package/dist/utils/trace.js +0 -63
  203. package/dist/utils/trace.js.map +0 -1
  204. package/src/commands.ts +0 -1134
  205. package/src/core/change-detection.ts +0 -712
  206. package/src/core/config.ts +0 -313
  207. package/src/core/index.ts +0 -5
  208. package/src/core/move-detection.ts +0 -169
  209. package/src/core/snapshot.ts +0 -275
  210. package/src/core/sync-engine.ts +0 -1795
  211. package/src/types/config.ts +0 -111
  212. package/src/types/documents.ts +0 -91
  213. package/src/types/index.ts +0 -3
  214. package/src/types/snapshot.ts +0 -67
  215. package/src/utils/content.ts +0 -34
  216. package/src/utils/directory.ts +0 -73
  217. package/src/utils/fs.ts +0 -297
  218. package/src/utils/index.ts +0 -4
  219. package/src/utils/mime-types.ts +0 -244
  220. package/src/utils/network-sync.ts +0 -319
  221. package/src/utils/node-polyfills.ts +0 -8
  222. package/src/utils/output.ts +0 -450
  223. package/src/utils/repo-factory.ts +0 -73
  224. package/src/utils/string-similarity.ts +0 -54
  225. package/src/utils/text-diff.ts +0 -101
  226. package/src/utils/trace.ts +0 -70
  227. package/test/integration/README.md +0 -328
  228. package/test/integration/clone-test.sh +0 -310
  229. package/test/integration/conflict-resolution-test.sh +0 -309
  230. package/test/integration/debug-both-nested.sh +0 -74
  231. package/test/integration/debug-concurrent-nested.sh +0 -87
  232. package/test/integration/debug-nested.sh +0 -73
  233. package/test/integration/deletion-behavior-test.sh +0 -487
  234. package/test/integration/deletion-sync-test-simple.sh +0 -193
  235. package/test/integration/deletion-sync-test.sh +0 -297
  236. package/test/integration/exclude-patterns.test.ts +0 -144
  237. package/test/integration/full-integration-test.sh +0 -363
  238. package/test/integration/fuzzer.test.ts +0 -818
  239. package/test/integration/in-memory-sync.test.ts +0 -830
  240. package/test/integration/init-sync.test.ts +0 -89
  241. package/test/integration/manual-sync-test.sh +0 -84
  242. package/test/integration/sync-deletion.test.ts +0 -280
  243. package/test/integration/sync-flow.test.ts +0 -291
  244. package/test/jest.setup.ts +0 -34
  245. package/test/run-tests.sh +0 -225
  246. package/test/unit/deletion-behavior.test.ts +0 -249
  247. package/test/unit/enhanced-mime-detection.test.ts +0 -244
  248. package/test/unit/snapshot.test.ts +0 -404
  249. package/test/unit/sync-convergence.test.ts +0 -298
  250. package/test/unit/sync-timing.test.ts +0 -134
  251. 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
- });