alepha 0.14.2 → 0.14.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/README.md +1 -1
  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 +706 -785
  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 +58 -137
  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 +29 -108
  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 +504 -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 +277 -351
  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 +787 -852
  35. package/dist/api/users/index.d.ts.map +1 -1
  36. package/dist/api/users/index.js +827 -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 +128 -128
  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/bin/index.d.ts +1 -2
  45. package/dist/bin/index.js +0 -1
  46. package/dist/bin/index.js.map +1 -1
  47. package/dist/cli/index.d.ts +252 -131
  48. package/dist/cli/index.d.ts.map +1 -1
  49. package/dist/cli/index.js +595 -395
  50. package/dist/cli/index.js.map +1 -1
  51. package/dist/command/index.d.ts +46 -11
  52. package/dist/command/index.d.ts.map +1 -1
  53. package/dist/command/index.js +99 -19
  54. package/dist/command/index.js.map +1 -1
  55. package/dist/core/index.browser.js +40 -22
  56. package/dist/core/index.browser.js.map +1 -1
  57. package/dist/core/index.d.ts +45 -1
  58. package/dist/core/index.d.ts.map +1 -1
  59. package/dist/core/index.js +40 -22
  60. package/dist/core/index.js.map +1 -1
  61. package/dist/core/index.native.js +40 -22
  62. package/dist/core/index.native.js.map +1 -1
  63. package/dist/fake/index.js +195 -168
  64. package/dist/fake/index.js.map +1 -1
  65. package/dist/file/index.d.ts +8 -0
  66. package/dist/file/index.d.ts.map +1 -1
  67. package/dist/file/index.js +3 -0
  68. package/dist/file/index.js.map +1 -1
  69. package/dist/logger/index.d.ts +1 -1
  70. package/dist/logger/index.d.ts.map +1 -1
  71. package/dist/logger/index.js +12 -2
  72. package/dist/logger/index.js.map +1 -1
  73. package/dist/mcp/index.js +1 -1
  74. package/dist/mcp/index.js.map +1 -1
  75. package/dist/orm/index.d.ts +59 -195
  76. package/dist/orm/index.d.ts.map +1 -1
  77. package/dist/orm/index.js +201 -430
  78. package/dist/orm/index.js.map +1 -1
  79. package/dist/security/index.d.ts +1 -1
  80. package/dist/security/index.d.ts.map +1 -1
  81. package/dist/security/index.js +1 -1
  82. package/dist/security/index.js.map +1 -1
  83. package/dist/server/auth/index.d.ts +171 -155
  84. package/dist/server/auth/index.d.ts.map +1 -1
  85. package/dist/server/auth/index.js +0 -1
  86. package/dist/server/auth/index.js.map +1 -1
  87. package/dist/server/cache/index.d.ts +12 -0
  88. package/dist/server/cache/index.d.ts.map +1 -1
  89. package/dist/server/cache/index.js +55 -2
  90. package/dist/server/cache/index.js.map +1 -1
  91. package/dist/server/compress/index.d.ts +6 -0
  92. package/dist/server/compress/index.d.ts.map +1 -1
  93. package/dist/server/compress/index.js +38 -1
  94. package/dist/server/compress/index.js.map +1 -1
  95. package/dist/server/core/index.browser.js +2 -2
  96. package/dist/server/core/index.browser.js.map +1 -1
  97. package/dist/server/core/index.d.ts +10 -10
  98. package/dist/server/core/index.d.ts.map +1 -1
  99. package/dist/server/core/index.js +7 -4
  100. package/dist/server/core/index.js.map +1 -1
  101. package/dist/server/links/index.browser.js +22 -6
  102. package/dist/server/links/index.browser.js.map +1 -1
  103. package/dist/server/links/index.d.ts +46 -44
  104. package/dist/server/links/index.d.ts.map +1 -1
  105. package/dist/server/links/index.js +24 -41
  106. package/dist/server/links/index.js.map +1 -1
  107. package/dist/server/static/index.d.ts.map +1 -1
  108. package/dist/server/static/index.js +4 -0
  109. package/dist/server/static/index.js.map +1 -1
  110. package/dist/server/swagger/index.d.ts +2 -1
  111. package/dist/server/swagger/index.d.ts.map +1 -1
  112. package/dist/server/swagger/index.js +9 -5
  113. package/dist/server/swagger/index.js.map +1 -1
  114. package/dist/vite/index.d.ts +101 -106
  115. package/dist/vite/index.d.ts.map +1 -1
  116. package/dist/vite/index.js +574 -503
  117. package/dist/vite/index.js.map +1 -1
  118. package/dist/websocket/index.d.ts +7 -7
  119. package/package.json +7 -7
  120. package/src/api/audits/controllers/{AuditController.ts → AdminAuditController.ts} +5 -6
  121. package/src/api/audits/entities/audits.ts +5 -5
  122. package/src/api/audits/index.browser.ts +1 -1
  123. package/src/api/audits/index.ts +3 -3
  124. package/src/api/audits/primitives/$audit.spec.ts +276 -0
  125. package/src/api/audits/services/AuditService.spec.ts +495 -0
  126. package/src/api/files/__tests__/$bucket.spec.ts +91 -0
  127. package/src/api/files/controllers/AdminFileStatsController.spec.ts +166 -0
  128. package/src/api/files/controllers/{StorageStatsController.ts → AdminFileStatsController.ts} +2 -2
  129. package/src/api/files/controllers/FileController.spec.ts +558 -0
  130. package/src/api/files/controllers/FileController.ts +4 -5
  131. package/src/api/files/entities/files.ts +5 -5
  132. package/src/api/files/index.browser.ts +1 -1
  133. package/src/api/files/index.ts +4 -4
  134. package/src/api/files/jobs/FileJobs.spec.ts +52 -0
  135. package/src/api/files/services/FileService.spec.ts +109 -0
  136. package/src/api/jobs/__tests__/JobController.spec.ts +343 -0
  137. package/src/api/jobs/controllers/{JobController.ts → AdminJobController.ts} +2 -2
  138. package/src/api/jobs/entities/jobExecutions.ts +5 -5
  139. package/src/api/jobs/index.ts +3 -3
  140. package/src/api/jobs/primitives/$job.spec.ts +476 -0
  141. package/src/api/notifications/controllers/{NotificationController.ts → AdminNotificationController.ts} +4 -5
  142. package/src/api/notifications/entities/notifications.ts +5 -5
  143. package/src/api/notifications/index.browser.ts +1 -1
  144. package/src/api/notifications/index.ts +4 -4
  145. package/src/api/parameters/controllers/{ConfigController.ts → AdminConfigController.ts} +46 -107
  146. package/src/api/parameters/entities/parameters.ts +7 -17
  147. package/src/api/parameters/index.ts +3 -3
  148. package/src/api/parameters/primitives/$config.spec.ts +356 -0
  149. package/src/api/parameters/schemas/activateConfigBodySchema.ts +12 -0
  150. package/src/api/parameters/schemas/checkScheduledResponseSchema.ts +8 -0
  151. package/src/api/parameters/schemas/configCurrentResponseSchema.ts +13 -0
  152. package/src/api/parameters/schemas/configHistoryResponseSchema.ts +9 -0
  153. package/src/api/parameters/schemas/configNameParamSchema.ts +10 -0
  154. package/src/api/parameters/schemas/configNamesResponseSchema.ts +8 -0
  155. package/src/api/parameters/schemas/configTreeNodeSchema.ts +13 -0
  156. package/src/api/parameters/schemas/configVersionParamSchema.ts +9 -0
  157. package/src/api/parameters/schemas/configVersionResponseSchema.ts +9 -0
  158. package/src/api/parameters/schemas/configsByStatusResponseSchema.ts +9 -0
  159. package/src/api/parameters/schemas/createConfigVersionBodySchema.ts +24 -0
  160. package/src/api/parameters/schemas/index.ts +15 -0
  161. package/src/api/parameters/schemas/parameterResponseSchema.ts +26 -0
  162. package/src/api/parameters/schemas/parameterStatusSchema.ts +13 -0
  163. package/src/api/parameters/schemas/rollbackConfigBodySchema.ts +15 -0
  164. package/src/api/parameters/schemas/statusParamSchema.ts +9 -0
  165. package/src/api/users/__tests__/EmailVerification.spec.ts +369 -0
  166. package/src/api/users/__tests__/PasswordReset.spec.ts +550 -0
  167. package/src/api/users/controllers/AdminIdentityController.spec.ts +365 -0
  168. package/src/api/users/controllers/{IdentityController.ts → AdminIdentityController.ts} +3 -4
  169. package/src/api/users/controllers/AdminSessionController.spec.ts +274 -0
  170. package/src/api/users/controllers/{SessionController.ts → AdminSessionController.ts} +3 -4
  171. package/src/api/users/controllers/AdminUserController.spec.ts +372 -0
  172. package/src/api/users/controllers/AdminUserController.ts +116 -0
  173. package/src/api/users/controllers/UserController.ts +4 -107
  174. package/src/api/users/controllers/UserRealmController.ts +3 -0
  175. package/src/api/users/entities/identities.ts +6 -6
  176. package/src/api/users/entities/sessions.ts +6 -6
  177. package/src/api/users/entities/users.ts +9 -9
  178. package/src/api/users/index.ts +9 -6
  179. package/src/api/users/primitives/$userRealm.ts +13 -8
  180. package/src/api/users/services/CredentialService.spec.ts +509 -0
  181. package/src/api/users/services/CredentialService.ts +46 -0
  182. package/src/api/users/services/IdentityService.ts +15 -0
  183. package/src/api/users/services/RegistrationService.spec.ts +630 -0
  184. package/src/api/users/services/RegistrationService.ts +18 -0
  185. package/src/api/users/services/SessionService.spec.ts +301 -0
  186. package/src/api/users/services/SessionService.ts +110 -1
  187. package/src/api/users/services/UserService.ts +67 -2
  188. package/src/api/verifications/__tests__/CodeVerification.spec.ts +318 -0
  189. package/src/api/verifications/__tests__/LinkVerification.spec.ts +279 -0
  190. package/src/api/verifications/entities/verifications.ts +6 -6
  191. package/src/api/verifications/jobs/VerificationJobs.spec.ts +50 -0
  192. package/src/batch/__tests__/startup-buffering.spec.ts +458 -0
  193. package/src/batch/primitives/$batch.spec.ts +766 -0
  194. package/src/batch/providers/BatchProvider.spec.ts +786 -0
  195. package/src/bin/index.ts +0 -1
  196. package/src/bucket/__tests__/shared.ts +194 -0
  197. package/src/bucket/primitives/$bucket.spec.ts +104 -0
  198. package/src/bucket/providers/FileStorageProvider.spec.ts +13 -0
  199. package/src/bucket/providers/LocalFileStorageProvider.spec.ts +77 -0
  200. package/src/bucket/providers/MemoryFileStorageProvider.spec.ts +82 -0
  201. package/src/cache/core/__tests__/shared.ts +377 -0
  202. package/src/cache/core/primitives/$cache.spec.ts +111 -0
  203. package/src/cache/redis/__tests__/cache-redis.spec.ts +70 -0
  204. package/src/cli/apps/AlephaCli.ts +25 -6
  205. package/src/cli/atoms/buildOptions.ts +88 -0
  206. package/src/cli/commands/build.ts +32 -69
  207. package/src/cli/commands/db.ts +0 -4
  208. package/src/cli/commands/dev.ts +34 -10
  209. package/src/cli/commands/gen/changelog.spec.ts +315 -0
  210. package/src/cli/commands/{changelog.ts → gen/changelog.ts} +9 -9
  211. package/src/cli/commands/gen/env.ts +53 -0
  212. package/src/cli/commands/gen/openapi.ts +71 -0
  213. package/src/cli/commands/gen/resource.ts +15 -0
  214. package/src/cli/commands/gen.ts +24 -0
  215. package/src/cli/commands/init.ts +2 -1
  216. package/src/cli/commands/root.ts +12 -3
  217. package/src/cli/commands/test.ts +0 -1
  218. package/src/cli/commands/typecheck.ts +5 -0
  219. package/src/cli/commands/verify.ts +1 -1
  220. package/src/cli/defineConfig.ts +49 -7
  221. package/src/cli/index.ts +2 -2
  222. package/src/cli/services/AlephaCliUtils.ts +105 -55
  223. package/src/cli/services/GitMessageParser.ts +1 -1
  224. package/src/command/helpers/Asker.spec.ts +127 -0
  225. package/src/command/helpers/Runner.spec.ts +126 -0
  226. package/src/command/helpers/Runner.ts +1 -1
  227. package/src/command/primitives/$command.spec.ts +1588 -0
  228. package/src/command/primitives/$command.ts +0 -6
  229. package/src/command/providers/CliProvider.ts +75 -27
  230. package/src/core/Alepha.ts +87 -0
  231. package/src/core/__tests__/Alepha-emit.spec.ts +22 -0
  232. package/src/core/__tests__/Alepha-graph.spec.ts +93 -0
  233. package/src/core/__tests__/Alepha-has.spec.ts +41 -0
  234. package/src/core/__tests__/Alepha-inject.spec.ts +93 -0
  235. package/src/core/__tests__/Alepha-register.spec.ts +81 -0
  236. package/src/core/__tests__/Alepha-start.spec.ts +176 -0
  237. package/src/core/__tests__/Alepha-with.spec.ts +14 -0
  238. package/src/core/__tests__/TypeBox-usecases.spec.ts +35 -0
  239. package/src/core/__tests__/TypeBoxLocale.spec.ts +15 -0
  240. package/src/core/__tests__/descriptor.spec.ts +34 -0
  241. package/src/core/__tests__/fixtures/A.ts +5 -0
  242. package/src/core/__tests__/pagination.spec.ts +77 -0
  243. package/src/core/helpers/jsonSchemaToTypeBox.ts +2 -2
  244. package/src/core/primitives/$atom.spec.ts +43 -0
  245. package/src/core/primitives/$hook.spec.ts +130 -0
  246. package/src/core/primitives/$inject.spec.ts +175 -0
  247. package/src/core/primitives/$module.spec.ts +115 -0
  248. package/src/core/providers/CodecManager.spec.ts +740 -0
  249. package/src/core/providers/EventManager.spec.ts +762 -0
  250. package/src/core/providers/EventManager.ts +4 -0
  251. package/src/core/providers/StateManager.spec.ts +365 -0
  252. package/src/core/providers/TypeProvider.spec.ts +1607 -0
  253. package/src/core/providers/TypeProvider.ts +20 -26
  254. package/src/datetime/primitives/$interval.spec.ts +103 -0
  255. package/src/datetime/providers/DateTimeProvider.spec.ts +86 -0
  256. package/src/email/primitives/$email.spec.ts +175 -0
  257. package/src/email/providers/LocalEmailProvider.spec.ts +341 -0
  258. package/src/fake/__tests__/keyName.example.ts +40 -0
  259. package/src/fake/__tests__/keyName.spec.ts +152 -0
  260. package/src/fake/__tests__/module.example.ts +32 -0
  261. package/src/fake/providers/FakeProvider.spec.ts +438 -0
  262. package/src/file/providers/FileSystemProvider.ts +8 -0
  263. package/src/file/providers/NodeFileSystemProvider.spec.ts +418 -0
  264. package/src/file/providers/NodeFileSystemProvider.ts +5 -0
  265. package/src/file/services/FileDetector.spec.ts +591 -0
  266. package/src/lock/core/__tests__/shared.ts +190 -0
  267. package/src/lock/core/providers/MemoryLockProvider.spec.ts +25 -0
  268. package/src/lock/redis/providers/RedisLockProvider.spec.ts +25 -0
  269. package/src/logger/__tests__/SimpleFormatterProvider.spec.ts +109 -0
  270. package/src/logger/index.ts +15 -3
  271. package/src/logger/primitives/$logger.spec.ts +108 -0
  272. package/src/logger/services/Logger.spec.ts +295 -0
  273. package/src/mcp/__tests__/errors.spec.ts +175 -0
  274. package/src/mcp/__tests__/integration.spec.ts +450 -0
  275. package/src/mcp/helpers/jsonrpc.spec.ts +380 -0
  276. package/src/mcp/primitives/$prompt.spec.ts +468 -0
  277. package/src/mcp/primitives/$resource.spec.ts +390 -0
  278. package/src/mcp/primitives/$tool.spec.ts +406 -0
  279. package/src/mcp/providers/McpServerProvider.spec.ts +797 -0
  280. package/src/mcp/transports/StdioMcpTransport.ts +1 -1
  281. package/src/orm/__tests__/$repository-crud.spec.ts +276 -0
  282. package/src/orm/__tests__/$repository-hooks.spec.ts +325 -0
  283. package/src/orm/__tests__/$repository-orderBy.spec.ts +128 -0
  284. package/src/orm/__tests__/$repository-pagination-sort.spec.ts +149 -0
  285. package/src/orm/__tests__/$repository-save.spec.ts +37 -0
  286. package/src/orm/__tests__/ModelBuilder-integration.spec.ts +490 -0
  287. package/src/orm/__tests__/ModelBuilder-types.spec.ts +186 -0
  288. package/src/orm/__tests__/PostgresProvider.spec.ts +46 -0
  289. package/src/orm/__tests__/delete-returning.spec.ts +256 -0
  290. package/src/orm/__tests__/deletedAt.spec.ts +80 -0
  291. package/src/orm/__tests__/enums.spec.ts +315 -0
  292. package/src/orm/__tests__/execute.spec.ts +72 -0
  293. package/src/orm/__tests__/fixtures/bigEntitySchema.ts +65 -0
  294. package/src/orm/__tests__/fixtures/userEntitySchema.ts +27 -0
  295. package/src/orm/__tests__/joins.spec.ts +1114 -0
  296. package/src/orm/__tests__/page.spec.ts +287 -0
  297. package/src/orm/__tests__/primaryKey.spec.ts +87 -0
  298. package/src/orm/__tests__/query-date-encoding.spec.ts +402 -0
  299. package/src/orm/__tests__/ref-auto-onDelete.spec.ts +156 -0
  300. package/src/orm/__tests__/references.spec.ts +102 -0
  301. package/src/orm/__tests__/security.spec.ts +710 -0
  302. package/src/orm/__tests__/sqlite.spec.ts +111 -0
  303. package/src/orm/__tests__/string-operators.spec.ts +429 -0
  304. package/src/orm/__tests__/timestamps.spec.ts +388 -0
  305. package/src/orm/__tests__/validation.spec.ts +183 -0
  306. package/src/orm/__tests__/version.spec.ts +64 -0
  307. package/src/orm/helpers/parseQueryString.spec.ts +196 -0
  308. package/src/orm/index.ts +2 -8
  309. package/src/orm/primitives/$repository.spec.ts +137 -0
  310. package/src/orm/primitives/$sequence.spec.ts +29 -0
  311. package/src/orm/primitives/$transaction.spec.ts +82 -0
  312. package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -3
  313. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
  314. package/src/orm/providers/drivers/CloudflareD1Provider.ts +1 -1
  315. package/src/orm/providers/drivers/DatabaseProvider.ts +1 -1
  316. package/src/orm/providers/drivers/NodePostgresProvider.ts +3 -3
  317. package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
  318. package/src/orm/providers/drivers/PglitePostgresProvider.ts +2 -2
  319. package/src/orm/services/ModelBuilder.spec.ts +575 -0
  320. package/src/orm/services/Repository.spec.ts +137 -0
  321. package/src/queue/core/__tests__/shared.ts +143 -0
  322. package/src/queue/core/providers/MemoryQueueProvider.spec.ts +23 -0
  323. package/src/queue/core/providers/WorkerProvider.spec.ts +394 -0
  324. package/src/queue/redis/providers/RedisQueueProvider.spec.ts +23 -0
  325. package/src/redis/__tests__/redis.spec.ts +58 -0
  326. package/src/retry/primitives/$retry.spec.ts +234 -0
  327. package/src/retry/providers/RetryProvider.spec.ts +438 -0
  328. package/src/router/__tests__/match.spec.ts +252 -0
  329. package/src/router/providers/RouterProvider.spec.ts +197 -0
  330. package/src/scheduler/__tests__/$scheduler-cron.spec.ts +25 -0
  331. package/src/scheduler/__tests__/$scheduler-interval.spec.ts +25 -0
  332. package/src/scheduler/__tests__/shared.ts +77 -0
  333. package/src/security/__tests__/bug-1-wildcard-after-start.spec.ts +229 -0
  334. package/src/security/__tests__/bug-2-password-validation.spec.ts +245 -0
  335. package/src/security/__tests__/bug-3-regex-vulnerability.spec.ts +407 -0
  336. package/src/security/__tests__/bug-4-oauth2-validation.spec.ts +439 -0
  337. package/src/security/__tests__/multi-layer-permissions.spec.ts +522 -0
  338. package/src/security/primitives/$permission.spec.ts +30 -0
  339. package/src/security/primitives/$permission.ts +2 -2
  340. package/src/security/primitives/$realm.spec.ts +101 -0
  341. package/src/security/primitives/$role.spec.ts +52 -0
  342. package/src/security/primitives/$serviceAccount.spec.ts +61 -0
  343. package/src/security/providers/SecurityProvider.spec.ts +350 -0
  344. package/src/server/auth/providers/ServerAuthProvider.ts +0 -2
  345. package/src/server/cache/providers/ServerCacheProvider.spec.ts +1125 -0
  346. package/src/server/cache/providers/ServerCacheProvider.ts +94 -9
  347. package/src/server/compress/providers/ServerCompressProvider.spec.ts +31 -0
  348. package/src/server/compress/providers/ServerCompressProvider.ts +63 -2
  349. package/src/server/cookies/providers/ServerCookiesProvider.spec.ts +253 -0
  350. package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +334 -0
  351. package/src/server/core/__tests__/ServerRouterProvider-requestId.spec.ts +129 -0
  352. package/src/server/core/helpers/ServerReply.ts +2 -2
  353. package/src/server/core/primitives/$action.spec.ts +191 -0
  354. package/src/server/core/primitives/$route.spec.ts +65 -0
  355. package/src/server/core/providers/ServerBodyParserProvider.spec.ts +93 -0
  356. package/src/server/core/providers/ServerLoggerProvider.spec.ts +100 -0
  357. package/src/server/core/providers/ServerProvider.ts +14 -2
  358. package/src/server/core/services/HttpClient.spec.ts +123 -0
  359. package/src/server/core/services/UserAgentParser.spec.ts +111 -0
  360. package/src/server/cors/providers/ServerCorsProvider.spec.ts +481 -0
  361. package/src/server/health/providers/ServerHealthProvider.spec.ts +22 -0
  362. package/src/server/helmet/providers/ServerHelmetProvider.spec.ts +105 -0
  363. package/src/server/links/__tests__/$action.spec.ts +238 -0
  364. package/src/server/links/__tests__/fixtures/CrudApp.ts +122 -0
  365. package/src/server/links/__tests__/requestId.spec.ts +120 -0
  366. package/src/server/links/primitives/$remote.spec.ts +228 -0
  367. package/src/server/links/providers/LinkProvider.spec.ts +54 -0
  368. package/src/server/links/providers/LinkProvider.ts +49 -3
  369. package/src/server/links/providers/ServerLinksProvider.ts +1 -53
  370. package/src/server/links/schemas/apiLinksResponseSchema.ts +7 -0
  371. package/src/server/metrics/providers/ServerMetricsProvider.spec.ts +25 -0
  372. package/src/server/multipart/providers/ServerMultipartProvider.spec.ts +528 -0
  373. package/src/server/proxy/primitives/$proxy.spec.ts +87 -0
  374. package/src/server/rate-limit/__tests__/ActionRateLimit.spec.ts +211 -0
  375. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +344 -0
  376. package/src/server/security/__tests__/BasicAuth.spec.ts +684 -0
  377. package/src/server/security/__tests__/ServerSecurityProvider-realm.spec.ts +388 -0
  378. package/src/server/security/providers/ServerSecurityProvider.spec.ts +123 -0
  379. package/src/server/static/primitives/$serve.spec.ts +193 -0
  380. package/src/server/static/providers/ServerStaticProvider.ts +10 -0
  381. package/src/server/swagger/__tests__/ui.spec.ts +52 -0
  382. package/src/server/swagger/primitives/$swagger.spec.ts +193 -0
  383. package/src/server/swagger/providers/ServerSwaggerProvider.ts +19 -12
  384. package/src/sms/primitives/$sms.spec.ts +165 -0
  385. package/src/sms/providers/LocalSmsProvider.spec.ts +224 -0
  386. package/src/sms/providers/MemorySmsProvider.spec.ts +193 -0
  387. package/src/thread/primitives/$thread.spec.ts +186 -0
  388. package/src/topic/core/__tests__/shared.ts +144 -0
  389. package/src/topic/core/providers/MemoryTopicProvider.spec.ts +23 -0
  390. package/src/topic/redis/providers/RedisTopicProvider.spec.ts +23 -0
  391. package/src/vite/helpers/importViteReact.ts +13 -0
  392. package/src/vite/index.ts +1 -21
  393. package/src/vite/plugins/viteAlephaDev.ts +32 -5
  394. package/src/vite/plugins/viteAlephaSsrPreload.ts +222 -0
  395. package/src/vite/tasks/buildClient.ts +11 -0
  396. package/src/vite/tasks/buildServer.ts +47 -3
  397. package/src/vite/tasks/devServer.ts +69 -0
  398. package/src/vite/tasks/index.ts +2 -1
  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/assets/viteConfigTs.ts +0 -14
  403. package/src/cli/commands/run.ts +0 -24
  404. package/src/vite/plugins/viteAlepha.ts +0 -37
  405. package/src/vite/plugins/viteAlephaBuild.ts +0 -281
@@ -121,6 +121,6 @@ export class StdioMcpTransport {
121
121
  */
122
122
  protected send(message: object): void {
123
123
  const json = JSON.stringify(message);
124
- process.stdout.write(json + "\n");
124
+ process.stdout.write(`${json}\n`);
125
125
  }
126
126
  }
@@ -0,0 +1,276 @@
1
+ import { Alepha, t } from "alepha";
2
+ import { describe, expect, it } from "vitest";
3
+ import { DbEntityNotFoundError } from "../errors/DbEntityNotFoundError.ts";
4
+ import { $entity, $repository, pg } from "../index.ts";
5
+ import type { InsertUserEntity } from "./fixtures/userEntitySchema.ts";
6
+
7
+ class App {
8
+ users = $repository(
9
+ $entity({
10
+ name: "users",
11
+ schema: t.object({
12
+ id: pg.primaryKey(),
13
+ createdAt: pg.createdAt(),
14
+ updatedAt: pg.updatedAt(),
15
+ name: t.text(),
16
+ profile: t.object({
17
+ age: t.number(),
18
+ }),
19
+ role: pg.default(t.text(), "user"),
20
+ }),
21
+ }),
22
+ );
23
+ }
24
+
25
+ const testBasicCrud = async (alepha: Alepha) => {
26
+ const app = alepha.inject(App);
27
+ await alepha.start();
28
+
29
+ // ========================================
30
+ // CREATE OPERATIONS
31
+ // ========================================
32
+
33
+ // Test: create single entity
34
+ const createdUser = await app.users.create({
35
+ name: "Alice",
36
+ profile: { age: 30 },
37
+ role: "admin",
38
+ });
39
+
40
+ expect(createdUser.id).toBeDefined();
41
+ expect(createdUser.name).toBe("Alice");
42
+ expect(createdUser.profile?.age).toBe(30);
43
+ expect(createdUser.role).toBe("admin");
44
+
45
+ const aliceId = createdUser.id;
46
+
47
+ // Test: createMany
48
+ const manyUsers: InsertUserEntity[] = [
49
+ { name: "Bob", profile: { age: 25 }, role: "user" },
50
+ { name: "Charlie", profile: { age: 35 }, role: "admin" },
51
+ { name: "David", profile: { age: 28 }, role: "user" },
52
+ { name: "Eve", profile: { age: 32 }, role: "moderator" },
53
+ ];
54
+
55
+ const createdMany = await app.users.createMany(manyUsers);
56
+ expect(createdMany).toHaveLength(4);
57
+ expect(createdMany[0].name).toBe("Bob");
58
+ expect(createdMany[1].name).toBe("Charlie");
59
+ expect(createdMany[2].name).toBe("David");
60
+ expect(createdMany[3].name).toBe("Eve");
61
+
62
+ // Test: createMany with empty array
63
+ const emptyResult = await app.users.createMany([]);
64
+ expect(emptyResult).toHaveLength(0);
65
+
66
+ // ========================================
67
+ // FIND OPERATIONS
68
+ // ========================================
69
+
70
+ // Test: find all
71
+ const allUsers = await app.users.findMany();
72
+ expect(allUsers).toHaveLength(5);
73
+
74
+ // Test: find with where clause (exact match)
75
+ const adminUsers = await app.users.findMany({
76
+ where: { role: { eq: "admin" } },
77
+ });
78
+ expect(adminUsers).toHaveLength(2);
79
+ expect(adminUsers.every((u) => u.role === "admin")).toBe(true);
80
+
81
+ // Test: find with limit
82
+ const limitedUsers = await app.users.findMany({ limit: 2 });
83
+ expect(limitedUsers).toHaveLength(2);
84
+
85
+ // Test: find with offset
86
+ const offsetUsers = await app.users.findMany({ offset: 3 });
87
+ expect(offsetUsers).toHaveLength(2);
88
+
89
+ // Test: find with limit and offset
90
+ const paginatedUsers = await app.users.findMany({ limit: 2, offset: 1 });
91
+ expect(paginatedUsers).toHaveLength(2);
92
+
93
+ // Test: findOne
94
+ const foundAlice = await app.users.findOne({
95
+ where: { name: { eq: "Alice" } },
96
+ });
97
+ expect(foundAlice.id).toBe(aliceId);
98
+ expect(foundAlice.name).toBe("Alice");
99
+
100
+ // Test: findOne throws when not found
101
+ await expect(
102
+ app.users.findOne({ where: { name: { eq: "NonExistent" } } }),
103
+ ).rejects.toThrowError(DbEntityNotFoundError);
104
+
105
+ // Test: findById
106
+ const userById = await app.users.findById(aliceId);
107
+ expect(userById.id).toBe(aliceId);
108
+ expect(userById.name).toBe("Alice");
109
+
110
+ // Test: findById throws when not found
111
+ await expect(app.users.findById(999999)).rejects.toThrowError(
112
+ DbEntityNotFoundError,
113
+ );
114
+
115
+ // ========================================
116
+ // COUNT OPERATIONS
117
+ // ========================================
118
+
119
+ // Test: count all
120
+ const totalCount = await app.users.count();
121
+ expect(totalCount).toBe(5);
122
+
123
+ // Test: count with where clause
124
+ const adminCount = await app.users.count({ role: { eq: "admin" } });
125
+ expect(adminCount).toBe(2);
126
+
127
+ const userCount = await app.users.count({ role: { eq: "user" } });
128
+ expect(userCount).toBe(2);
129
+
130
+ const moderatorCount = await app.users.count({ role: { eq: "moderator" } });
131
+ expect(moderatorCount).toBe(1);
132
+
133
+ // ========================================
134
+ // UPDATE OPERATIONS
135
+ // ========================================
136
+
137
+ // Test: updateOne
138
+ const updatedAlice = await app.users.updateOne(
139
+ { id: { eq: aliceId } },
140
+ { name: "Alice Updated", role: "admin" }, // Keep role since defaults apply
141
+ );
142
+ expect(updatedAlice.id).toBe(aliceId);
143
+ expect(updatedAlice.name).toBe("Alice Updated");
144
+ expect(updatedAlice.role).toBe("admin");
145
+
146
+ // Test: updateOne throws when not found
147
+ await expect(
148
+ app.users.updateOne({ id: { eq: 999999 } }, { name: "Should Fail" }),
149
+ ).rejects.toThrowError(DbEntityNotFoundError);
150
+
151
+ // Test: updateById
152
+ const updatedByIdAlice = await app.users.updateById(aliceId, {
153
+ role: "superadmin",
154
+ });
155
+ expect(updatedByIdAlice.id).toBe(aliceId);
156
+ expect(updatedByIdAlice.role).toBe("superadmin");
157
+ expect(updatedByIdAlice.name).toBe("Alice Updated"); // Previous update persisted
158
+
159
+ // Verify update persisted
160
+ const verifyAlice = await app.users.findById(aliceId);
161
+ expect(verifyAlice.name).toBe("Alice Updated");
162
+ expect(verifyAlice.role).toBe("superadmin");
163
+
164
+ // Test: updateMany
165
+ const updatedUserIds = await app.users.updateMany(
166
+ { role: { eq: "user" } },
167
+ { role: "member" },
168
+ );
169
+ expect(updatedUserIds).toHaveLength(2);
170
+
171
+ // Verify updateMany worked
172
+ const members = await app.users.findMany({
173
+ where: { role: { eq: "member" } },
174
+ });
175
+ expect(members).toHaveLength(2);
176
+ expect(members.every((u) => u.role === "member")).toBe(true);
177
+
178
+ // Test: save
179
+ const userToSave = await app.users.findOne({
180
+ where: { name: { eq: "Charlie" } },
181
+ });
182
+ userToSave.name = "Charlie Saved";
183
+ userToSave.profile = { age: 36 };
184
+ await app.users.save(userToSave);
185
+
186
+ const savedUser = await app.users.findById(userToSave.id);
187
+ expect(savedUser.name).toBe("Charlie Saved");
188
+ expect(savedUser.profile?.age).toBe(36);
189
+
190
+ // ========================================
191
+ // DELETE OPERATIONS
192
+ // ========================================
193
+
194
+ // Test: deleteOne
195
+ const bob = await app.users.findOne({ where: { name: { eq: "Bob" } } });
196
+ const deletedOneIds = await app.users.deleteOne({ id: { eq: bob.id } });
197
+ expect(deletedOneIds).toHaveLength(1);
198
+ expect(deletedOneIds[0]).toBe(bob.id);
199
+
200
+ // Verify bob is deleted
201
+ const usersAfterDeleteOne = await app.users.findMany();
202
+ expect(usersAfterDeleteOne).toHaveLength(4);
203
+ expect(usersAfterDeleteOne.every((u) => u.name !== "Bob")).toBe(true);
204
+
205
+ // Test: deleteById
206
+ const eve = await app.users.findOne({ where: { name: { eq: "Eve" } } });
207
+ const deletedByIdResult = await app.users.deleteById(eve.id);
208
+ expect(deletedByIdResult).toHaveLength(1);
209
+ expect(deletedByIdResult[0]).toBe(eve.id);
210
+
211
+ // Test: deleteById throws when not found
212
+ await expect(app.users.deleteById(999999)).rejects.toThrowError(
213
+ DbEntityNotFoundError,
214
+ );
215
+
216
+ // Verify eve is deleted
217
+ const usersAfterDeleteById = await app.users.findMany();
218
+ expect(usersAfterDeleteById).toHaveLength(3);
219
+
220
+ // Test: destroy (delete by entity)
221
+ const david = await app.users.findOne({ where: { name: { eq: "David" } } });
222
+ const destroyedIds = await app.users.destroy(david);
223
+ expect(destroyedIds).toHaveLength(1);
224
+ expect(destroyedIds[0]).toBe(david.id);
225
+
226
+ // Verify david is deleted
227
+ const usersAfterDestroy = await app.users.findMany();
228
+ expect(usersAfterDestroy).toHaveLength(2);
229
+
230
+ // Test: deleteMany
231
+ const deletedManyIds = await app.users.deleteMany({
232
+ role: { eq: "superadmin" },
233
+ });
234
+ expect(deletedManyIds).toHaveLength(1);
235
+ expect(deletedManyIds[0]).toBe(aliceId);
236
+
237
+ // Verify only Charlie remains
238
+ const remainingUsers = await app.users.findMany();
239
+ expect(remainingUsers).toHaveLength(1);
240
+ expect(remainingUsers[0].name).toBe("Charlie Saved");
241
+
242
+ // Test: clear (delete all)
243
+ await app.users.createMany([
244
+ { name: "Test1", profile: { age: 20 }, role: "user" },
245
+ { name: "Test2", profile: { age: 21 }, role: "user" },
246
+ { name: "Test3", profile: { age: 22 }, role: "admin" },
247
+ ]);
248
+
249
+ const beforeClearCount = await app.users.count();
250
+ expect(beforeClearCount).toBe(4);
251
+
252
+ const clearedIds = await app.users.clear({ force: true });
253
+ expect(clearedIds).toHaveLength(4);
254
+
255
+ const afterClearCount = await app.users.count();
256
+ expect(afterClearCount).toBe(0);
257
+
258
+ const emptyFind = await app.users.findMany();
259
+ expect(emptyFind).toHaveLength(0);
260
+ };
261
+
262
+ describe("$repository - CRUD operations", () => {
263
+ it("should support basic CRUD operations (postgres)", async () => {
264
+ await testBasicCrud(Alepha.create());
265
+ });
266
+
267
+ it("should support basic CRUD operations (pglite)", async () => {
268
+ process.env.DATABASE_URL = "pglite://:memory:";
269
+ await testBasicCrud(Alepha.create());
270
+ });
271
+
272
+ it("should support basic CRUD operations (sqlite)", async () => {
273
+ process.env.DATABASE_URL = "sqlite://:memory:";
274
+ await testBasicCrud(Alepha.create());
275
+ });
276
+ });
@@ -0,0 +1,325 @@
1
+ import { $hook, Alepha, t } from "alepha";
2
+ import { describe, expect, it } from "vitest";
3
+ import { $entity, $repository, pg } from "../index.ts";
4
+
5
+ class HookTracker {
6
+ events: Array<{ name: string; data: any }> = [];
7
+
8
+ clear() {
9
+ this.events = [];
10
+ }
11
+
12
+ record(name: string, data: any) {
13
+ this.events.push({ name, data });
14
+ }
15
+
16
+ find(name: string) {
17
+ return this.events.filter((e) => e.name === name);
18
+ }
19
+
20
+ findLast(name: string) {
21
+ const events = this.find(name);
22
+ return events[events.length - 1];
23
+ }
24
+ }
25
+
26
+ class App {
27
+ tracker = new HookTracker();
28
+
29
+ users = $repository(
30
+ $entity({
31
+ name: "users",
32
+ schema: t.object({
33
+ id: pg.primaryKey(),
34
+ name: t.text(),
35
+ email: t.text(),
36
+ }),
37
+ }),
38
+ );
39
+
40
+ // Hook: Create Before
41
+ createBefore = $hook({
42
+ on: "repository:create:before",
43
+ handler: async ({ data, tableName }) => {
44
+ this.tracker.record("create:before", { tableName, data });
45
+ },
46
+ });
47
+
48
+ // Hook: Create After
49
+ createAfter = $hook({
50
+ on: "repository:create:after",
51
+ handler: async ({ data, entity, tableName }) => {
52
+ this.tracker.record("create:after", { tableName, data, entity });
53
+ },
54
+ });
55
+
56
+ // Hook: Read Before
57
+ readBefore = $hook({
58
+ on: "repository:read:before",
59
+ handler: async ({ query, tableName }) => {
60
+ this.tracker.record("read:before", { tableName, query });
61
+ },
62
+ });
63
+
64
+ // Hook: Read After
65
+ readAfter = $hook({
66
+ on: "repository:read:after",
67
+ handler: async ({ query, entities, tableName }) => {
68
+ this.tracker.record("read:after", { tableName, query, entities });
69
+ },
70
+ });
71
+
72
+ // Hook: Update Before
73
+ updateBefore = $hook({
74
+ on: "repository:update:before",
75
+ handler: async ({ where, data, tableName }) => {
76
+ this.tracker.record("update:before", { tableName, where, data });
77
+ },
78
+ });
79
+
80
+ // Hook: Update After
81
+ updateAfter = $hook({
82
+ on: "repository:update:after",
83
+ handler: async ({ where, data, entities, tableName }) => {
84
+ this.tracker.record("update:after", { tableName, where, data, entities });
85
+ },
86
+ });
87
+
88
+ // Hook: Delete Before
89
+ deleteBefore = $hook({
90
+ on: "repository:delete:before",
91
+ handler: async ({ where, tableName }) => {
92
+ this.tracker.record("delete:before", { tableName, where });
93
+ },
94
+ });
95
+
96
+ // Hook: Delete After
97
+ deleteAfter = $hook({
98
+ on: "repository:delete:after",
99
+ handler: async ({ where, ids, tableName }) => {
100
+ this.tracker.record("delete:after", { tableName, where, ids });
101
+ },
102
+ });
103
+ }
104
+
105
+ const testRepositoryHooks = async (alepha: Alepha) => {
106
+ const app = alepha.inject(App);
107
+ await alepha.start();
108
+
109
+ // ========================================
110
+ // CREATE HOOKS
111
+ // ========================================
112
+
113
+ // Test: create hook (single entity)
114
+ app.tracker.clear();
115
+ const user1 = await app.users.create({
116
+ name: "Alice",
117
+ email: "alice@example.com",
118
+ });
119
+
120
+ expect(app.tracker.find("create:before")).toHaveLength(1);
121
+ expect(app.tracker.find("create:after")).toHaveLength(1);
122
+
123
+ const createBeforeEvent = app.tracker.findLast("create:before");
124
+ expect(createBeforeEvent.data.tableName).toBe("users");
125
+ expect(createBeforeEvent.data.data.name).toBe("Alice");
126
+
127
+ const createAfterEvent = app.tracker.findLast("create:after");
128
+ expect(createAfterEvent.data.tableName).toBe("users");
129
+ expect(createAfterEvent.data.entity.id).toBe(user1.id);
130
+ expect(createAfterEvent.data.entity.name).toBe("Alice");
131
+
132
+ // Test: createMany hook
133
+ app.tracker.clear();
134
+ const users = await app.users.createMany([
135
+ { name: "Bob", email: "bob@example.com" },
136
+ { name: "Charlie", email: "charlie@example.com" },
137
+ ]);
138
+
139
+ expect(app.tracker.find("create:before")).toHaveLength(1);
140
+ expect(app.tracker.find("create:after")).toHaveLength(1);
141
+
142
+ const createManyBeforeEvent = app.tracker.findLast("create:before");
143
+ expect(createManyBeforeEvent.data.tableName).toBe("users");
144
+ expect(createManyBeforeEvent.data.data).toHaveLength(2);
145
+
146
+ const createManyAfterEvent = app.tracker.findLast("create:after");
147
+ expect(createManyAfterEvent.data.tableName).toBe("users");
148
+ expect(createManyAfterEvent.data.entity).toHaveLength(2);
149
+ expect(createManyAfterEvent.data.entity[0].name).toBe("Bob");
150
+ expect(createManyAfterEvent.data.entity[1].name).toBe("Charlie");
151
+
152
+ // ========================================
153
+ // READ HOOKS
154
+ // ========================================
155
+
156
+ // Test: find hook
157
+ app.tracker.clear();
158
+ const allUsers = await app.users.findMany();
159
+
160
+ expect(app.tracker.find("read:before")).toHaveLength(1);
161
+ expect(app.tracker.find("read:after")).toHaveLength(1);
162
+
163
+ const readBeforeEvent = app.tracker.findLast("read:before");
164
+ expect(readBeforeEvent.data.tableName).toBe("users");
165
+ expect(readBeforeEvent.data.query).toBeDefined();
166
+
167
+ const readAfterEvent = app.tracker.findLast("read:after");
168
+ expect(readAfterEvent.data.tableName).toBe("users");
169
+ expect(readAfterEvent.data.entities).toHaveLength(3);
170
+
171
+ // Test: findOne hook (uses find internally)
172
+ app.tracker.clear();
173
+ await app.users.findOne({ where: { name: { eq: "Alice" } } });
174
+
175
+ expect(app.tracker.find("read:before")).toHaveLength(1);
176
+ expect(app.tracker.find("read:after")).toHaveLength(1);
177
+
178
+ // Test: findById hook (uses find internally)
179
+ app.tracker.clear();
180
+ await app.users.findById(user1.id);
181
+
182
+ expect(app.tracker.find("read:before")).toHaveLength(1);
183
+ expect(app.tracker.find("read:after")).toHaveLength(1);
184
+
185
+ // Test: paginate hook (uses find internally)
186
+ app.tracker.clear();
187
+ await app.users.paginate({ page: 0, size: 10 });
188
+
189
+ // paginate calls find once
190
+ expect(app.tracker.find("read:before")).toHaveLength(1);
191
+ expect(app.tracker.find("read:after")).toHaveLength(1);
192
+
193
+ // ========================================
194
+ // UPDATE HOOKS
195
+ // ========================================
196
+
197
+ // Test: updateOne hook
198
+ app.tracker.clear();
199
+ await app.users.updateOne(
200
+ { id: { eq: user1.id } },
201
+ { name: "Alice Updated" },
202
+ );
203
+
204
+ expect(app.tracker.find("update:before")).toHaveLength(1);
205
+ expect(app.tracker.find("update:after")).toHaveLength(1);
206
+
207
+ const updateBeforeEvent = app.tracker.findLast("update:before");
208
+ expect(updateBeforeEvent.data.tableName).toBe("users");
209
+ expect(updateBeforeEvent.data.data.name).toBe("Alice Updated");
210
+
211
+ const updateAfterEvent = app.tracker.findLast("update:after");
212
+ expect(updateAfterEvent.data.tableName).toBe("users");
213
+ expect(updateAfterEvent.data.entities).toHaveLength(1);
214
+ expect(updateAfterEvent.data.entities[0].name).toBe("Alice Updated");
215
+
216
+ // Test: updateById hook (uses updateOne internally)
217
+ app.tracker.clear();
218
+ await app.users.updateById(user1.id, { email: "alice.new@example.com" });
219
+
220
+ expect(app.tracker.find("update:before")).toHaveLength(1);
221
+ expect(app.tracker.find("update:after")).toHaveLength(1);
222
+
223
+ // Test: updateMany hook
224
+ app.tracker.clear();
225
+ const allBeforeUpdate = await app.users.findMany();
226
+ const bobAndCharlie = allBeforeUpdate.filter(
227
+ (u) => u.name === "Bob" || u.name === "Charlie",
228
+ );
229
+ await app.users.updateMany(
230
+ {
231
+ or: [{ name: { eq: "Bob" } }, { name: { eq: "Charlie" } }],
232
+ },
233
+ { email: "updated@example.com" },
234
+ );
235
+
236
+ expect(app.tracker.find("update:before")).toHaveLength(1);
237
+ expect(app.tracker.find("update:after")).toHaveLength(1);
238
+
239
+ const updateManyAfterEvent = app.tracker.findLast("update:after");
240
+ expect(updateManyAfterEvent.data.entities.length).toBeGreaterThanOrEqual(2);
241
+
242
+ // Test: save hook (uses updateOne internally)
243
+ app.tracker.clear();
244
+ const userToSave = await app.users.findById(user1.id);
245
+ userToSave.name = "Alice Saved";
246
+ await app.users.save(userToSave);
247
+
248
+ // save calls find (read) once, and updateOne (update) once
249
+ expect(app.tracker.find("read:before").length).toBeGreaterThanOrEqual(1);
250
+ expect(app.tracker.find("read:after").length).toBeGreaterThanOrEqual(1);
251
+ expect(app.tracker.find("update:before")).toHaveLength(1);
252
+ expect(app.tracker.find("update:after")).toHaveLength(1);
253
+
254
+ // ========================================
255
+ // DELETE HOOKS
256
+ // ========================================
257
+
258
+ // Test: deleteMany hook
259
+ app.tracker.clear();
260
+ const bobUser = bobAndCharlie.find((u) => u.name === "Bob");
261
+ if (!bobUser) throw new Error("Bob not found");
262
+
263
+ await app.users.deleteMany({ id: { eq: bobUser.id } });
264
+
265
+ expect(app.tracker.find("delete:before")).toHaveLength(1);
266
+ expect(app.tracker.find("delete:after")).toHaveLength(1);
267
+
268
+ const deleteBeforeEvent = app.tracker.findLast("delete:before");
269
+ expect(deleteBeforeEvent.data.tableName).toBe("users");
270
+
271
+ const deleteAfterEvent = app.tracker.findLast("delete:after");
272
+ expect(deleteAfterEvent.data.tableName).toBe("users");
273
+ expect(deleteAfterEvent.data.ids).toHaveLength(1);
274
+ expect(deleteAfterEvent.data.ids[0]).toBe(bobUser.id);
275
+
276
+ // Test: deleteOne hook (uses deleteMany internally)
277
+ app.tracker.clear();
278
+ const charlieUser = bobAndCharlie.find((u) => u.name === "Charlie");
279
+ if (!charlieUser) throw new Error("Charlie not found");
280
+
281
+ await app.users.deleteOne({ id: { eq: charlieUser.id } });
282
+
283
+ expect(app.tracker.find("delete:before")).toHaveLength(1);
284
+ expect(app.tracker.find("delete:after")).toHaveLength(1);
285
+
286
+ // Test: deleteById hook (uses deleteMany internally)
287
+ app.tracker.clear();
288
+ const remainingUsers = await app.users.findMany();
289
+ const lastUser = remainingUsers[0];
290
+
291
+ await app.users.deleteById(lastUser.id);
292
+
293
+ expect(app.tracker.find("delete:before")).toHaveLength(1);
294
+ expect(app.tracker.find("delete:after")).toHaveLength(1);
295
+
296
+ // ========================================
297
+ // HOOK ORDER VERIFICATION
298
+ // ========================================
299
+
300
+ // Test: hooks fire in correct order (before -> after)
301
+ app.tracker.clear();
302
+ await app.users.create({ name: "Test", email: "test@example.com" });
303
+
304
+ const allEvents = app.tracker.events.map((e) => e.name);
305
+ const createBeforeIndex = allEvents.indexOf("create:before");
306
+ const createAfterIndex = allEvents.indexOf("create:after");
307
+
308
+ expect(createBeforeIndex).toBeLessThan(createAfterIndex);
309
+ };
310
+
311
+ describe("$repository - Hooks", () => {
312
+ it("should fire hooks for all operations (postgres)", async () => {
313
+ await testRepositoryHooks(Alepha.create());
314
+ });
315
+
316
+ it("should fire hooks for all operations (pglite)", async () => {
317
+ process.env.DATABASE_URL = "pglite://:memory:";
318
+ await testRepositoryHooks(Alepha.create());
319
+ });
320
+
321
+ it("should fire hooks for all operations (sqlite)", async () => {
322
+ process.env.DATABASE_URL = "sqlite://:memory:";
323
+ await testRepositoryHooks(Alepha.create());
324
+ });
325
+ });