@wener/common 2.0.3 → 2.0.6

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 (312) hide show
  1. package/lib/ai/qwen3vl/index.js +1 -1
  2. package/lib/ai/qwen3vl/utils.js +15 -15
  3. package/lib/ai/qwen3vl/utils.js.map +1 -1
  4. package/lib/ai/vision/DocLayoutElementTypeSchema.js +22 -22
  5. package/lib/ai/vision/ImageAnnotationSchema.js +63 -47
  6. package/lib/ai/vision/index.js +2 -2
  7. package/lib/ai/vision/resolveImageAnnotation.js +81 -95
  8. package/lib/cn/ChineseResidentIdNo.js +55 -41
  9. package/lib/cn/ChineseResidentIdNo.js.map +1 -1
  10. package/lib/cn/ChineseResidentIdNo.mod.js +6 -1
  11. package/lib/cn/ChineseResidentIdNo.test.js +22 -21
  12. package/lib/cn/DivisionCode.js +220 -235
  13. package/lib/cn/DivisionCode.mod.js +6 -1
  14. package/lib/cn/DivisionCode.test.js +92 -121
  15. package/lib/cn/Mod11.js +18 -37
  16. package/lib/cn/Mod11.js.map +1 -1
  17. package/lib/cn/Mod31.js +23 -41
  18. package/lib/cn/UnifiedSocialCreditCode.js +143 -137
  19. package/lib/cn/UnifiedSocialCreditCode.mod.js +6 -1
  20. package/lib/cn/UnifiedSocialCreditCode.test.js +21 -15
  21. package/lib/cn/formatChineseAmount.js +46 -71
  22. package/lib/cn/index.js +6 -6
  23. package/lib/cn/mod.js +5 -3
  24. package/lib/cn/parseChineseNumber.js +81 -85
  25. package/lib/cn/parseChineseNumber.test.js +183 -261
  26. package/lib/cn/pinyin/cartesianProduct.js +19 -19
  27. package/lib/cn/pinyin/cartesianProduct.test.js +78 -178
  28. package/lib/cn/pinyin/loader.js +13 -11
  29. package/lib/cn/pinyin/preload.js +2 -1
  30. package/lib/cn/pinyin/toPinyin.test.js +149 -161
  31. package/lib/cn/pinyin/toPinyinPure.js +28 -23
  32. package/lib/cn/pinyin/transform.js +11 -11
  33. package/lib/cn/types.d.js +2 -2
  34. package/lib/consola/createStandardConsolaReporter.js +14 -15
  35. package/lib/consola/formatLogObject.js +149 -133
  36. package/lib/consola/formatLogObject.js.map +1 -1
  37. package/lib/consola/formatLogObject.test.js +167 -178
  38. package/lib/consola/index.js +2 -2
  39. package/lib/data/formatSort.js +14 -12
  40. package/lib/data/formatSort.test.js +33 -33
  41. package/lib/data/index.js +3 -3
  42. package/lib/data/maybeNumber.js +23 -23
  43. package/lib/data/maybeNumber.js.map +1 -1
  44. package/lib/data/parseSort.js +75 -68
  45. package/lib/data/parseSort.test.js +196 -187
  46. package/lib/data/resolvePagination.js +38 -39
  47. package/lib/data/resolvePagination.test.js +228 -218
  48. package/lib/data/types.d.js +2 -2
  49. package/lib/data/types.d.js.map +1 -1
  50. package/lib/dayjs/dayjs.js +20 -20
  51. package/lib/dayjs/formatDuration.js +56 -56
  52. package/lib/dayjs/formatDuration.js.map +1 -1
  53. package/lib/dayjs/formatDuration.test.js +63 -77
  54. package/lib/dayjs/index.js +4 -4
  55. package/lib/dayjs/parseDuration.js +21 -26
  56. package/lib/dayjs/parseRelativeTime.js +65 -66
  57. package/lib/dayjs/parseRelativeTime.test.js +227 -243
  58. package/lib/dayjs/resolveRelativeTime.js +74 -144
  59. package/lib/dayjs/resolveRelativeTime.js.map +1 -1
  60. package/lib/dayjs/resolveRelativeTime.test.js +296 -307
  61. package/lib/decimal/index.js +1 -1
  62. package/lib/decimal/parseDecimal.js +12 -12
  63. package/lib/drain3/Drain.js +321 -0
  64. package/lib/drain3/Drain.js.map +1 -0
  65. package/lib/drain3/LogCluster.js +38 -0
  66. package/lib/drain3/LogCluster.js.map +1 -0
  67. package/lib/drain3/Node.js +39 -0
  68. package/lib/drain3/Node.js.map +1 -0
  69. package/lib/drain3/TemplateMiner.js +205 -0
  70. package/lib/drain3/TemplateMiner.js.map +1 -0
  71. package/lib/drain3/index.js +31 -0
  72. package/lib/drain3/index.js.map +1 -0
  73. package/lib/drain3/persistence/FilePersistence.js +24 -0
  74. package/lib/drain3/persistence/FilePersistence.js.map +1 -0
  75. package/lib/drain3/persistence/MemoryPersistence.js +18 -0
  76. package/lib/drain3/persistence/MemoryPersistence.js.map +1 -0
  77. package/lib/drain3/persistence/PersistenceHandler.js +5 -0
  78. package/lib/drain3/persistence/PersistenceHandler.js.map +1 -0
  79. package/lib/drain3/types.js +7 -0
  80. package/lib/drain3/types.js.map +1 -0
  81. package/lib/emittery/emitter.js +7 -7
  82. package/lib/emittery/index.js +1 -1
  83. package/lib/foundation/schema/SexType.js +15 -12
  84. package/lib/foundation/schema/index.js +1 -1
  85. package/lib/foundation/schema/parseSexType.js +15 -16
  86. package/lib/foundation/schema/types.js +8 -6
  87. package/lib/fs/FileSystemError.js +18 -18
  88. package/lib/fs/IFileSystem.d.js +2 -2
  89. package/lib/fs/IFileSystem.d.js.map +1 -1
  90. package/lib/fs/MemoryFileSystem.test.js +172 -181
  91. package/lib/fs/createBrowserFileSystem.js +222 -233
  92. package/lib/fs/createBrowserFileSystem.js.map +1 -1
  93. package/lib/fs/createMemoryFileSystem.js +473 -510
  94. package/lib/fs/createMemoryFileSystem.js.map +1 -1
  95. package/lib/fs/createSandboxFileSystem.js +102 -101
  96. package/lib/fs/createSandboxFileSystem.js.map +1 -1
  97. package/lib/fs/createWebDavFileSystem.js +162 -132
  98. package/lib/fs/createWebDavFileSystem.js.map +1 -1
  99. package/lib/fs/createWebFileSystem.js +202 -0
  100. package/lib/fs/createWebFileSystem.js.map +1 -0
  101. package/lib/fs/findMimeType.js +14 -14
  102. package/lib/fs/findMimeType.js.map +1 -1
  103. package/lib/fs/index.js +7 -7
  104. package/lib/fs/index.js.map +1 -1
  105. package/lib/fs/minio/createMinioFileSystem.js +977 -0
  106. package/lib/fs/minio/createMinioFileSystem.js.map +1 -0
  107. package/lib/fs/minio/index.js +2 -0
  108. package/lib/fs/minio/index.js.map +1 -0
  109. package/lib/fs/orpc/FileSystemContract.js +57 -57
  110. package/lib/fs/orpc/createContractClientFileSystem.js +88 -88
  111. package/lib/fs/orpc/createContractClientFileSystem.js.map +1 -1
  112. package/lib/fs/orpc/index.js +2 -2
  113. package/lib/fs/orpc/server/createFileSystemContractImpl.js +62 -60
  114. package/lib/fs/orpc/server/createFileSystemContractImpl.js.map +1 -1
  115. package/lib/fs/orpc/server/index.js +1 -1
  116. package/lib/fs/s3/createS3MiniFileSystem.js +756 -689
  117. package/lib/fs/s3/createS3MiniFileSystem.js.map +1 -1
  118. package/lib/fs/s3/index.js +1 -1
  119. package/lib/fs/s3/s3mini.test.js +524 -553
  120. package/lib/fs/scandir.js +56 -56
  121. package/lib/fs/server/createDatabaseFileSystem.js +834 -741
  122. package/lib/fs/server/createDatabaseFileSystem.js.map +1 -1
  123. package/lib/fs/server/createNodeFileSystem.js +407 -380
  124. package/lib/fs/server/createNodeFileSystem.js.map +1 -1
  125. package/lib/fs/server/dbfs.test.js +201 -214
  126. package/lib/fs/server/index.js +1 -1
  127. package/lib/fs/server/loadTestDatabase.js +40 -43
  128. package/lib/fs/tests/runFileSystemTest.js +352 -315
  129. package/lib/fs/tests/runFileSystemTest.js.map +1 -1
  130. package/lib/fs/types.js +17 -20
  131. package/lib/fs/utils/getFileUrl.js +24 -30
  132. package/lib/fs/utils.js +17 -17
  133. package/lib/fs/utils.js.map +1 -1
  134. package/lib/fs/webdav/index.js +2 -0
  135. package/lib/fs/webdav/index.js.map +1 -0
  136. package/lib/index.js +2 -2
  137. package/lib/jsonschema/JsonSchema.js +216 -155
  138. package/lib/jsonschema/JsonSchema.js.map +1 -1
  139. package/lib/jsonschema/JsonSchema.test.js +123 -124
  140. package/lib/jsonschema/forEachJsonSchema.js +41 -41
  141. package/lib/jsonschema/forEachJsonSchema.js.map +1 -1
  142. package/lib/jsonschema/index.js +2 -2
  143. package/lib/jsonschema/types.d.js +2 -2
  144. package/lib/jsonschema/types.d.js.map +1 -1
  145. package/lib/meta/defineFileType.js +32 -38
  146. package/lib/meta/defineInit.js +39 -35
  147. package/lib/meta/defineMetadata.js +37 -34
  148. package/lib/meta/defineMetadata.js.map +1 -1
  149. package/lib/meta/defineMetadata.test.js +13 -12
  150. package/lib/meta/index.js +3 -3
  151. package/lib/orpc/createOpenApiContractClient.js +26 -24
  152. package/lib/orpc/createOpenApiContractClient.js.map +1 -1
  153. package/lib/orpc/createRpcContractClient.js +37 -31
  154. package/lib/orpc/index.js +2 -2
  155. package/lib/orpc/resolveLinkPlugins.js +25 -25
  156. package/lib/password/PHC.js +187 -189
  157. package/lib/password/PHC.js.map +1 -1
  158. package/lib/password/PHC.test.js +517 -535
  159. package/lib/password/Password.js +85 -80
  160. package/lib/password/Password.test.js +330 -364
  161. package/lib/password/createArgon2PasswordAlgorithm.js +50 -51
  162. package/lib/password/createArgon2PasswordAlgorithm.js.map +1 -1
  163. package/lib/password/createBase64PasswordAlgorithm.js +11 -11
  164. package/lib/password/createBase64PasswordAlgorithm.js.map +1 -1
  165. package/lib/password/createBcryptPasswordAlgorithm.js +20 -18
  166. package/lib/password/createBcryptPasswordAlgorithm.js.map +1 -1
  167. package/lib/password/createPBKDF2PasswordAlgorithm.js +65 -52
  168. package/lib/password/createPBKDF2PasswordAlgorithm.js.map +1 -1
  169. package/lib/password/createScryptPasswordAlgorithm.js +74 -63
  170. package/lib/password/createScryptPasswordAlgorithm.js.map +1 -1
  171. package/lib/password/index.js +5 -5
  172. package/lib/password/server/index.js +1 -1
  173. package/lib/resource/Identifiable.js +2 -2
  174. package/lib/resource/ListQuery.js +42 -42
  175. package/lib/resource/ListQuery.js.map +1 -1
  176. package/lib/resource/getTitleOfResource.js +5 -5
  177. package/lib/resource/index.js +2 -2
  178. package/lib/resource/index.js.map +1 -1
  179. package/lib/resource/schema/AnyResourceSchema.js +91 -89
  180. package/lib/resource/schema/BaseResourceSchema.js +26 -26
  181. package/lib/resource/schema/ResourceActionType.js +117 -115
  182. package/lib/resource/schema/ResourceStatus.js +94 -92
  183. package/lib/resource/schema/ResourceType.js +25 -23
  184. package/lib/resource/schema/index.js +5 -5
  185. package/lib/resource/schema/types.js +86 -55
  186. package/lib/resource/schema/types.test.js +16 -13
  187. package/lib/s3/formatS3Url.js +60 -60
  188. package/lib/s3/formatS3Url.js.map +1 -1
  189. package/lib/s3/formatS3Url.test.js +238 -261
  190. package/lib/s3/index.js +2 -2
  191. package/lib/s3/parseS3Url.js +61 -60
  192. package/lib/s3/parseS3Url.js.map +1 -1
  193. package/lib/s3/parseS3Url.test.js +270 -269
  194. package/lib/schema/SchemaRegistry.js +41 -42
  195. package/lib/schema/SchemaRegistry.js.map +1 -1
  196. package/lib/schema/SchemaRegistry.mod.js +1 -1
  197. package/lib/schema/TypeSchema.d.js +2 -2
  198. package/lib/schema/TypeSchema.d.js.map +1 -1
  199. package/lib/schema/createSchemaData.js +113 -67
  200. package/lib/schema/createSchemaData.js.map +1 -1
  201. package/lib/schema/findJsonSchemaByPath.js +28 -23
  202. package/lib/schema/findJsonSchemaByPath.js.map +1 -1
  203. package/lib/schema/formatZodError.js +113 -134
  204. package/lib/schema/formatZodError.js.map +1 -1
  205. package/lib/schema/formatZodError.test.js +192 -195
  206. package/lib/schema/getSchemaCache.js +7 -7
  207. package/lib/schema/getSchemaOptions.js +17 -16
  208. package/lib/schema/index.js +6 -6
  209. package/lib/schema/toJsonSchema.js +196 -190
  210. package/lib/schema/toJsonSchema.js.map +1 -1
  211. package/lib/schema/toJsonSchema.test.js +34 -26
  212. package/lib/schema/validate.js +106 -97
  213. package/lib/schema/validate.js.map +1 -1
  214. package/lib/tools/generateSchema.js +40 -40
  215. package/lib/tools/renderJsonSchemaToMarkdownDoc.js +74 -74
  216. package/lib/utils/buildBaseUrl.js +8 -8
  217. package/lib/utils/buildRedactorFormSchema.js +55 -54
  218. package/lib/utils/buildRedactorFormSchema.js.map +1 -1
  219. package/lib/utils/getEstimateProcessTime.js +24 -19
  220. package/lib/utils/index.js +3 -3
  221. package/lib/utils/resolveFeatureOptions.js +9 -9
  222. package/package.json +37 -18
  223. package/src/ai/qwen3vl/utils.ts +1 -1
  224. package/src/ai/vision/index.ts +2 -2
  225. package/src/cn/ChineseResidentIdNo.ts +1 -1
  226. package/src/cn/Mod11.ts +1 -1
  227. package/src/cn/__snapshots__/ChineseResidentIdNo.test.ts.snap +1 -1
  228. package/src/cn/__snapshots__/UnifiedSocialCreditCode.test.ts.snap +0 -23
  229. package/src/cn/index.ts +1 -2
  230. package/src/cn/parseChineseNumber.test.ts +4 -4
  231. package/src/consola/formatLogObject.ts +6 -6
  232. package/src/consola/index.ts +1 -1
  233. package/src/data/index.ts +3 -4
  234. package/src/data/maybeNumber.ts +1 -1
  235. package/src/data/parseSort.test.ts +0 -1
  236. package/src/data/resolvePagination.ts +2 -2
  237. package/src/data/types.d.ts +2 -2
  238. package/src/dayjs/formatDuration.ts +10 -11
  239. package/src/dayjs/index.ts +1 -1
  240. package/src/dayjs/parseRelativeTime.ts +1 -1
  241. package/src/dayjs/resolveRelativeTime.ts +11 -14
  242. package/src/drain3/Drain.test.ts +378 -0
  243. package/src/drain3/Drain.ts +394 -0
  244. package/src/drain3/LogCluster.ts +46 -0
  245. package/src/drain3/Node.ts +53 -0
  246. package/src/drain3/TemplateMiner.ts +246 -0
  247. package/src/drain3/index.ts +34 -0
  248. package/src/drain3/persistence/FilePersistence.ts +24 -0
  249. package/src/drain3/persistence/MemoryPersistence.ts +23 -0
  250. package/src/drain3/persistence/PersistenceHandler.ts +19 -0
  251. package/src/drain3/types.ts +75 -0
  252. package/src/fs/IFileSystem.d.ts +1 -2
  253. package/src/fs/createBrowserFileSystem.ts +7 -5
  254. package/src/fs/createMemoryFileSystem.ts +9 -13
  255. package/src/fs/createSandboxFileSystem.ts +1 -1
  256. package/src/fs/createWebDavFileSystem.ts +30 -17
  257. package/src/fs/createWebFileSystem.ts +242 -0
  258. package/src/fs/findMimeType.ts +1 -4
  259. package/src/fs/index.ts +5 -5
  260. package/src/fs/minio/createMinioFileSystem.ts +1148 -0
  261. package/src/fs/minio/index.ts +1 -0
  262. package/src/fs/orpc/createContractClientFileSystem.ts +5 -5
  263. package/src/fs/orpc/server/createFileSystemContractImpl.ts +1 -1
  264. package/src/fs/s3/createS3MiniFileSystem.ts +120 -79
  265. package/src/fs/s3/s3fs.test.ts +441 -0
  266. package/src/fs/s3/s3mini.test.ts +2 -2
  267. package/src/fs/server/createDatabaseFileSystem.ts +78 -114
  268. package/src/fs/server/createNodeFileSystem.ts +32 -13
  269. package/src/fs/server/dbfs.test.ts +13 -8
  270. package/src/fs/server/index.ts +1 -0
  271. package/src/fs/server/loadTestDatabase.ts +8 -119
  272. package/src/fs/tests/runFileSystemTest.ts +29 -28
  273. package/src/fs/utils.ts +1 -1
  274. package/src/fs/webdav/index.ts +1 -0
  275. package/src/jsonschema/JsonSchema.ts +5 -5
  276. package/src/jsonschema/forEachJsonSchema.ts +1 -1
  277. package/src/jsonschema/index.ts +1 -1
  278. package/src/jsonschema/types.d.ts +1 -1
  279. package/src/meta/defineMetadata.ts +1 -1
  280. package/src/meta/index.ts +2 -3
  281. package/src/orm/createSqliteDialect.ts +17 -0
  282. package/src/orm/index.ts +1 -0
  283. package/src/orpc/createOpenApiContractClient.ts +3 -3
  284. package/src/orpc/index.ts +1 -1
  285. package/src/password/PHC.ts +3 -3
  286. package/src/password/createArgon2PasswordAlgorithm.ts +2 -2
  287. package/src/password/createBase64PasswordAlgorithm.ts +2 -2
  288. package/src/password/createBcryptPasswordAlgorithm.ts +4 -2
  289. package/src/password/createPBKDF2PasswordAlgorithm.ts +2 -2
  290. package/src/password/createScryptPasswordAlgorithm.ts +4 -4
  291. package/src/password/index.ts +2 -2
  292. package/src/resource/ListQuery.ts +4 -1
  293. package/src/resource/index.ts +3 -3
  294. package/src/resource/schema/index.ts +4 -4
  295. package/src/s3/formatS3Url.test.ts +1 -1
  296. package/src/s3/formatS3Url.ts +2 -2
  297. package/src/s3/index.ts +1 -1
  298. package/src/s3/parseS3Url.ts +1 -1
  299. package/src/schema/SchemaRegistry.ts +2 -2
  300. package/src/schema/TypeSchema.d.ts +6 -6
  301. package/src/schema/createSchemaData.ts +5 -5
  302. package/src/schema/findJsonSchemaByPath.ts +5 -5
  303. package/src/schema/formatZodError.test.ts +2 -1
  304. package/src/schema/formatZodError.ts +50 -62
  305. package/src/schema/index.ts +5 -5
  306. package/src/schema/toJsonSchema.ts +6 -6
  307. package/src/schema/validate.ts +2 -2
  308. package/src/utils/buildRedactorFormSchema.ts +4 -4
  309. package/src/utils/formatNumber.ts +18 -0
  310. package/src/utils/formatPercent.ts +17 -0
  311. package/src/utils/index.ts +3 -3
  312. package/src/utils/resolveFeatureOptions.ts +1 -1
@@ -0,0 +1,441 @@
1
+ import { afterAll, beforeEach, describe, expect, test } from 'vitest';
2
+ import { createMinioFileSystem } from '../minio/createMinioFileSystem';
3
+
4
+ const S3_URL = process.env.S3_URL;
5
+
6
+ // Use a test prefix to avoid conflicts with real data
7
+ const TEST_PREFIX = `test-${Date.now()}`;
8
+
9
+ describe.skipIf(!S3_URL)('MinioFileSystem', () => {
10
+ let fs: ReturnType<typeof createMinioFileSystem>;
11
+
12
+ beforeEach(() => {
13
+ fs = createMinioFileSystem({
14
+ url: S3_URL!,
15
+ prefix: TEST_PREFIX,
16
+ });
17
+ });
18
+
19
+ // Cleanup: remove all test files after all tests
20
+ afterAll(async () => {
21
+ if (S3_URL && fs) {
22
+ try {
23
+ await fs.rm('/', { recursive: true, force: true });
24
+ } catch {
25
+ // Ignore cleanup errors
26
+ }
27
+ }
28
+ });
29
+
30
+ describe('readdir', () => {
31
+ test('should list root directory', async () => {
32
+ const entries = await fs.readdir('/');
33
+ expect(Array.isArray(entries)).toBe(true);
34
+ entries.forEach((entry) => {
35
+ expect(entry).toHaveProperty('path');
36
+ expect(entry).toHaveProperty('name');
37
+ expect(entry).toHaveProperty('kind');
38
+ expect(entry).toHaveProperty('size');
39
+ expect(entry).toHaveProperty('mtime');
40
+ expect(['file', 'directory']).toContain(entry.kind);
41
+ });
42
+ });
43
+
44
+ test('should list directory with recursive option', async () => {
45
+ // Create test structure
46
+ await fs.writeFile('/test-file.txt', 'test');
47
+ await fs.mkdir('/test-dir');
48
+ await fs.writeFile('/test-dir/nested.txt', 'nested');
49
+
50
+ const entries = await fs.readdir('/', { recursive: true });
51
+ const fileNames = entries.map((e) => e.name);
52
+ const paths = entries.map((e) => e.path);
53
+ expect(fileNames).toContain('test-file.txt');
54
+ expect(fileNames).toContain('test-dir');
55
+ // In recursive mode, nested files should appear with their relative path
56
+ expect(paths.some((p) => p.includes('nested.txt'))).toBe(true);
57
+ });
58
+
59
+ test('should filter by kind', async () => {
60
+ await fs.writeFile('/filter-file.txt', 'test');
61
+ await fs.mkdir('/filter-dir');
62
+
63
+ const files = await fs.readdir('/', { kind: 'file' });
64
+ expect(files.every((f) => f.kind === 'file')).toBe(true);
65
+
66
+ const dirs = await fs.readdir('/', { kind: 'directory' });
67
+ expect(dirs.every((d) => d.kind === 'directory')).toBe(true);
68
+ });
69
+
70
+ test('should filter hidden files', async () => {
71
+ await fs.writeFile('/.hidden', 'hidden');
72
+ await fs.writeFile('/visible.txt', 'visible');
73
+
74
+ const entries = await fs.readdir('/', { hidden: false });
75
+ const names = entries.map((e) => e.name);
76
+ expect(names).not.toContain('.hidden');
77
+ expect(names).toContain('visible.txt');
78
+
79
+ const allEntries = await fs.readdir('/', { hidden: true });
80
+ const allNames = allEntries.map((e) => e.name);
81
+ expect(allNames).toContain('.hidden');
82
+ });
83
+
84
+ test('should support depth option', async () => {
85
+ await fs.mkdir('/depth1', { recursive: true });
86
+ await fs.mkdir('/depth1/depth2', { recursive: true });
87
+ await fs.writeFile('/depth1/depth2/file.txt', 'test');
88
+
89
+ const depth1 = await fs.readdir('/', { depth: 1 });
90
+ expect(depth1.some((e) => e.name === 'depth1')).toBe(true);
91
+ expect(depth1.some((e) => e.name === 'file.txt')).toBe(false);
92
+
93
+ const depth2 = await fs.readdir('/', { depth: 2 });
94
+ expect(depth2.some((e) => e.name === 'file.txt')).toBe(true);
95
+ });
96
+ });
97
+
98
+ describe('stat', () => {
99
+ test('should stat root directory', async () => {
100
+ const stat = await fs.stat('/');
101
+ expect(stat.kind).toBe('directory');
102
+ expect(stat.path).toBe('/');
103
+ });
104
+
105
+ test('should stat a file', async () => {
106
+ await fs.writeFile('/stat-test.txt', 'test content');
107
+ const stat = await fs.stat('/stat-test.txt');
108
+ expect(stat.kind).toBe('file');
109
+ expect(stat.size).toBe(12); // "test content" is 12 bytes
110
+ expect(stat.name).toBe('stat-test.txt');
111
+ });
112
+
113
+ test('should stat a directory', async () => {
114
+ await fs.mkdir('/stat-dir');
115
+ const stat = await fs.stat('/stat-dir');
116
+ expect(stat.kind).toBe('directory');
117
+ expect(stat.name).toBe('stat-dir');
118
+ });
119
+
120
+ test('should throw error for non-existent file', async () => {
121
+ await expect(fs.stat('/nonexistent.txt')).rejects.toThrow('File not found');
122
+ });
123
+ });
124
+
125
+ describe('readFile and writeFile', () => {
126
+ test('should write and read text file', async () => {
127
+ const content = 'Hello, World!';
128
+ await fs.writeFile('/hello.txt', content);
129
+ const read = await fs.readFile('/hello.txt', { encoding: 'text' });
130
+ expect(read).toBe(content);
131
+ });
132
+
133
+ test('should write and read binary file', async () => {
134
+ const content = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
135
+ await fs.writeFile('/binary.bin', content);
136
+ const read = await fs.readFile('/binary.bin');
137
+ expect(Buffer.from(read)).toEqual(content);
138
+ });
139
+
140
+ test('should overwrite file by default', async () => {
141
+ await fs.writeFile('/overwrite.txt', 'original');
142
+ await fs.writeFile('/overwrite.txt', 'updated');
143
+ const content = await fs.readFile('/overwrite.txt', { encoding: 'text' });
144
+ expect(content).toBe('updated');
145
+ });
146
+
147
+ test('should prevent overwrite when overwrite=false', async () => {
148
+ await fs.writeFile('/no-overwrite.txt', 'original');
149
+ await expect(fs.writeFile('/no-overwrite.txt', 'new', { overwrite: false })).rejects.toThrow(
150
+ 'File already exists',
151
+ );
152
+ });
153
+
154
+ test('should handle large files', async () => {
155
+ const largeContent = 'x'.repeat(10000);
156
+ await fs.writeFile('/large.txt', largeContent);
157
+ const read = await fs.readFile('/large.txt', { encoding: 'text' });
158
+ expect(read).toBe(largeContent);
159
+ expect(read.length).toBe(10000);
160
+ });
161
+
162
+ test('should throw error when reading non-existent file', async () => {
163
+ await expect(fs.readFile('/nonexistent.txt')).rejects.toThrow('File not found');
164
+ });
165
+ });
166
+
167
+ describe('mkdir', () => {
168
+ test('should create directory', async () => {
169
+ await fs.mkdir('/new-dir');
170
+ expect(await fs.exists('/new-dir')).toBe(true);
171
+ const stat = await fs.stat('/new-dir');
172
+ expect(stat.kind).toBe('directory');
173
+ });
174
+
175
+ test('should create nested directories recursively', async () => {
176
+ await fs.mkdir('/nested/deep/dir', { recursive: true });
177
+ expect(await fs.exists('/nested/deep/dir')).toBe(true);
178
+ });
179
+
180
+ test('should handle creating root directory', async () => {
181
+ await expect(fs.mkdir('/')).resolves.not.toThrow();
182
+ });
183
+ });
184
+
185
+ describe('rm', () => {
186
+ test('should remove a file', async () => {
187
+ await fs.writeFile('/to-remove.txt', 'content');
188
+ expect(await fs.exists('/to-remove.txt')).toBe(true);
189
+
190
+ await fs.rm('/to-remove.txt');
191
+ expect(await fs.exists('/to-remove.txt')).toBe(false);
192
+ });
193
+
194
+ test('should remove directory recursively', async () => {
195
+ await fs.mkdir('/rm-dir', { recursive: true });
196
+ await fs.writeFile('/rm-dir/file1.txt', 'file1');
197
+ await fs.writeFile('/rm-dir/file2.txt', 'file2');
198
+ await fs.mkdir('/rm-dir/subdir', { recursive: true });
199
+ await fs.writeFile('/rm-dir/subdir/file3.txt', 'file3');
200
+
201
+ await fs.rm('/rm-dir', { recursive: true });
202
+ expect(await fs.exists('/rm-dir')).toBe(false);
203
+ });
204
+
205
+ test('should handle force option', async () => {
206
+ await expect(fs.rm('/nonexistent.txt')).rejects.toThrow();
207
+ await expect(fs.rm('/nonexistent.txt', { force: true })).resolves.not.toThrow();
208
+ });
209
+ });
210
+
211
+ describe('rename', () => {
212
+ test('should rename a file', async () => {
213
+ await fs.writeFile('/old-name.txt', 'content');
214
+ await fs.rename('/old-name.txt', '/new-name.txt');
215
+
216
+ expect(await fs.exists('/old-name.txt')).toBe(false);
217
+ expect(await fs.exists('/new-name.txt')).toBe(true);
218
+ const content = await fs.readFile('/new-name.txt', { encoding: 'text' });
219
+ expect(content).toBe('content');
220
+ });
221
+
222
+ test('should rename a directory', async () => {
223
+ await fs.mkdir('/old-dir', { recursive: true });
224
+ await fs.writeFile('/old-dir/file.txt', 'content');
225
+
226
+ // Clean up any existing new-dir first
227
+ try {
228
+ await fs.rm('/new-dir', { recursive: true, force: true });
229
+ } catch {
230
+ // Ignore if doesn't exist
231
+ }
232
+
233
+ await fs.rename('/old-dir', '/new-dir');
234
+
235
+ expect(await fs.exists('/old-dir')).toBe(false);
236
+ expect(await fs.exists('/new-dir')).toBe(true);
237
+ expect(await fs.exists('/new-dir/file.txt')).toBe(true);
238
+ const content = await fs.readFile('/new-dir/file.txt', { encoding: 'text' });
239
+ expect(content).toBe('content');
240
+ });
241
+
242
+ test('should prevent overwrite when overwrite=false', async () => {
243
+ await fs.writeFile('/source.txt', 'source');
244
+ await fs.writeFile('/target.txt', 'target');
245
+
246
+ await expect(fs.rename('/source.txt', '/target.txt', { overwrite: false })).rejects.toThrow(
247
+ 'Destination already exists',
248
+ );
249
+ });
250
+
251
+ test('should allow overwrite when overwrite=true', async () => {
252
+ await fs.writeFile('/source.txt', 'source');
253
+ await fs.writeFile('/target.txt', 'target');
254
+
255
+ await fs.rename('/source.txt', '/target.txt', { overwrite: true });
256
+ expect(await fs.exists('/source.txt')).toBe(false);
257
+ const content = await fs.readFile('/target.txt', { encoding: 'text' });
258
+ expect(content).toBe('source');
259
+ });
260
+ });
261
+
262
+ describe('copy', () => {
263
+ test('should copy a file', async () => {
264
+ await fs.writeFile('/source.txt', 'source content');
265
+ await fs.copy('/source.txt', '/dest.txt');
266
+
267
+ expect(await fs.exists('/source.txt')).toBe(true);
268
+ expect(await fs.exists('/dest.txt')).toBe(true);
269
+ const source = await fs.readFile('/source.txt', { encoding: 'text' });
270
+ const dest = await fs.readFile('/dest.txt', { encoding: 'text' });
271
+ expect(source).toBe(dest);
272
+ expect(dest).toBe('source content');
273
+ });
274
+
275
+ test('should copy a directory recursively', async () => {
276
+ await fs.mkdir('/copy-source', { recursive: true });
277
+ await fs.writeFile('/copy-source/file1.txt', 'file1');
278
+ await fs.writeFile('/copy-source/file2.txt', 'file2');
279
+ await fs.mkdir('/copy-source/subdir', { recursive: true });
280
+ await fs.writeFile('/copy-source/subdir/file3.txt', 'file3');
281
+
282
+ await fs.copy('/copy-source', '/copy-dest');
283
+
284
+ expect(await fs.exists('/copy-dest/file1.txt')).toBe(true);
285
+ expect(await fs.exists('/copy-dest/file2.txt')).toBe(true);
286
+ expect(await fs.exists('/copy-dest/subdir/file3.txt')).toBe(true);
287
+ });
288
+
289
+ test('should prevent overwrite when overwrite=false', async () => {
290
+ await fs.writeFile('/copy-src.txt', 'source');
291
+ await fs.writeFile('/copy-dst.txt', 'target');
292
+
293
+ await expect(fs.copy('/copy-src.txt', '/copy-dst.txt', { overwrite: false })).rejects.toThrow(
294
+ 'Destination already exists',
295
+ );
296
+ });
297
+
298
+ test('should allow overwrite when overwrite=true', async () => {
299
+ await fs.writeFile('/copy-src.txt', 'source');
300
+ await fs.writeFile('/copy-dst.txt', 'target');
301
+
302
+ await fs.copy('/copy-src.txt', '/copy-dst.txt', { overwrite: true });
303
+ const content = await fs.readFile('/copy-dst.txt', { encoding: 'text' });
304
+ expect(content).toBe('source');
305
+ });
306
+ });
307
+
308
+ describe('exists', () => {
309
+ test('should return true for existing file', async () => {
310
+ await fs.writeFile('/exists-file.txt', 'test');
311
+ expect(await fs.exists('/exists-file.txt')).toBe(true);
312
+ });
313
+
314
+ test('should return true for existing directory', async () => {
315
+ await fs.mkdir('/exists-dir');
316
+ expect(await fs.exists('/exists-dir')).toBe(true);
317
+ });
318
+
319
+ test('should return true for root directory', async () => {
320
+ expect(await fs.exists('/')).toBe(true);
321
+ });
322
+
323
+ test('should return false for non-existent path', async () => {
324
+ expect(await fs.exists('/nonexistent')).toBe(false);
325
+ });
326
+ });
327
+
328
+ describe('streams', () => {
329
+ test('should support createReadStream', async () => {
330
+ await fs.writeFile('/stream-read.txt', 'Stream content');
331
+ const stream = fs.createReadStream('/stream-read.txt');
332
+ expect(stream).toBeDefined();
333
+
334
+ const chunks: Buffer[] = [];
335
+ // Use stream events instead of async iteration for compatibility
336
+ await new Promise<void>((resolve, reject) => {
337
+ stream.on('data', (chunk) => {
338
+ chunks.push(chunk);
339
+ });
340
+ stream.on('end', () => {
341
+ resolve();
342
+ });
343
+ stream.on('error', reject);
344
+ });
345
+ expect(Buffer.concat(chunks).toString()).toBe('Stream content');
346
+ });
347
+
348
+ test('should support createReadStream with range', async () => {
349
+ await fs.writeFile('/stream-range.txt', 'Hello, World!');
350
+ const stream = fs.createReadStream('/stream-range.txt', { range: { start: 0, end: 4 } });
351
+
352
+ const chunks: Buffer[] = [];
353
+ // Use stream events instead of async iteration for compatibility
354
+ await new Promise<void>((resolve, reject) => {
355
+ stream.on('data', (chunk) => {
356
+ chunks.push(chunk);
357
+ });
358
+ stream.on('end', () => {
359
+ resolve();
360
+ });
361
+ stream.on('error', reject);
362
+ });
363
+ expect(Buffer.concat(chunks).toString()).toBe('Hello');
364
+ });
365
+
366
+ test('should support createReadableStream', async () => {
367
+ await fs.writeFile('/readable-stream.txt', 'Readable stream content');
368
+ const stream = fs.createReadableStream('/readable-stream.txt');
369
+
370
+ const reader = stream.getReader();
371
+ const chunks: Uint8Array[] = [];
372
+ while (true) {
373
+ const { done, value } = await reader.read();
374
+ if (done) break;
375
+ if (value) chunks.push(value);
376
+ }
377
+
378
+ const content = Buffer.concat(chunks.map((c) => Buffer.from(c))).toString();
379
+ expect(content).toBe('Readable stream content');
380
+ });
381
+
382
+ test('should support createWritableStream', async () => {
383
+ const stream = fs.createWritableStream('/writable-stream.txt');
384
+ const writer = stream.getWriter();
385
+
386
+ await writer.write(new TextEncoder().encode('Part 1'));
387
+ await writer.write(new TextEncoder().encode('Part 2'));
388
+ await writer.close();
389
+
390
+ const content = await fs.readFile('/writable-stream.txt', { encoding: 'text' });
391
+ expect(content).toBe('Part 1Part 2');
392
+ });
393
+ });
394
+
395
+ describe('edge cases', () => {
396
+ test('should handle empty file', async () => {
397
+ await fs.writeFile('/empty.txt', '');
398
+ const stat = await fs.stat('/empty.txt');
399
+ expect(stat.size).toBe(0);
400
+ const content = await fs.readFile('/empty.txt', { encoding: 'text' });
401
+ expect(content).toBe('');
402
+ });
403
+
404
+ test('should handle special characters in filename', async () => {
405
+ await fs.writeFile('/file with spaces.txt', 'content');
406
+ expect(await fs.exists('/file with spaces.txt')).toBe(true);
407
+ const content = await fs.readFile('/file with spaces.txt', { encoding: 'text' });
408
+ expect(content).toBe('content');
409
+ });
410
+
411
+ test('should handle unicode content', async () => {
412
+ const unicode = '你好世界 🌍';
413
+ await fs.writeFile('/unicode.txt', unicode);
414
+ const content = await fs.readFile('/unicode.txt', { encoding: 'text' });
415
+ expect(content).toBe(unicode);
416
+ });
417
+
418
+ test('should handle path normalization', async () => {
419
+ await fs.writeFile('/normalize.txt', 'test');
420
+ expect(await fs.exists('/normalize.txt')).toBe(true);
421
+ expect(await fs.exists('/./normalize.txt')).toBe(true);
422
+ });
423
+ });
424
+
425
+ describe('prefix support', () => {
426
+ test('should scope operations to prefix', async () => {
427
+ const prefixedFs = createMinioFileSystem({
428
+ url: S3_URL!,
429
+ prefix: `${TEST_PREFIX}/prefixed`,
430
+ });
431
+
432
+ await prefixedFs.writeFile('/test.txt', 'prefixed content');
433
+ expect(await prefixedFs.exists('/test.txt')).toBe(true);
434
+ const content = await prefixedFs.readFile('/test.txt', { encoding: 'text' });
435
+ expect(content).toBe('prefixed content');
436
+
437
+ // Cleanup
438
+ await prefixedFs.rm('/', { recursive: true, force: true });
439
+ });
440
+ });
441
+ });
@@ -249,8 +249,8 @@ describe('s3mini listObjects behavior', () => {
249
249
  const normalizedPrefix = prefix.replace(/^\/+/, '').replace(/\/+$/, '');
250
250
 
251
251
  const stripPrefix = (key: string): string => {
252
- if (!normalizedPrefix || !key.startsWith(normalizedPrefix + '/')) {
253
- return key.startsWith('/') ? key : '/' + key;
252
+ if (!normalizedPrefix || !key.startsWith(`${normalizedPrefix}/`)) {
253
+ return key.startsWith('/') ? key : `/${key}`;
254
254
  }
255
255
  const withoutPrefix = key.slice(normalizedPrefix.length);
256
256
  return withoutPrefix || '/';