@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,84 @@
1
+ import type { ParsedS3Options } from './parseS3Url';
2
+
3
+ function isValidBucketName(bucket: string): boolean {
4
+ const bucketRegex = /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/;
5
+ return bucketRegex.test(bucket) && !bucket.includes('..') && !bucket.includes('.-') && !bucket.includes('-.');
6
+ }
7
+
8
+ export function formatS3Url(
9
+ o: ParsedS3Options,
10
+ {
11
+ credentials,
12
+ useParams,
13
+ }: {
14
+ credentials?: boolean;
15
+ useParams?: boolean;
16
+ } = {},
17
+ ): string {
18
+ if (!o || typeof o !== 'object') {
19
+ throw new Error('S3 options must be an object');
20
+ }
21
+
22
+ if (!o.endpoint) {
23
+ throw new Error('Endpoint is required');
24
+ }
25
+
26
+ let url: URL;
27
+ let ep = o.endpoint || 's3.amazonaws.com';
28
+ try {
29
+ if (URL.canParse(ep)) {
30
+ url = new URL(ep);
31
+ } else {
32
+ url = new URL(`https://${ep}`);
33
+ }
34
+ } catch (error) {
35
+ throw new Error(`Invalid endpoint: ${ep}`);
36
+ }
37
+
38
+ const { useSsl = true, region, pathStyle, port, accessKeyId, secretAccessKey, bucket } = o;
39
+ url.protocol = useSsl ? 'https:' : 'http:';
40
+
41
+ if (credentials) {
42
+ if (accessKeyId && secretAccessKey) {
43
+ url.username = encodeURIComponent(accessKeyId);
44
+ url.password = encodeURIComponent(secretAccessKey);
45
+ } else {
46
+ throw new Error('Access Key ID and Secret Access Key are required for credentials');
47
+ }
48
+ }
49
+
50
+ if (useParams) {
51
+ if (region) {
52
+ url.searchParams.set('region', region);
53
+ }
54
+ if (pathStyle !== undefined) {
55
+ url.searchParams.set('pathStyle', String(pathStyle));
56
+ }
57
+ }
58
+
59
+ if (port) {
60
+ const portNum = Number(port);
61
+ if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
62
+ throw new Error('Port must be a valid number between 1 and 65535');
63
+ }
64
+ url.port = String(portNum);
65
+ }
66
+
67
+ if (bucket) {
68
+ if (!isValidBucketName(bucket)) {
69
+ throw new Error(`Invalid bucket name: ${bucket}`);
70
+ }
71
+
72
+ if (pathStyle) {
73
+ url.pathname = `/${bucket}${url.pathname}`;
74
+ } else {
75
+ // Check if bucket is already in hostname (virtual-hosted style)
76
+ const bucketPattern = new RegExp(`^${bucket.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.`);
77
+ if (!bucketPattern.test(url.hostname)) {
78
+ url.hostname = `${bucket}.${url.hostname}`;
79
+ }
80
+ }
81
+ }
82
+
83
+ return url.toString();
84
+ }
@@ -0,0 +1,2 @@
1
+ export { parseS3Url, type ParseS3UrlOptions } from './parseS3Url';
2
+ export { formatS3Url } from './formatS3Url';
@@ -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
+ }
@@ -0,0 +1,48 @@
1
+ import { getGlobalStates } from '@wener/utils';
2
+ import type { JsonSchemaDef } from '../jsonschema';
3
+ import { toJsonSchema, type SchemaOutput, type TypeSchema } from './index';
4
+
5
+ export namespace SchemaRegistry {
6
+ const types = getGlobalStates('@wener/common/resource/schema/SchemaRegistry', () => new Map<string, JsonSchemaDef>());
7
+
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
+ }
19
+ }
20
+ throw new Error(`Schema not found: ${key}`);
21
+ }
22
+
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;
35
+ }
36
+
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
+ }
45
+ }
46
+ types.set(getKey(key), toJsonSchema(def));
47
+ }
48
+ }
@@ -59,7 +59,7 @@ function _create(schema: JsonSchemaDef, options: CreateOptions, ctx: { required:
59
59
  .with({ type: 'object' }, () => {
60
60
  const out: Record<string, any> = {};
61
61
 
62
- let required = schema.required || [];
62
+ let required = Array.isArray(schema.required) ? schema.required : [];
63
63
  for (const [k, v] of Object.entries(schema.properties || {}) as [string, JsonSchemaDef][]) {
64
64
  const value = _create(v, options, {
65
65
  required: required.includes(k),
@@ -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
+ });