alepha 0.15.1 → 0.15.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 (523) hide show
  1. package/README.md +68 -80
  2. package/dist/api/audits/index.d.ts +10 -33
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +10 -33
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +10 -3
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +10 -3
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +162 -155
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/jobs/index.js +10 -3
  13. package/dist/api/jobs/index.js.map +1 -1
  14. package/dist/api/keys/index.d.ts +413 -0
  15. package/dist/api/keys/index.d.ts.map +1 -0
  16. package/dist/api/keys/index.js +476 -0
  17. package/dist/api/keys/index.js.map +1 -0
  18. package/dist/api/notifications/index.d.ts +10 -4
  19. package/dist/api/notifications/index.d.ts.map +1 -1
  20. package/dist/api/notifications/index.js +10 -4
  21. package/dist/api/notifications/index.js.map +1 -1
  22. package/dist/api/parameters/index.d.ts +43 -50
  23. package/dist/api/parameters/index.d.ts.map +1 -1
  24. package/dist/api/parameters/index.js +30 -37
  25. package/dist/api/parameters/index.js.map +1 -1
  26. package/dist/api/users/index.d.ts +1081 -760
  27. package/dist/api/users/index.d.ts.map +1 -1
  28. package/dist/api/users/index.js +2539 -218
  29. package/dist/api/users/index.js.map +1 -1
  30. package/dist/api/verifications/index.d.ts +138 -132
  31. package/dist/api/verifications/index.d.ts.map +1 -1
  32. package/dist/api/verifications/index.js +12 -4
  33. package/dist/api/verifications/index.js.map +1 -1
  34. package/dist/batch/index.d.ts +20 -40
  35. package/dist/batch/index.d.ts.map +1 -1
  36. package/dist/batch/index.js +31 -44
  37. package/dist/batch/index.js.map +1 -1
  38. package/dist/bucket/index.d.ts +440 -8
  39. package/dist/bucket/index.d.ts.map +1 -1
  40. package/dist/bucket/index.js +1861 -12
  41. package/dist/bucket/index.js.map +1 -1
  42. package/dist/cache/core/index.d.ts +179 -7
  43. package/dist/cache/core/index.d.ts.map +1 -1
  44. package/dist/cache/core/index.js +213 -7
  45. package/dist/cache/core/index.js.map +1 -1
  46. package/dist/cache/redis/index.d.ts +1 -0
  47. package/dist/cache/redis/index.d.ts.map +1 -1
  48. package/dist/cache/redis/index.js +4 -0
  49. package/dist/cache/redis/index.js.map +1 -1
  50. package/dist/cli/index.d.ts +638 -5645
  51. package/dist/cli/index.d.ts.map +1 -1
  52. package/dist/cli/index.js +2550 -368
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/command/index.d.ts +203 -45
  55. package/dist/command/index.d.ts.map +1 -1
  56. package/dist/command/index.js +2060 -71
  57. package/dist/command/index.js.map +1 -1
  58. package/dist/core/index.browser.js +70 -40
  59. package/dist/core/index.browser.js.map +1 -1
  60. package/dist/core/index.d.ts +34 -13
  61. package/dist/core/index.d.ts.map +1 -1
  62. package/dist/core/index.js +90 -40
  63. package/dist/core/index.js.map +1 -1
  64. package/dist/core/index.native.js +70 -40
  65. package/dist/core/index.native.js.map +1 -1
  66. package/dist/datetime/index.d.ts +15 -0
  67. package/dist/datetime/index.d.ts.map +1 -1
  68. package/dist/datetime/index.js +15 -0
  69. package/dist/datetime/index.js.map +1 -1
  70. package/dist/email/index.d.ts +323 -20
  71. package/dist/email/index.d.ts.map +1 -1
  72. package/dist/email/index.js +1857 -7
  73. package/dist/email/index.js.map +1 -1
  74. package/dist/fake/index.d.ts +90 -8
  75. package/dist/fake/index.d.ts.map +1 -1
  76. package/dist/fake/index.js +91 -20
  77. package/dist/fake/index.js.map +1 -1
  78. package/dist/lock/core/index.d.ts +11 -4
  79. package/dist/lock/core/index.d.ts.map +1 -1
  80. package/dist/lock/core/index.js +11 -4
  81. package/dist/lock/core/index.js.map +1 -1
  82. package/dist/logger/index.d.ts +17 -66
  83. package/dist/logger/index.d.ts.map +1 -1
  84. package/dist/logger/index.js +14 -63
  85. package/dist/logger/index.js.map +1 -1
  86. package/dist/mcp/index.d.ts +10 -30
  87. package/dist/mcp/index.d.ts.map +1 -1
  88. package/dist/mcp/index.js +12 -35
  89. package/dist/mcp/index.js.map +1 -1
  90. package/dist/orm/index.browser.js +3 -3
  91. package/dist/orm/index.browser.js.map +1 -1
  92. package/dist/orm/index.bun.js +39 -20
  93. package/dist/orm/index.bun.js.map +1 -1
  94. package/dist/orm/index.d.ts +517 -540
  95. package/dist/orm/index.d.ts.map +1 -1
  96. package/dist/orm/index.js +58 -71
  97. package/dist/orm/index.js.map +1 -1
  98. package/dist/queue/core/index.d.ts +18 -10
  99. package/dist/queue/core/index.d.ts.map +1 -1
  100. package/dist/queue/core/index.js +14 -6
  101. package/dist/queue/core/index.js.map +1 -1
  102. package/dist/react/auth/index.browser.js +108 -0
  103. package/dist/react/auth/index.browser.js.map +1 -0
  104. package/dist/react/auth/index.d.ts +100 -0
  105. package/dist/react/auth/index.d.ts.map +1 -0
  106. package/dist/react/auth/index.js +145 -0
  107. package/dist/react/auth/index.js.map +1 -0
  108. package/dist/react/core/index.d.ts +469 -0
  109. package/dist/react/core/index.d.ts.map +1 -0
  110. package/dist/react/core/index.js +464 -0
  111. package/dist/react/core/index.js.map +1 -0
  112. package/dist/react/form/index.d.ts +232 -0
  113. package/dist/react/form/index.d.ts.map +1 -0
  114. package/dist/react/form/index.js +432 -0
  115. package/dist/react/form/index.js.map +1 -0
  116. package/dist/react/head/index.browser.js +423 -0
  117. package/dist/react/head/index.browser.js.map +1 -0
  118. package/dist/react/head/index.d.ts +288 -0
  119. package/dist/react/head/index.d.ts.map +1 -0
  120. package/dist/react/head/index.js +465 -0
  121. package/dist/react/head/index.js.map +1 -0
  122. package/dist/react/i18n/index.d.ts +175 -0
  123. package/dist/react/i18n/index.d.ts.map +1 -0
  124. package/dist/react/i18n/index.js +224 -0
  125. package/dist/react/i18n/index.js.map +1 -0
  126. package/dist/react/router/index.browser.js +1974 -0
  127. package/dist/react/router/index.browser.js.map +1 -0
  128. package/dist/react/router/index.d.ts +1956 -0
  129. package/dist/react/router/index.d.ts.map +1 -0
  130. package/dist/react/router/index.js +4722 -0
  131. package/dist/react/router/index.js.map +1 -0
  132. package/dist/react/websocket/index.d.ts +117 -0
  133. package/dist/react/websocket/index.d.ts.map +1 -0
  134. package/dist/react/websocket/index.js +107 -0
  135. package/dist/react/websocket/index.js.map +1 -0
  136. package/dist/redis/index.bun.js +4 -0
  137. package/dist/redis/index.bun.js.map +1 -1
  138. package/dist/redis/index.d.ts +41 -44
  139. package/dist/redis/index.d.ts.map +1 -1
  140. package/dist/redis/index.js +16 -25
  141. package/dist/redis/index.js.map +1 -1
  142. package/dist/retry/index.d.ts +11 -2
  143. package/dist/retry/index.d.ts.map +1 -1
  144. package/dist/retry/index.js +11 -2
  145. package/dist/retry/index.js.map +1 -1
  146. package/dist/scheduler/index.d.ts +11 -2
  147. package/dist/scheduler/index.d.ts.map +1 -1
  148. package/dist/scheduler/index.js +11 -2
  149. package/dist/scheduler/index.js.map +1 -1
  150. package/dist/security/index.d.ts +140 -49
  151. package/dist/security/index.d.ts.map +1 -1
  152. package/dist/security/index.js +164 -32
  153. package/dist/security/index.js.map +1 -1
  154. package/dist/server/auth/index.d.ts +12 -7
  155. package/dist/server/auth/index.d.ts.map +1 -1
  156. package/dist/server/auth/index.js +12 -7
  157. package/dist/server/auth/index.js.map +1 -1
  158. package/dist/server/cache/index.d.ts +7 -22
  159. package/dist/server/cache/index.d.ts.map +1 -1
  160. package/dist/server/cache/index.js +7 -22
  161. package/dist/server/cache/index.js.map +1 -1
  162. package/dist/server/compress/index.d.ts +10 -2
  163. package/dist/server/compress/index.d.ts.map +1 -1
  164. package/dist/server/compress/index.js +10 -2
  165. package/dist/server/compress/index.js.map +1 -1
  166. package/dist/server/cookies/index.d.ts +40 -16
  167. package/dist/server/cookies/index.d.ts.map +1 -1
  168. package/dist/server/cookies/index.js +7 -5
  169. package/dist/server/cookies/index.js.map +1 -1
  170. package/dist/server/core/index.d.ts +124 -23
  171. package/dist/server/core/index.d.ts.map +1 -1
  172. package/dist/server/core/index.js +231 -14
  173. package/dist/server/core/index.js.map +1 -1
  174. package/dist/server/cors/index.d.ts +13 -23
  175. package/dist/server/cors/index.d.ts.map +1 -1
  176. package/dist/server/cors/index.js +7 -21
  177. package/dist/server/cors/index.js.map +1 -1
  178. package/dist/server/health/index.d.ts +8 -2
  179. package/dist/server/health/index.d.ts.map +1 -1
  180. package/dist/server/health/index.js +8 -2
  181. package/dist/server/health/index.js.map +1 -1
  182. package/dist/server/helmet/index.d.ts +11 -3
  183. package/dist/server/helmet/index.d.ts.map +1 -1
  184. package/dist/server/helmet/index.js +11 -3
  185. package/dist/server/helmet/index.js.map +1 -1
  186. package/dist/server/links/index.d.ts +11 -6
  187. package/dist/server/links/index.d.ts.map +1 -1
  188. package/dist/server/links/index.js +11 -6
  189. package/dist/server/links/index.js.map +1 -1
  190. package/dist/server/metrics/index.d.ts +10 -3
  191. package/dist/server/metrics/index.d.ts.map +1 -1
  192. package/dist/server/metrics/index.js +10 -3
  193. package/dist/server/metrics/index.js.map +1 -1
  194. package/dist/server/multipart/index.d.ts +9 -3
  195. package/dist/server/multipart/index.d.ts.map +1 -1
  196. package/dist/server/multipart/index.js +9 -3
  197. package/dist/server/multipart/index.js.map +1 -1
  198. package/dist/server/proxy/index.d.ts +8 -2
  199. package/dist/server/proxy/index.d.ts.map +1 -1
  200. package/dist/server/proxy/index.js +8 -2
  201. package/dist/server/proxy/index.js.map +1 -1
  202. package/dist/server/rate-limit/index.d.ts +30 -35
  203. package/dist/server/rate-limit/index.d.ts.map +1 -1
  204. package/dist/server/rate-limit/index.js +18 -55
  205. package/dist/server/rate-limit/index.js.map +1 -1
  206. package/dist/server/static/index.d.ts +137 -4
  207. package/dist/server/static/index.d.ts.map +1 -1
  208. package/dist/server/static/index.js +1853 -5
  209. package/dist/server/static/index.js.map +1 -1
  210. package/dist/server/swagger/index.d.ts +309 -6
  211. package/dist/server/swagger/index.d.ts.map +1 -1
  212. package/dist/server/swagger/index.js +1854 -6
  213. package/dist/server/swagger/index.js.map +1 -1
  214. package/dist/sms/index.d.ts +309 -7
  215. package/dist/sms/index.d.ts.map +1 -1
  216. package/dist/sms/index.js +1856 -7
  217. package/dist/sms/index.js.map +1 -1
  218. package/dist/system/index.browser.js +1218 -0
  219. package/dist/system/index.browser.js.map +1 -0
  220. package/dist/{file → system}/index.d.ts +343 -16
  221. package/dist/system/index.d.ts.map +1 -0
  222. package/dist/{file → system}/index.js +419 -22
  223. package/dist/system/index.js.map +1 -0
  224. package/dist/thread/index.d.ts +11 -2
  225. package/dist/thread/index.d.ts.map +1 -1
  226. package/dist/thread/index.js +11 -2
  227. package/dist/thread/index.js.map +1 -1
  228. package/dist/topic/core/index.d.ts +12 -5
  229. package/dist/topic/core/index.d.ts.map +1 -1
  230. package/dist/topic/core/index.js +12 -5
  231. package/dist/topic/core/index.js.map +1 -1
  232. package/dist/vite/index.d.ts +5 -6272
  233. package/dist/vite/index.d.ts.map +1 -1
  234. package/dist/vite/index.js +23 -10
  235. package/dist/vite/index.js.map +1 -1
  236. package/dist/websocket/index.d.ts +12 -8
  237. package/dist/websocket/index.d.ts.map +1 -1
  238. package/dist/websocket/index.js +12 -8
  239. package/dist/websocket/index.js.map +1 -1
  240. package/package.json +82 -11
  241. package/src/api/audits/index.ts +10 -33
  242. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  243. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  244. package/src/api/files/controllers/FileController.spec.ts +1 -1
  245. package/src/api/files/index.ts +10 -3
  246. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  247. package/src/api/files/services/FileService.spec.ts +1 -1
  248. package/src/api/jobs/index.ts +10 -3
  249. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  250. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  251. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  252. package/src/api/keys/index.ts +49 -0
  253. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  254. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  255. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  256. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  257. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  258. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  259. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  260. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  261. package/src/api/keys/services/ApiKeyService.ts +306 -0
  262. package/src/api/logs/TODO.md +55 -0
  263. package/src/api/notifications/index.ts +10 -4
  264. package/src/api/parameters/index.ts +9 -30
  265. package/src/api/parameters/primitives/$config.ts +12 -4
  266. package/src/api/parameters/services/ConfigStore.ts +9 -3
  267. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  268. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  269. package/src/api/users/index.ts +14 -3
  270. package/src/api/users/primitives/$realm.ts +33 -5
  271. package/src/api/users/providers/RealmProvider.ts +1 -12
  272. package/src/api/users/services/SessionService.ts +1 -1
  273. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  274. package/src/api/verifications/index.ts +10 -4
  275. package/src/batch/index.ts +9 -36
  276. package/src/batch/primitives/$batch.ts +0 -8
  277. package/src/batch/providers/BatchProvider.ts +29 -2
  278. package/src/bucket/__tests__/shared.ts +1 -1
  279. package/src/bucket/index.ts +13 -6
  280. package/src/bucket/primitives/$bucket.ts +1 -1
  281. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  282. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  283. package/src/cache/core/__tests__/shared.ts +30 -0
  284. package/src/cache/core/index.ts +11 -6
  285. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  286. package/src/cache/core/providers/CacheProvider.ts +17 -0
  287. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  288. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  289. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  290. package/src/cli/apps/AlephaCli.ts +1 -14
  291. package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -1
  292. package/src/cli/atoms/buildOptions.ts +99 -9
  293. package/src/cli/commands/build.ts +150 -37
  294. package/src/cli/commands/db.ts +22 -18
  295. package/src/cli/commands/deploy.ts +1 -1
  296. package/src/cli/commands/dev.ts +1 -20
  297. package/src/cli/commands/gen/env.ts +5 -2
  298. package/src/cli/commands/gen/openapi.ts +5 -2
  299. package/src/cli/commands/init.spec.ts +588 -0
  300. package/src/cli/commands/init.ts +115 -58
  301. package/src/cli/commands/lint.ts +7 -1
  302. package/src/cli/commands/typecheck.ts +11 -0
  303. package/src/cli/providers/AppEntryProvider.ts +1 -1
  304. package/src/cli/providers/ViteBuildProvider.ts +8 -50
  305. package/src/cli/providers/ViteDevServerProvider.ts +35 -16
  306. package/src/cli/services/AlephaCliUtils.ts +52 -121
  307. package/src/cli/services/PackageManagerUtils.ts +129 -11
  308. package/src/cli/services/ProjectScaffolder.spec.ts +97 -0
  309. package/src/cli/services/ProjectScaffolder.ts +148 -81
  310. package/src/cli/services/ViteUtils.ts +82 -0
  311. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +37 -24
  312. package/src/cli/templates/apiAppSecurityTs.ts +11 -0
  313. package/src/cli/templates/apiIndexTs.ts +30 -0
  314. package/src/cli/templates/gitignore.ts +39 -0
  315. package/src/cli/{assets → templates}/mainCss.ts +11 -2
  316. package/src/cli/templates/mainServerTs.ts +33 -0
  317. package/src/cli/templates/webAppRouterTs.ts +74 -0
  318. package/src/cli/templates/webHelloComponentTsx.ts +30 -0
  319. package/src/command/helpers/Runner.spec.ts +139 -0
  320. package/src/command/helpers/Runner.ts +7 -22
  321. package/src/command/index.ts +12 -4
  322. package/src/command/providers/CliProvider.spec.ts +1392 -0
  323. package/src/command/providers/CliProvider.ts +320 -47
  324. package/src/core/Alepha.ts +34 -27
  325. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  326. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  327. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  328. package/src/core/index.shared.ts +1 -0
  329. package/src/core/index.ts +20 -0
  330. package/src/core/providers/EventManager.spec.ts +0 -71
  331. package/src/core/providers/EventManager.ts +3 -15
  332. package/src/core/providers/Json.ts +2 -14
  333. package/src/datetime/index.ts +15 -0
  334. package/src/email/index.ts +10 -5
  335. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  336. package/src/email/providers/LocalEmailProvider.ts +1 -1
  337. package/src/fake/__tests__/keyName.example.ts +1 -1
  338. package/src/fake/__tests__/keyName.spec.ts +5 -5
  339. package/src/fake/index.ts +9 -6
  340. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  341. package/src/fake/providers/FakeProvider.ts +133 -19
  342. package/src/lock/core/index.ts +11 -4
  343. package/src/logger/index.ts +17 -66
  344. package/src/mcp/index.ts +10 -27
  345. package/src/mcp/transports/SseMcpTransport.ts +0 -11
  346. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  347. package/src/orm/index.browser.ts +2 -2
  348. package/src/orm/index.bun.ts +5 -3
  349. package/src/orm/index.ts +23 -53
  350. package/src/orm/providers/drivers/BunSqliteProvider.ts +5 -1
  351. package/src/orm/providers/drivers/CloudflareD1Provider.ts +57 -30
  352. package/src/orm/providers/drivers/DatabaseProvider.ts +9 -1
  353. package/src/orm/providers/drivers/NodeSqliteProvider.ts +4 -1
  354. package/src/orm/services/Repository.ts +7 -3
  355. package/src/queue/core/index.ts +14 -6
  356. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  357. package/src/react/auth/hooks/useAuth.ts +32 -0
  358. package/src/react/auth/index.browser.ts +13 -0
  359. package/src/react/auth/index.shared.ts +2 -0
  360. package/src/react/auth/index.ts +48 -0
  361. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  362. package/src/react/auth/services/ReactAuth.ts +135 -0
  363. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  364. package/src/react/core/components/ClientOnly.tsx +49 -0
  365. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  366. package/src/react/core/contexts/AlephaContext.ts +7 -0
  367. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  368. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  369. package/src/react/core/hooks/useAction.ts +480 -0
  370. package/src/react/core/hooks/useAlepha.ts +26 -0
  371. package/src/react/core/hooks/useClient.ts +17 -0
  372. package/src/react/core/hooks/useEvents.ts +51 -0
  373. package/src/react/core/hooks/useInject.ts +12 -0
  374. package/src/react/core/hooks/useStore.ts +52 -0
  375. package/src/react/core/index.ts +90 -0
  376. package/src/react/form/components/FormState.tsx +17 -0
  377. package/src/react/form/errors/FormValidationError.ts +18 -0
  378. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  379. package/src/react/form/hooks/useForm.ts +47 -0
  380. package/src/react/form/hooks/useFormState.ts +130 -0
  381. package/src/react/form/index.ts +44 -0
  382. package/src/react/form/services/FormModel.ts +614 -0
  383. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  384. package/src/react/head/helpers/SeoExpander.ts +142 -0
  385. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  386. package/src/react/head/hooks/useHead.ts +62 -0
  387. package/src/react/head/index.browser.ts +26 -0
  388. package/src/react/head/index.ts +44 -0
  389. package/src/react/head/interfaces/Head.ts +105 -0
  390. package/src/react/head/primitives/$head.ts +25 -0
  391. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  392. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  393. package/src/react/head/providers/HeadProvider.ts +168 -0
  394. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  395. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  396. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  397. package/src/react/i18n/components/Localize.tsx +35 -0
  398. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  399. package/src/react/i18n/hooks/useI18n.ts +18 -0
  400. package/src/react/i18n/index.ts +41 -0
  401. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  402. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  403. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  404. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  405. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  406. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  407. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  408. package/src/react/router/components/ErrorViewer.tsx +872 -0
  409. package/src/react/router/components/Link.tsx +23 -0
  410. package/src/react/router/components/NestedView.tsx +223 -0
  411. package/src/react/router/components/NotFound.tsx +30 -0
  412. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  413. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  414. package/src/react/router/errors/Redirection.ts +28 -0
  415. package/src/react/router/hooks/useActive.ts +52 -0
  416. package/src/react/router/hooks/useQueryParams.ts +63 -0
  417. package/src/react/router/hooks/useRouter.ts +20 -0
  418. package/src/react/router/hooks/useRouterState.ts +11 -0
  419. package/src/react/router/index.browser.ts +45 -0
  420. package/src/react/router/index.shared.ts +19 -0
  421. package/src/react/router/index.ts +146 -0
  422. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  423. package/src/react/router/primitives/$page.spec.tsx +676 -0
  424. package/src/react/router/primitives/$page.ts +489 -0
  425. package/src/react/router/providers/ReactBrowserProvider.ts +312 -0
  426. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  427. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  428. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  429. package/src/react/router/providers/ReactPreloadProvider.spec.ts +142 -0
  430. package/src/react/router/providers/ReactPreloadProvider.ts +85 -0
  431. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  432. package/src/react/router/providers/ReactServerProvider.ts +487 -0
  433. package/src/react/router/providers/ReactServerTemplateProvider.spec.ts +210 -0
  434. package/src/react/router/providers/ReactServerTemplateProvider.ts +542 -0
  435. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  436. package/src/react/router/services/ReactPageServerService.ts +48 -0
  437. package/src/react/router/services/ReactPageService.ts +27 -0
  438. package/src/react/router/services/ReactRouter.ts +262 -0
  439. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  440. package/src/react/websocket/index.ts +7 -0
  441. package/src/redis/__tests__/redis.spec.ts +13 -0
  442. package/src/redis/index.ts +9 -25
  443. package/src/redis/providers/BunRedisProvider.ts +9 -0
  444. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  445. package/src/redis/providers/RedisProvider.ts +16 -0
  446. package/src/retry/index.ts +11 -2
  447. package/src/router/index.ts +15 -0
  448. package/src/scheduler/index.ts +11 -2
  449. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  450. package/src/security/__tests__/ServerSecurityProvider.spec.ts +90 -5
  451. package/src/security/index.ts +15 -10
  452. package/src/security/interfaces/IssuerResolver.ts +27 -0
  453. package/src/security/primitives/$issuer.ts +55 -0
  454. package/src/security/providers/SecurityProvider.ts +179 -0
  455. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  456. package/src/security/providers/ServerSecurityProvider.ts +63 -41
  457. package/src/server/auth/index.ts +12 -7
  458. package/src/server/cache/index.ts +7 -22
  459. package/src/server/compress/index.ts +10 -2
  460. package/src/server/cookies/index.ts +7 -5
  461. package/src/server/cookies/primitives/$cookie.ts +33 -11
  462. package/src/server/core/index.ts +16 -6
  463. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  464. package/src/server/core/primitives/$action.spec.ts +1 -1
  465. package/src/server/core/primitives/$action.ts +8 -3
  466. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +9 -3
  467. package/src/server/core/providers/NodeHttpServerProvider.ts +9 -3
  468. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  469. package/src/server/core/services/ServerRequestParser.ts +306 -13
  470. package/src/server/cors/index.ts +7 -21
  471. package/src/server/cors/primitives/$cors.ts +6 -2
  472. package/src/server/health/index.ts +8 -2
  473. package/src/server/helmet/index.ts +11 -3
  474. package/src/server/links/index.ts +11 -6
  475. package/src/server/metrics/index.ts +10 -3
  476. package/src/server/multipart/index.ts +9 -3
  477. package/src/server/proxy/index.ts +8 -2
  478. package/src/server/rate-limit/index.ts +21 -25
  479. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  480. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  481. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  482. package/src/server/static/index.ts +8 -2
  483. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  484. package/src/server/swagger/index.ts +9 -4
  485. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  486. package/src/sms/index.ts +9 -5
  487. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  488. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  489. package/src/system/index.browser.ts +36 -0
  490. package/src/system/index.ts +62 -0
  491. package/src/system/index.workerd.ts +1 -0
  492. package/src/{file → system}/providers/FileSystemProvider.ts +24 -0
  493. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  494. package/src/system/providers/MemoryShellProvider.ts +164 -0
  495. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  496. package/src/{file → system}/providers/NodeFileSystemProvider.ts +47 -2
  497. package/src/system/providers/NodeShellProvider.ts +184 -0
  498. package/src/system/providers/ShellProvider.ts +74 -0
  499. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  500. package/src/thread/index.ts +11 -2
  501. package/src/topic/core/index.ts +12 -5
  502. package/src/vite/tasks/buildClient.ts +2 -7
  503. package/src/vite/tasks/buildServer.ts +19 -13
  504. package/src/vite/tasks/generateCloudflare.ts +10 -7
  505. package/src/vite/tasks/generateDocker.ts +4 -0
  506. package/src/websocket/index.ts +12 -8
  507. package/dist/file/index.d.ts.map +0 -1
  508. package/dist/file/index.js.map +0 -1
  509. package/src/cli/assets/apiIndexTs.ts +0 -16
  510. package/src/cli/assets/mainServerTs.ts +0 -24
  511. package/src/cli/assets/webAppRouterTs.ts +0 -16
  512. package/src/cli/assets/webHelloComponentTsx.ts +0 -20
  513. package/src/cli/providers/ViteTemplateProvider.ts +0 -27
  514. package/src/file/index.ts +0 -43
  515. /package/src/cli/{assets → templates}/apiHelloControllerTs.ts +0 -0
  516. /package/src/cli/{assets → templates}/biomeJson.ts +0 -0
  517. /package/src/cli/{assets → templates}/dummySpecTs.ts +0 -0
  518. /package/src/cli/{assets → templates}/editorconfig.ts +0 -0
  519. /package/src/cli/{assets → templates}/mainBrowserTs.ts +0 -0
  520. /package/src/cli/{assets → templates}/tsconfigJson.ts +0 -0
  521. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  522. /package/src/{file → system}/errors/FileError.ts +0 -0
  523. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -0,0 +1,771 @@
1
+ import * as Value from "typebox/value";
2
+ import { describe, test } from "vitest";
3
+ import { t } from "../providers/TypeProvider.ts";
4
+ import { jsonSchemaToTypeBox } from "./jsonSchemaToTypeBox.ts";
5
+
6
+ describe("jsonSchemaToTypeBox", () => {
7
+ describe("reserved/special property names", () => {
8
+ test("should handle properties with JSON Schema reserved names", ({
9
+ expect,
10
+ }) => {
11
+ const jsonSchema = {
12
+ type: "object",
13
+ properties: {
14
+ type: { type: "string" },
15
+ properties: { type: "string" },
16
+ required: { type: "boolean" },
17
+ items: { type: "string" },
18
+ enum: { type: "string" },
19
+ format: { type: "string" },
20
+ default: { type: "string" },
21
+ },
22
+ required: ["type", "properties"],
23
+ };
24
+
25
+ const result = jsonSchemaToTypeBox(jsonSchema);
26
+
27
+ expect(result.properties.type).toBeDefined();
28
+ expect(result.properties.type.type).toBe("string");
29
+ expect(result.properties.properties).toBeDefined();
30
+ expect(result.properties.required).toBeDefined();
31
+ expect(result.properties.items).toBeDefined();
32
+ expect(result.properties.enum).toBeDefined();
33
+ expect(result.properties.format).toBeDefined();
34
+ expect(result.properties.default).toBeDefined();
35
+
36
+ const validData = {
37
+ type: "user",
38
+ properties: "some value",
39
+ required: true,
40
+ items: "item",
41
+ enum: "value",
42
+ format: "custom",
43
+ default: "def",
44
+ };
45
+ expect(Value.Check(result, validData)).toBe(true);
46
+
47
+ // Missing required field
48
+ const invalidData = { type: "user" };
49
+ expect(Value.Check(result, invalidData)).toBe(false);
50
+ });
51
+
52
+ test("should handle 'description' property alongside other fields", ({
53
+ expect,
54
+ }) => {
55
+ const jsonSchema = {
56
+ type: "object",
57
+ title: "Form Schema",
58
+ description: "This is the schema-level description",
59
+ properties: {
60
+ id: { type: "integer" },
61
+ name: { type: "string", minLength: 1 },
62
+ description: {
63
+ type: "string",
64
+ description: "User-provided description",
65
+ },
66
+ email: { type: "string", format: "email" },
67
+ active: { type: "boolean" },
68
+ },
69
+ required: ["id", "name", "description"],
70
+ };
71
+
72
+ const result = jsonSchemaToTypeBox(jsonSchema);
73
+
74
+ // Schema-level metadata preserved
75
+ expect(result.title).toBe("Form Schema");
76
+ expect(result.description).toBe("This is the schema-level description");
77
+
78
+ // All properties converted correctly
79
+ expect(result.properties.id.type).toBe("integer");
80
+ expect(result.properties.name.type).toBe("string");
81
+ expect(result.properties.description.type).toBe("string");
82
+ expect(result.properties.description.description).toBe(
83
+ "User-provided description",
84
+ );
85
+ expect(result.properties.email.format).toBe("email");
86
+ expect(result.properties.active.type).toBe("boolean");
87
+
88
+ const validData = {
89
+ id: 1,
90
+ name: "Test",
91
+ description: "My description",
92
+ email: "test@example.com",
93
+ active: true,
94
+ };
95
+ expect(Value.Check(result, validData)).toBe(true);
96
+
97
+ // Wrong type for description
98
+ const invalidData = {
99
+ id: 1,
100
+ name: "Test",
101
+ description: 123,
102
+ };
103
+ expect(Value.Check(result, invalidData)).toBe(false);
104
+ });
105
+
106
+ test("should handle 'disabled' property in a form-like schema", ({
107
+ expect,
108
+ }) => {
109
+ const jsonSchema = {
110
+ type: "object",
111
+ properties: {
112
+ id: { type: "string", format: "uuid" },
113
+ label: { type: "string" },
114
+ value: { type: "string" },
115
+ disabled: { type: "boolean", default: false },
116
+ readonly: { type: "boolean" },
117
+ hidden: { type: "boolean" },
118
+ required: { type: "boolean" },
119
+ },
120
+ required: ["id", "label", "disabled"],
121
+ };
122
+
123
+ const result = jsonSchemaToTypeBox(jsonSchema);
124
+
125
+ expect(result.properties.id.format).toBe("uuid");
126
+ expect(result.properties.label.type).toBe("string");
127
+ expect(result.properties.value.type).toBe("string");
128
+ expect(result.properties.disabled.type).toBe("boolean");
129
+ expect(result.properties.disabled.default).toBe(false);
130
+ expect(result.properties.readonly.type).toBe("boolean");
131
+ expect(result.properties.hidden.type).toBe("boolean");
132
+ expect(result.properties.required.type).toBe("boolean");
133
+
134
+ const validData = {
135
+ id: "550e8400-e29b-41d4-a716-446655440000",
136
+ label: "Username",
137
+ disabled: false,
138
+ };
139
+ expect(Value.Check(result, validData)).toBe(true);
140
+
141
+ // disabled should be boolean, not string
142
+ const invalidData = {
143
+ id: "550e8400-e29b-41d4-a716-446655440000",
144
+ label: "Username",
145
+ disabled: "false",
146
+ };
147
+ expect(Value.Check(result, invalidData)).toBe(false);
148
+ });
149
+
150
+ test("should handle both 'description' and 'disabled' with nested objects", ({
151
+ expect,
152
+ }) => {
153
+ const jsonSchema = {
154
+ type: "object",
155
+ description: "Root schema description",
156
+ properties: {
157
+ name: { type: "string" },
158
+ description: { type: "string" },
159
+ disabled: { type: "boolean" },
160
+ settings: {
161
+ type: "object",
162
+ description: "Nested settings description",
163
+ properties: {
164
+ description: { type: "string" },
165
+ disabled: { type: "boolean" },
166
+ theme: { type: "string" },
167
+ },
168
+ required: ["theme"],
169
+ },
170
+ },
171
+ required: ["name", "description", "disabled"],
172
+ };
173
+
174
+ const result = jsonSchemaToTypeBox(jsonSchema);
175
+
176
+ // Root level
177
+ expect(result.description).toBe("Root schema description");
178
+ expect(result.properties.description.type).toBe("string");
179
+ expect(result.properties.disabled.type).toBe("boolean");
180
+
181
+ // Nested level
182
+ const settingsSchema = result.properties.settings;
183
+ expect(settingsSchema.description).toBe("Nested settings description");
184
+ expect(settingsSchema.properties.description.type).toBe("string");
185
+ expect(settingsSchema.properties.disabled.type).toBe("boolean");
186
+ expect(settingsSchema.properties.theme.type).toBe("string");
187
+
188
+ const validData = {
189
+ name: "Config",
190
+ description: "Main config",
191
+ disabled: false,
192
+ settings: {
193
+ description: "Settings desc",
194
+ disabled: true,
195
+ theme: "dark",
196
+ },
197
+ };
198
+ expect(Value.Check(result, validData)).toBe(true);
199
+ });
200
+ });
201
+
202
+ describe("basic type conversion", () => {
203
+ test("should convert all basic types", ({ expect }) => {
204
+ const jsonSchema = {
205
+ type: "object",
206
+ properties: {
207
+ stringField: { type: "string" },
208
+ numberField: { type: "number" },
209
+ integerField: { type: "integer" },
210
+ booleanField: { type: "boolean" },
211
+ nullField: { type: "null" },
212
+ },
213
+ required: [
214
+ "stringField",
215
+ "numberField",
216
+ "integerField",
217
+ "booleanField",
218
+ "nullField",
219
+ ],
220
+ };
221
+
222
+ const result = jsonSchemaToTypeBox(jsonSchema);
223
+
224
+ expect(result.properties.stringField.type).toBe("string");
225
+ expect(result.properties.numberField.type).toBe("number");
226
+ expect(result.properties.integerField.type).toBe("integer");
227
+ expect(result.properties.booleanField.type).toBe("boolean");
228
+ expect(result.properties.nullField.type).toBe("null");
229
+
230
+ const validData = {
231
+ stringField: "test",
232
+ numberField: 3.14,
233
+ integerField: 42,
234
+ booleanField: true,
235
+ nullField: null,
236
+ };
237
+ expect(Value.Check(result, validData)).toBe(true);
238
+ });
239
+
240
+ test("should handle string formats", ({ expect }) => {
241
+ const jsonSchema = {
242
+ type: "object",
243
+ properties: {
244
+ email: { type: "string", format: "email" },
245
+ uuid: { type: "string", format: "uuid" },
246
+ datetime: { type: "string", format: "date-time" },
247
+ date: { type: "string", format: "date" },
248
+ time: { type: "string", format: "time" },
249
+ url: { type: "string", format: "url" },
250
+ uri: { type: "string", format: "uri" },
251
+ },
252
+ };
253
+
254
+ const result = jsonSchemaToTypeBox(jsonSchema);
255
+
256
+ expect(result.properties.email.format).toBe("email");
257
+ expect(result.properties.uuid.format).toBe("uuid");
258
+ expect(result.properties.datetime.format).toBe("date-time");
259
+ expect(result.properties.date.format).toBe("date");
260
+ expect(result.properties.time.format).toBe("time");
261
+ expect(result.properties.url.format).toBe("url");
262
+ expect(result.properties.uri.format).toBe("url");
263
+ });
264
+
265
+ test("should handle validation constraints", ({ expect }) => {
266
+ const jsonSchema = {
267
+ type: "object",
268
+ properties: {
269
+ username: {
270
+ type: "string",
271
+ minLength: 3,
272
+ maxLength: 20,
273
+ pattern: "^[a-z]+$",
274
+ },
275
+ age: { type: "integer", minimum: 0, maximum: 120 },
276
+ score: { type: "number", exclusiveMinimum: 0, exclusiveMaximum: 100 },
277
+ tags: {
278
+ type: "array",
279
+ items: { type: "string" },
280
+ minItems: 1,
281
+ maxItems: 5,
282
+ },
283
+ },
284
+ required: ["username", "age"],
285
+ };
286
+
287
+ const result = jsonSchemaToTypeBox(jsonSchema);
288
+
289
+ expect(result.properties.username.minLength).toBe(3);
290
+ expect(result.properties.username.maxLength).toBe(20);
291
+ expect(result.properties.username.pattern).toBe("^[a-z]+$");
292
+ expect(result.properties.age.minimum).toBe(0);
293
+ expect(result.properties.age.maximum).toBe(120);
294
+ expect(result.properties.score.exclusiveMinimum).toBe(0);
295
+ expect(result.properties.score.exclusiveMaximum).toBe(100);
296
+ expect(result.properties.tags.minItems).toBe(1);
297
+ expect(result.properties.tags.maxItems).toBe(5);
298
+
299
+ const validData = {
300
+ username: "john",
301
+ age: 25,
302
+ score: 50,
303
+ tags: ["a", "b"],
304
+ };
305
+ expect(Value.Check(result, validData)).toBe(true);
306
+
307
+ // username too short
308
+ const invalidUsername = { username: "ab", age: 25 };
309
+ expect(Value.Check(result, invalidUsername)).toBe(false);
310
+
311
+ // age negative
312
+ const invalidAge = { username: "john", age: -1 };
313
+ expect(Value.Check(result, invalidAge)).toBe(false);
314
+ });
315
+ });
316
+
317
+ describe("enums and const", () => {
318
+ test("should handle string enums", ({ expect }) => {
319
+ const jsonSchema = {
320
+ type: "object",
321
+ properties: {
322
+ status: { type: "string", enum: ["pending", "active", "inactive"] },
323
+ priority: { type: "string", enum: ["low", "medium", "high"] },
324
+ name: { type: "string" },
325
+ },
326
+ required: ["status", "name"],
327
+ };
328
+
329
+ const result = jsonSchemaToTypeBox(jsonSchema);
330
+
331
+ const validData = { status: "active", name: "Test" };
332
+ expect(Value.Check(result, validData)).toBe(true);
333
+
334
+ const invalidData = { status: "unknown", name: "Test" };
335
+ expect(Value.Check(result, invalidData)).toBe(false);
336
+ });
337
+
338
+ test("should handle const values", ({ expect }) => {
339
+ const jsonSchema = {
340
+ type: "object",
341
+ properties: {
342
+ version: { const: "1.0.0" },
343
+ type: { const: "config" },
344
+ count: { const: 42 },
345
+ enabled: { const: true },
346
+ },
347
+ required: ["version", "type"],
348
+ };
349
+
350
+ const result = jsonSchemaToTypeBox(jsonSchema);
351
+
352
+ const validData = {
353
+ version: "1.0.0",
354
+ type: "config",
355
+ count: 42,
356
+ enabled: true,
357
+ };
358
+ expect(Value.Check(result, validData)).toBe(true);
359
+
360
+ const invalidData = { version: "2.0.0", type: "config" };
361
+ expect(Value.Check(result, invalidData)).toBe(false);
362
+ });
363
+ });
364
+
365
+ describe("anyOf/oneOf nullable patterns", () => {
366
+ test("should handle anyOf with nullable string", ({ expect }) => {
367
+ const jsonSchema = {
368
+ type: "object",
369
+ properties: {
370
+ name: { type: "string" },
371
+ description: {
372
+ anyOf: [
373
+ {
374
+ type: "string",
375
+ maxLength: 255,
376
+ "~options": { trim: true, lowercase: false },
377
+ },
378
+ { type: "null" },
379
+ ],
380
+ },
381
+ },
382
+ required: ["name"],
383
+ };
384
+
385
+ const result = jsonSchemaToTypeBox(jsonSchema);
386
+
387
+ // description should be a union of [null, string]
388
+ expect(result.properties.description).toBeDefined();
389
+ expect(result.properties.description.anyOf).toBeDefined();
390
+
391
+ // Valid with string
392
+ const validWithString = { name: "Test", description: "A description" };
393
+ expect(Value.Check(result, validWithString)).toBe(true);
394
+
395
+ // Valid with null
396
+ const validWithNull = { name: "Test", description: null };
397
+ expect(Value.Check(result, validWithNull)).toBe(true);
398
+
399
+ // Valid without description (optional)
400
+ const validWithout = { name: "Test" };
401
+ expect(Value.Check(result, validWithout)).toBe(true);
402
+
403
+ // Invalid with number
404
+ const invalidData = { name: "Test", description: 123 };
405
+ expect(Value.Check(result, invalidData)).toBe(false);
406
+ });
407
+
408
+ test("should handle anyOf with nullable boolean", ({ expect }) => {
409
+ const jsonSchema = {
410
+ type: "object",
411
+ properties: {
412
+ disabled: {
413
+ anyOf: [
414
+ { type: "boolean", description: "If true, access is disabled." },
415
+ { type: "null" },
416
+ ],
417
+ },
418
+ },
419
+ };
420
+
421
+ const result = jsonSchemaToTypeBox(jsonSchema);
422
+
423
+ // Valid with boolean
424
+ expect(Value.Check(result, { disabled: true })).toBe(true);
425
+ expect(Value.Check(result, { disabled: false })).toBe(true);
426
+
427
+ // Valid with null
428
+ expect(Value.Check(result, { disabled: null })).toBe(true);
429
+
430
+ // Invalid with string
431
+ expect(Value.Check(result, { disabled: "yes" })).toBe(false);
432
+ });
433
+
434
+ test("should handle complex schema with multiple anyOf nullable fields", ({
435
+ expect,
436
+ }) => {
437
+ const jsonString = `{"type":"object","required":["contracts"],"properties":{"name":{"type":"string","maxLength":64,"~options":{"trim":true,"lowercase":false},"description":"The name of the third party."},"description":{"anyOf":[{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"Short description about the third party."},{"type":"null"}]},"email":{"anyOf":[{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"format":"email","description":"Email address used for communication."},{"type":"null"}]},"company":{"anyOf":[{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"Company legal name."},{"type":"null"}]},"disabled":{"anyOf":[{"type":"boolean","description":"If true, all API access will be disabled."},{"type":"null"}]},"contracts":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string","maxLength":64,"~options":{"trim":true,"lowercase":false}},"startDate":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"The start date of the contract validity.","format":"date"},"endDate":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"The end date of the contract validity.","format":"date"},"roles":{"type":"array","items":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false}},"maxItems":1000,"description":"Roles assigned to this contract."},"disabled":{"type":"boolean","description":"Manually disable this contract."},"clientId":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"The client ID for this contract."},"clientSecret":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"Will be generated if contract is valid."}},"additionalProperties":false},"maxItems":1000}},"additionalProperties":false}`;
438
+
439
+ const jsonSchema = JSON.parse(jsonString);
440
+ const result = jsonSchemaToTypeBox(jsonSchema);
441
+
442
+ // Check that nullable fields are converted correctly
443
+ expect(result.properties.name.type).toBe("string");
444
+ expect(result.properties.description.anyOf).toBeDefined();
445
+ expect(result.properties.email.anyOf).toBeDefined();
446
+ expect(result.properties.company.anyOf).toBeDefined();
447
+ expect(result.properties.disabled.anyOf).toBeDefined();
448
+ expect(result.properties.contracts.type).toBe("array");
449
+
450
+ // Valid data with all nulls
451
+ const validWithNulls = {
452
+ contracts: [],
453
+ description: null,
454
+ email: null,
455
+ company: null,
456
+ disabled: null,
457
+ };
458
+ expect(Value.Check(result, validWithNulls)).toBe(true);
459
+
460
+ // Valid data with actual values
461
+ const validWithValues = {
462
+ name: "Acme Corp",
463
+ description: "A company",
464
+ email: "contact@acme.com",
465
+ company: "Acme Inc.",
466
+ disabled: false,
467
+ contracts: [
468
+ {
469
+ label: "Main",
470
+ disabled: false,
471
+ },
472
+ ],
473
+ };
474
+ expect(Value.Check(result, validWithValues)).toBe(true);
475
+
476
+ // Invalid: wrong type for nullable field
477
+ const invalidData = {
478
+ contracts: [],
479
+ description: 123, // should be string or null
480
+ };
481
+ expect(Value.Check(result, invalidData)).toBe(false);
482
+ });
483
+
484
+ test("should handle oneOf with nullable type", ({ expect }) => {
485
+ const jsonSchema = {
486
+ type: "object",
487
+ properties: {
488
+ value: {
489
+ oneOf: [{ type: "string" }, { type: "null" }],
490
+ },
491
+ },
492
+ };
493
+
494
+ const result = jsonSchemaToTypeBox(jsonSchema);
495
+
496
+ expect(Value.Check(result, { value: "test" })).toBe(true);
497
+ expect(Value.Check(result, { value: null })).toBe(true);
498
+ expect(Value.Check(result, { value: 123 })).toBe(false);
499
+ });
500
+ });
501
+
502
+ describe("arrays", () => {
503
+ test("should handle arrays with complex items", ({ expect }) => {
504
+ const jsonSchema = {
505
+ type: "object",
506
+ properties: {
507
+ users: {
508
+ type: "array",
509
+ items: {
510
+ type: "object",
511
+ properties: {
512
+ id: { type: "integer" },
513
+ name: { type: "string" },
514
+ description: { type: "string" },
515
+ disabled: { type: "boolean" },
516
+ },
517
+ required: ["id", "name"],
518
+ },
519
+ },
520
+ },
521
+ required: ["users"],
522
+ };
523
+
524
+ const result = jsonSchemaToTypeBox(jsonSchema);
525
+
526
+ const validData = {
527
+ users: [
528
+ { id: 1, name: "Alice", description: "Admin", disabled: false },
529
+ { id: 2, name: "Bob" },
530
+ ],
531
+ };
532
+ expect(Value.Check(result, validData)).toBe(true);
533
+
534
+ // Missing required field in array item
535
+ const invalidData = {
536
+ users: [{ id: 1 }],
537
+ };
538
+ expect(Value.Check(result, invalidData)).toBe(false);
539
+ });
540
+ });
541
+
542
+ describe("optional properties", () => {
543
+ test("should make non-required properties optional", ({ expect }) => {
544
+ const jsonSchema = {
545
+ type: "object",
546
+ properties: {
547
+ id: { type: "integer" },
548
+ name: { type: "string" },
549
+ description: { type: "string" },
550
+ disabled: { type: "boolean" },
551
+ metadata: { type: "object" },
552
+ },
553
+ required: ["id"],
554
+ };
555
+
556
+ const result = jsonSchemaToTypeBox(jsonSchema);
557
+
558
+ // Only id is required
559
+ const minimalValidData = { id: 1 };
560
+ expect(Value.Check(result, minimalValidData)).toBe(true);
561
+
562
+ // All fields provided
563
+ const fullValidData = {
564
+ id: 1,
565
+ name: "Test",
566
+ description: "A description",
567
+ disabled: true,
568
+ metadata: { key: "value" },
569
+ };
570
+ expect(Value.Check(result, fullValidData)).toBe(true);
571
+
572
+ // Missing required id
573
+ const invalidData = { name: "Test" };
574
+ expect(Value.Check(result, invalidData)).toBe(false);
575
+ });
576
+ });
577
+
578
+ describe("~options handling", () => {
579
+ test("should preserve ~options with trim and lowercase", ({ expect }) => {
580
+ const jsonSchema = {
581
+ type: "object",
582
+ properties: {
583
+ name: {
584
+ type: "string",
585
+ maxLength: 64,
586
+ "~options": { trim: true, lowercase: false },
587
+ },
588
+ email: {
589
+ type: "string",
590
+ format: "email",
591
+ "~options": { trim: true, lowercase: true },
592
+ },
593
+ description: {
594
+ type: "string",
595
+ maxLength: 255,
596
+ "~options": { trim: true, lowercase: false },
597
+ },
598
+ },
599
+ required: ["name"],
600
+ };
601
+
602
+ const result = jsonSchemaToTypeBox(jsonSchema);
603
+
604
+ // Should preserve ~options
605
+ expect(result.properties.name["~options"]).toEqual({
606
+ trim: true,
607
+ lowercase: false,
608
+ });
609
+ expect(result.properties.email["~options"]).toEqual({
610
+ trim: true,
611
+ lowercase: true,
612
+ });
613
+ expect(result.properties.description["~options"]).toEqual({
614
+ trim: true,
615
+ lowercase: false,
616
+ });
617
+ });
618
+
619
+ test("should use t.text() behavior when ~options present", ({ expect }) => {
620
+ // Create a schema using t.text() directly for comparison
621
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
622
+ const directTextSchema = t.text({ maxLength: 64 }) as any;
623
+
624
+ // This should have ~options
625
+ expect(directTextSchema["~options"]).toBeDefined();
626
+ expect(directTextSchema["~options"].trim).toBe(true);
627
+
628
+ // Now convert from JSON schema with ~options
629
+ const jsonSchema = {
630
+ type: "string",
631
+ maxLength: 64,
632
+ "~options": { trim: true, lowercase: false },
633
+ };
634
+
635
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
636
+ const result = jsonSchemaToTypeBox(jsonSchema) as any;
637
+
638
+ // Should preserve the ~options
639
+ expect(result["~options"]).toEqual({ trim: true, lowercase: false });
640
+ });
641
+ });
642
+
643
+ describe("real-world schemas", () => {
644
+ test("should handle third party schema with description and disabled properties", ({
645
+ expect,
646
+ }) => {
647
+ const jsonSchema =
648
+ '{"type":"object","required":["name","contracts"],"properties":{"name":{"type":"string","maxLength":64,"~options":{"trim":true,"lowercase":false},"description":"The name of the third party."},"description":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"Short description about the third party."},"retailerId":{"type":"integer","title":"Retailer","description":"Optional retailer associated with this third party."},"email":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"format":"email","description":"Email address used for communication."},"company":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"Company legal name."},"disabled":{"type":"boolean","description":"If true, all API access will be disabled."},"contracts":{"type":"array","items":{"type":"object","required":["label"],"properties":{"label":{"type":"string","maxLength":64,"~options":{"trim":true,"lowercase":false}},"startDate":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"The start date of the contract validity.","format":"date"},"endDate":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false},"description":"The end date of the contract validity.","format":"date"},"roles":{"type":"array","items":{"type":"string","maxLength":255,"~options":{"trim":true,"lowercase":false}},"maxItems":1000,"description":"Roles assigned to this contract."},"disabled":{"type":"boolean","description":"Manually disable this contract."}},"additionalProperties":false},"maxItems":1000,"description":"List of API credentials associated with the third party."}},"additionalProperties":false}';
649
+
650
+ const result = jsonSchemaToTypeBox(JSON.parse(jsonSchema));
651
+
652
+ // Check root properties
653
+ expect(result.properties.name).toBeDefined();
654
+ expect(result.properties.name.type).toBe("string");
655
+ expect(result.properties.name.maxLength).toBe(64);
656
+ expect(result.properties.name.description).toBe(
657
+ "The name of the third party.",
658
+ );
659
+ expect(result.properties.name["~options"]).toEqual({
660
+ trim: true,
661
+ lowercase: false,
662
+ });
663
+
664
+ // Check 'description' property (not schema description)
665
+ expect(result.properties.description).toBeDefined();
666
+ expect(result.properties.description.type).toBe("string");
667
+ expect(result.properties.description.description).toBe(
668
+ "Short description about the third party.",
669
+ );
670
+
671
+ // Check 'disabled' property
672
+ expect(result.properties.disabled).toBeDefined();
673
+ expect(result.properties.disabled.type).toBe("boolean");
674
+ expect(result.properties.disabled.description).toBe(
675
+ "If true, all API access will be disabled.",
676
+ );
677
+
678
+ // Check nested contracts array
679
+ expect(result.properties.contracts).toBeDefined();
680
+ expect(result.properties.contracts.type).toBe("array");
681
+ expect(result.properties.contracts.maxItems).toBe(1000);
682
+
683
+ const contractItemSchema = result.properties.contracts.items;
684
+ expect(contractItemSchema.properties.label).toBeDefined();
685
+ expect(contractItemSchema.properties.label["~options"]).toEqual({
686
+ trim: true,
687
+ lowercase: false,
688
+ });
689
+ expect(contractItemSchema.properties.startDate.format).toBe("date");
690
+ expect(contractItemSchema.properties.startDate["~options"]).toEqual({
691
+ trim: true,
692
+ lowercase: false,
693
+ });
694
+ expect(contractItemSchema.properties.endDate.format).toBe("date");
695
+ expect(contractItemSchema.properties.disabled).toBeDefined();
696
+ expect(contractItemSchema.properties.disabled.type).toBe("boolean");
697
+
698
+ // Validate with actual data
699
+ const validData = {
700
+ name: "Acme Corp",
701
+ contracts: [
702
+ {
703
+ label: "Main Contract",
704
+ startDate: "2024-01-01",
705
+ endDate: "2024-12-31",
706
+ roles: ["admin", "viewer"],
707
+ disabled: false,
708
+ },
709
+ ],
710
+ };
711
+ expect(Value.Check(result, validData)).toBe(true);
712
+
713
+ // Validate with all optional fields
714
+ const fullValidData = {
715
+ name: "Acme Corp",
716
+ description: "A third party vendor",
717
+ retailerId: 123,
718
+ email: "contact@acme.com",
719
+ company: "Acme Corporation Inc.",
720
+ disabled: false,
721
+ contracts: [
722
+ {
723
+ label: "Contract A",
724
+ disabled: true,
725
+ },
726
+ {
727
+ label: "Contract B",
728
+ startDate: "2024-06-01",
729
+ roles: ["reader"],
730
+ },
731
+ ],
732
+ };
733
+ expect(Value.Check(result, fullValidData)).toBe(true);
734
+
735
+ // Invalid: missing required 'name'
736
+ const missingName = {
737
+ contracts: [{ label: "Test" }],
738
+ };
739
+ expect(Value.Check(result, missingName)).toBe(false);
740
+
741
+ // Invalid: missing required 'contracts'
742
+ const missingContracts = {
743
+ name: "Test",
744
+ };
745
+ expect(Value.Check(result, missingContracts)).toBe(false);
746
+
747
+ // Invalid: wrong type for 'disabled'
748
+ const wrongDisabledType = {
749
+ name: "Test",
750
+ disabled: "yes",
751
+ contracts: [{ label: "Test" }],
752
+ };
753
+ expect(Value.Check(result, wrongDisabledType)).toBe(false);
754
+
755
+ // Invalid: wrong type for 'description'
756
+ const wrongDescriptionType = {
757
+ name: "Test",
758
+ description: 123,
759
+ contracts: [{ label: "Test" }],
760
+ };
761
+ expect(Value.Check(result, wrongDescriptionType)).toBe(false);
762
+
763
+ // Invalid: contract missing required 'label'
764
+ const contractMissingLabel = {
765
+ name: "Test",
766
+ contracts: [{ disabled: false }],
767
+ };
768
+ expect(Value.Check(result, contractMissingLabel)).toBe(false);
769
+ });
770
+ });
771
+ });