alepha 0.14.1 → 0.14.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 (402) hide show
  1. package/README.md +3 -3
  2. package/dist/api/audits/index.browser.js +5 -5
  3. package/dist/api/audits/index.browser.js.map +1 -1
  4. package/dist/api/audits/index.d.ts +784 -784
  5. package/dist/api/audits/index.d.ts.map +1 -1
  6. package/dist/api/audits/index.js +13 -13
  7. package/dist/api/audits/index.js.map +1 -1
  8. package/dist/api/files/index.browser.js +5 -5
  9. package/dist/api/files/index.browser.js.map +1 -1
  10. package/dist/api/files/index.d.ts +57 -57
  11. package/dist/api/files/index.d.ts.map +1 -1
  12. package/dist/api/files/index.js +71 -71
  13. package/dist/api/files/index.js.map +1 -1
  14. package/dist/api/jobs/index.browser.js +5 -5
  15. package/dist/api/jobs/index.browser.js.map +1 -1
  16. package/dist/api/jobs/index.d.ts +165 -165
  17. package/dist/api/jobs/index.d.ts.map +1 -1
  18. package/dist/api/jobs/index.js +10 -10
  19. package/dist/api/jobs/index.js.map +1 -1
  20. package/dist/api/notifications/index.browser.js +10 -10
  21. package/dist/api/notifications/index.browser.js.map +1 -1
  22. package/dist/api/notifications/index.d.ts +583 -171
  23. package/dist/api/notifications/index.d.ts.map +1 -1
  24. package/dist/api/notifications/index.js +12 -12
  25. package/dist/api/notifications/index.js.map +1 -1
  26. package/dist/api/parameters/index.browser.js +163 -10
  27. package/dist/api/parameters/index.browser.js.map +1 -1
  28. package/dist/api/parameters/index.d.ts +281 -276
  29. package/dist/api/parameters/index.d.ts.map +1 -1
  30. package/dist/api/parameters/index.js +196 -91
  31. package/dist/api/parameters/index.js.map +1 -1
  32. package/dist/api/users/index.browser.js +19 -19
  33. package/dist/api/users/index.browser.js.map +1 -1
  34. package/dist/api/users/index.d.ts +778 -764
  35. package/dist/api/users/index.d.ts.map +1 -1
  36. package/dist/api/users/index.js +831 -596
  37. package/dist/api/users/index.js.map +1 -1
  38. package/dist/api/verifications/index.browser.js +6 -6
  39. package/dist/api/verifications/index.browser.js.map +1 -1
  40. package/dist/api/verifications/index.d.ts +125 -125
  41. package/dist/api/verifications/index.d.ts.map +1 -1
  42. package/dist/api/verifications/index.js +6 -6
  43. package/dist/api/verifications/index.js.map +1 -1
  44. package/dist/batch/index.js.map +1 -1
  45. package/dist/bin/index.d.ts +1 -2
  46. package/dist/bin/index.js +0 -1
  47. package/dist/bin/index.js.map +1 -1
  48. package/dist/cache/core/index.js.map +1 -1
  49. package/dist/cli/index.d.ts +249 -218
  50. package/dist/cli/index.d.ts.map +1 -1
  51. package/dist/cli/index.js +951 -821
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/command/index.d.ts +40 -0
  54. package/dist/command/index.d.ts.map +1 -1
  55. package/dist/command/index.js +97 -17
  56. package/dist/command/index.js.map +1 -1
  57. package/dist/core/index.browser.js +14 -18
  58. package/dist/core/index.browser.js.map +1 -1
  59. package/dist/core/index.d.ts +29 -0
  60. package/dist/core/index.d.ts.map +1 -1
  61. package/dist/core/index.js +21 -24
  62. package/dist/core/index.js.map +1 -1
  63. package/dist/core/index.native.js +21 -24
  64. package/dist/core/index.native.js.map +1 -1
  65. package/dist/datetime/index.js.map +1 -1
  66. package/dist/fake/index.js +195 -168
  67. package/dist/fake/index.js.map +1 -1
  68. package/dist/file/index.d.ts +8 -0
  69. package/dist/file/index.d.ts.map +1 -1
  70. package/dist/file/index.js +3 -0
  71. package/dist/file/index.js.map +1 -1
  72. package/dist/lock/redis/index.js.map +1 -1
  73. package/dist/logger/index.js.map +1 -1
  74. package/dist/mcp/index.d.ts.map +1 -1
  75. package/dist/mcp/index.js.map +1 -1
  76. package/dist/orm/index.browser.js +26 -5
  77. package/dist/orm/index.browser.js.map +1 -1
  78. package/dist/orm/index.d.ts +146 -121
  79. package/dist/orm/index.d.ts.map +1 -1
  80. package/dist/orm/index.js +49 -24
  81. package/dist/orm/index.js.map +1 -1
  82. package/dist/redis/index.js.map +1 -1
  83. package/dist/retry/index.js.map +1 -1
  84. package/dist/router/index.js.map +1 -1
  85. package/dist/scheduler/index.d.ts +6 -6
  86. package/dist/scheduler/index.js.map +1 -1
  87. package/dist/security/index.d.ts +29 -29
  88. package/dist/security/index.d.ts.map +1 -1
  89. package/dist/security/index.js +1 -1
  90. package/dist/security/index.js.map +1 -1
  91. package/dist/server/auth/index.d.ts +171 -155
  92. package/dist/server/auth/index.d.ts.map +1 -1
  93. package/dist/server/auth/index.js +0 -1
  94. package/dist/server/auth/index.js.map +1 -1
  95. package/dist/server/cache/index.js.map +1 -1
  96. package/dist/server/compress/index.d.ts.map +1 -1
  97. package/dist/server/compress/index.js +2 -0
  98. package/dist/server/compress/index.js.map +1 -1
  99. package/dist/server/cookies/index.browser.js.map +1 -1
  100. package/dist/server/cookies/index.js.map +1 -1
  101. package/dist/server/core/index.browser.js.map +1 -1
  102. package/dist/server/core/index.d.ts.map +1 -1
  103. package/dist/server/core/index.js +1 -1
  104. package/dist/server/core/index.js.map +1 -1
  105. package/dist/server/health/index.d.ts +17 -17
  106. package/dist/server/helmet/index.js.map +1 -1
  107. package/dist/server/links/index.browser.js +22 -6
  108. package/dist/server/links/index.browser.js.map +1 -1
  109. package/dist/server/links/index.d.ts +46 -44
  110. package/dist/server/links/index.d.ts.map +1 -1
  111. package/dist/server/links/index.js +24 -41
  112. package/dist/server/links/index.js.map +1 -1
  113. package/dist/server/multipart/index.js.map +1 -1
  114. package/dist/server/rate-limit/index.js.map +1 -1
  115. package/dist/server/security/index.js.map +1 -1
  116. package/dist/server/swagger/index.d.ts +2 -1
  117. package/dist/server/swagger/index.d.ts.map +1 -1
  118. package/dist/server/swagger/index.js +8 -3
  119. package/dist/server/swagger/index.js.map +1 -1
  120. package/dist/thread/index.js.map +1 -1
  121. package/dist/topic/core/index.js.map +1 -1
  122. package/dist/vite/index.d.ts.map +1 -1
  123. package/dist/vite/index.js +12 -4
  124. package/dist/vite/index.js.map +1 -1
  125. package/dist/websocket/index.browser.js.map +1 -1
  126. package/dist/websocket/index.js.map +1 -1
  127. package/package.json +7 -7
  128. package/src/api/audits/controllers/{AuditController.ts → AdminAuditController.ts} +5 -6
  129. package/src/api/audits/entities/audits.ts +5 -5
  130. package/src/api/audits/index.browser.ts +1 -1
  131. package/src/api/audits/index.ts +3 -3
  132. package/src/api/audits/primitives/$audit.spec.ts +276 -0
  133. package/src/api/audits/services/AuditService.spec.ts +495 -0
  134. package/src/api/files/__tests__/$bucket.spec.ts +91 -0
  135. package/src/api/files/controllers/AdminFileStatsController.spec.ts +166 -0
  136. package/src/api/files/controllers/{StorageStatsController.ts → AdminFileStatsController.ts} +2 -2
  137. package/src/api/files/controllers/FileController.spec.ts +558 -0
  138. package/src/api/files/controllers/FileController.ts +4 -5
  139. package/src/api/files/entities/files.ts +5 -5
  140. package/src/api/files/index.browser.ts +1 -1
  141. package/src/api/files/index.ts +4 -4
  142. package/src/api/files/jobs/FileJobs.spec.ts +52 -0
  143. package/src/api/files/services/FileService.spec.ts +109 -0
  144. package/src/api/jobs/__tests__/JobController.spec.ts +343 -0
  145. package/src/api/jobs/controllers/{JobController.ts → AdminJobController.ts} +2 -2
  146. package/src/api/jobs/entities/jobExecutions.ts +5 -5
  147. package/src/api/jobs/index.ts +3 -3
  148. package/src/api/jobs/primitives/$job.spec.ts +476 -0
  149. package/src/api/notifications/controllers/{NotificationController.ts → AdminNotificationController.ts} +4 -5
  150. package/src/api/notifications/entities/notifications.ts +5 -5
  151. package/src/api/notifications/index.browser.ts +1 -1
  152. package/src/api/notifications/index.ts +4 -4
  153. package/src/api/parameters/controllers/{ConfigController.ts → AdminConfigController.ts} +46 -107
  154. package/src/api/parameters/entities/parameters.ts +7 -17
  155. package/src/api/parameters/index.ts +3 -3
  156. package/src/api/parameters/primitives/$config.spec.ts +356 -0
  157. package/src/api/parameters/schemas/activateConfigBodySchema.ts +12 -0
  158. package/src/api/parameters/schemas/checkScheduledResponseSchema.ts +8 -0
  159. package/src/api/parameters/schemas/configCurrentResponseSchema.ts +13 -0
  160. package/src/api/parameters/schemas/configHistoryResponseSchema.ts +9 -0
  161. package/src/api/parameters/schemas/configNameParamSchema.ts +10 -0
  162. package/src/api/parameters/schemas/configNamesResponseSchema.ts +8 -0
  163. package/src/api/parameters/schemas/configTreeNodeSchema.ts +13 -0
  164. package/src/api/parameters/schemas/configVersionParamSchema.ts +9 -0
  165. package/src/api/parameters/schemas/configVersionResponseSchema.ts +9 -0
  166. package/src/api/parameters/schemas/configsByStatusResponseSchema.ts +9 -0
  167. package/src/api/parameters/schemas/createConfigVersionBodySchema.ts +24 -0
  168. package/src/api/parameters/schemas/index.ts +15 -0
  169. package/src/api/parameters/schemas/parameterResponseSchema.ts +26 -0
  170. package/src/api/parameters/schemas/parameterStatusSchema.ts +13 -0
  171. package/src/api/parameters/schemas/rollbackConfigBodySchema.ts +15 -0
  172. package/src/api/parameters/schemas/statusParamSchema.ts +9 -0
  173. package/src/api/users/__tests__/EmailVerification.spec.ts +369 -0
  174. package/src/api/users/__tests__/PasswordReset.spec.ts +550 -0
  175. package/src/api/users/controllers/AdminIdentityController.spec.ts +365 -0
  176. package/src/api/users/controllers/{IdentityController.ts → AdminIdentityController.ts} +3 -4
  177. package/src/api/users/controllers/AdminSessionController.spec.ts +274 -0
  178. package/src/api/users/controllers/{SessionController.ts → AdminSessionController.ts} +3 -4
  179. package/src/api/users/controllers/AdminUserController.spec.ts +372 -0
  180. package/src/api/users/controllers/AdminUserController.ts +116 -0
  181. package/src/api/users/controllers/UserController.ts +4 -107
  182. package/src/api/users/controllers/UserRealmController.ts +3 -0
  183. package/src/api/users/entities/identities.ts +6 -6
  184. package/src/api/users/entities/sessions.ts +6 -6
  185. package/src/api/users/entities/users.ts +9 -9
  186. package/src/api/users/index.ts +13 -6
  187. package/src/api/users/primitives/$userRealm.ts +13 -8
  188. package/src/api/users/services/CredentialService.spec.ts +509 -0
  189. package/src/api/users/services/CredentialService.ts +46 -0
  190. package/src/api/users/services/IdentityService.ts +15 -0
  191. package/src/api/users/services/RegistrationService.spec.ts +630 -0
  192. package/src/api/users/services/RegistrationService.ts +18 -0
  193. package/src/api/users/services/SessionService.spec.ts +301 -0
  194. package/src/api/users/services/SessionService.ts +110 -1
  195. package/src/api/users/services/UserService.ts +67 -2
  196. package/src/api/verifications/__tests__/CodeVerification.spec.ts +318 -0
  197. package/src/api/verifications/__tests__/LinkVerification.spec.ts +279 -0
  198. package/src/api/verifications/entities/verifications.ts +6 -6
  199. package/src/api/verifications/jobs/VerificationJobs.spec.ts +50 -0
  200. package/src/batch/__tests__/startup-buffering.spec.ts +458 -0
  201. package/src/batch/primitives/$batch.spec.ts +766 -0
  202. package/src/batch/providers/BatchProvider.spec.ts +786 -0
  203. package/src/bin/index.ts +0 -1
  204. package/src/bucket/__tests__/shared.ts +194 -0
  205. package/src/bucket/primitives/$bucket.spec.ts +104 -0
  206. package/src/bucket/providers/FileStorageProvider.spec.ts +13 -0
  207. package/src/bucket/providers/LocalFileStorageProvider.spec.ts +77 -0
  208. package/src/bucket/providers/MemoryFileStorageProvider.spec.ts +82 -0
  209. package/src/cache/core/__tests__/shared.ts +377 -0
  210. package/src/cache/core/primitives/$cache.spec.ts +111 -0
  211. package/src/cache/redis/__tests__/cache-redis.spec.ts +70 -0
  212. package/src/cli/apps/AlephaCli.ts +54 -16
  213. package/src/cli/apps/AlephaPackageBuilderCli.ts +2 -1
  214. package/src/cli/assets/appRouterTs.ts +1 -1
  215. package/src/cli/commands/{ViteCommands.ts → build.ts} +2 -105
  216. package/src/cli/commands/clean.ts +14 -0
  217. package/src/cli/commands/{DrizzleCommands.ts → db.ts} +10 -117
  218. package/src/cli/commands/{DeployCommands.ts → deploy.ts} +1 -1
  219. package/src/cli/commands/dev.ts +69 -0
  220. package/src/cli/commands/format.ts +17 -0
  221. package/src/cli/commands/gen/changelog.spec.ts +315 -0
  222. package/src/cli/commands/{ChangelogCommands.ts → gen/changelog.ts} +16 -31
  223. package/src/cli/commands/gen/openapi.ts +71 -0
  224. package/src/cli/commands/gen.ts +18 -0
  225. package/src/cli/commands/{CoreCommands.ts → init.ts} +4 -40
  226. package/src/cli/commands/lint.ts +17 -0
  227. package/src/cli/commands/root.ts +41 -0
  228. package/src/cli/commands/run.ts +24 -0
  229. package/src/cli/commands/test.ts +42 -0
  230. package/src/cli/commands/typecheck.ts +24 -0
  231. package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
  232. package/src/cli/defineConfig.ts +10 -1
  233. package/src/cli/index.ts +17 -7
  234. package/src/cli/services/AlephaCliUtils.ts +71 -32
  235. package/src/cli/services/GitMessageParser.ts +1 -1
  236. package/src/command/helpers/Asker.spec.ts +127 -0
  237. package/src/command/helpers/Runner.spec.ts +126 -0
  238. package/src/command/primitives/$command.spec.ts +1588 -0
  239. package/src/command/providers/CliProvider.ts +74 -24
  240. package/src/core/Alepha.ts +52 -4
  241. package/src/core/__tests__/Alepha-emit.spec.ts +22 -0
  242. package/src/core/__tests__/Alepha-graph.spec.ts +93 -0
  243. package/src/core/__tests__/Alepha-has.spec.ts +41 -0
  244. package/src/core/__tests__/Alepha-inject.spec.ts +93 -0
  245. package/src/core/__tests__/Alepha-register.spec.ts +81 -0
  246. package/src/core/__tests__/Alepha-start.spec.ts +176 -0
  247. package/src/core/__tests__/Alepha-with.spec.ts +14 -0
  248. package/src/core/__tests__/TypeBox-usecases.spec.ts +35 -0
  249. package/src/core/__tests__/TypeBoxLocale.spec.ts +15 -0
  250. package/src/core/__tests__/descriptor.spec.ts +34 -0
  251. package/src/core/__tests__/fixtures/A.ts +5 -0
  252. package/src/core/__tests__/pagination.spec.ts +77 -0
  253. package/src/core/helpers/jsonSchemaToTypeBox.ts +2 -2
  254. package/src/core/primitives/$atom.spec.ts +43 -0
  255. package/src/core/primitives/$hook.spec.ts +130 -0
  256. package/src/core/primitives/$inject.spec.ts +175 -0
  257. package/src/core/primitives/$module.spec.ts +115 -0
  258. package/src/core/providers/CodecManager.spec.ts +740 -0
  259. package/src/core/providers/EventManager.spec.ts +762 -0
  260. package/src/core/providers/EventManager.ts +4 -0
  261. package/src/core/providers/StateManager.spec.ts +365 -0
  262. package/src/core/providers/TypeProvider.spec.ts +1607 -0
  263. package/src/core/providers/TypeProvider.ts +20 -26
  264. package/src/datetime/primitives/$interval.spec.ts +103 -0
  265. package/src/datetime/providers/DateTimeProvider.spec.ts +86 -0
  266. package/src/email/primitives/$email.spec.ts +175 -0
  267. package/src/email/providers/LocalEmailProvider.spec.ts +341 -0
  268. package/src/fake/__tests__/keyName.example.ts +40 -0
  269. package/src/fake/__tests__/keyName.spec.ts +152 -0
  270. package/src/fake/__tests__/module.example.ts +32 -0
  271. package/src/fake/providers/FakeProvider.spec.ts +438 -0
  272. package/src/file/providers/FileSystemProvider.ts +8 -0
  273. package/src/file/providers/NodeFileSystemProvider.spec.ts +418 -0
  274. package/src/file/providers/NodeFileSystemProvider.ts +5 -0
  275. package/src/file/services/FileDetector.spec.ts +591 -0
  276. package/src/lock/core/__tests__/shared.ts +190 -0
  277. package/src/lock/core/providers/MemoryLockProvider.spec.ts +25 -0
  278. package/src/lock/redis/providers/RedisLockProvider.spec.ts +25 -0
  279. package/src/logger/__tests__/SimpleFormatterProvider.spec.ts +109 -0
  280. package/src/logger/primitives/$logger.spec.ts +108 -0
  281. package/src/logger/services/Logger.spec.ts +295 -0
  282. package/src/mcp/__tests__/errors.spec.ts +175 -0
  283. package/src/mcp/__tests__/integration.spec.ts +450 -0
  284. package/src/mcp/helpers/jsonrpc.spec.ts +380 -0
  285. package/src/mcp/primitives/$prompt.spec.ts +468 -0
  286. package/src/mcp/primitives/$resource.spec.ts +390 -0
  287. package/src/mcp/primitives/$tool.spec.ts +406 -0
  288. package/src/mcp/providers/McpServerProvider.spec.ts +797 -0
  289. package/src/orm/__tests__/$repository-crud.spec.ts +276 -0
  290. package/src/orm/__tests__/$repository-hooks.spec.ts +325 -0
  291. package/src/orm/__tests__/$repository-orderBy.spec.ts +128 -0
  292. package/src/orm/__tests__/$repository-pagination-sort.spec.ts +149 -0
  293. package/src/orm/__tests__/$repository-save.spec.ts +37 -0
  294. package/src/orm/__tests__/ModelBuilder-integration.spec.ts +490 -0
  295. package/src/orm/__tests__/ModelBuilder-types.spec.ts +186 -0
  296. package/src/orm/__tests__/PostgresProvider.spec.ts +46 -0
  297. package/src/orm/__tests__/delete-returning.spec.ts +256 -0
  298. package/src/orm/__tests__/deletedAt.spec.ts +80 -0
  299. package/src/orm/__tests__/enums.spec.ts +315 -0
  300. package/src/orm/__tests__/execute.spec.ts +72 -0
  301. package/src/orm/__tests__/fixtures/bigEntitySchema.ts +65 -0
  302. package/src/orm/__tests__/fixtures/userEntitySchema.ts +27 -0
  303. package/src/orm/__tests__/joins.spec.ts +1114 -0
  304. package/src/orm/__tests__/page.spec.ts +287 -0
  305. package/src/orm/__tests__/primaryKey.spec.ts +87 -0
  306. package/src/orm/__tests__/query-date-encoding.spec.ts +402 -0
  307. package/src/orm/__tests__/ref-auto-onDelete.spec.ts +156 -0
  308. package/src/orm/__tests__/references.spec.ts +102 -0
  309. package/src/orm/__tests__/security.spec.ts +710 -0
  310. package/src/orm/__tests__/sqlite.spec.ts +111 -0
  311. package/src/orm/__tests__/string-operators.spec.ts +429 -0
  312. package/src/orm/__tests__/timestamps.spec.ts +388 -0
  313. package/src/orm/__tests__/validation.spec.ts +183 -0
  314. package/src/orm/__tests__/version.spec.ts +64 -0
  315. package/src/orm/helpers/parseQueryString.spec.ts +196 -0
  316. package/src/orm/index.browser.ts +1 -1
  317. package/src/orm/index.ts +10 -6
  318. package/src/orm/primitives/$repository.spec.ts +137 -0
  319. package/src/orm/primitives/$sequence.spec.ts +29 -0
  320. package/src/orm/primitives/$transaction.spec.ts +82 -0
  321. package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
  322. package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -3
  323. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
  324. package/src/orm/providers/drivers/CloudflareD1Provider.ts +1 -1
  325. package/src/orm/providers/drivers/DatabaseProvider.ts +1 -1
  326. package/src/orm/providers/drivers/NodePostgresProvider.ts +3 -3
  327. package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
  328. package/src/orm/providers/drivers/PglitePostgresProvider.ts +2 -2
  329. package/src/orm/services/ModelBuilder.spec.ts +575 -0
  330. package/src/orm/services/Repository.spec.ts +137 -0
  331. package/src/queue/core/__tests__/shared.ts +143 -0
  332. package/src/queue/core/providers/MemoryQueueProvider.spec.ts +23 -0
  333. package/src/queue/core/providers/WorkerProvider.spec.ts +378 -0
  334. package/src/queue/redis/providers/RedisQueueProvider.spec.ts +23 -0
  335. package/src/redis/__tests__/redis.spec.ts +58 -0
  336. package/src/retry/primitives/$retry.spec.ts +234 -0
  337. package/src/retry/providers/RetryProvider.spec.ts +438 -0
  338. package/src/router/__tests__/match.spec.ts +252 -0
  339. package/src/router/providers/RouterProvider.spec.ts +197 -0
  340. package/src/scheduler/__tests__/$scheduler-cron.spec.ts +25 -0
  341. package/src/scheduler/__tests__/$scheduler-interval.spec.ts +25 -0
  342. package/src/scheduler/__tests__/shared.ts +77 -0
  343. package/src/security/__tests__/bug-1-wildcard-after-start.spec.ts +229 -0
  344. package/src/security/__tests__/bug-2-password-validation.spec.ts +245 -0
  345. package/src/security/__tests__/bug-3-regex-vulnerability.spec.ts +407 -0
  346. package/src/security/__tests__/bug-4-oauth2-validation.spec.ts +439 -0
  347. package/src/security/__tests__/multi-layer-permissions.spec.ts +522 -0
  348. package/src/security/primitives/$permission.spec.ts +30 -0
  349. package/src/security/primitives/$permission.ts +2 -2
  350. package/src/security/primitives/$realm.spec.ts +101 -0
  351. package/src/security/primitives/$role.spec.ts +52 -0
  352. package/src/security/primitives/$serviceAccount.spec.ts +61 -0
  353. package/src/security/providers/SecurityProvider.spec.ts +350 -0
  354. package/src/server/auth/providers/ServerAuthProvider.ts +0 -2
  355. package/src/server/cache/providers/ServerCacheProvider.spec.ts +942 -0
  356. package/src/server/compress/providers/ServerCompressProvider.spec.ts +31 -0
  357. package/src/server/compress/providers/ServerCompressProvider.ts +2 -0
  358. package/src/server/cookies/providers/ServerCookiesProvider.spec.ts +253 -0
  359. package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +334 -0
  360. package/src/server/core/__tests__/ServerRouterProvider-requestId.spec.ts +129 -0
  361. package/src/server/core/primitives/$action.spec.ts +191 -0
  362. package/src/server/core/primitives/$route.spec.ts +65 -0
  363. package/src/server/core/providers/ServerBodyParserProvider.spec.ts +93 -0
  364. package/src/server/core/providers/ServerLoggerProvider.spec.ts +100 -0
  365. package/src/server/core/providers/ServerProvider.ts +3 -1
  366. package/src/server/core/services/HttpClient.spec.ts +123 -0
  367. package/src/server/core/services/UserAgentParser.spec.ts +111 -0
  368. package/src/server/cors/providers/ServerCorsProvider.spec.ts +481 -0
  369. package/src/server/health/providers/ServerHealthProvider.spec.ts +22 -0
  370. package/src/server/helmet/providers/ServerHelmetProvider.spec.ts +105 -0
  371. package/src/server/links/__tests__/$action.spec.ts +238 -0
  372. package/src/server/links/__tests__/fixtures/CrudApp.ts +122 -0
  373. package/src/server/links/__tests__/requestId.spec.ts +120 -0
  374. package/src/server/links/primitives/$remote.spec.ts +228 -0
  375. package/src/server/links/providers/LinkProvider.spec.ts +54 -0
  376. package/src/server/links/providers/LinkProvider.ts +49 -3
  377. package/src/server/links/providers/ServerLinksProvider.ts +1 -53
  378. package/src/server/links/schemas/apiLinksResponseSchema.ts +7 -0
  379. package/src/server/metrics/providers/ServerMetricsProvider.spec.ts +25 -0
  380. package/src/server/multipart/providers/ServerMultipartProvider.spec.ts +528 -0
  381. package/src/server/proxy/primitives/$proxy.spec.ts +87 -0
  382. package/src/server/rate-limit/__tests__/ActionRateLimit.spec.ts +211 -0
  383. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +344 -0
  384. package/src/server/security/__tests__/BasicAuth.spec.ts +684 -0
  385. package/src/server/security/__tests__/ServerSecurityProvider-realm.spec.ts +388 -0
  386. package/src/server/security/providers/ServerSecurityProvider.spec.ts +123 -0
  387. package/src/server/static/primitives/$serve.spec.ts +193 -0
  388. package/src/server/swagger/__tests__/ui.spec.ts +52 -0
  389. package/src/server/swagger/primitives/$swagger.spec.ts +193 -0
  390. package/src/server/swagger/providers/ServerSwaggerProvider.ts +18 -8
  391. package/src/sms/primitives/$sms.spec.ts +165 -0
  392. package/src/sms/providers/LocalSmsProvider.spec.ts +224 -0
  393. package/src/sms/providers/MemorySmsProvider.spec.ts +193 -0
  394. package/src/thread/primitives/$thread.spec.ts +186 -0
  395. package/src/topic/core/__tests__/shared.ts +144 -0
  396. package/src/topic/core/providers/MemoryTopicProvider.spec.ts +23 -0
  397. package/src/topic/redis/providers/RedisTopicProvider.spec.ts +23 -0
  398. package/src/vite/plugins/viteAlephaDev.ts +16 -4
  399. package/src/vite/tasks/runAlepha.ts +7 -1
  400. package/src/websocket/__tests__/$websocket-new.spec.ts +195 -0
  401. package/src/websocket/primitives/$channel.spec.ts +30 -0
  402. package/src/cli/commands/BiomeCommands.ts +0 -29
@@ -0,0 +1,786 @@
1
+ import { Alepha } from "alepha";
2
+ import { DateTimeProvider } from "alepha/datetime";
3
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
4
+ import { AlephaBatch, BatchProvider } from "../index.ts";
5
+
6
+ describe("BatchProvider", () => {
7
+ let alepha: Alepha;
8
+ let batchProvider: BatchProvider;
9
+ let time: DateTimeProvider;
10
+
11
+ beforeEach(async () => {
12
+ alepha = Alepha.create().with(AlephaBatch);
13
+ batchProvider = alepha.inject(BatchProvider);
14
+ time = alepha.inject(DateTimeProvider);
15
+ await alepha.start();
16
+ });
17
+
18
+ afterEach(async () => {
19
+ await alepha.stop();
20
+ });
21
+
22
+ test("should create a batch context", () => {
23
+ const handler = vi.fn();
24
+ const context = batchProvider.createContext(alepha, { handler });
25
+
26
+ expect(context).toBeDefined();
27
+ expect(context.itemStates).toBeInstanceOf(Map);
28
+ expect(context.partitions).toBeInstanceOf(Map);
29
+ expect(context.activeHandlers).toEqual([]);
30
+ expect(context.isShuttingDown).toBe(false);
31
+ expect(context.isReady).toBe(false);
32
+ });
33
+
34
+ test("should push items and return unique IDs", () => {
35
+ const handler = vi.fn();
36
+ const context = batchProvider.createContext(alepha, { handler });
37
+
38
+ const id1 = batchProvider.push(context, "item-1");
39
+ const id2 = batchProvider.push(context, "item-2");
40
+
41
+ expect(id1).toBeDefined();
42
+ expect(id2).toBeDefined();
43
+ expect(id1).not.toBe(id2);
44
+
45
+ // Verify IDs are valid UUIDs
46
+ const uuidRegex =
47
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
48
+ expect(id1).toMatch(uuidRegex);
49
+ expect(id2).toMatch(uuidRegex);
50
+ });
51
+
52
+ test("should store items in the correct partition", () => {
53
+ const handler = vi.fn();
54
+ const context = batchProvider.createContext(alepha, {
55
+ handler,
56
+ partitionBy: (item: { key: string; value: number }) => item.key,
57
+ });
58
+
59
+ batchProvider.push(context, { key: "A", value: 1 });
60
+ batchProvider.push(context, { key: "B", value: 2 });
61
+ batchProvider.push(context, { key: "A", value: 3 });
62
+
63
+ expect(context.partitions.has("A")).toBe(true);
64
+ expect(context.partitions.has("B")).toBe(true);
65
+
66
+ const partitionA = context.partitions.get("A");
67
+ const partitionB = context.partitions.get("B");
68
+
69
+ expect(partitionA?.itemIds).toHaveLength(2);
70
+ expect(partitionB?.itemIds).toHaveLength(1);
71
+ });
72
+
73
+ test("should use default partition when partitionBy is not provided", () => {
74
+ const handler = vi.fn();
75
+ const context = batchProvider.createContext(alepha, { handler });
76
+
77
+ batchProvider.push(context, "item-1");
78
+ batchProvider.push(context, "item-2");
79
+
80
+ expect(context.partitions.has("default")).toBe(true);
81
+ expect(context.partitions.get("default")?.itemIds).toHaveLength(2);
82
+ });
83
+
84
+ test("should get item status", () => {
85
+ const handler = vi.fn();
86
+ const context = batchProvider.createContext(alepha, { handler });
87
+
88
+ const id = batchProvider.push(context, "item-1");
89
+
90
+ const status = batchProvider.status(context, id);
91
+ expect(status).toEqual({ status: "pending" });
92
+ });
93
+
94
+ test("should return undefined status for non-existent item", () => {
95
+ const handler = vi.fn();
96
+ const context = batchProvider.createContext(alepha, { handler });
97
+
98
+ const status = batchProvider.status(context, "non-existent-id");
99
+ expect(status).toBeUndefined();
100
+ });
101
+
102
+ test("should throw error when waiting for non-existent item", async () => {
103
+ const handler = vi.fn();
104
+ const context = batchProvider.createContext(alepha, { handler });
105
+
106
+ await expect(
107
+ batchProvider.wait(context, "non-existent-id"),
108
+ ).rejects.toThrow("Item with id 'non-existent-id' not found");
109
+ });
110
+
111
+ test("should flush partition and process items", async () => {
112
+ const handler = vi.fn(async (items: string[]) => {
113
+ return items.map((item) => `processed-${item}`);
114
+ });
115
+ const context = batchProvider.createContext(alepha, { handler });
116
+
117
+ const id1 = batchProvider.push(context, "item-1");
118
+ const id2 = batchProvider.push(context, "item-2");
119
+
120
+ await batchProvider.flush(context);
121
+
122
+ expect(handler).toHaveBeenCalledTimes(1);
123
+ expect(handler).toHaveBeenCalledWith(["item-1", "item-2"]);
124
+
125
+ // Check status after flush
126
+ const status1 = batchProvider.status(context, id1);
127
+ const status2 = batchProvider.status(context, id2);
128
+
129
+ expect(status1?.status).toBe("completed");
130
+ expect(status2?.status).toBe("completed");
131
+ });
132
+
133
+ test("should flush specific partition only", async () => {
134
+ const handler = vi.fn(async (items: any[]) => "processed");
135
+ const context = batchProvider.createContext(alepha, {
136
+ handler,
137
+ partitionBy: (item: { key: string; value: number }) => item.key,
138
+ });
139
+
140
+ batchProvider.push(context, { key: "A", value: 1 });
141
+ batchProvider.push(context, { key: "B", value: 2 });
142
+
143
+ await batchProvider.flush(context, "A");
144
+
145
+ expect(handler).toHaveBeenCalledTimes(1);
146
+ expect(handler).toHaveBeenCalledWith([{ key: "A", value: 1 }]);
147
+
148
+ // Partition B should still have items
149
+ expect(context.partitions.has("B")).toBe(true);
150
+ expect(context.partitions.get("B")?.itemIds).toHaveLength(1);
151
+ });
152
+
153
+ test("should wait for item result after processing", async () => {
154
+ const handler = vi.fn(async (items: string[]) => {
155
+ return `batch-result-${items.length}`;
156
+ });
157
+ const context = batchProvider.createContext(alepha, { handler });
158
+
159
+ const id = batchProvider.push(context, "item-1");
160
+
161
+ // Start wait before flush (promise created)
162
+ const waitPromise = batchProvider.wait(context, id);
163
+
164
+ // Flush to process items
165
+ await batchProvider.flush(context);
166
+
167
+ const result = await waitPromise;
168
+ expect(result).toBe("batch-result-1");
169
+ });
170
+
171
+ test("should return cached result on subsequent wait calls", async () => {
172
+ const handler = vi.fn(async () => "result");
173
+ const context = batchProvider.createContext(alepha, { handler });
174
+
175
+ const id = batchProvider.push(context, "item");
176
+ await batchProvider.flush(context);
177
+
178
+ const result1 = await batchProvider.wait(context, id);
179
+ const result2 = await batchProvider.wait(context, id);
180
+
181
+ expect(result1).toBe("result");
182
+ expect(result2).toBe("result");
183
+ expect(handler).toHaveBeenCalledTimes(1);
184
+ });
185
+
186
+ test("should handle handler errors and mark items as failed", async () => {
187
+ const handler = vi.fn(async () => {
188
+ throw new Error("Handler failed");
189
+ });
190
+ const context = batchProvider.createContext(alepha, { handler });
191
+
192
+ const id = batchProvider.push(context, "item");
193
+
194
+ await batchProvider.flush(context);
195
+
196
+ const status = batchProvider.status(context, id);
197
+ expect(status?.status).toBe("failed");
198
+ if (status?.status === "failed") {
199
+ expect(status.error.message).toBe("Handler failed");
200
+ }
201
+ });
202
+
203
+ test("should reject wait promise on handler error", async () => {
204
+ const handler = vi.fn(async () => {
205
+ throw new Error("Handler failed");
206
+ });
207
+ const context = batchProvider.createContext(alepha, { handler });
208
+
209
+ const id = batchProvider.push(context, "item");
210
+ const waitPromise = batchProvider.wait(context, id);
211
+
212
+ await batchProvider.flush(context);
213
+
214
+ await expect(waitPromise).rejects.toThrow("Handler failed");
215
+ });
216
+
217
+ test("should respect maxSize when flushing", async () => {
218
+ const handler = vi.fn(async (items: string[]) => `batch-${items.length}`);
219
+ const context = batchProvider.createContext(alepha, {
220
+ handler,
221
+ maxSize: 2,
222
+ });
223
+
224
+ batchProvider.push(context, "item-1");
225
+ batchProvider.push(context, "item-2");
226
+ batchProvider.push(context, "item-3");
227
+ batchProvider.push(context, "item-4");
228
+ batchProvider.push(context, "item-5");
229
+
230
+ // Mark context as ready to enable size-based flushing
231
+ await batchProvider.markReady(context);
232
+
233
+ // Should have flushed twice (2 items each), leaving 1 item
234
+ await vi.waitFor(() => {
235
+ expect(handler).toHaveBeenCalledTimes(2);
236
+ });
237
+
238
+ expect(handler).toHaveBeenNthCalledWith(1, ["item-1", "item-2"]);
239
+ expect(handler).toHaveBeenNthCalledWith(2, ["item-3", "item-4"]);
240
+ });
241
+
242
+ test("should respect concurrency limit", async () => {
243
+ // This test verifies that concurrency limits work by measuring
244
+ // when handlers start - first 2 should start together, next 2 after delay
245
+
246
+ const handlerStartTimes: number[] = [];
247
+
248
+ const handler = vi.fn(async (items: string[]) => {
249
+ const startTime = Date.now();
250
+ handlerStartTimes.push(startTime);
251
+ // Use a real delay (not time.wait) to ensure parallel execution
252
+ await new Promise((resolve) => setTimeout(resolve, 50));
253
+ return "done";
254
+ });
255
+
256
+ const context = batchProvider.createContext(alepha, {
257
+ handler,
258
+ maxSize: 1,
259
+ concurrency: 2,
260
+ partitionBy: (item: string) => item, // Each item in its own partition
261
+ });
262
+
263
+ batchProvider.push(context, "a");
264
+ batchProvider.push(context, "b");
265
+ batchProvider.push(context, "c");
266
+ batchProvider.push(context, "d");
267
+
268
+ // Flush all partitions - this should respect concurrency
269
+ await batchProvider.flush(context);
270
+
271
+ expect(handler).toHaveBeenCalledTimes(4);
272
+
273
+ // With concurrency=2, first 2 handlers should start at roughly the same time,
274
+ // and the other 2 should start ~50ms later (after first batch completes)
275
+ const [t1, t2, t3, t4] = handlerStartTimes;
276
+
277
+ // First two should start within 10ms of each other
278
+ expect(Math.abs(t1 - t2)).toBeLessThan(20);
279
+
280
+ // Third and fourth should start after first batch (50ms later)
281
+ // They should also start within 10ms of each other
282
+ expect(Math.abs(t3 - t4)).toBeLessThan(20);
283
+
284
+ // Gap between first and third should be around 50ms (handler duration)
285
+ expect(t3 - t1).toBeGreaterThanOrEqual(40);
286
+ });
287
+
288
+ test("should markReady and start processing buffered items", async () => {
289
+ const handler = vi.fn(async (items: string[]) => "processed");
290
+ const context = batchProvider.createContext(alepha, {
291
+ handler,
292
+ maxSize: 5,
293
+ });
294
+
295
+ // Push items before marking ready
296
+ batchProvider.push(context, "item-1");
297
+ batchProvider.push(context, "item-2");
298
+ batchProvider.push(context, "item-3");
299
+ batchProvider.push(context, "item-4");
300
+ batchProvider.push(context, "item-5");
301
+
302
+ expect(handler).not.toHaveBeenCalled();
303
+ expect(context.isReady).toBe(false);
304
+
305
+ await batchProvider.markReady(context);
306
+
307
+ expect(context.isReady).toBe(true);
308
+
309
+ await vi.waitFor(() => {
310
+ expect(handler).toHaveBeenCalledTimes(1);
311
+ });
312
+ });
313
+
314
+ test("should handle shutdown and flush all remaining items", async () => {
315
+ const handler = vi.fn(async (items: string[]) => "processed");
316
+ const context = batchProvider.createContext(alepha, {
317
+ handler,
318
+ maxSize: 100, // Large size to prevent auto-flush
319
+ });
320
+
321
+ batchProvider.push(context, "item-1");
322
+ batchProvider.push(context, "item-2");
323
+
324
+ expect(handler).not.toHaveBeenCalled();
325
+
326
+ await batchProvider.shutdown(context);
327
+
328
+ expect(context.isShuttingDown).toBe(true);
329
+ expect(handler).toHaveBeenCalledTimes(1);
330
+ expect(handler).toHaveBeenCalledWith(["item-1", "item-2"]);
331
+ });
332
+
333
+ test("should handle empty flush gracefully", async () => {
334
+ const handler = vi.fn();
335
+ const context = batchProvider.createContext(alepha, { handler });
336
+
337
+ await batchProvider.flush(context);
338
+
339
+ expect(handler).not.toHaveBeenCalled();
340
+ });
341
+
342
+ test("should handle flush of non-existent partition gracefully", async () => {
343
+ const handler = vi.fn();
344
+ const context = batchProvider.createContext(alepha, { handler });
345
+
346
+ await batchProvider.flush(context, "non-existent");
347
+
348
+ expect(handler).not.toHaveBeenCalled();
349
+ });
350
+
351
+ test("should use default options when not specified", () => {
352
+ const handler = vi.fn();
353
+ const context = batchProvider.createContext(alepha, { handler });
354
+
355
+ // Access protected methods through any cast for testing
356
+ const provider = batchProvider as any;
357
+
358
+ expect(provider.getMaxSize(context)).toBe(10);
359
+ expect(provider.getConcurrency(context)).toBe(1);
360
+ expect(provider.getMaxDuration(context)).toEqual([1, "second"]);
361
+ });
362
+
363
+ test("should use custom options when specified", () => {
364
+ const handler = vi.fn();
365
+ const context = batchProvider.createContext(alepha, {
366
+ handler,
367
+ maxSize: 50,
368
+ concurrency: 5,
369
+ maxDuration: [30, "seconds"],
370
+ });
371
+
372
+ const provider = batchProvider as any;
373
+
374
+ expect(provider.getMaxSize(context)).toBe(50);
375
+ expect(provider.getConcurrency(context)).toBe(5);
376
+ expect(provider.getMaxDuration(context)).toEqual([30, "seconds"]);
377
+ });
378
+
379
+ test("should retry failed handler with retry options", async () => {
380
+ let attempts = 0;
381
+ const handler = vi.fn(async (items: string[]) => {
382
+ attempts++;
383
+ if (attempts < 3) {
384
+ throw new Error("Temporary failure");
385
+ }
386
+ return "success";
387
+ });
388
+
389
+ const context = batchProvider.createContext(alepha, {
390
+ handler,
391
+ retry: { max: 3, backoff: 10 },
392
+ });
393
+
394
+ const id = batchProvider.push(context, "item");
395
+ await batchProvider.flush(context);
396
+
397
+ const status = batchProvider.status(context, id);
398
+ expect(status?.status).toBe("completed");
399
+ expect(handler).toHaveBeenCalledTimes(3);
400
+ });
401
+
402
+ test("should fail after max retries exceeded", async () => {
403
+ const handler = vi.fn(async () => {
404
+ throw new Error("Always fails");
405
+ });
406
+
407
+ const context = batchProvider.createContext(alepha, {
408
+ handler,
409
+ retry: { max: 2, backoff: 10 },
410
+ });
411
+
412
+ const id = batchProvider.push(context, "item");
413
+ await batchProvider.flush(context);
414
+
415
+ const status = batchProvider.status(context, id);
416
+ expect(status?.status).toBe("failed");
417
+ expect(handler).toHaveBeenCalledTimes(2);
418
+ });
419
+
420
+ test("should handle timeout-based flushing when ready", async () => {
421
+ const handler = vi.fn(async (items: string[]) => "processed");
422
+ const context = batchProvider.createContext(alepha, {
423
+ handler,
424
+ maxSize: 100,
425
+ maxDuration: [100, "milliseconds"],
426
+ });
427
+
428
+ await batchProvider.markReady(context);
429
+
430
+ batchProvider.push(context, "item-1");
431
+ batchProvider.push(context, "item-2");
432
+
433
+ expect(handler).not.toHaveBeenCalled();
434
+
435
+ // Wait for timeout
436
+ await time.travel([150, "milliseconds"]);
437
+
438
+ await vi.waitFor(() => {
439
+ expect(handler).toHaveBeenCalledTimes(1);
440
+ });
441
+
442
+ expect(handler).toHaveBeenCalledWith(["item-1", "item-2"]);
443
+ });
444
+
445
+ test("should not create timeout for items pushed before ready", async () => {
446
+ const handler = vi.fn(async () => "processed");
447
+ const context = batchProvider.createContext(alepha, {
448
+ handler,
449
+ maxSize: 100,
450
+ maxDuration: [100, "milliseconds"],
451
+ });
452
+
453
+ // Push before ready
454
+ batchProvider.push(context, "item");
455
+
456
+ // Travel time forward before marking ready
457
+ await time.travel([200, "milliseconds"]);
458
+
459
+ // Handler should NOT have been called
460
+ expect(handler).not.toHaveBeenCalled();
461
+
462
+ // Now mark ready
463
+ await batchProvider.markReady(context);
464
+
465
+ // Wait for timeout
466
+ await time.travel([150, "milliseconds"]);
467
+
468
+ await vi.waitFor(() => {
469
+ expect(handler).toHaveBeenCalledTimes(1);
470
+ });
471
+ });
472
+
473
+ test("should handle items arriving during flush", async () => {
474
+ let flushCount = 0;
475
+ const handler = vi.fn(async (items: string[]) => {
476
+ flushCount++;
477
+ await time.wait(50); // Simulate slow processing
478
+ return `batch-${flushCount}`;
479
+ });
480
+
481
+ const context = batchProvider.createContext(alepha, {
482
+ handler,
483
+ maxSize: 2,
484
+ });
485
+
486
+ await batchProvider.markReady(context);
487
+
488
+ // Push 2 items to trigger first flush
489
+ batchProvider.push(context, "item-1");
490
+ batchProvider.push(context, "item-2");
491
+
492
+ // Wait for processing to start
493
+ await time.wait(10);
494
+
495
+ // Push more items during processing
496
+ batchProvider.push(context, "item-3");
497
+ batchProvider.push(context, "item-4");
498
+
499
+ // Wait for all processing
500
+ await vi.waitFor(() => {
501
+ expect(handler).toHaveBeenCalledTimes(2);
502
+ });
503
+
504
+ expect(handler).toHaveBeenNthCalledWith(1, ["item-1", "item-2"]);
505
+ expect(handler).toHaveBeenNthCalledWith(2, ["item-3", "item-4"]);
506
+ });
507
+
508
+ test("should skip retries during shutdown", async () => {
509
+ let attempts = 0;
510
+ const handler = vi.fn(async () => {
511
+ attempts++;
512
+ throw new Error("Always fails");
513
+ });
514
+
515
+ const context = batchProvider.createContext(alepha, {
516
+ handler,
517
+ retry: { max: 5, backoff: 100 },
518
+ });
519
+
520
+ batchProvider.push(context, "item");
521
+
522
+ // Shutdown should call handler directly without retries
523
+ await batchProvider.shutdown(context);
524
+
525
+ // Handler should only be called once (no retries during shutdown)
526
+ expect(handler).toHaveBeenCalledTimes(1);
527
+ });
528
+
529
+ test("should mark items as processing during flush", async () => {
530
+ let statusDuringProcessing: any;
531
+ const handler = vi.fn(async (items: string[]) => {
532
+ // Capture status while processing
533
+ statusDuringProcessing = batchProvider.status(context, id);
534
+ await time.wait(10);
535
+ return "done";
536
+ });
537
+
538
+ const context = batchProvider.createContext(alepha, { handler });
539
+
540
+ const id = batchProvider.push(context, "item");
541
+
542
+ // Status before flush
543
+ expect(batchProvider.status(context, id)?.status).toBe("pending");
544
+
545
+ const flushPromise = batchProvider.flush(context);
546
+
547
+ await flushPromise;
548
+
549
+ // Status should have been "processing" during handler execution
550
+ expect(statusDuringProcessing?.status).toBe("processing");
551
+
552
+ // Status after flush
553
+ expect(batchProvider.status(context, id)?.status).toBe("completed");
554
+ });
555
+
556
+ test("should clear partition timeout on flush", async () => {
557
+ const handler = vi.fn(async () => "processed");
558
+ const context = batchProvider.createContext(alepha, {
559
+ handler,
560
+ maxSize: 100,
561
+ maxDuration: [5, "seconds"],
562
+ });
563
+
564
+ await batchProvider.markReady(context);
565
+
566
+ batchProvider.push(context, "item");
567
+
568
+ const partition = context.partitions.get("default");
569
+ expect(partition?.timeout).toBeDefined();
570
+
571
+ await batchProvider.flush(context);
572
+
573
+ // Partition should be deleted after flush
574
+ expect(context.partitions.has("default")).toBe(false);
575
+ });
576
+
577
+ test("should restart timeout after flush if items remain", async () => {
578
+ const handler = vi.fn(async (items: string[]) => {
579
+ // Slow handler to allow new items to arrive
580
+ await time.wait(50);
581
+ return "processed";
582
+ });
583
+
584
+ const context = batchProvider.createContext(alepha, {
585
+ handler,
586
+ maxSize: 2,
587
+ maxDuration: [100, "milliseconds"],
588
+ });
589
+
590
+ await batchProvider.markReady(context);
591
+
592
+ // Push 2 items to trigger flush
593
+ batchProvider.push(context, "item-1");
594
+ batchProvider.push(context, "item-2");
595
+
596
+ // Wait for processing to start
597
+ await time.wait(10);
598
+
599
+ // Push another item during processing
600
+ batchProvider.push(context, "item-3");
601
+
602
+ // Wait for first flush to complete
603
+ await time.wait(100);
604
+
605
+ // Partition should still exist with timeout for item-3
606
+ const partition = context.partitions.get("default");
607
+ if (partition && partition.itemIds.length > 0) {
608
+ expect(partition.timeout).toBeDefined();
609
+ }
610
+
611
+ // Wait for timeout to flush item-3
612
+ await time.travel([150, "milliseconds"]);
613
+
614
+ await vi.waitFor(() => {
615
+ expect(handler).toHaveBeenCalledTimes(2);
616
+ });
617
+
618
+ expect(handler).toHaveBeenNthCalledWith(2, ["item-3"]);
619
+ });
620
+
621
+ // Tests for new quick win features
622
+
623
+ test("should clear completed items with clearCompleted", async () => {
624
+ const handler = vi.fn(async () => "result");
625
+ const context = batchProvider.createContext(alepha, { handler });
626
+
627
+ const id1 = batchProvider.push(context, "item-1");
628
+ const id2 = batchProvider.push(context, "item-2");
629
+ const id3 = batchProvider.push(context, "item-3");
630
+
631
+ await batchProvider.flush(context);
632
+
633
+ // All items should be completed
634
+ expect(context.itemStates.size).toBe(3);
635
+
636
+ // Clear completed items
637
+ const cleared = batchProvider.clearCompleted(context);
638
+
639
+ expect(cleared).toBe(3);
640
+ expect(context.itemStates.size).toBe(0);
641
+ });
642
+
643
+ test("should clear only failed items when status filter is provided", async () => {
644
+ // Use partitionBy to control which items fail
645
+ const handler = vi.fn(async (items: Array<{ type: string }>) => {
646
+ if (items[0].type === "fail") {
647
+ throw new Error("This batch fails");
648
+ }
649
+ return "success";
650
+ });
651
+
652
+ const context = batchProvider.createContext(alepha, {
653
+ handler,
654
+ maxSize: 1,
655
+ retry: { max: 1 }, // Disable retries - only 1 attempt
656
+ partitionBy: (item: { type: string }) => item.type,
657
+ });
658
+
659
+ batchProvider.push(context, { type: "fail" }); // Will fail
660
+ batchProvider.push(context, { type: "success" }); // Will succeed
661
+
662
+ await batchProvider.flush(context);
663
+
664
+ expect(context.itemStates.size).toBe(2);
665
+
666
+ // Clear only failed items
667
+ const cleared = batchProvider.clearCompleted(context, "failed");
668
+
669
+ expect(cleared).toBe(1);
670
+ expect(context.itemStates.size).toBe(1);
671
+
672
+ // The remaining item should be completed
673
+ const remaining = Array.from(context.itemStates.values())[0];
674
+ expect(remaining.status).toBe("completed");
675
+ });
676
+
677
+ test("should clear only completed items when status filter is provided", async () => {
678
+ // Use partitionBy to control which items fail
679
+ const handler = vi.fn(async (items: Array<{ type: string }>) => {
680
+ if (items[0].type === "fail") {
681
+ throw new Error("This batch fails");
682
+ }
683
+ return "success";
684
+ });
685
+
686
+ const context = batchProvider.createContext(alepha, {
687
+ handler,
688
+ maxSize: 1,
689
+ retry: { max: 1 }, // Disable retries - only 1 attempt
690
+ partitionBy: (item: { type: string }) => item.type,
691
+ });
692
+
693
+ batchProvider.push(context, { type: "fail" }); // Will fail
694
+ batchProvider.push(context, { type: "success" }); // Will succeed
695
+
696
+ await batchProvider.flush(context);
697
+
698
+ // Clear only completed items
699
+ const cleared = batchProvider.clearCompleted(context, "completed");
700
+
701
+ expect(cleared).toBe(1);
702
+ expect(context.itemStates.size).toBe(1);
703
+
704
+ // The remaining item should be failed
705
+ const remaining = Array.from(context.itemStates.values())[0];
706
+ expect(remaining.status).toBe("failed");
707
+ });
708
+
709
+ test("should enforce maxQueueSize limit", () => {
710
+ const handler = vi.fn();
711
+ const context = batchProvider.createContext(alepha, {
712
+ handler,
713
+ maxQueueSize: 3,
714
+ });
715
+
716
+ // Push up to the limit
717
+ batchProvider.push(context, "item-1");
718
+ batchProvider.push(context, "item-2");
719
+ batchProvider.push(context, "item-3");
720
+
721
+ // Fourth push should throw
722
+ expect(() => batchProvider.push(context, "item-4")).toThrow(
723
+ "Batch queue size exceeded for partition 'default' (max: 3)",
724
+ );
725
+ });
726
+
727
+ test("should enforce maxQueueSize per partition", () => {
728
+ const handler = vi.fn();
729
+ const context = batchProvider.createContext(alepha, {
730
+ handler,
731
+ maxQueueSize: 2,
732
+ partitionBy: (item: { key: string; value: number }) => item.key,
733
+ });
734
+
735
+ // Fill partition A
736
+ batchProvider.push(context, { key: "A", value: 1 });
737
+ batchProvider.push(context, { key: "A", value: 2 });
738
+
739
+ // Partition A is full, but B should still work
740
+ batchProvider.push(context, { key: "B", value: 1 });
741
+
742
+ // Partition A should throw
743
+ expect(() => batchProvider.push(context, { key: "A", value: 3 })).toThrow(
744
+ "Batch queue size exceeded for partition 'A' (max: 2)",
745
+ );
746
+
747
+ // Partition B should still accept
748
+ batchProvider.push(context, { key: "B", value: 2 });
749
+ });
750
+
751
+ test("should handle partitionBy errors gracefully", () => {
752
+ const handler = vi.fn();
753
+ const context = batchProvider.createContext(alepha, {
754
+ handler,
755
+ partitionBy: (item: { key?: string; value?: string }) => {
756
+ if (!item.key) {
757
+ throw new Error("Missing key");
758
+ }
759
+ return item.key;
760
+ },
761
+ });
762
+
763
+ // Item with key should work
764
+ batchProvider.push(context, { key: "A" });
765
+ expect(context.partitions.has("A")).toBe(true);
766
+
767
+ // Item without key should fall back to 'default' partition
768
+ batchProvider.push(context, { value: "no-key" });
769
+ expect(context.partitions.has("default")).toBe(true);
770
+ expect(context.partitions.get("default")?.itemIds).toHaveLength(1);
771
+ });
772
+
773
+ test("should return 0 when clearing with no completed items", () => {
774
+ const handler = vi.fn();
775
+ const context = batchProvider.createContext(alepha, { handler });
776
+
777
+ // Push items but don't flush
778
+ batchProvider.push(context, "item-1");
779
+ batchProvider.push(context, "item-2");
780
+
781
+ const cleared = batchProvider.clearCompleted(context);
782
+
783
+ expect(cleared).toBe(0);
784
+ expect(context.itemStates.size).toBe(2);
785
+ });
786
+ });