@wener/common 2.0.2 → 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 (351) 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 +21 -14
  14. package/lib/cn/ChineseResidentIdNo.js.map +1 -0
  15. package/lib/cn/ChineseResidentIdNo.test.js +1 -1
  16. package/lib/cn/DivisionCode.js +30 -25
  17. package/lib/cn/DivisionCode.js.map +1 -0
  18. package/lib/cn/DivisionCode.test.js +1 -1
  19. package/lib/cn/Mod11.js +38 -81
  20. package/lib/cn/Mod11.js.map +1 -0
  21. package/lib/cn/Mod31.js +41 -90
  22. package/lib/cn/Mod31.js.map +1 -0
  23. package/lib/cn/UnifiedSocialCreditCode.js +43 -34
  24. package/lib/cn/UnifiedSocialCreditCode.js.map +1 -0
  25. package/lib/cn/UnifiedSocialCreditCode.test.js +1 -1
  26. package/lib/cn/formatChineseAmount.js +77 -0
  27. package/lib/cn/formatChineseAmount.js.map +1 -0
  28. package/lib/cn/index.js +7 -1
  29. package/lib/cn/index.js.map +1 -0
  30. package/lib/cn/parseChineseNumber.js +94 -0
  31. package/lib/cn/parseChineseNumber.js.map +1 -0
  32. package/lib/cn/parseChineseNumber.test.js +278 -0
  33. package/lib/cn/pinyin/cartesianProduct.js +22 -0
  34. package/lib/cn/pinyin/cartesianProduct.js.map +1 -0
  35. package/lib/cn/pinyin/cartesianProduct.test.js +179 -0
  36. package/lib/cn/pinyin/data.json +23573 -0
  37. package/lib/cn/pinyin/loader.js +14 -0
  38. package/lib/cn/pinyin/loader.js.map +1 -0
  39. package/lib/cn/pinyin/preload.js +3 -0
  40. package/lib/cn/pinyin/preload.js.map +1 -0
  41. package/lib/cn/pinyin/toPinyin.test.js +167 -0
  42. package/lib/cn/pinyin/toPinyinPure.js +33 -0
  43. package/lib/cn/pinyin/toPinyinPure.js.map +1 -0
  44. package/lib/cn/pinyin/transform.js +14 -0
  45. package/lib/cn/pinyin/transform.js.map +1 -0
  46. package/lib/cn/types.d.js +2 -0
  47. package/lib/cn/types.d.js.map +1 -0
  48. package/lib/consola/createStandardConsolaReporter.js +6 -6
  49. package/lib/consola/createStandardConsolaReporter.js.map +1 -0
  50. package/lib/consola/formatLogObject.js +65 -145
  51. package/lib/consola/formatLogObject.js.map +1 -0
  52. package/lib/consola/formatLogObject.test.js +184 -0
  53. package/lib/consola/index.js +1 -0
  54. package/lib/consola/index.js.map +1 -0
  55. package/lib/data/formatSort.js +6 -5
  56. package/lib/data/formatSort.js.map +1 -0
  57. package/lib/data/index.js +1 -0
  58. package/lib/data/index.js.map +1 -0
  59. package/lib/data/maybeNumber.js +5 -7
  60. package/lib/data/maybeNumber.js.map +1 -0
  61. package/lib/data/parseSort.js +22 -28
  62. package/lib/data/parseSort.js.map +1 -0
  63. package/lib/data/resolvePagination.js +13 -17
  64. package/lib/data/resolvePagination.js.map +1 -0
  65. package/lib/data/types.d.js +2 -0
  66. package/lib/data/types.d.js.map +1 -0
  67. package/lib/dayjs/dayjs.js +21 -19
  68. package/lib/dayjs/dayjs.js.map +1 -0
  69. package/lib/dayjs/formatDuration.js +15 -14
  70. package/lib/dayjs/formatDuration.js.map +1 -0
  71. package/lib/dayjs/index.js +2 -0
  72. package/lib/dayjs/index.js.map +1 -0
  73. package/lib/dayjs/parseDuration.js +5 -8
  74. package/lib/dayjs/parseDuration.js.map +1 -0
  75. package/lib/dayjs/parseRelativeTime.js +90 -0
  76. package/lib/dayjs/parseRelativeTime.js.map +1 -0
  77. package/lib/dayjs/parseRelativeTime.test.js +247 -0
  78. package/lib/dayjs/resolveRelativeTime.js +158 -0
  79. package/lib/dayjs/resolveRelativeTime.js.map +1 -0
  80. package/lib/dayjs/resolveRelativeTime.test.js +310 -0
  81. package/lib/decimal/index.js +1 -0
  82. package/lib/decimal/index.js.map +1 -0
  83. package/lib/decimal/parseDecimal.js +3 -1
  84. package/lib/decimal/parseDecimal.js.map +1 -0
  85. package/lib/emittery/emitter.js +10 -0
  86. package/lib/emittery/emitter.js.map +1 -0
  87. package/lib/emittery/index.js +2 -0
  88. package/lib/emittery/index.js.map +1 -0
  89. package/lib/foundation/schema/SexType.js +5 -3
  90. package/lib/foundation/schema/SexType.js.map +1 -0
  91. package/lib/foundation/schema/index.js +1 -0
  92. package/lib/foundation/schema/index.js.map +1 -0
  93. package/lib/foundation/schema/parseSexType.js +1 -0
  94. package/lib/foundation/schema/parseSexType.js.map +1 -0
  95. package/lib/foundation/schema/types.js +4 -2
  96. package/lib/foundation/schema/types.js.map +1 -0
  97. package/lib/fs/FileSystemError.js +23 -0
  98. package/lib/fs/FileSystemError.js.map +1 -0
  99. package/lib/fs/IFileSystem.d.js +3 -0
  100. package/lib/fs/IFileSystem.d.js.map +1 -0
  101. package/lib/fs/MemoryFileSystem.test.js +188 -0
  102. package/lib/fs/createBrowserFileSystem.js +248 -0
  103. package/lib/fs/createBrowserFileSystem.js.map +1 -0
  104. package/lib/fs/createMemoryFileSystem.js +516 -0
  105. package/lib/fs/createMemoryFileSystem.js.map +1 -0
  106. package/lib/fs/createSandboxFileSystem.js +108 -0
  107. package/lib/fs/createSandboxFileSystem.js.map +1 -0
  108. package/lib/fs/createWebDavFileSystem.js +137 -0
  109. package/lib/fs/createWebDavFileSystem.js.map +1 -0
  110. package/lib/fs/findMimeType.js +17 -0
  111. package/lib/fs/findMimeType.js.map +1 -0
  112. package/lib/fs/index.js +8 -0
  113. package/lib/fs/index.js.map +1 -0
  114. package/lib/fs/orpc/FileSystemContract.js +93 -0
  115. package/lib/fs/orpc/FileSystemContract.js.map +1 -0
  116. package/lib/fs/orpc/createContractClientFileSystem.js +93 -0
  117. package/lib/fs/orpc/createContractClientFileSystem.js.map +1 -0
  118. package/lib/fs/orpc/index.js +3 -0
  119. package/lib/fs/orpc/index.js.map +1 -0
  120. package/lib/fs/orpc/server/createFileSystemContractImpl.js +63 -0
  121. package/lib/fs/orpc/server/createFileSystemContractImpl.js.map +1 -0
  122. package/lib/fs/orpc/server/index.js +2 -0
  123. package/lib/fs/orpc/server/index.js.map +1 -0
  124. package/lib/fs/s3/createS3MiniFileSystem.js +705 -0
  125. package/lib/fs/s3/createS3MiniFileSystem.js.map +1 -0
  126. package/lib/fs/s3/index.js +2 -0
  127. package/lib/fs/s3/index.js.map +1 -0
  128. package/lib/fs/s3/s3mini.test.js +584 -0
  129. package/lib/fs/scandir.js +59 -0
  130. package/lib/fs/scandir.js.map +1 -0
  131. package/lib/fs/server/createDatabaseFileSystem.js +750 -0
  132. package/lib/fs/server/createDatabaseFileSystem.js.map +1 -0
  133. package/lib/fs/server/createNodeFileSystem.js +401 -0
  134. package/lib/fs/server/createNodeFileSystem.js.map +1 -0
  135. package/lib/fs/server/dbfs.test.js +221 -0
  136. package/lib/fs/server/index.js +2 -0
  137. package/lib/fs/server/index.js.map +1 -0
  138. package/lib/fs/server/loadTestDatabase.js +127 -0
  139. package/lib/fs/server/loadTestDatabase.js.map +1 -0
  140. package/lib/fs/tests/runFileSystemTest.js +318 -0
  141. package/lib/fs/tests/runFileSystemTest.js.map +1 -0
  142. package/lib/fs/types.js +27 -0
  143. package/lib/fs/types.js.map +1 -0
  144. package/lib/fs/utils/getFileUrl.js +35 -0
  145. package/lib/fs/utils/getFileUrl.js.map +1 -0
  146. package/lib/fs/utils.js +22 -0
  147. package/lib/fs/utils.js.map +1 -0
  148. package/lib/index.js +1 -0
  149. package/lib/index.js.map +1 -0
  150. package/lib/jsonschema/JsonSchema.js +146 -172
  151. package/lib/jsonschema/JsonSchema.js.map +1 -0
  152. package/lib/jsonschema/forEachJsonSchema.js +44 -0
  153. package/lib/jsonschema/forEachJsonSchema.js.map +1 -0
  154. package/lib/jsonschema/index.js +2 -0
  155. package/lib/jsonschema/index.js.map +1 -0
  156. package/lib/jsonschema/types.d.js +2 -0
  157. package/lib/jsonschema/types.d.js.map +1 -0
  158. package/lib/meta/defineFileType.js +20 -103
  159. package/lib/meta/defineFileType.js.map +1 -0
  160. package/lib/meta/defineInit.js +31 -250
  161. package/lib/meta/defineInit.js.map +1 -0
  162. package/lib/meta/defineMetadata.js +24 -140
  163. package/lib/meta/defineMetadata.js.map +1 -0
  164. package/lib/meta/index.js +1 -0
  165. package/lib/meta/index.js.map +1 -0
  166. package/lib/orpc/createOpenApiContractClient.js +27 -0
  167. package/lib/orpc/createOpenApiContractClient.js.map +1 -0
  168. package/lib/orpc/createRpcContractClient.js +34 -0
  169. package/lib/orpc/createRpcContractClient.js.map +1 -0
  170. package/lib/orpc/index.js +3 -0
  171. package/lib/orpc/index.js.map +1 -0
  172. package/lib/orpc/resolveLinkPlugins.js +28 -0
  173. package/lib/orpc/resolveLinkPlugins.js.map +1 -0
  174. package/lib/password/PHC.js +63 -87
  175. package/lib/password/PHC.js.map +1 -0
  176. package/lib/password/PHC.test.js +11 -3
  177. package/lib/password/Password.js +29 -294
  178. package/lib/password/Password.js.map +1 -0
  179. package/lib/password/Password.test.js +35 -22
  180. package/lib/password/createArgon2PasswordAlgorithm.js +35 -191
  181. package/lib/password/createArgon2PasswordAlgorithm.js.map +1 -0
  182. package/lib/password/createBase64PasswordAlgorithm.js +8 -141
  183. package/lib/password/createBase64PasswordAlgorithm.js.map +1 -0
  184. package/lib/password/createBcryptPasswordAlgorithm.js +13 -168
  185. package/lib/password/createBcryptPasswordAlgorithm.js.map +1 -0
  186. package/lib/password/createPBKDF2PasswordAlgorithm.js +46 -228
  187. package/lib/password/createPBKDF2PasswordAlgorithm.js.map +1 -0
  188. package/lib/password/createScryptPasswordAlgorithm.js +55 -211
  189. package/lib/password/createScryptPasswordAlgorithm.js.map +1 -0
  190. package/lib/password/index.js +1 -0
  191. package/lib/password/index.js.map +1 -0
  192. package/lib/password/server/index.js +1 -0
  193. package/lib/password/server/index.js.map +1 -0
  194. package/lib/resource/Identifiable.js +2 -0
  195. package/lib/resource/Identifiable.js.map +1 -0
  196. package/lib/resource/ListQuery.js +21 -93
  197. package/lib/resource/ListQuery.js.map +1 -0
  198. package/lib/resource/getTitleOfResource.js +3 -5
  199. package/lib/resource/getTitleOfResource.js.map +1 -0
  200. package/lib/resource/index.js +1 -0
  201. package/lib/resource/index.js.map +1 -0
  202. package/lib/resource/schema/AnyResourceSchema.js +2 -1
  203. package/lib/resource/schema/AnyResourceSchema.js.map +1 -0
  204. package/lib/resource/schema/BaseResourceSchema.js +2 -1
  205. package/lib/resource/schema/BaseResourceSchema.js.map +1 -0
  206. package/lib/resource/schema/ResourceActionType.js +6 -4
  207. package/lib/resource/schema/ResourceActionType.js.map +1 -0
  208. package/lib/resource/schema/ResourceStatus.js +5 -3
  209. package/lib/resource/schema/ResourceStatus.js.map +1 -0
  210. package/lib/resource/schema/ResourceType.js +5 -3
  211. package/lib/resource/schema/ResourceType.js.map +1 -0
  212. package/lib/resource/schema/index.js +1 -0
  213. package/lib/resource/schema/index.js.map +1 -0
  214. package/lib/resource/schema/types.js +16 -20
  215. package/lib/resource/schema/types.js.map +1 -0
  216. package/lib/s3/formatS3Url.js +65 -0
  217. package/lib/s3/formatS3Url.js.map +1 -0
  218. package/lib/s3/formatS3Url.test.js +262 -0
  219. package/lib/s3/index.js +3 -0
  220. package/lib/s3/index.js.map +1 -0
  221. package/lib/s3/parseS3Url.js +65 -0
  222. package/lib/s3/parseS3Url.js.map +1 -0
  223. package/lib/s3/parseS3Url.test.js +270 -0
  224. package/lib/schema/SchemaRegistry.js +38 -38
  225. package/lib/schema/SchemaRegistry.js.map +1 -0
  226. package/lib/schema/TypeSchema.d.js +2 -0
  227. package/lib/schema/TypeSchema.d.js.map +1 -0
  228. package/lib/schema/createSchemaData.js +26 -125
  229. package/lib/schema/createSchemaData.js.map +1 -0
  230. package/lib/schema/findJsonSchemaByPath.js +13 -36
  231. package/lib/schema/findJsonSchemaByPath.js.map +1 -0
  232. package/lib/schema/formatZodError.js +140 -0
  233. package/lib/schema/formatZodError.js.map +1 -0
  234. package/lib/schema/formatZodError.test.js +196 -0
  235. package/lib/schema/getSchemaCache.js +5 -5
  236. package/lib/schema/getSchemaCache.js.map +1 -0
  237. package/lib/schema/getSchemaOptions.js +8 -11
  238. package/lib/schema/getSchemaOptions.js.map +1 -0
  239. package/lib/schema/index.js +2 -1
  240. package/lib/schema/index.js.map +1 -0
  241. package/lib/schema/toJsonSchema.js +47 -290
  242. package/lib/schema/toJsonSchema.js.map +1 -0
  243. package/lib/schema/validate.js +33 -45
  244. package/lib/schema/validate.js.map +1 -0
  245. package/lib/tools/generateSchema.js +39 -197
  246. package/lib/tools/generateSchema.js.map +1 -0
  247. package/lib/tools/renderJsonSchemaToMarkdownDoc.js +55 -143
  248. package/lib/tools/renderJsonSchemaToMarkdownDoc.js.map +1 -0
  249. package/lib/utils/buildBaseUrl.js +13 -0
  250. package/lib/utils/buildBaseUrl.js.map +1 -0
  251. package/lib/utils/buildRedactorFormSchema.js +59 -0
  252. package/lib/utils/buildRedactorFormSchema.js.map +1 -0
  253. package/lib/utils/getEstimateProcessTime.js +12 -11
  254. package/lib/utils/getEstimateProcessTime.js.map +1 -0
  255. package/lib/utils/index.js +3 -0
  256. package/lib/utils/index.js.map +1 -0
  257. package/lib/utils/resolveFeatureOptions.js +12 -0
  258. package/lib/utils/resolveFeatureOptions.js.map +1 -0
  259. package/package.json +61 -13
  260. package/src/ai/qwen3vl/index.ts +1 -0
  261. package/src/ai/qwen3vl/utils.ts +36 -0
  262. package/src/ai/vision/DocLayoutElementTypeSchema.ts +30 -0
  263. package/src/ai/vision/ImageAnnotationSchema.ts +60 -0
  264. package/src/ai/vision/index.ts +2 -0
  265. package/src/ai/vision/resolveImageAnnotation.ts +135 -0
  266. package/src/cn/ChineseResidentIdNo.test.ts +1 -1
  267. package/src/cn/ChineseResidentIdNo.ts +8 -0
  268. package/src/cn/DivisionCode.test.ts +1 -1
  269. package/src/cn/DivisionCode.ts +8 -0
  270. package/src/cn/UnifiedSocialCreditCode.test.ts +1 -1
  271. package/src/cn/UnifiedSocialCreditCode.ts +15 -0
  272. package/src/cn/__snapshots__/UnifiedSocialCreditCode.test.ts.snap +23 -0
  273. package/src/cn/formatChineseAmount.ts +61 -0
  274. package/src/cn/index.ts +7 -1
  275. package/src/cn/parseChineseNumber.test.ts +159 -0
  276. package/src/cn/parseChineseNumber.ts +97 -0
  277. package/src/cn/pinyin/cartesianProduct.test.ts +64 -0
  278. package/src/cn/pinyin/cartesianProduct.ts +24 -0
  279. package/src/cn/pinyin/data.json +23573 -0
  280. package/src/cn/pinyin/loader.ts +12 -0
  281. package/src/cn/pinyin/preload.ts +3 -0
  282. package/src/cn/pinyin/toPinyin.test.ts +12 -0
  283. package/src/cn/pinyin/toPinyinPure.ts +43 -0
  284. package/src/cn/pinyin/transform.ts +12 -0
  285. package/src/consola/formatLogObject.test.ts +27 -0
  286. package/src/consola/formatLogObject.ts +34 -6
  287. package/src/dayjs/dayjs.ts +18 -18
  288. package/src/dayjs/index.ts +3 -1
  289. package/src/dayjs/parseRelativeTime.test.ts +185 -0
  290. package/src/dayjs/parseRelativeTime.ts +115 -0
  291. package/src/dayjs/resolveRelativeTime.test.ts +357 -0
  292. package/src/dayjs/resolveRelativeTime.ts +167 -0
  293. package/src/emittery/emitter.ts +9 -0
  294. package/src/emittery/index.ts +1 -0
  295. package/src/fs/FileSystemError.ts +26 -0
  296. package/src/fs/IFileSystem.d.ts +102 -0
  297. package/src/fs/MemoryFileSystem.test.ts +37 -0
  298. package/src/fs/createBrowserFileSystem.ts +291 -0
  299. package/src/fs/createMemoryFileSystem.ts +604 -0
  300. package/src/fs/createSandboxFileSystem.ts +136 -0
  301. package/src/fs/createWebDavFileSystem.ts +172 -0
  302. package/src/fs/findMimeType.ts +23 -0
  303. package/src/fs/index.ts +8 -0
  304. package/src/fs/orpc/FileSystemContract.ts +92 -0
  305. package/src/fs/orpc/createContractClientFileSystem.ts +115 -0
  306. package/src/fs/orpc/index.ts +2 -0
  307. package/src/fs/orpc/server/createFileSystemContractImpl.ts +64 -0
  308. package/src/fs/orpc/server/index.ts +1 -0
  309. package/src/fs/s3/createS3MiniFileSystem.ts +830 -0
  310. package/src/fs/s3/index.ts +1 -0
  311. package/src/fs/s3/s3mini.test.ts +264 -0
  312. package/src/fs/scandir.ts +75 -0
  313. package/src/fs/server/createDatabaseFileSystem.ts +668 -0
  314. package/src/fs/server/createNodeFileSystem.ts +499 -0
  315. package/src/fs/server/dbfs.test.ts +47 -0
  316. package/src/fs/server/index.ts +1 -0
  317. package/src/fs/server/loadTestDatabase.ts +131 -0
  318. package/src/fs/tests/runFileSystemTest.ts +288 -0
  319. package/src/fs/types.ts +29 -0
  320. package/src/fs/utils/getFileUrl.ts +44 -0
  321. package/src/fs/utils.ts +23 -0
  322. package/src/jsonschema/JsonSchema.ts +118 -110
  323. package/src/jsonschema/forEachJsonSchema.ts +50 -0
  324. package/src/jsonschema/index.ts +1 -0
  325. package/src/orpc/createOpenApiContractClient.ts +52 -0
  326. package/src/orpc/createRpcContractClient.ts +50 -0
  327. package/src/orpc/index.ts +2 -0
  328. package/src/orpc/resolveLinkPlugins.ts +29 -0
  329. package/src/password/PHC.ts +3 -3
  330. package/src/password/Password.test.ts +1 -1
  331. package/src/password/createPBKDF2PasswordAlgorithm.ts +2 -2
  332. package/src/resource/schema/AnyResourceSchema.ts +16 -2
  333. package/src/s3/formatS3Url.test.ts +254 -0
  334. package/src/s3/formatS3Url.ts +84 -0
  335. package/src/s3/index.ts +2 -0
  336. package/src/s3/parseS3Url.test.ts +258 -0
  337. package/src/s3/parseS3Url.ts +88 -0
  338. package/src/schema/SchemaRegistry.ts +35 -33
  339. package/src/schema/formatZodError.test.ts +196 -0
  340. package/src/schema/formatZodError.ts +151 -0
  341. package/src/schema/getSchemaOptions.ts +2 -2
  342. package/src/schema/index.ts +1 -1
  343. package/src/utils/buildBaseUrl.ts +12 -0
  344. package/src/utils/buildRedactorFormSchema.ts +85 -0
  345. package/src/utils/index.ts +4 -0
  346. package/src/utils/resolveFeatureOptions.ts +14 -0
  347. package/src/cn/ChineseResidentIdNo.mod.ts +0 -7
  348. package/src/cn/DivisionCode.mod.ts +0 -7
  349. package/src/cn/UnifiedSocialCreditCode.mod.ts +0 -7
  350. package/src/cn/mod.ts +0 -3
  351. package/src/schema/SchemaRegistry.mod.ts +0 -1
@@ -0,0 +1,258 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { parseS3Url } from './parseS3Url';
3
+
4
+ describe('parseS3Url', () => {
5
+ beforeEach(() => {
6
+ vi.restoreAllMocks();
7
+ });
8
+
9
+ it('should return undefined for empty URL', () => {
10
+ const result = parseS3Url({ url: '' });
11
+ expect(result).toBeUndefined();
12
+ });
13
+
14
+ it('should return undefined for undefined URL', () => {
15
+ const result = parseS3Url({ url: undefined });
16
+ expect(result).toBeUndefined();
17
+ });
18
+
19
+ it('should parse basic HTTPS URL', () => {
20
+ const result = parseS3Url({ url: 'https://s3.amazonaws.com/bucket-name' });
21
+ expect(result).toEqual({
22
+ accessKeyId: undefined,
23
+ secretAccessKey: undefined,
24
+ endpoint: 's3.amazonaws.com',
25
+ port: 443,
26
+ useSsl: true,
27
+ region: undefined,
28
+ pathStyle: true,
29
+ bucket: 'bucket-name',
30
+ });
31
+ });
32
+
33
+ it('should parse basic HTTP URL', () => {
34
+ const result = parseS3Url({ url: 'http://s3.amazonaws.com/bucket-name' });
35
+ expect(result).toEqual({
36
+ accessKeyId: undefined,
37
+ secretAccessKey: undefined,
38
+ endpoint: 's3.amazonaws.com',
39
+ port: 80,
40
+ useSsl: false,
41
+ region: undefined,
42
+ pathStyle: true,
43
+ bucket: 'bucket-name',
44
+ });
45
+ });
46
+
47
+ it('should parse S3 protocol URL', () => {
48
+ const result = parseS3Url({ url: 's3://s3.amazonaws.com/bucket-name' });
49
+ expect(result).toEqual({
50
+ accessKeyId: undefined,
51
+ secretAccessKey: undefined,
52
+ endpoint: 's3.amazonaws.com',
53
+ port: 443,
54
+ useSsl: true,
55
+ region: undefined,
56
+ pathStyle: true,
57
+ bucket: 'bucket-name',
58
+ });
59
+ });
60
+
61
+ it('should parse URL with credentials', () => {
62
+ const result = parseS3Url({ url: 'https://user:pass@s3.amazonaws.com/bucket-name' });
63
+ expect(result).toEqual({
64
+ accessKeyId: 'user',
65
+ secretAccessKey: 'pass',
66
+ endpoint: 's3.amazonaws.com',
67
+ port: 443,
68
+ useSsl: true,
69
+ region: undefined,
70
+ pathStyle: true,
71
+ bucket: 'bucket-name',
72
+ });
73
+ });
74
+
75
+ it('should parse URL with port', () => {
76
+ const result = parseS3Url({ url: 'https://s3.amazonaws.com:9000/bucket-name' });
77
+ expect(result).toEqual({
78
+ accessKeyId: undefined,
79
+ secretAccessKey: undefined,
80
+ endpoint: 's3.amazonaws.com',
81
+ port: 9000,
82
+ useSsl: true,
83
+ region: undefined,
84
+ pathStyle: true,
85
+ bucket: 'bucket-name',
86
+ });
87
+ });
88
+
89
+ it('should parse URL with region query parameter', () => {
90
+ const result = parseS3Url({ url: 'https://s3.amazonaws.com/bucket-name?region=us-east-1' });
91
+ expect(result).toEqual({
92
+ accessKeyId: undefined,
93
+ secretAccessKey: undefined,
94
+ endpoint: 's3.amazonaws.com',
95
+ port: 443,
96
+ useSsl: true,
97
+ region: 'us-east-1',
98
+ pathStyle: true,
99
+ bucket: 'bucket-name',
100
+ });
101
+ });
102
+
103
+ it('should parse URL with pathStyle parameter', () => {
104
+ const result = parseS3Url({ url: 'https://s3.amazonaws.com/bucket-name?pathStyle=true' });
105
+ expect(result).toEqual({
106
+ accessKeyId: undefined,
107
+ secretAccessKey: undefined,
108
+ endpoint: 's3.amazonaws.com',
109
+ port: 443,
110
+ useSsl: true,
111
+ region: undefined,
112
+ pathStyle: true,
113
+ bucket: 'bucket-name',
114
+ });
115
+ });
116
+
117
+ it('should parse URL with pathStyle=false parameter', () => {
118
+ const result = parseS3Url({ url: 'https://s3.amazonaws.com/bucket-name?pathStyle=false' });
119
+ expect(result).toEqual({
120
+ accessKeyId: undefined,
121
+ secretAccessKey: undefined,
122
+ endpoint: 's3.amazonaws.com',
123
+ port: 443,
124
+ useSsl: true,
125
+ region: undefined,
126
+ pathStyle: false,
127
+ bucket: 'bucket-name',
128
+ });
129
+ });
130
+
131
+ it('should detect path style for IP addresses', () => {
132
+ const result = parseS3Url({ url: 'https://192.168.1.1/bucket-name' });
133
+ expect(result).toEqual({
134
+ accessKeyId: undefined,
135
+ secretAccessKey: undefined,
136
+ endpoint: '192.168.1.1',
137
+ port: 443,
138
+ useSsl: true,
139
+ region: undefined,
140
+ pathStyle: true,
141
+ bucket: 'bucket-name',
142
+ });
143
+ });
144
+
145
+ it('should detect path style for IPv6 addresses', () => {
146
+ const result = parseS3Url({ url: 'https://[2001:db8::1]/bucket-name' });
147
+ expect(result).toEqual({
148
+ accessKeyId: undefined,
149
+ secretAccessKey: undefined,
150
+ endpoint: '[2001:db8::1]',
151
+ port: 443,
152
+ useSsl: true,
153
+ region: undefined,
154
+ pathStyle: true,
155
+ bucket: 'bucket-name',
156
+ });
157
+ });
158
+
159
+ it('should parse AWS virtual hosted style URL', () => {
160
+ const result = parseS3Url({ url: 'https://my-bucket.s3.us-east-1.amazonaws.com' });
161
+ expect(result).toEqual({
162
+ accessKeyId: undefined,
163
+ secretAccessKey: undefined,
164
+ endpoint: 'my-bucket.s3.us-east-1.amazonaws.com',
165
+ port: 443,
166
+ useSsl: true,
167
+ region: 'us-east-1',
168
+ pathStyle: false,
169
+ bucket: 'my-bucket',
170
+ });
171
+ });
172
+
173
+ it('should parse AWS virtual hosted style URL with custom endpoint', () => {
174
+ const result = parseS3Url({ url: 'https://my-bucket.s3.amazonaws.com' });
175
+ expect(result).toEqual({
176
+ accessKeyId: undefined,
177
+ secretAccessKey: undefined,
178
+ endpoint: 'my-bucket.s3.amazonaws.com',
179
+ port: 443,
180
+ useSsl: true,
181
+ region: undefined,
182
+ pathStyle: false,
183
+ bucket: 'my-bucket',
184
+ });
185
+ });
186
+
187
+ it('should override parameters with provided options', () => {
188
+ const result = parseS3Url({
189
+ url: 'https://s3.amazonaws.com/bucket-name',
190
+ region: 'us-west-2',
191
+ pathStyle: false,
192
+ });
193
+ expect(result).toEqual({
194
+ accessKeyId: undefined,
195
+ secretAccessKey: undefined,
196
+ endpoint: 's3.amazonaws.com',
197
+ port: 443,
198
+ useSsl: true,
199
+ region: 'us-west-2',
200
+ pathStyle: false,
201
+ bucket: 'bucket-name',
202
+ });
203
+ });
204
+
205
+ it('should use S3_URL environment variable by default', () => {
206
+ vi.stubEnv('S3_URL', 'https://env-bucket.s3.amazonaws.com');
207
+ const result = parseS3Url();
208
+ expect(result).toEqual({
209
+ accessKeyId: undefined,
210
+ secretAccessKey: undefined,
211
+ endpoint: 'env-bucket.s3.amazonaws.com',
212
+ port: 443,
213
+ useSsl: true,
214
+ region: undefined,
215
+ pathStyle: false,
216
+ bucket: 'env-bucket',
217
+ });
218
+ });
219
+
220
+ it('should throw error for invalid URL', () => {
221
+ expect(() => parseS3Url({ url: 'invalid-url' })).toThrow('Invalid S3 URL: invalid-url');
222
+ });
223
+
224
+ it('should handle complex URL with multiple parameters', () => {
225
+ const result = parseS3Url({
226
+ url: 'https://user:pass@custom-s3.com:9000/my-bucket?region=us-east-1&pathStyle=true',
227
+ });
228
+ expect(result).toEqual({
229
+ accessKeyId: 'user',
230
+ secretAccessKey: 'pass',
231
+ endpoint: 'custom-s3.com',
232
+ port: 9000,
233
+ useSsl: true,
234
+ region: 'us-east-1',
235
+ pathStyle: true,
236
+ bucket: 'my-bucket',
237
+ });
238
+ });
239
+
240
+ it('should ignore undefined and null values in rest parameters', () => {
241
+ const result = parseS3Url({
242
+ url: 'https://s3.amazonaws.com/bucket-name',
243
+ region: 'us-west-2',
244
+ port: undefined,
245
+ useSsl: null as any,
246
+ });
247
+ expect(result).toEqual({
248
+ accessKeyId: undefined,
249
+ secretAccessKey: undefined,
250
+ endpoint: 's3.amazonaws.com',
251
+ port: 443,
252
+ useSsl: true,
253
+ region: 'us-west-2',
254
+ pathStyle: true,
255
+ bucket: 'bucket-name',
256
+ });
257
+ });
258
+ });
@@ -0,0 +1,88 @@
1
+ import { parseBoolean } from '@wener/utils';
2
+
3
+ export type ParsedS3Options = {
4
+ accessKeyId?: string;
5
+ secretAccessKey?: string;
6
+ region?: string;
7
+ useSsl?: boolean;
8
+ bucket?: string;
9
+ endpoint: string;
10
+ port?: number;
11
+ pathStyle?: boolean;
12
+ url?: string;
13
+ };
14
+
15
+ export type ParseS3UrlOptions = Partial<ParsedS3Options>;
16
+
17
+ function isValidIpAddress(hostname: string): boolean {
18
+ const ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
19
+ const ipv6Regex = /^\[([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}]$/;
20
+ return ipv4Regex.test(hostname) || ipv6Regex.test(hostname);
21
+ }
22
+
23
+ export function parseS3Url({ url = process.env.S3_URL, ...rest }: ParseS3UrlOptions = {}): ParsedS3Options | undefined {
24
+ if (!url) return undefined;
25
+
26
+ const normalizedUrl = url.startsWith('s3://') ? url.replace(/^s3:\/\//, 'https://') : url;
27
+ let parsed: URL;
28
+ try {
29
+ parsed = new URL(normalizedUrl);
30
+ } catch (error) {
31
+ throw new Error(`Invalid S3 URL: ${url}`);
32
+ }
33
+
34
+ const pathStyleParam = parsed.searchParams.get('pathStyle');
35
+ let pathStyle: boolean | undefined = rest.pathStyle;
36
+ pathStyle ??= parseBoolean(pathStyleParam, true);
37
+
38
+ // Auto-detect path style for IP addresses
39
+ if (pathStyle === undefined && isValidIpAddress(parsed.hostname)) {
40
+ pathStyle = true;
41
+ }
42
+
43
+ const result: ParsedS3Options = {
44
+ accessKeyId: parsed.username || undefined,
45
+ secretAccessKey: parsed.password || undefined,
46
+ endpoint: parsed.hostname,
47
+ port: parsed.port ? Number(parsed.port) : undefined,
48
+ useSsl: parsed.protocol === 'https:',
49
+ region: parsed.searchParams.get('region') || undefined,
50
+ pathStyle,
51
+ };
52
+
53
+ if (!result.port) {
54
+ if (result.useSsl) {
55
+ result.port = 443;
56
+ } else {
57
+ result.port = 80;
58
+ }
59
+ }
60
+
61
+ const pathSegments = parsed.pathname.split('/').filter(Boolean);
62
+ const awsVirtualHostMatch = parsed.hostname.match(/^(.*?)\.s3[.-]([a-z0-9-]+)?\.?amazonaws\.com$/);
63
+
64
+ if (result.pathStyle) {
65
+ result.bucket = pathSegments[0];
66
+ } else if (awsVirtualHostMatch) {
67
+ result.bucket = awsVirtualHostMatch[1];
68
+ result.endpoint = parsed.hostname;
69
+ if (!result.region && awsVirtualHostMatch[2]) {
70
+ result.region = awsVirtualHostMatch[2];
71
+ }
72
+ result.pathStyle = false;
73
+ } else {
74
+ result.bucket = pathSegments[0];
75
+ if (result.pathStyle === undefined) {
76
+ result.pathStyle = true;
77
+ }
78
+ }
79
+
80
+ // Type-safe property assignment
81
+ for (const [key, value] of Object.entries(rest)) {
82
+ if (value !== undefined && value !== null && key in result) {
83
+ (result as Record<string, unknown>)[key] = value;
84
+ }
85
+ }
86
+
87
+ return result;
88
+ }
@@ -2,45 +2,47 @@ import { getGlobalStates } from '@wener/utils';
2
2
  import type { JsonSchemaDef } from '../jsonschema';
3
3
  import { toJsonSchema, type SchemaOutput, type TypeSchema } from './index';
4
4
 
5
- const types = getGlobalStates('@wener/common/resource/schema/SchemaRegistry', () => new Map<string, JsonSchemaDef>());
5
+ export namespace SchemaRegistry {
6
+ const types = getGlobalStates('@wener/common/resource/schema/SchemaRegistry', () => new Map<string, JsonSchemaDef>());
6
7
 
7
- export function get(name: string): JsonSchemaDef;
8
- export function get<S extends TypeSchema>(schema: S): JsonSchemaDef<SchemaOutput<S>>;
9
- export function get(needle: TypeSchema | string) {
10
- let key = getKey(needle);
11
- let found = types.get(key);
12
- if (found) {
13
- return found;
14
- } else {
15
- if (needle && typeof needle !== 'string') {
16
- return toJsonSchema(needle);
8
+ export function get(name: string): JsonSchemaDef;
9
+ export function get<S extends TypeSchema>(schema: S): JsonSchemaDef<SchemaOutput<S>>;
10
+ export function get(needle: TypeSchema | string) {
11
+ let key = getKey(needle);
12
+ let found = types.get(key);
13
+ if (found) {
14
+ return found;
15
+ } else {
16
+ if (needle && typeof needle !== 'string') {
17
+ return toJsonSchema(needle);
18
+ }
17
19
  }
20
+ throw new Error(`Schema not found: ${key}`);
18
21
  }
19
- throw new Error(`Schema not found: ${key}`);
20
- }
21
22
 
22
- function getKey(s: TypeSchema | string) {
23
- let key;
24
- if (typeof s === 'string') {
25
- key = s;
26
- } else {
27
- let js = toJsonSchema(s);
28
- key = js.$id || js.title;
29
- }
30
- if (!key) {
31
- throw new Error(`Schema must have $id or title`);
23
+ function getKey(s: TypeSchema | string) {
24
+ let key;
25
+ if (typeof s === 'string') {
26
+ key = s;
27
+ } else {
28
+ let js = toJsonSchema(s);
29
+ key = js.$id || js.title;
30
+ }
31
+ if (!key) {
32
+ throw new Error(`Schema must have $id or title`);
33
+ }
34
+ return key;
32
35
  }
33
- return key;
34
- }
35
36
 
36
- export function set(key: TypeSchema | string, def: TypeSchema): void;
37
- export function set(key: TypeSchema, def?: TypeSchema): void;
38
- export function set(key: TypeSchema | string, def?: TypeSchema) {
39
- if (!def) {
40
- def = key as TypeSchema;
41
- if (!def || typeof def !== 'object') {
42
- throw new Error(`Invalid schema definition for: ${getKey(key)}`);
37
+ export function set(key: TypeSchema | string, def: TypeSchema): void;
38
+ export function set(key: TypeSchema, def?: TypeSchema): void;
39
+ export function set(key: TypeSchema | string, def?: TypeSchema) {
40
+ if (!def) {
41
+ def = key as TypeSchema;
42
+ if (!def || typeof def !== 'object') {
43
+ throw new Error(`Invalid schema definition for: ${getKey(key)}`);
44
+ }
43
45
  }
46
+ types.set(getKey(key), toJsonSchema(def));
44
47
  }
45
- types.set(getKey(key), toJsonSchema(def));
46
48
  }
@@ -0,0 +1,196 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { z } from 'zod/v4';
3
+ import { formatZodError } from './formatZodError';
4
+
5
+ describe('formatZodError', () => {
6
+ it('should format invalid_type error', () => {
7
+ const schema = z.object({
8
+ name: z.string(),
9
+ age: z.number(),
10
+ });
11
+
12
+ const result = schema.safeParse({ name: 123, age: 'not-a-number' });
13
+ expect(result.success).toBe(false);
14
+ if (!result.success) {
15
+ const formatted = formatZodError(result.error);
16
+ expect(formatted).toContain('name');
17
+ expect(formatted).toContain('age');
18
+ }
19
+ });
20
+
21
+ it('should format required field error', () => {
22
+ const schema = z.object({
23
+ name: z.string(),
24
+ email: z.string().email(),
25
+ });
26
+
27
+ const result = schema.safeParse({});
28
+ expect(result.success).toBe(false);
29
+ if (!result.success) {
30
+ const formatted = formatZodError(result.error);
31
+ expect(formatted).toContain('必填');
32
+ }
33
+ });
34
+
35
+ it('should format email validation error', () => {
36
+ const schema = z.object({
37
+ email: z.string().email(),
38
+ });
39
+
40
+ const result = schema.safeParse({ email: 'invalid-email' });
41
+ expect(result.success).toBe(false);
42
+ if (!result.success) {
43
+ const formatted = formatZodError(result.error);
44
+ expect(formatted).toContain('请输入有效的邮箱地址');
45
+ }
46
+ });
47
+
48
+ it('should format URL validation error', () => {
49
+ const schema = z.object({
50
+ url: z.string().url(),
51
+ });
52
+
53
+ const result = schema.safeParse({ url: 'not-a-url' });
54
+ expect(result.success).toBe(false);
55
+ if (!result.success) {
56
+ const formatted = formatZodError(result.error);
57
+ expect(formatted).toContain('请输入有效的 URL');
58
+ }
59
+ });
60
+
61
+ it('should format too_small error for string', () => {
62
+ const schema = z.object({
63
+ name: z.string().min(5),
64
+ });
65
+
66
+ const result = schema.safeParse({ name: 'abc' });
67
+ expect(result.success).toBe(false);
68
+ if (!result.success) {
69
+ const formatted = formatZodError(result.error);
70
+ expect(formatted).toContain('至少需要');
71
+ }
72
+ });
73
+
74
+ it('should format too_small error for number', () => {
75
+ const schema = z.object({
76
+ age: z.number().min(18),
77
+ });
78
+
79
+ const result = schema.safeParse({ age: 16 });
80
+ expect(result.success).toBe(false);
81
+ if (!result.success) {
82
+ const formatted = formatZodError(result.error);
83
+ expect(formatted).toContain('至少需要 18');
84
+ }
85
+ });
86
+
87
+ it('should format too_big error for string', () => {
88
+ const schema = z.object({
89
+ name: z.string().max(10),
90
+ });
91
+
92
+ const result = schema.safeParse({ name: 'this is too long' });
93
+ expect(result.success).toBe(false);
94
+ if (!result.success) {
95
+ const formatted = formatZodError(result.error);
96
+ expect(formatted).toContain('最多 10 个字符');
97
+ }
98
+ });
99
+
100
+ it('should format invalid_enum_value error', () => {
101
+ const schema = z.object({
102
+ status: z.enum(['active', 'inactive', 'pending']),
103
+ });
104
+
105
+ const result = schema.safeParse({ status: 'invalid' });
106
+ expect(result.success).toBe(false);
107
+ if (!result.success) {
108
+ const formatted = formatZodError(result.error);
109
+ expect(formatted).toContain('必须是以下值之一');
110
+ }
111
+ });
112
+
113
+ it('should format invalid_literal error', () => {
114
+ const schema = z.object({
115
+ type: z.literal('user'),
116
+ });
117
+
118
+ const result = schema.safeParse({ type: 'admin' });
119
+ expect(result.success).toBe(false);
120
+ if (!result.success) {
121
+ const formatted = formatZodError(result.error);
122
+ expect(formatted).toContain('必须是 user');
123
+ }
124
+ });
125
+
126
+ it('should format nested object errors', () => {
127
+ const schema = z.object({
128
+ user: z.object({
129
+ name: z.string(),
130
+ email: z.string().email(),
131
+ }),
132
+ });
133
+
134
+ const result = schema.safeParse({
135
+ user: {
136
+ name: '',
137
+ email: 'invalid',
138
+ },
139
+ });
140
+ expect(result.success).toBe(false);
141
+ if (!result.success) {
142
+ const formatted = formatZodError(result.error);
143
+ expect(formatted).toContain('user');
144
+ expect(formatted).toContain('email');
145
+ }
146
+ });
147
+
148
+ it('should format array errors', () => {
149
+ const schema = z.object({
150
+ tags: z.array(z.string()).min(1),
151
+ });
152
+
153
+ const result = schema.safeParse({ tags: [] });
154
+ expect(result.success).toBe(false);
155
+ if (!result.success) {
156
+ const formatted = formatZodError(result.error);
157
+ expect(formatted).toContain('至少需要 1 项');
158
+ }
159
+ });
160
+
161
+ it('should format multiple errors', () => {
162
+ const schema = z.object({
163
+ name: z.string().min(1),
164
+ email: z.string().email(),
165
+ age: z.number().min(18),
166
+ });
167
+
168
+ const result = schema.safeParse({
169
+ name: '',
170
+ email: 'invalid',
171
+ age: 15,
172
+ });
173
+ expect(result.success).toBe(false);
174
+ if (!result.success) {
175
+ const formatted = formatZodError(result.error);
176
+ // Should contain multiple error messages separated by ';'
177
+ const parts = formatted.split(';');
178
+ expect(parts.length).toBeGreaterThan(1);
179
+ }
180
+ });
181
+
182
+ it('should use schema description when available', () => {
183
+ const schema = z.object({
184
+ name: z.string().describe('姓名'),
185
+ email: z.string().email().describe('邮箱地址'),
186
+ });
187
+
188
+ const result = schema.safeParse({ name: '', email: 'invalid' });
189
+ expect(result.success).toBe(false);
190
+ if (!result.success) {
191
+ const formatted = formatZodError(result.error, schema);
192
+ expect(formatted).toContain('姓名');
193
+ expect(formatted).toContain('邮箱地址');
194
+ }
195
+ });
196
+ });