@wener/common 2.0.1 → 2.0.3

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 (405) hide show
  1. package/lib/ai/qwen3vl/index.js +2 -0
  2. package/lib/ai/qwen3vl/index.js.map +1 -0
  3. package/lib/ai/qwen3vl/utils.js +31 -0
  4. package/lib/ai/qwen3vl/utils.js.map +1 -0
  5. package/lib/ai/vision/DocLayoutElementTypeSchema.js +28 -0
  6. package/lib/ai/vision/DocLayoutElementTypeSchema.js.map +1 -0
  7. package/lib/ai/vision/ImageAnnotationSchema.js +50 -0
  8. package/lib/ai/vision/ImageAnnotationSchema.js.map +1 -0
  9. package/lib/ai/vision/index.js +3 -0
  10. package/lib/ai/vision/index.js.map +1 -0
  11. package/lib/ai/vision/resolveImageAnnotation.js +105 -0
  12. package/lib/ai/vision/resolveImageAnnotation.js.map +1 -0
  13. package/lib/cn/ChineseResidentIdNo.js +48 -0
  14. package/lib/cn/ChineseResidentIdNo.js.map +1 -0
  15. package/lib/cn/ChineseResidentIdNo.mod.js +1 -0
  16. package/lib/cn/{ResidentIdentityCardNumber.test.js → ChineseResidentIdNo.test.js} +7 -6
  17. package/lib/cn/DivisionCode.js +217 -301
  18. package/lib/cn/DivisionCode.js.map +1 -0
  19. package/lib/cn/DivisionCode.mod.js +1 -0
  20. package/lib/cn/DivisionCode.test.js +9 -15
  21. package/lib/cn/Mod11.js +43 -0
  22. package/lib/cn/Mod11.js.map +1 -0
  23. package/lib/cn/Mod31.js +49 -0
  24. package/lib/cn/Mod31.js.map +1 -0
  25. package/lib/cn/UnifiedSocialCreditCode.js +137 -113
  26. package/lib/cn/UnifiedSocialCreditCode.js.map +1 -0
  27. package/lib/cn/UnifiedSocialCreditCode.mod.js +1 -0
  28. package/lib/cn/UnifiedSocialCreditCode.test.js +1 -1
  29. package/lib/cn/formatChineseAmount.js +77 -0
  30. package/lib/cn/formatChineseAmount.js.map +1 -0
  31. package/lib/cn/index.js +6 -2
  32. package/lib/cn/index.js.map +1 -0
  33. package/lib/cn/mod.js +6 -0
  34. package/lib/cn/parseChineseNumber.js +94 -0
  35. package/lib/cn/parseChineseNumber.js.map +1 -0
  36. package/lib/cn/parseChineseNumber.test.js +278 -0
  37. package/lib/cn/pinyin/cartesianProduct.js +22 -0
  38. package/lib/cn/pinyin/cartesianProduct.js.map +1 -0
  39. package/lib/cn/pinyin/cartesianProduct.test.js +179 -0
  40. package/lib/cn/pinyin/data.json +23573 -0
  41. package/lib/cn/pinyin/loader.js +14 -0
  42. package/lib/cn/pinyin/loader.js.map +1 -0
  43. package/lib/cn/pinyin/preload.js +3 -0
  44. package/lib/cn/pinyin/preload.js.map +1 -0
  45. package/lib/cn/pinyin/toPinyin.test.js +167 -0
  46. package/lib/cn/pinyin/toPinyinPure.js +33 -0
  47. package/lib/cn/pinyin/toPinyinPure.js.map +1 -0
  48. package/lib/cn/pinyin/transform.js +14 -0
  49. package/lib/cn/pinyin/transform.js.map +1 -0
  50. package/lib/cn/types.d.js +2 -0
  51. package/lib/cn/types.d.js.map +1 -0
  52. package/lib/consola/createStandardConsolaReporter.js +6 -6
  53. package/lib/consola/createStandardConsolaReporter.js.map +1 -0
  54. package/lib/consola/formatLogObject.js +67 -135
  55. package/lib/consola/formatLogObject.js.map +1 -0
  56. package/lib/consola/formatLogObject.test.js +184 -0
  57. package/lib/consola/index.js +1 -0
  58. package/lib/consola/index.js.map +1 -0
  59. package/lib/data/formatSort.js +6 -5
  60. package/lib/data/formatSort.js.map +1 -0
  61. package/lib/data/index.js +1 -0
  62. package/lib/data/index.js.map +1 -0
  63. package/lib/data/maybeNumber.js +5 -7
  64. package/lib/data/maybeNumber.js.map +1 -0
  65. package/lib/data/parseSort.js +22 -28
  66. package/lib/data/parseSort.js.map +1 -0
  67. package/lib/data/resolvePagination.js +13 -17
  68. package/lib/data/resolvePagination.js.map +1 -0
  69. package/lib/data/types.d.js +2 -0
  70. package/lib/data/types.d.js.map +1 -0
  71. package/lib/dayjs/dayjs.js +40 -0
  72. package/lib/dayjs/dayjs.js.map +1 -0
  73. package/lib/dayjs/formatDuration.js +59 -0
  74. package/lib/dayjs/formatDuration.js.map +1 -0
  75. package/lib/dayjs/formatDuration.test.js +90 -0
  76. package/lib/dayjs/index.js +5 -0
  77. package/lib/dayjs/index.js.map +1 -0
  78. package/lib/dayjs/parseDuration.js +29 -0
  79. package/lib/dayjs/parseDuration.js.map +1 -0
  80. package/lib/dayjs/parseRelativeTime.js +90 -0
  81. package/lib/dayjs/parseRelativeTime.js.map +1 -0
  82. package/lib/dayjs/parseRelativeTime.test.js +247 -0
  83. package/lib/dayjs/resolveRelativeTime.js +158 -0
  84. package/lib/dayjs/resolveRelativeTime.js.map +1 -0
  85. package/lib/dayjs/resolveRelativeTime.test.js +310 -0
  86. package/lib/decimal/index.js +2 -0
  87. package/lib/decimal/index.js.map +1 -0
  88. package/lib/decimal/parseDecimal.js +15 -0
  89. package/lib/decimal/parseDecimal.js.map +1 -0
  90. package/lib/emittery/emitter.js +10 -0
  91. package/lib/emittery/emitter.js.map +1 -0
  92. package/lib/emittery/index.js +2 -0
  93. package/lib/emittery/index.js.map +1 -0
  94. package/lib/foundation/schema/SexType.js +16 -0
  95. package/lib/foundation/schema/SexType.js.map +1 -0
  96. package/lib/foundation/schema/index.js +2 -0
  97. package/lib/foundation/schema/index.js.map +1 -0
  98. package/lib/foundation/schema/parseSexType.js +19 -0
  99. package/lib/foundation/schema/parseSexType.js.map +1 -0
  100. package/lib/foundation/schema/types.js +7 -0
  101. package/lib/foundation/schema/types.js.map +1 -0
  102. package/lib/fs/FileSystemError.js +23 -0
  103. package/lib/fs/FileSystemError.js.map +1 -0
  104. package/lib/fs/IFileSystem.d.js +3 -0
  105. package/lib/fs/IFileSystem.d.js.map +1 -0
  106. package/lib/fs/MemoryFileSystem.test.js +188 -0
  107. package/lib/fs/createBrowserFileSystem.js +248 -0
  108. package/lib/fs/createBrowserFileSystem.js.map +1 -0
  109. package/lib/fs/createMemoryFileSystem.js +516 -0
  110. package/lib/fs/createMemoryFileSystem.js.map +1 -0
  111. package/lib/fs/createSandboxFileSystem.js +108 -0
  112. package/lib/fs/createSandboxFileSystem.js.map +1 -0
  113. package/lib/fs/createWebDavFileSystem.js +137 -0
  114. package/lib/fs/createWebDavFileSystem.js.map +1 -0
  115. package/lib/fs/findMimeType.js +17 -0
  116. package/lib/fs/findMimeType.js.map +1 -0
  117. package/lib/fs/index.js +8 -0
  118. package/lib/fs/index.js.map +1 -0
  119. package/lib/fs/orpc/FileSystemContract.js +93 -0
  120. package/lib/fs/orpc/FileSystemContract.js.map +1 -0
  121. package/lib/fs/orpc/createContractClientFileSystem.js +93 -0
  122. package/lib/fs/orpc/createContractClientFileSystem.js.map +1 -0
  123. package/lib/fs/orpc/index.js +3 -0
  124. package/lib/fs/orpc/index.js.map +1 -0
  125. package/lib/fs/orpc/server/createFileSystemContractImpl.js +63 -0
  126. package/lib/fs/orpc/server/createFileSystemContractImpl.js.map +1 -0
  127. package/lib/fs/orpc/server/index.js +2 -0
  128. package/lib/fs/orpc/server/index.js.map +1 -0
  129. package/lib/fs/s3/createS3MiniFileSystem.js +705 -0
  130. package/lib/fs/s3/createS3MiniFileSystem.js.map +1 -0
  131. package/lib/fs/s3/index.js +2 -0
  132. package/lib/fs/s3/index.js.map +1 -0
  133. package/lib/fs/s3/s3mini.test.js +584 -0
  134. package/lib/fs/scandir.js +59 -0
  135. package/lib/fs/scandir.js.map +1 -0
  136. package/lib/fs/server/createDatabaseFileSystem.js +750 -0
  137. package/lib/fs/server/createDatabaseFileSystem.js.map +1 -0
  138. package/lib/fs/server/createNodeFileSystem.js +401 -0
  139. package/lib/fs/server/createNodeFileSystem.js.map +1 -0
  140. package/lib/fs/server/dbfs.test.js +221 -0
  141. package/lib/fs/server/index.js +2 -0
  142. package/lib/fs/server/index.js.map +1 -0
  143. package/lib/fs/server/loadTestDatabase.js +127 -0
  144. package/lib/fs/server/loadTestDatabase.js.map +1 -0
  145. package/lib/fs/tests/runFileSystemTest.js +318 -0
  146. package/lib/fs/tests/runFileSystemTest.js.map +1 -0
  147. package/lib/fs/types.js +27 -0
  148. package/lib/fs/types.js.map +1 -0
  149. package/lib/fs/utils/getFileUrl.js +35 -0
  150. package/lib/fs/utils/getFileUrl.js.map +1 -0
  151. package/lib/fs/utils.js +22 -0
  152. package/lib/fs/utils.js.map +1 -0
  153. package/lib/index.js +1 -0
  154. package/lib/index.js.map +1 -0
  155. package/lib/jsonschema/JsonSchema.js +146 -172
  156. package/lib/jsonschema/JsonSchema.js.map +1 -0
  157. package/lib/jsonschema/forEachJsonSchema.js +44 -0
  158. package/lib/jsonschema/forEachJsonSchema.js.map +1 -0
  159. package/lib/jsonschema/index.js +2 -0
  160. package/lib/jsonschema/index.js.map +1 -0
  161. package/lib/jsonschema/types.d.js +2 -0
  162. package/lib/jsonschema/types.d.js.map +1 -0
  163. package/lib/meta/defineFileType.js +20 -103
  164. package/lib/meta/defineFileType.js.map +1 -0
  165. package/lib/meta/defineInit.js +31 -250
  166. package/lib/meta/defineInit.js.map +1 -0
  167. package/lib/meta/defineMetadata.js +24 -140
  168. package/lib/meta/defineMetadata.js.map +1 -0
  169. package/lib/meta/index.js +1 -0
  170. package/lib/meta/index.js.map +1 -0
  171. package/lib/orpc/createOpenApiContractClient.js +27 -0
  172. package/lib/orpc/createOpenApiContractClient.js.map +1 -0
  173. package/lib/orpc/createRpcContractClient.js +34 -0
  174. package/lib/orpc/createRpcContractClient.js.map +1 -0
  175. package/lib/orpc/index.js +3 -0
  176. package/lib/orpc/index.js.map +1 -0
  177. package/lib/orpc/resolveLinkPlugins.js +28 -0
  178. package/lib/orpc/resolveLinkPlugins.js.map +1 -0
  179. package/lib/password/PHC.js +63 -87
  180. package/lib/password/PHC.js.map +1 -0
  181. package/lib/password/PHC.test.js +11 -3
  182. package/lib/password/Password.js +30 -292
  183. package/lib/password/Password.js.map +1 -0
  184. package/lib/password/Password.test.js +35 -22
  185. package/lib/password/createArgon2PasswordAlgorithm.js +35 -191
  186. package/lib/password/createArgon2PasswordAlgorithm.js.map +1 -0
  187. package/lib/password/createBase64PasswordAlgorithm.js +8 -141
  188. package/lib/password/createBase64PasswordAlgorithm.js.map +1 -0
  189. package/lib/password/createBcryptPasswordAlgorithm.js +13 -168
  190. package/lib/password/createBcryptPasswordAlgorithm.js.map +1 -0
  191. package/lib/password/createPBKDF2PasswordAlgorithm.js +46 -228
  192. package/lib/password/createPBKDF2PasswordAlgorithm.js.map +1 -0
  193. package/lib/password/createScryptPasswordAlgorithm.js +55 -211
  194. package/lib/password/createScryptPasswordAlgorithm.js.map +1 -0
  195. package/lib/password/index.js +1 -0
  196. package/lib/password/index.js.map +1 -0
  197. package/lib/password/server/index.js +1 -0
  198. package/lib/password/server/index.js.map +1 -0
  199. package/lib/resource/Identifiable.js +2 -0
  200. package/lib/resource/Identifiable.js.map +1 -0
  201. package/lib/resource/ListQuery.js +47 -0
  202. package/lib/resource/ListQuery.js.map +1 -0
  203. package/lib/resource/getTitleOfResource.js +3 -5
  204. package/lib/resource/getTitleOfResource.js.map +1 -0
  205. package/lib/resource/index.js +2 -0
  206. package/lib/resource/index.js.map +1 -0
  207. package/lib/resource/schema/AnyResourceSchema.js +3 -2
  208. package/lib/resource/schema/AnyResourceSchema.js.map +1 -0
  209. package/lib/resource/schema/BaseResourceSchema.js +2 -1
  210. package/lib/resource/schema/BaseResourceSchema.js.map +1 -0
  211. package/lib/resource/schema/ResourceActionType.js +6 -4
  212. package/lib/resource/schema/ResourceActionType.js.map +1 -0
  213. package/lib/resource/schema/ResourceStatus.js +5 -3
  214. package/lib/resource/schema/ResourceStatus.js.map +1 -0
  215. package/lib/resource/schema/ResourceType.js +5 -3
  216. package/lib/resource/schema/ResourceType.js.map +1 -0
  217. package/lib/resource/schema/index.js +6 -0
  218. package/lib/resource/schema/index.js.map +1 -0
  219. package/lib/resource/schema/types.js +16 -20
  220. package/lib/resource/schema/types.js.map +1 -0
  221. package/lib/s3/formatS3Url.js +65 -0
  222. package/lib/s3/formatS3Url.js.map +1 -0
  223. package/lib/s3/formatS3Url.test.js +262 -0
  224. package/lib/s3/index.js +3 -0
  225. package/lib/s3/index.js.map +1 -0
  226. package/lib/s3/parseS3Url.js +65 -0
  227. package/lib/s3/parseS3Url.js.map +1 -0
  228. package/lib/s3/parseS3Url.test.js +270 -0
  229. package/lib/schema/SchemaRegistry.js +45 -0
  230. package/lib/schema/SchemaRegistry.js.map +1 -0
  231. package/lib/schema/SchemaRegistry.mod.js +2 -0
  232. package/lib/schema/TypeSchema.d.js +2 -0
  233. package/lib/schema/TypeSchema.d.js.map +1 -0
  234. package/lib/schema/createSchemaData.js +26 -125
  235. package/lib/schema/createSchemaData.js.map +1 -0
  236. package/lib/schema/findJsonSchemaByPath.js +13 -36
  237. package/lib/schema/findJsonSchemaByPath.js.map +1 -0
  238. package/lib/schema/formatZodError.js +140 -0
  239. package/lib/schema/formatZodError.js.map +1 -0
  240. package/lib/schema/formatZodError.test.js +196 -0
  241. package/lib/schema/getSchemaCache.js +5 -5
  242. package/lib/schema/getSchemaCache.js.map +1 -0
  243. package/lib/schema/getSchemaOptions.js +8 -11
  244. package/lib/schema/getSchemaOptions.js.map +1 -0
  245. package/lib/schema/index.js +2 -0
  246. package/lib/schema/index.js.map +1 -0
  247. package/lib/schema/toJsonSchema.js +47 -290
  248. package/lib/schema/toJsonSchema.js.map +1 -0
  249. package/lib/schema/validate.js +33 -45
  250. package/lib/schema/validate.js.map +1 -0
  251. package/lib/tools/generateSchema.js +39 -197
  252. package/lib/tools/generateSchema.js.map +1 -0
  253. package/lib/tools/renderJsonSchemaToMarkdownDoc.js +55 -143
  254. package/lib/tools/renderJsonSchemaToMarkdownDoc.js.map +1 -0
  255. package/lib/utils/buildBaseUrl.js +13 -0
  256. package/lib/utils/buildBaseUrl.js.map +1 -0
  257. package/lib/utils/buildRedactorFormSchema.js +59 -0
  258. package/lib/utils/buildRedactorFormSchema.js.map +1 -0
  259. package/lib/utils/getEstimateProcessTime.js +21 -0
  260. package/lib/utils/getEstimateProcessTime.js.map +1 -0
  261. package/lib/utils/index.js +4 -0
  262. package/lib/utils/index.js.map +1 -0
  263. package/lib/utils/resolveFeatureOptions.js +12 -0
  264. package/lib/utils/resolveFeatureOptions.js.map +1 -0
  265. package/package.json +77 -20
  266. package/src/ai/qwen3vl/index.ts +1 -0
  267. package/src/ai/qwen3vl/utils.ts +36 -0
  268. package/src/ai/vision/DocLayoutElementTypeSchema.ts +30 -0
  269. package/src/ai/vision/ImageAnnotationSchema.ts +60 -0
  270. package/src/ai/vision/index.ts +2 -0
  271. package/src/ai/vision/resolveImageAnnotation.ts +135 -0
  272. package/src/cn/ChineseResidentIdNo.test.ts +18 -0
  273. package/src/cn/ChineseResidentIdNo.ts +74 -0
  274. package/src/cn/DivisionCode.test.ts +3 -13
  275. package/src/cn/DivisionCode.ts +138 -193
  276. package/src/cn/{Mod11Checksum.ts → Mod11.ts} +3 -1
  277. package/src/cn/{Mod31Checksum.ts → Mod31.ts} +2 -0
  278. package/src/cn/UnifiedSocialCreditCode.test.ts +2 -2
  279. package/src/cn/UnifiedSocialCreditCode.ts +119 -124
  280. package/src/cn/__snapshots__/ChineseResidentIdNo.test.ts.snap +14 -0
  281. package/src/cn/__snapshots__/UnifiedSocialCreditCode.test.ts.snap +41 -12
  282. package/src/cn/formatChineseAmount.ts +61 -0
  283. package/src/cn/index.ts +6 -2
  284. package/src/cn/parseChineseNumber.test.ts +159 -0
  285. package/src/cn/parseChineseNumber.ts +97 -0
  286. package/src/cn/pinyin/cartesianProduct.test.ts +64 -0
  287. package/src/cn/pinyin/cartesianProduct.ts +24 -0
  288. package/src/cn/pinyin/data.json +23573 -0
  289. package/src/cn/pinyin/loader.ts +12 -0
  290. package/src/cn/pinyin/preload.ts +3 -0
  291. package/src/cn/pinyin/toPinyin.test.ts +12 -0
  292. package/src/cn/pinyin/toPinyinPure.ts +43 -0
  293. package/src/cn/pinyin/transform.ts +12 -0
  294. package/src/consola/formatLogObject.test.ts +27 -0
  295. package/src/consola/formatLogObject.ts +46 -10
  296. package/src/dayjs/dayjs.ts +40 -0
  297. package/src/dayjs/formatDuration.test.ts +14 -0
  298. package/src/dayjs/formatDuration.ts +86 -0
  299. package/src/dayjs/index.ts +5 -0
  300. package/src/dayjs/parseDuration.ts +40 -0
  301. package/src/dayjs/parseRelativeTime.test.ts +185 -0
  302. package/src/dayjs/parseRelativeTime.ts +115 -0
  303. package/src/dayjs/resolveRelativeTime.test.ts +357 -0
  304. package/src/dayjs/resolveRelativeTime.ts +167 -0
  305. package/src/decimal/index.ts +1 -0
  306. package/src/decimal/parseDecimal.ts +16 -0
  307. package/src/emittery/emitter.ts +9 -0
  308. package/src/emittery/index.ts +1 -0
  309. package/src/foundation/schema/SexType.ts +21 -0
  310. package/src/foundation/schema/index.ts +1 -0
  311. package/src/foundation/schema/parseSexType.ts +19 -0
  312. package/src/foundation/schema/types.ts +8 -0
  313. package/src/fs/FileSystemError.ts +26 -0
  314. package/src/fs/IFileSystem.d.ts +102 -0
  315. package/src/fs/MemoryFileSystem.test.ts +37 -0
  316. package/src/fs/createBrowserFileSystem.ts +291 -0
  317. package/src/fs/createMemoryFileSystem.ts +604 -0
  318. package/src/fs/createSandboxFileSystem.ts +136 -0
  319. package/src/fs/createWebDavFileSystem.ts +172 -0
  320. package/src/fs/findMimeType.ts +23 -0
  321. package/src/fs/index.ts +8 -0
  322. package/src/fs/orpc/FileSystemContract.ts +92 -0
  323. package/src/fs/orpc/createContractClientFileSystem.ts +115 -0
  324. package/src/fs/orpc/index.ts +2 -0
  325. package/src/fs/orpc/server/createFileSystemContractImpl.ts +64 -0
  326. package/src/fs/orpc/server/index.ts +1 -0
  327. package/src/fs/s3/createS3MiniFileSystem.ts +830 -0
  328. package/src/fs/s3/index.ts +1 -0
  329. package/src/fs/s3/s3mini.test.ts +264 -0
  330. package/src/fs/scandir.ts +75 -0
  331. package/src/fs/server/createDatabaseFileSystem.ts +668 -0
  332. package/src/fs/server/createNodeFileSystem.ts +499 -0
  333. package/src/fs/server/dbfs.test.ts +47 -0
  334. package/src/fs/server/index.ts +1 -0
  335. package/src/fs/server/loadTestDatabase.ts +131 -0
  336. package/src/fs/tests/runFileSystemTest.ts +288 -0
  337. package/src/fs/types.ts +29 -0
  338. package/src/fs/utils/getFileUrl.ts +44 -0
  339. package/src/fs/utils.ts +23 -0
  340. package/src/jsonschema/JsonSchema.ts +118 -110
  341. package/src/jsonschema/forEachJsonSchema.ts +50 -0
  342. package/src/jsonschema/index.ts +1 -0
  343. package/src/orpc/createOpenApiContractClient.ts +52 -0
  344. package/src/orpc/createRpcContractClient.ts +50 -0
  345. package/src/orpc/index.ts +2 -0
  346. package/src/orpc/resolveLinkPlugins.ts +29 -0
  347. package/src/password/PHC.ts +3 -3
  348. package/src/password/Password.test.ts +1 -1
  349. package/src/password/Password.ts +2 -2
  350. package/src/password/createPBKDF2PasswordAlgorithm.ts +2 -2
  351. package/src/resource/ListQuery.ts +53 -0
  352. package/src/resource/index.ts +1 -0
  353. package/src/resource/schema/AnyResourceSchema.ts +17 -3
  354. package/src/resource/schema/index.ts +5 -0
  355. package/src/s3/formatS3Url.test.ts +254 -0
  356. package/src/s3/formatS3Url.ts +84 -0
  357. package/src/s3/index.ts +2 -0
  358. package/src/s3/parseS3Url.test.ts +258 -0
  359. package/src/s3/parseS3Url.ts +88 -0
  360. package/src/schema/SchemaRegistry.ts +48 -0
  361. package/src/schema/createSchemaData.ts +1 -1
  362. package/src/schema/formatZodError.test.ts +196 -0
  363. package/src/schema/formatZodError.ts +151 -0
  364. package/src/schema/getSchemaOptions.ts +3 -3
  365. package/src/schema/index.ts +1 -0
  366. package/src/utils/buildBaseUrl.ts +12 -0
  367. package/src/utils/buildRedactorFormSchema.ts +85 -0
  368. package/src/utils/getEstimateProcessTime.ts +36 -0
  369. package/src/utils/index.ts +5 -0
  370. package/src/utils/resolveFeatureOptions.ts +14 -0
  371. package/lib/cn/Mod11Checksum.js +0 -85
  372. package/lib/cn/Mod31Checksum.js +0 -97
  373. package/lib/cn/ResidentIdentityCardNumber.js +0 -50
  374. package/lib/cn/formatDate.js +0 -13
  375. package/lib/cn/parseSex.js +0 -20
  376. package/lib/resource/schema/SchemaRegistry.js +0 -38
  377. package/lib/resource/schema/SexType.js +0 -10
  378. package/lib/search/AdvanceSearch.js +0 -9
  379. package/lib/search/AdvanceSearch.test.js +0 -435
  380. package/lib/search/formatAdvanceSearch.js +0 -78
  381. package/lib/search/index.js +0 -1
  382. package/lib/search/optimizeAdvanceSearch.js +0 -143
  383. package/lib/search/parseAdvanceSearch.js +0 -20
  384. package/lib/search/parser.d.js +0 -1
  385. package/lib/search/parser.js +0 -3088
  386. package/lib/search/types.d.js +0 -1
  387. package/src/cn/ResidentIdentityCardNumber.test.ts +0 -17
  388. package/src/cn/ResidentIdentityCardNumber.ts +0 -96
  389. package/src/cn/__snapshots__/ResidentIdentityCardNumber.test.ts.snap +0 -15
  390. package/src/cn/formatDate.ts +0 -12
  391. package/src/cn/parseSex.ts +0 -13
  392. package/src/resource/schema/SchemaRegistry.ts +0 -42
  393. package/src/resource/schema/SexType.ts +0 -13
  394. package/src/search/AdvanceSearch.test.ts +0 -149
  395. package/src/search/AdvanceSearch.ts +0 -14
  396. package/src/search/Makefile +0 -2
  397. package/src/search/__snapshots__/AdvanceSearch.test.ts.snap +0 -675
  398. package/src/search/formatAdvanceSearch.ts +0 -52
  399. package/src/search/index.ts +0 -1
  400. package/src/search/optimizeAdvanceSearch.ts +0 -77
  401. package/src/search/parseAdvanceSearch.ts +0 -23
  402. package/src/search/parser.d.ts +0 -8
  403. package/src/search/parser.js +0 -2794
  404. package/src/search/parser.peggy +0 -237
  405. package/src/search/types.d.ts +0 -45
@@ -0,0 +1,830 @@
1
+ import { basename, dirname, normalize } from 'node:path';
2
+ import { Readable } from 'node:stream';
3
+ import { formatS3Url, parseS3Url, type ParseS3UrlOptions } from '@wener/common/s3';
4
+ import { S3mini, sanitizeETag } from 's3mini';
5
+ import type {
6
+ CopyOptions,
7
+ CreateReadStreamOptions,
8
+ CreateWriteStreamOptions,
9
+ IFileStat,
10
+ IFileSystem,
11
+ MkdirOptions,
12
+ ReaddirOptions,
13
+ ReadFileOptions,
14
+ RenameOptions,
15
+ RmOptions,
16
+ StatOptions,
17
+ WriteFileOptions,
18
+ } from '../IFileSystem';
19
+
20
+ type CreateS3MiniFileSystemOptions = ParseS3UrlOptions & {
21
+ client?: S3mini;
22
+ /**
23
+ * Optional prefix to scope all operations within a specific folder in the bucket.
24
+ * All file operations will be relative to this prefix.
25
+ * Should not include leading or trailing slashes.
26
+ * Example: 'data/uploads' or 'backups/2024'
27
+ */
28
+ prefix?: string;
29
+ };
30
+
31
+ export function createS3MiniFileSystem(options: CreateS3MiniFileSystemOptions = {}): S3FS {
32
+ const parsed = parseS3Url(options);
33
+ if (!parsed) {
34
+ throw new Error('S3 URL or connection options are required');
35
+ }
36
+
37
+ const { client, prefix } = options;
38
+ if (!client && (!parsed.endpoint || !parsed.bucket)) {
39
+ throw new Error('S3 endpoint and bucket are required when client is not provided');
40
+ }
41
+
42
+ let s3mini: S3mini;
43
+ let bucket: string;
44
+
45
+ if (client) {
46
+ s3mini = client;
47
+ bucket = client.bucketName || parsed.bucket || '';
48
+ } else {
49
+ bucket = parsed.bucket || '';
50
+
51
+ // Construct full endpoint URL with bucket for S3mini
52
+ const endpointUrl = formatS3Url(parsed, {
53
+ credentials: false,
54
+ useParams: false,
55
+ });
56
+
57
+ s3mini = new S3mini({
58
+ accessKeyId: parsed.accessKeyId || '',
59
+ secretAccessKey: parsed.secretAccessKey || '',
60
+ endpoint: endpointUrl,
61
+ region: parsed.region,
62
+ });
63
+ }
64
+
65
+ // Normalize prefix: remove leading/trailing slashes, ensure it ends with / when not empty
66
+ const normalizedPrefix = prefix ? prefix.replace(/^\/+/, '').replace(/\/+$/, '') : '';
67
+
68
+ return new S3FS(s3mini, bucket, normalizedPrefix);
69
+ }
70
+
71
+ class S3FS implements IFileSystem {
72
+ constructor(
73
+ readonly client: S3mini,
74
+ private readonly bucket: string,
75
+ private readonly prefix: string = '',
76
+ ) {}
77
+
78
+ /**
79
+ * Normalize path to S3 key format (remove leading slash, handle relative paths)
80
+ * and prepend the prefix if one is set
81
+ */
82
+ private normalizeKey(path: string): string {
83
+ if (!path || path === '/') {
84
+ return this.prefix;
85
+ }
86
+ const normalized = normalize(path).replace(/^\/+/, '').replace(/\\/g, '/');
87
+
88
+ // Prepend prefix if set
89
+ if (this.prefix) {
90
+ return this.prefix + '/' + normalized;
91
+ }
92
+ return normalized;
93
+ }
94
+
95
+ /**
96
+ * Remove prefix from S3 key to get the file system path
97
+ */
98
+ private stripPrefix(key: string): string {
99
+ if (!this.prefix || !key.startsWith(this.prefix + '/')) {
100
+ // If key doesn't start with prefix, return as-is (shouldn't happen normally)
101
+ return key.startsWith('/') ? key : '/' + key;
102
+ }
103
+ const withoutPrefix = key.slice(this.prefix.length);
104
+ return withoutPrefix || '/';
105
+ }
106
+
107
+ /**
108
+ * Convert S3 key back to file system path (strip prefix and add leading slash)
109
+ */
110
+ private keyToPath(key: string): string {
111
+ if (!key) {
112
+ return '/';
113
+ }
114
+ return this.stripPrefix(key);
115
+ }
116
+
117
+ /**
118
+ * Get directory path from a key
119
+ */
120
+ private getDirectory(key: string): string {
121
+ if (!key) {
122
+ return '/';
123
+ }
124
+ const dir = dirname(key).replace(/\\/g, '/');
125
+ return dir === '.' ? '/' : '/' + dir;
126
+ }
127
+
128
+ /**
129
+ * Check if a key represents a directory (ends with /)
130
+ */
131
+ private isDirectoryKey(key: string): boolean {
132
+ return key.endsWith('/');
133
+ }
134
+
135
+ private toFileStat(
136
+ key: string,
137
+ obj: { Key: string; Size: number | string; LastModified?: Date | string; ETag?: string },
138
+ ): IFileStat {
139
+ const isDir = this.isDirectoryKey(key);
140
+ const path = this.keyToPath(key);
141
+ const directory = this.getDirectory(key);
142
+ const size = typeof obj.Size === 'string' ? parseInt(obj.Size, 10) || 0 : obj.Size || 0;
143
+ const mtime = obj.LastModified ? new Date(obj.LastModified).getTime() : Date.now();
144
+
145
+ return {
146
+ directory,
147
+ path,
148
+ name: isDir ? basename(key.slice(0, -1)) || basename(directory) || '/' : basename(key),
149
+ kind: isDir ? 'directory' : 'file',
150
+ mtime,
151
+ size,
152
+ meta: obj.ETag ? { etag: sanitizeETag(obj.ETag) } : {},
153
+ };
154
+ }
155
+
156
+ private checkAborted(signal?: AbortSignal): void {
157
+ if (signal?.aborted) {
158
+ throw new Error('The operation was aborted');
159
+ }
160
+ }
161
+
162
+ async readdir(dir: string, options: ReaddirOptions = {}): Promise<IFileStat[]> {
163
+ const { glob, recursive, depth = 1, kind, hidden = true, signal } = options;
164
+ this.checkAborted(signal);
165
+
166
+ const dirPrefix = this.normalizeKey(dir);
167
+ const prefixWithSlash = dirPrefix ? (dirPrefix.endsWith('/') ? dirPrefix : dirPrefix + '/') : '';
168
+
169
+ try {
170
+ // When delimiter is '/', S3 returns:
171
+ // - Files: Key without trailing /, Size > 0
172
+ // - Directories: Key with trailing /, Size: 0
173
+ const delimiter = recursive ? undefined : '/';
174
+
175
+ const objects = await this.client.listObjects(delimiter ?? '', prefixWithSlash, undefined, {
176
+ delimiter,
177
+ signal,
178
+ });
179
+
180
+ if (!objects || objects.length === 0) {
181
+ return [];
182
+ }
183
+
184
+ let results: IFileStat[] = [];
185
+ const seenPaths = new Set<string>();
186
+
187
+ for (const obj of objects) {
188
+ this.checkAborted(signal);
189
+
190
+ const key = obj.Key || '';
191
+ if (!key) continue;
192
+
193
+ // Skip if not under our prefix
194
+ if (prefixWithSlash && !key.startsWith(prefixWithSlash)) {
195
+ continue;
196
+ }
197
+
198
+ // Strip the prefix from the key for relative path calculation
199
+ const relativeKey = prefixWithSlash ? key.slice(prefixWithSlash.length) : key;
200
+
201
+ // Skip empty relative keys (the directory itself)
202
+ if (!relativeKey || relativeKey === '/') {
203
+ continue;
204
+ }
205
+
206
+ // Directory: Key ends with '/' (from CommonPrefixes or explicit marker)
207
+ const isDir = key.endsWith('/');
208
+
209
+ // For non-recursive, only show immediate children
210
+ if (!recursive && depth === 1) {
211
+ // For directories, the relative key looks like "dirname/"
212
+ // For files, the relative key looks like "filename"
213
+ // We only want immediate children, so no more slashes in the middle
214
+ const keyWithoutTrailingSlash = relativeKey.replace(/\/$/, '');
215
+ if (keyWithoutTrailingSlash.includes('/')) {
216
+ // This is deeper than one level, skip
217
+ continue;
218
+ }
219
+ }
220
+
221
+ // Deduplicate by path
222
+ const pathKey = key.replace(/\/$/, ''); // Normalize for deduplication
223
+ if (seenPaths.has(pathKey)) {
224
+ continue;
225
+ }
226
+ seenPaths.add(pathKey);
227
+
228
+ const stat = this.toFileStat(key, {
229
+ Key: key,
230
+ Size: obj.Size,
231
+ LastModified: obj.LastModified,
232
+ ETag: obj.ETag,
233
+ });
234
+
235
+ // Filter by hidden
236
+ if (!hidden && stat.name.startsWith('.')) {
237
+ continue;
238
+ }
239
+
240
+ // Filter by kind
241
+ if (kind && stat.kind !== kind) {
242
+ continue;
243
+ }
244
+
245
+ results.push(stat);
246
+ }
247
+
248
+ // Handle recursive with depth > 1
249
+ if (!recursive && depth > 1) {
250
+ const subdirs = results.filter((entry) => entry.kind === 'directory');
251
+ for (const subdir of subdirs) {
252
+ this.checkAborted(signal);
253
+ const maxDepth = depth - 1;
254
+ if (maxDepth > 0) {
255
+ const subEntries = await this.readdir(subdir.path, {
256
+ ...options,
257
+ depth: maxDepth,
258
+ });
259
+ results = [...results, ...subEntries];
260
+ }
261
+ }
262
+ }
263
+
264
+ // Handle glob filtering
265
+ if (glob) {
266
+ const { matcher } = await import('micromatch');
267
+ const match = matcher(glob);
268
+ results = results.filter((entry) => match(entry.path));
269
+ }
270
+
271
+ return results;
272
+ } catch (error: any) {
273
+ if (error.code === 'NoSuchKey' || error.message?.includes('404')) {
274
+ throw new Error(`Directory not found: ${dir}`);
275
+ }
276
+ throw error;
277
+ }
278
+ }
279
+
280
+ async stat(entry: string, options: StatOptions = {}): Promise<IFileStat> {
281
+ const { signal } = options;
282
+ this.checkAborted(signal);
283
+
284
+ const key = this.normalizeKey(entry);
285
+ if (!key) {
286
+ // Root directory
287
+ return {
288
+ directory: '/',
289
+ path: '/',
290
+ name: '/',
291
+ kind: 'directory',
292
+ mtime: Date.now(),
293
+ size: 0,
294
+ meta: {},
295
+ };
296
+ }
297
+
298
+ try {
299
+ // Try to check if object exists and get metadata
300
+ const exists = await this.client.objectExists(key, { signal });
301
+ if (exists === true) {
302
+ // Get ETag and size
303
+ const etag = await this.client.getEtag(key, { signal });
304
+ const size = await this.client.getContentLength(key);
305
+
306
+ // Get LastModified from listing (since objectExists doesn't return it)
307
+ const objects = await this.client.listObjects('/', key, 1, { delimiter: '/', signal });
308
+ const obj = objects?.[0];
309
+
310
+ return this.toFileStat(key, {
311
+ Key: key,
312
+ Size: size,
313
+ LastModified: obj?.LastModified || new Date(),
314
+ ETag: etag || undefined,
315
+ });
316
+ }
317
+
318
+ // If object not found, try checking if it's a directory (prefix listing)
319
+ const dirKey = key.endsWith('/') ? key : key + '/';
320
+ const objects = await this.client.listObjects('/', dirKey, 1, { delimiter: '/', signal });
321
+
322
+ if (objects && objects.length > 0) {
323
+ // It's a directory
324
+ return {
325
+ directory: this.getDirectory(key),
326
+ path: this.keyToPath(key),
327
+ name: basename(key.replace(/\/$/, '')) || '/',
328
+ kind: 'directory',
329
+ mtime: Date.now(),
330
+ size: 0,
331
+ meta: {},
332
+ };
333
+ }
334
+
335
+ throw new Error(`File not found: ${entry}`);
336
+ } catch (error: any) {
337
+ if (error.message?.includes('not found') || error.code === 'NoSuchKey') {
338
+ throw new Error(`File not found: ${entry}`);
339
+ }
340
+ throw error;
341
+ }
342
+ }
343
+
344
+ async mkdir(path: string, options: MkdirOptions = {}): Promise<void> {
345
+ const { recursive = false, signal } = options;
346
+ this.checkAborted(signal);
347
+
348
+ // In S3, directories don't actually exist - they're just prefixes
349
+ // Optionally create a marker object (empty object with trailing slash)
350
+ const key = this.normalizeKey(path);
351
+ if (!key) {
352
+ return; // Root directory, nothing to do
353
+ }
354
+
355
+ // Ensure it ends with / to indicate directory
356
+ const dirKey = key.endsWith('/') ? key : key + '/';
357
+
358
+ // Try to create a marker object (0-byte object)
359
+ try {
360
+ await this.client.putObject(dirKey, '', 'application/x-directory', undefined, undefined);
361
+ } catch (error: any) {
362
+ // If it already exists or we don't have permission, that's okay for mkdir
363
+ if (!error.message?.includes('409') && !error.message?.includes('403')) {
364
+ throw error;
365
+ }
366
+ }
367
+ }
368
+
369
+ async readFile(path: string, options?: ReadFileOptions & { encoding: 'text' }): Promise<string>;
370
+ async readFile(path: string, options?: ReadFileOptions): Promise<Uint8Array>;
371
+ async readFile(path: string, options: ReadFileOptions = {}): Promise<string | Uint8Array> {
372
+ const { encoding = 'binary', signal, onDownloadProgress } = options;
373
+ this.checkAborted(signal);
374
+
375
+ const key = this.normalizeKey(path);
376
+ if (!key) {
377
+ throw new Error('Cannot read root directory');
378
+ }
379
+
380
+ try {
381
+ // Use getObjectArrayBuffer for binary data
382
+ const data = await this.client.getObjectArrayBuffer(key, { signal });
383
+
384
+ if (!data) {
385
+ throw new Error(`File not found: ${path}`);
386
+ }
387
+
388
+ // Handle progress reporting if needed
389
+ if (onDownloadProgress) {
390
+ onDownloadProgress({ loaded: data.byteLength, total: data.byteLength });
391
+ }
392
+
393
+ if (encoding === 'text') {
394
+ return new TextDecoder().decode(data);
395
+ }
396
+
397
+ return new Uint8Array(data);
398
+ } catch (error: any) {
399
+ if (error.code === 'NoSuchKey' || error.message?.includes('404')) {
400
+ throw new Error(`File not found: ${path}`);
401
+ }
402
+ throw error;
403
+ }
404
+ }
405
+
406
+ async writeFile(
407
+ path: string,
408
+ data: string | Buffer | ArrayBuffer | Readable | ArrayBufferView,
409
+ options: WriteFileOptions = {},
410
+ ): Promise<void> {
411
+ const { signal, overwrite = true, onUploadProgress } = options;
412
+ this.checkAborted(signal);
413
+
414
+ const key = this.normalizeKey(path);
415
+ if (!key) {
416
+ throw new Error('Cannot write to root directory');
417
+ }
418
+
419
+ // Check if file exists and overwrite is false
420
+ if (!overwrite) {
421
+ const exists = await this.exists(path);
422
+ if (exists) {
423
+ throw new Error(`File already exists: ${path}`);
424
+ }
425
+ }
426
+
427
+ // Convert data to buffer or string
428
+ let body: string | Buffer;
429
+ if (data instanceof Readable) {
430
+ // For streams, we need to read them into a buffer
431
+ const chunks: Buffer[] = [];
432
+ let loaded = 0;
433
+
434
+ if (onUploadProgress) {
435
+ data.on('data', (chunk: Buffer) => {
436
+ chunks.push(chunk);
437
+ loaded += chunk.length;
438
+ onUploadProgress({ loaded, total: -1 });
439
+ });
440
+ } else {
441
+ data.on('data', (chunk: Buffer) => {
442
+ chunks.push(chunk);
443
+ });
444
+ }
445
+
446
+ body = await new Promise<Buffer>((resolve, reject) => {
447
+ const allChunks: Buffer[] = [];
448
+ data.on('data', (chunk) => allChunks.push(chunk));
449
+ data.on('end', () => resolve(Buffer.concat(allChunks)));
450
+ data.on('error', reject);
451
+
452
+ if (signal) {
453
+ signal.addEventListener('abort', () => {
454
+ data.destroy();
455
+ reject(new Error('The operation was aborted'));
456
+ });
457
+ }
458
+ });
459
+ } else if (data instanceof ArrayBuffer) {
460
+ body = Buffer.from(data);
461
+ } else if (data instanceof Buffer) {
462
+ body = data;
463
+ } else if (typeof data === 'string') {
464
+ body = data;
465
+ } else {
466
+ // ArrayBufferView
467
+ body = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
468
+ }
469
+
470
+ try {
471
+ await this.client.putObject(key, body, undefined, undefined, undefined);
472
+ } catch (error: any) {
473
+ throw error;
474
+ }
475
+ }
476
+
477
+ async rm(path: string, options: RmOptions = {}): Promise<void> {
478
+ const { recursive = false, force = false, signal } = options;
479
+ this.checkAborted(signal);
480
+
481
+ const key = this.normalizeKey(path);
482
+ if (!key) {
483
+ throw new Error('Cannot remove root directory');
484
+ }
485
+
486
+ try {
487
+ if (recursive) {
488
+ // List all objects with this prefix (no delimiter = recursive)
489
+ const prefix = key.endsWith('/') ? key : key + '/';
490
+ const objects = await this.client.listObjects('', prefix, undefined, { signal });
491
+
492
+ if (objects) {
493
+ // Delete all objects
494
+ const keys = objects.map((obj) => obj.Key || '').filter(Boolean);
495
+ if (keys.length > 0) {
496
+ await this.client.deleteObjects(keys);
497
+ }
498
+ }
499
+
500
+ // Also delete the marker object if it exists
501
+ const markerKey = prefix;
502
+ try {
503
+ await this.client.deleteObject(markerKey);
504
+ } catch {
505
+ // Ignore if marker doesn't exist
506
+ }
507
+ } else {
508
+ // Delete single object
509
+ await this.client.deleteObject(key);
510
+ }
511
+ } catch (error: any) {
512
+ if (force && (error.code === 'NoSuchKey' || error.message?.includes('404'))) {
513
+ return;
514
+ }
515
+ throw error;
516
+ }
517
+ }
518
+
519
+ async rename(oldPath: string, newPath: string, options: RenameOptions = {}): Promise<void> {
520
+ const { signal, overwrite = false } = options;
521
+ this.checkAborted(signal);
522
+
523
+ const oldKey = this.normalizeKey(oldPath);
524
+ const newKey = this.normalizeKey(newPath);
525
+
526
+ if (!oldKey) {
527
+ throw new Error('Cannot rename root directory');
528
+ }
529
+
530
+ // Check if target exists and overwrite is false
531
+ if (!overwrite) {
532
+ const exists = await this.exists(newPath);
533
+ if (exists) {
534
+ throw new Error(`Destination already exists: ${newPath}`);
535
+ }
536
+ }
537
+
538
+ try {
539
+ // Check if it's a directory (has objects with prefix)
540
+ const isDir = oldKey.endsWith('/');
541
+ const prefix = isDir ? oldKey : oldKey + '/';
542
+
543
+ const objects = await this.client.listObjects('', prefix, undefined, { signal });
544
+
545
+ if (objects && objects.length > 0) {
546
+ // It's a directory or has multiple objects, move all
547
+ const newPrefix = newKey.endsWith('/') ? newKey : newKey + '/';
548
+
549
+ // Move all objects
550
+ await Promise.all(
551
+ objects.map(async (obj) => {
552
+ const objKey = obj.Key || '';
553
+ if (!objKey) return;
554
+
555
+ const relativeKey = objKey.slice(prefix.length);
556
+ const newObjKey = newPrefix + relativeKey;
557
+
558
+ // Move object (copy + delete)
559
+ await this.client.moveObject(objKey, newObjKey);
560
+ }),
561
+ );
562
+
563
+ // Move marker object if it exists
564
+ try {
565
+ await this.client.moveObject(prefix, newPrefix);
566
+ } catch {
567
+ // Ignore if marker doesn't exist
568
+ }
569
+ } else {
570
+ // Single file - use moveObject
571
+ await this.client.moveObject(oldKey, newKey);
572
+ }
573
+ } catch (error: any) {
574
+ if (error.code === 'NoSuchKey' || error.message?.includes('404')) {
575
+ throw new Error(`Source file not found: ${oldPath}`);
576
+ }
577
+ throw error;
578
+ }
579
+ }
580
+
581
+ async exists(path: string): Promise<boolean> {
582
+ try {
583
+ const key = this.normalizeKey(path);
584
+ if (!key) {
585
+ return true; // Root always exists
586
+ }
587
+
588
+ const exists = await this.client.objectExists(key);
589
+ if (exists === true) {
590
+ return true;
591
+ }
592
+
593
+ // Check if it's a directory
594
+ const dirKey = key.endsWith('/') ? key : key + '/';
595
+ const objects = await this.client.listObjects('/', dirKey, 1, { delimiter: '/' });
596
+ return objects !== null && objects.length > 0;
597
+ } catch {
598
+ return false;
599
+ }
600
+ }
601
+
602
+ async copy(src: string, dest: string, options: CopyOptions = {}): Promise<void> {
603
+ const { signal, overwrite = true, shallow = false } = options;
604
+ this.checkAborted(signal);
605
+
606
+ const srcKey = this.normalizeKey(src);
607
+ const destKey = this.normalizeKey(dest);
608
+
609
+ if (!srcKey) {
610
+ throw new Error('Cannot copy root directory');
611
+ }
612
+
613
+ // Check if source exists
614
+ try {
615
+ const srcStat = await this.stat(src);
616
+
617
+ // Check if destination exists and overwrite is false
618
+ if (!overwrite) {
619
+ const exists = await this.exists(dest);
620
+ if (exists) {
621
+ throw new Error(`Destination already exists: ${dest}`);
622
+ }
623
+ }
624
+
625
+ if (srcStat.kind === 'directory') {
626
+ // Copy directory recursively
627
+ const srcPrefix = srcKey.endsWith('/') ? srcKey : srcKey + '/';
628
+ const destPrefix = destKey.endsWith('/') ? destKey : destKey + '/';
629
+
630
+ const objects = await this.client.listObjects(shallow ? '/' : '', srcPrefix, undefined, {
631
+ ...(shallow ? { delimiter: '/' } : {}),
632
+ signal,
633
+ });
634
+
635
+ if (objects) {
636
+ // Copy all objects
637
+ await Promise.all(
638
+ objects.map(async (obj) => {
639
+ const objKey = obj.Key || '';
640
+ if (!objKey) return;
641
+
642
+ const relativeKey = objKey.slice(srcPrefix.length);
643
+ const newObjKey = destPrefix + relativeKey;
644
+
645
+ await this.client.copyObject(objKey, newObjKey);
646
+ }),
647
+ );
648
+ }
649
+
650
+ // Copy marker object if it exists
651
+ try {
652
+ await this.client.copyObject(srcPrefix, destPrefix);
653
+ } catch {
654
+ // Ignore if marker doesn't exist
655
+ }
656
+ } else {
657
+ // Copy single file
658
+ await this.client.copyObject(srcKey, destKey);
659
+ }
660
+ } catch (error: any) {
661
+ if (error.code === 'NoSuchKey' || error.message?.includes('404')) {
662
+ throw new Error(`Source file not found: ${src}`);
663
+ }
664
+ throw error;
665
+ }
666
+ }
667
+
668
+ createReadStream(path: string, options: CreateReadStreamOptions = {}): Readable {
669
+ const key = this.normalizeKey(path);
670
+ if (!key) {
671
+ throw new Error('Cannot read root directory');
672
+ }
673
+
674
+ const { range, signal } = options;
675
+
676
+ // Use getObjectRaw with range support
677
+ const responsePromise = this.client.getObjectRaw(key, !range, range?.start, range?.end, { signal }, undefined);
678
+
679
+ // Convert Response to Readable stream
680
+ let nodeStream: Readable | null = null;
681
+
682
+ const stream = new Readable({
683
+ async read() {
684
+ if (!nodeStream) {
685
+ try {
686
+ const response = await responsePromise;
687
+ if (!response.body) {
688
+ this.emit('error', new Error('No response body'));
689
+ return;
690
+ }
691
+
692
+ // Convert ReadableStream to Node Readable
693
+ const reader = response.body.getReader();
694
+ const decoder = new TextDecoder();
695
+
696
+ nodeStream = new Readable({
697
+ async read() {
698
+ try {
699
+ const { done, value } = await reader.read();
700
+ if (done) {
701
+ this.push(null);
702
+ } else {
703
+ this.push(Buffer.from(value));
704
+ }
705
+ } catch (err) {
706
+ this.emit('error', err);
707
+ }
708
+ },
709
+ });
710
+
711
+ nodeStream.on('data', (chunk) => {
712
+ this.push(chunk);
713
+ });
714
+
715
+ nodeStream.on('end', () => {
716
+ this.push(null);
717
+ });
718
+
719
+ nodeStream.on('error', (err) => {
720
+ this.emit('error', err);
721
+ });
722
+ } catch (err: any) {
723
+ this.emit('error', err);
724
+ }
725
+ }
726
+ },
727
+ });
728
+
729
+ signal?.addEventListener('abort', () => {
730
+ stream.destroy(new Error('The operation was aborted'));
731
+ });
732
+
733
+ return stream;
734
+ }
735
+
736
+ createReadableStream(path: string, options: CreateReadStreamOptions = {}): ReadableStream {
737
+ const key = this.normalizeKey(path);
738
+ if (!key) {
739
+ throw new Error('Cannot read root directory');
740
+ }
741
+
742
+ const { range, signal } = options;
743
+
744
+ // Use getObjectRaw which returns a Response with ReadableStream
745
+ const responsePromise = this.client.getObjectRaw(key, !range, range?.start, range?.end, { signal }, undefined);
746
+
747
+ return new ReadableStream({
748
+ async start(controller) {
749
+ try {
750
+ const response = await responsePromise;
751
+ if (!response.body) {
752
+ controller.error(new Error('No response body'));
753
+ return;
754
+ }
755
+
756
+ const reader = response.body.getReader();
757
+
758
+ signal?.addEventListener('abort', () => {
759
+ reader.cancel(new Error('The operation was aborted'));
760
+ controller.error(new Error('The operation was aborted'));
761
+ });
762
+
763
+ while (true) {
764
+ const { done, value } = await reader.read();
765
+ if (done) {
766
+ controller.close();
767
+ break;
768
+ }
769
+ controller.enqueue(value);
770
+ }
771
+ } catch (err) {
772
+ controller.error(err);
773
+ }
774
+ },
775
+ });
776
+ }
777
+
778
+ createWritableStream(path: string, options: CreateWriteStreamOptions = {}): WritableStream {
779
+ const key = this.normalizeKey(path);
780
+ if (!key) {
781
+ throw new Error('Cannot write to root directory');
782
+ }
783
+
784
+ const { signal, overwrite = true } = options;
785
+ this.checkAborted(signal);
786
+
787
+ // Create a WritableStream that buffers data and uploads when done
788
+ const buffer: Uint8Array[] = [];
789
+ let controller: WritableStreamDefaultController;
790
+ const client = this.client;
791
+ const checkAborted = this.checkAborted.bind(this);
792
+
793
+ return new WritableStream({
794
+ start(ctrl) {
795
+ controller = ctrl;
796
+ },
797
+ async write(chunk) {
798
+ buffer.push(chunk);
799
+ },
800
+ async close() {
801
+ try {
802
+ checkAborted(signal);
803
+ const data = Buffer.concat(buffer.map((chunk) => Buffer.from(chunk)));
804
+ await client.putObject(key, data, undefined, undefined, undefined);
805
+ // Controller closes automatically when close() completes successfully
806
+ } catch (error) {
807
+ controller.error(error);
808
+ }
809
+ },
810
+ abort(reason) {
811
+ buffer.length = 0;
812
+ controller.error(reason);
813
+ },
814
+ });
815
+ }
816
+
817
+ getUrl(path: IFileStat | string, options?: any): string | undefined {
818
+ if (typeof path === 'object' && path?.kind !== 'file') {
819
+ return;
820
+ }
821
+ const key = typeof path === 'string' ? this.normalizeKey(path) : this.normalizeKey(path.path);
822
+ if (!key) {
823
+ return;
824
+ }
825
+
826
+ // Construct URL from endpoint - S3mini doesn't provide presigned URLs in basic API
827
+ // This is a fallback - real implementation would need presigned URLs
828
+ return undefined;
829
+ }
830
+ }