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
@@ -0,0 +1,388 @@
1
+ import { Alepha, t } from "alepha";
2
+ import { describe, expect, it } from "vitest";
3
+ import { $entity, $repository } from "../index.ts";
4
+ import { pg } from "../providers/DatabaseTypeProvider.ts";
5
+
6
+ // Helper function to parse dates from various formats
7
+ const parseDate = (dateValue: any): Date => {
8
+ if (!dateValue) return new Date(0);
9
+ if (typeof dateValue === "string") return new Date(dateValue);
10
+ if (dateValue.toDate) return dateValue.toDate();
11
+ if (dateValue.toISOString) return new Date(dateValue.toISOString());
12
+ return new Date(dateValue);
13
+ };
14
+
15
+ // Test entity with timestamp fields
16
+ const articleEntity = $entity({
17
+ name: "articles",
18
+ schema: t.object({
19
+ id: pg.primaryKey(),
20
+ title: t.string(),
21
+ content: t.text(),
22
+ status: t.string(),
23
+ createdAt: pg.createdAt(),
24
+ updatedAt: pg.updatedAt(),
25
+ }),
26
+ indexes: [
27
+ "createdAt",
28
+ "updatedAt",
29
+ {
30
+ columns: ["status", "createdAt"],
31
+ name: "status_created_idx",
32
+ },
33
+ ],
34
+ });
35
+
36
+ const userActivityEntity = $entity({
37
+ name: "user_activities",
38
+ schema: t.object({
39
+ id: pg.primaryKey(),
40
+ userId: t.integer(),
41
+ action: t.string(),
42
+ metadata: t.optional(t.record(t.string(), t.any())),
43
+ createdAt: pg.createdAt(),
44
+ updatedAt: t.optional(pg.updatedAt()),
45
+ }),
46
+ });
47
+
48
+ class TimestampTestApp {
49
+ articles = $repository(articleEntity);
50
+ activities = $repository(userActivityEntity);
51
+ }
52
+
53
+ /**
54
+ * Shared test function that runs on both PostgreSQL and SQLite
55
+ */
56
+ const testTimestamps = async (alepha: Alepha) => {
57
+ const app = alepha.inject(TimestampTestApp);
58
+ await alepha.start();
59
+
60
+ // Test 1: createdAt should be automatically set on insert
61
+ const beforeCreate = new Date();
62
+
63
+ const article1 = await app.articles.create({
64
+ title: "First Article",
65
+ content: "This is the first article content",
66
+ status: "draft",
67
+ });
68
+
69
+ const afterCreate = new Date();
70
+
71
+ expect(article1.id).toBeDefined();
72
+ expect(article1.createdAt).toBeDefined();
73
+ expect(article1.updatedAt).toBeDefined();
74
+
75
+ // Parse the dates - they come as dayjs objects from DrizzleSchemaCodec
76
+ const createdDate = parseDate(article1.createdAt);
77
+ const updatedDate = parseDate(article1.updatedAt);
78
+
79
+ // createdAt should be between beforeCreate and afterCreate
80
+ expect(createdDate.getTime()).toBeGreaterThanOrEqual(
81
+ beforeCreate.getTime() - 1000,
82
+ ); // Allow 1s margin
83
+ expect(createdDate.getTime()).toBeLessThanOrEqual(
84
+ afterCreate.getTime() + 1000,
85
+ );
86
+
87
+ // Initially, updatedAt should equal createdAt
88
+ expect(Math.abs(createdDate.getTime() - updatedDate.getTime())).toBeLessThan(
89
+ 1000,
90
+ ); // Within 1 second
91
+
92
+ // Test 2: Wait a bit and update the record
93
+ await new Promise((resolve) => setTimeout(resolve, 100)); // Wait 100ms
94
+
95
+ const beforeUpdate = new Date();
96
+
97
+ const updatedArticle = await app.articles.updateById(article1.id, {
98
+ status: "published",
99
+ title: "Updated First Article",
100
+ });
101
+
102
+ const afterUpdate = new Date();
103
+
104
+ expect(updatedArticle.title).toBe("Updated First Article");
105
+ expect(updatedArticle.status).toBe("published");
106
+
107
+ const newUpdatedDate = parseDate(updatedArticle.updatedAt);
108
+ const unchangedCreatedDate = parseDate(updatedArticle.createdAt);
109
+
110
+ // createdAt should NOT change
111
+ expect(unchangedCreatedDate.getTime()).toBe(createdDate.getTime());
112
+
113
+ // updatedAt should be updated and be later than createdAt
114
+ expect(newUpdatedDate.getTime()).toBeGreaterThan(createdDate.getTime());
115
+ expect(newUpdatedDate.getTime()).toBeGreaterThanOrEqual(
116
+ beforeUpdate.getTime() - 1000,
117
+ );
118
+ expect(newUpdatedDate.getTime()).toBeLessThanOrEqual(
119
+ afterUpdate.getTime() + 1000,
120
+ );
121
+
122
+ // Test 3: Create multiple articles and verify they have different timestamps
123
+ const articles = await Promise.all([
124
+ app.articles.create({
125
+ title: "Article 2",
126
+ content: "Content 2",
127
+ status: "draft",
128
+ }),
129
+ app.articles.create({
130
+ title: "Article 3",
131
+ content: "Content 3",
132
+ status: "published",
133
+ }),
134
+ app.articles.create({
135
+ title: "Article 4",
136
+ content: "Content 4",
137
+ status: "archived",
138
+ }),
139
+ ]);
140
+
141
+ // All should have createdAt and updatedAt
142
+ for (const article of articles) {
143
+ expect(article.createdAt).toBeDefined();
144
+ expect(article.updatedAt).toBeDefined();
145
+ }
146
+
147
+ // Test 4: Query using createdAt ordering
148
+ const sortedByCreated = await app.articles.findMany({
149
+ orderBy: { column: "createdAt", direction: "asc" },
150
+ });
151
+
152
+ // First article should be article1 (created first)
153
+ expect(sortedByCreated[0].id).toBe(article1.id);
154
+
155
+ // Verify the order is correct
156
+ for (let i = 1; i < sortedByCreated.length; i++) {
157
+ const prevDate = parseDate(sortedByCreated[i - 1].createdAt);
158
+ const currDate = parseDate(sortedByCreated[i].createdAt);
159
+ expect(currDate.getTime()).toBeGreaterThanOrEqual(prevDate.getTime());
160
+ }
161
+
162
+ // Test 5: Query with createdAt filters
163
+ // Use the actual createdAt value from the first article (already a Dayjs object)
164
+ const recentArticles = await app.articles.findMany({
165
+ where: {
166
+ createdAt: {
167
+ gte: article1.createdAt as any, // Cast to any since it's actually compatible at runtime
168
+ },
169
+ },
170
+ });
171
+
172
+ expect(recentArticles.length).toBeGreaterThan(0);
173
+ expect(recentArticles.some((a) => a.id === article1.id)).toBe(true);
174
+
175
+ // Test 6: Test with user activities (optional updatedAt)
176
+ const activity = await app.activities.create({
177
+ userId: 1,
178
+ action: "login",
179
+ metadata: { ip: "192.168.1.1", browser: "Chrome" },
180
+ });
181
+
182
+ expect(activity.createdAt).toBeDefined();
183
+ // updatedAt is optional, might be null or undefined initially
184
+
185
+ // Test 7: Batch operations should maintain timestamps
186
+ const batchArticles = await Promise.all([
187
+ app.articles.create({
188
+ title: "Batch 1",
189
+ content: "Batch content 1",
190
+ status: "draft",
191
+ }),
192
+ app.articles.create({
193
+ title: "Batch 2",
194
+ content: "Batch content 2",
195
+ status: "draft",
196
+ }),
197
+ ]);
198
+
199
+ // Update them all at once
200
+ await new Promise((resolve) => setTimeout(resolve, 100)); // Wait 100ms
201
+
202
+ const batchUpdates = await Promise.all(
203
+ batchArticles.map((article) =>
204
+ app.articles.updateById(article.id, {
205
+ status: "published",
206
+ }),
207
+ ),
208
+ );
209
+
210
+ // All should have updated timestamps
211
+ for (const updated of batchUpdates) {
212
+ const createdTime = parseDate(updated.createdAt).getTime();
213
+ const updatedTime = parseDate(updated.updatedAt).getTime();
214
+ expect(updatedTime).toBeGreaterThan(createdTime);
215
+ }
216
+
217
+ // Test 8: Query using composite index with timestamps
218
+ const statusCreatedQuery = await app.articles.findMany({
219
+ where: {
220
+ status: { eq: "published" },
221
+ },
222
+ orderBy: "createdAt",
223
+ });
224
+
225
+ expect(statusCreatedQuery.length).toBeGreaterThan(0);
226
+ expect(statusCreatedQuery.every((a) => a.status === "published")).toBe(true);
227
+
228
+ // Clean up
229
+ await app.articles.clear({ force: true });
230
+ await app.activities.clear({ force: true });
231
+ };
232
+
233
+ describe("Timestamp Fields (createdAt/updatedAt)", () => {
234
+ it("should handle timestamps correctly in PostgreSQL", async () => {
235
+ await testTimestamps(Alepha.create());
236
+ });
237
+
238
+ it("should handle timestamps correctly in SQLite", async () => {
239
+ await testTimestamps(
240
+ Alepha.create({
241
+ env: {
242
+ DATABASE_URL: "sqlite://:memory:",
243
+ },
244
+ }),
245
+ );
246
+ });
247
+
248
+ it("should create proper indexes on timestamp fields", async () => {
249
+ const alepha = Alepha.create();
250
+ const app = alepha.inject(TimestampTestApp);
251
+ await alepha.start();
252
+
253
+ // Create some test data
254
+ const article = await app.articles.create({
255
+ title: "Index Test Article",
256
+ content: "Testing indexes on timestamp fields",
257
+ status: "draft",
258
+ });
259
+
260
+ // Query using the createdAt index - use range query instead of exact match
261
+ // due to potential microsecond precision differences
262
+ const createdTime = parseDate(article.createdAt);
263
+ const startTime = new Date(createdTime.getTime() - 1000); // 1 second before
264
+ const endTime = new Date(createdTime.getTime() + 1000); // 1 second after
265
+
266
+ const byCreated = await app.articles.findMany({
267
+ where: {
268
+ createdAt: {
269
+ gte: startTime.toISOString() as any,
270
+ lte: endTime.toISOString() as any,
271
+ },
272
+ },
273
+ });
274
+
275
+ expect(byCreated.length).toBeGreaterThan(0);
276
+ expect(byCreated.some((a) => a.id === article.id)).toBe(true);
277
+
278
+ // Query using the composite index
279
+ const byStatusAndCreated = await app.articles.findMany({
280
+ where: {
281
+ status: { eq: "draft" },
282
+ createdAt: { gte: startTime.toISOString() as any },
283
+ },
284
+ });
285
+
286
+ expect(byStatusAndCreated.length).toBeGreaterThan(0);
287
+ expect(byStatusAndCreated.some((a) => a.id === article.id)).toBe(true);
288
+
289
+ await app.articles.clear({ force: true });
290
+ });
291
+
292
+ it("should handle timestamps with different formats", async () => {
293
+ // Test entity with custom timestamp configuration
294
+ const eventEntity = $entity({
295
+ name: "events",
296
+ schema: t.object({
297
+ id: pg.primaryKey(),
298
+ name: t.string(),
299
+ startTime: pg.createdAt(),
300
+ endTime: t.optional(pg.updatedAt()),
301
+ scheduledAt: t.string(), // Regular string field for ISO date
302
+ }),
303
+ });
304
+
305
+ class EventApp {
306
+ events = $repository(eventEntity);
307
+ }
308
+
309
+ const alepha = Alepha.create();
310
+ const app = alepha.inject(EventApp);
311
+ await alepha.start();
312
+
313
+ const now = new Date();
314
+ const event = await app.events.create({
315
+ name: "Test Event",
316
+ scheduledAt: now.toISOString(),
317
+ });
318
+
319
+ expect(event.startTime).toBeDefined();
320
+ expect(event.scheduledAt).toBeDefined();
321
+
322
+ // startTime should be auto-generated
323
+ const startTime = parseDate(event.startTime);
324
+ expect(startTime).toBeInstanceOf(Date);
325
+ expect(startTime.getTime()).toBeGreaterThan(0);
326
+
327
+ // scheduledAt should match what we provided
328
+ expect(event.scheduledAt).toBe(now.toISOString());
329
+
330
+ await app.events.clear({ force: true });
331
+ });
332
+
333
+ it("should handle bulk inserts with timestamps", async () => {
334
+ const alepha = Alepha.create();
335
+ const app = alepha.inject(TimestampTestApp);
336
+ await alepha.start();
337
+
338
+ const beforeBulk = new Date();
339
+
340
+ // Create multiple articles at once
341
+ const articles = await Promise.all(
342
+ Array.from({ length: 10 }, (_, i) =>
343
+ app.articles.create({
344
+ title: `Bulk Article ${i}`,
345
+ content: `Content for article ${i}`,
346
+ status: i % 2 === 0 ? "draft" : "published",
347
+ }),
348
+ ),
349
+ );
350
+
351
+ const afterBulk = new Date();
352
+
353
+ expect(articles).toHaveLength(10);
354
+
355
+ // All should have timestamps within the expected range
356
+ for (const article of articles) {
357
+ const created = parseDate(article.createdAt);
358
+ const updated = parseDate(article.updatedAt);
359
+
360
+ expect(created.getTime()).toBeGreaterThanOrEqual(
361
+ beforeBulk.getTime() - 1000,
362
+ );
363
+ expect(created.getTime()).toBeLessThanOrEqual(afterBulk.getTime() + 1000);
364
+ expect(Math.abs(created.getTime() - updated.getTime())).toBeLessThan(
365
+ 1000,
366
+ );
367
+ }
368
+
369
+ // Query and verify ordering
370
+ const ordered = await app.articles.findMany({
371
+ orderBy: [
372
+ { column: "createdAt", direction: "asc" },
373
+ { column: "id", direction: "asc" },
374
+ ],
375
+ });
376
+
377
+ expect(ordered).toHaveLength(10);
378
+
379
+ // Verify chronological order
380
+ for (let i = 1; i < ordered.length; i++) {
381
+ const prev = parseDate(ordered[i - 1].createdAt);
382
+ const curr = parseDate(ordered[i].createdAt);
383
+ expect(curr.getTime()).toBeGreaterThanOrEqual(prev.getTime());
384
+ }
385
+
386
+ await app.articles.clear({ force: true });
387
+ });
388
+ });
@@ -0,0 +1,183 @@
1
+ import { Alepha, t } from "alepha";
2
+ import { test } from "vitest";
3
+ import { $entity, $repository, pg } from "../index.ts";
4
+
5
+ const TestEntity = $entity({
6
+ name: "test_validation",
7
+ schema: t.object({
8
+ id: pg.primaryKey(),
9
+ createdAt: pg.createdAt(),
10
+ updatedAt: pg.updatedAt(),
11
+ name: t.text(),
12
+ age: t.number(),
13
+ status: t.text(),
14
+ }),
15
+ });
16
+
17
+ class App {
18
+ items = $repository(TestEntity);
19
+ }
20
+
21
+ test("between operator validation - requires exactly 2 values", async ({
22
+ expect,
23
+ }) => {
24
+ const alepha = Alepha.create();
25
+ const app = alepha.inject(App);
26
+ await alepha.start();
27
+
28
+ const repository = app.items;
29
+
30
+ // Insert test data
31
+ await repository.create({ name: "Item 1", age: 10, status: "active" });
32
+ await repository.create({ name: "Item 2", age: 20, status: "active" });
33
+ await repository.create({ name: "Item 3", age: 30, status: "active" });
34
+
35
+ // Test 1: Valid between with exactly 2 values should work
36
+ const result1 = await repository.findMany({
37
+ where: { age: { between: [15, 25] } },
38
+ });
39
+ expect(result1).toHaveLength(1);
40
+ expect(result1[0].name).toBe("Item 2");
41
+
42
+ // Test 2: Empty array should throw
43
+ await expect(async () => {
44
+ await repository.findMany({
45
+ where: { age: { between: [] as any } },
46
+ });
47
+ }).rejects.toThrow("between operator requires exactly 2 values [min, max]");
48
+
49
+ // Test 3: Single value should throw
50
+ await expect(async () => {
51
+ await repository.findMany({
52
+ where: { age: { between: [15] as any } },
53
+ });
54
+ }).rejects.toThrow("between operator requires exactly 2 values [min, max]");
55
+
56
+ // Test 4: More than 2 values should throw
57
+ await expect(async () => {
58
+ await repository.findMany({
59
+ where: { age: { between: [15, 25, 35] as any } },
60
+ });
61
+ }).rejects.toThrow("between operator requires exactly 2 values [min, max]");
62
+
63
+ // Test 5: Non-array should throw
64
+ await expect(async () => {
65
+ await repository.findMany({
66
+ where: { age: { between: 15 as any } },
67
+ });
68
+ }).rejects.toThrow("between operator requires exactly 2 values [min, max]");
69
+ });
70
+
71
+ test("notBetween operator validation - requires exactly 2 values", async ({
72
+ expect,
73
+ }) => {
74
+ const alepha = Alepha.create();
75
+ const app = alepha.inject(App);
76
+ await alepha.start();
77
+
78
+ const repository = app.items;
79
+
80
+ // Insert test data
81
+ await repository.create({ name: "Item 1", age: 10, status: "active" });
82
+ await repository.create({ name: "Item 2", age: 20, status: "active" });
83
+ await repository.create({ name: "Item 3", age: 30, status: "active" });
84
+
85
+ // Test 1: Valid notBetween with exactly 2 values should work
86
+ const result1 = await repository.findMany({
87
+ where: { age: { notBetween: [15, 25] } },
88
+ orderBy: { column: "age", direction: "asc" },
89
+ });
90
+ expect(result1).toHaveLength(2);
91
+ expect(result1.map((r: any) => r.name)).toEqual(["Item 1", "Item 3"]);
92
+
93
+ // Test 2: Empty array should throw
94
+ await expect(async () => {
95
+ await repository.findMany({
96
+ where: { age: { notBetween: [] as any } },
97
+ });
98
+ }).rejects.toThrow(
99
+ "notBetween operator requires exactly 2 values [min, max]",
100
+ );
101
+
102
+ // Test 3: Single value should throw
103
+ await expect(async () => {
104
+ await repository.findMany({
105
+ where: { age: { notBetween: [15] as any } },
106
+ });
107
+ }).rejects.toThrow(
108
+ "notBetween operator requires exactly 2 values [min, max]",
109
+ );
110
+ });
111
+
112
+ test("inArray operator validation - requires at least one value", async ({
113
+ expect,
114
+ }) => {
115
+ const alepha = Alepha.create();
116
+ const app = alepha.inject(App);
117
+ await alepha.start();
118
+
119
+ const repository = app.items;
120
+
121
+ // Insert test data
122
+ await repository.create({ name: "Item 1", age: 10, status: "active" });
123
+ await repository.create({ name: "Item 2", age: 20, status: "pending" });
124
+ await repository.create({ name: "Item 3", age: 30, status: "inactive" });
125
+
126
+ // Test 1: Valid inArray should work
127
+ const result1 = await repository.findMany({
128
+ where: { status: { inArray: ["active", "pending"] } },
129
+ orderBy: { column: "age", direction: "asc" },
130
+ });
131
+ expect(result1).toHaveLength(2);
132
+ expect(result1.map((r: any) => r.status)).toEqual(["active", "pending"]);
133
+
134
+ // Test 2: Empty array should throw
135
+ await expect(async () => {
136
+ await repository.findMany({
137
+ where: { status: { inArray: [] } },
138
+ });
139
+ }).rejects.toThrow("inArray operator requires at least one value");
140
+
141
+ // Test 3: Non-array should throw
142
+ await expect(async () => {
143
+ await repository.findMany({
144
+ where: { status: { inArray: "active" as any } },
145
+ });
146
+ }).rejects.toThrow("inArray operator requires at least one value");
147
+ });
148
+
149
+ test("notInArray operator validation - requires at least one value", async ({
150
+ expect,
151
+ }) => {
152
+ const alepha = Alepha.create();
153
+ const app = alepha.inject(App);
154
+ await alepha.start();
155
+
156
+ const repository = app.items;
157
+
158
+ // Insert test data
159
+ await repository.create({ name: "Item 1", age: 10, status: "active" });
160
+ await repository.create({ name: "Item 2", age: 20, status: "pending" });
161
+ await repository.create({ name: "Item 3", age: 30, status: "inactive" });
162
+
163
+ // Test 1: Valid notInArray should work
164
+ const result1 = await repository.findMany({
165
+ where: { status: { notInArray: ["active", "pending"] } },
166
+ });
167
+ expect(result1).toHaveLength(1);
168
+ expect(result1[0].status).toBe("inactive");
169
+
170
+ // Test 2: Empty array should throw
171
+ await expect(async () => {
172
+ await repository.findMany({
173
+ where: { status: { notInArray: [] } },
174
+ });
175
+ }).rejects.toThrow("notInArray operator requires at least one value");
176
+
177
+ // Test 3: Non-array should throw
178
+ await expect(async () => {
179
+ await repository.findMany({
180
+ where: { status: { notInArray: "active" as any } },
181
+ });
182
+ }).rejects.toThrow("notInArray operator requires at least one value");
183
+ });
@@ -0,0 +1,64 @@
1
+ import { $inject, Alepha, t } from "alepha";
2
+ import { DateTimeProvider } from "alepha/datetime";
3
+ import { describe, expect, it } from "vitest";
4
+ import { DbVersionMismatchError } from "../errors/DbVersionMismatchError.ts";
5
+ import { $entity, $repository, pg, type TransactionContext } from "../index.ts";
6
+
7
+ class A {
8
+ dt = $inject(DateTimeProvider);
9
+
10
+ repository = $repository(
11
+ $entity({
12
+ name: "a",
13
+ schema: t.object({
14
+ id: pg.primaryKey(),
15
+ counter: t.integer(),
16
+ __v: pg.version(),
17
+ }),
18
+ }),
19
+ );
20
+
21
+ incFn = async (
22
+ id: number,
23
+ val: number,
24
+ waitMs = 0,
25
+ tx?: TransactionContext,
26
+ ) => {
27
+ const { counter } = await this.repository.findById(id, {
28
+ tx,
29
+ });
30
+
31
+ if (waitMs) {
32
+ await this.dt.wait(waitMs);
33
+ }
34
+
35
+ return await this.repository.updateById(
36
+ id,
37
+ {
38
+ counter: counter + val,
39
+ },
40
+ { tx },
41
+ );
42
+ };
43
+ }
44
+
45
+ describe("version", () => {
46
+ it("should detect version mismatch on concurrent updates", async () => {
47
+ const alepha = Alepha.create();
48
+ const app = alepha.inject(A);
49
+ await alepha.start();
50
+
51
+ const { id } = await app.repository.create({ counter: 0 });
52
+ const r1 = await app.repository.findById(id);
53
+ const r2 = await app.repository.findById(id);
54
+
55
+ r1.counter += 1;
56
+ r2.counter += 1;
57
+
58
+ await app.repository.save(r1);
59
+
60
+ await expect(() => app.repository.save(r2)).rejects.toThrow(
61
+ new DbVersionMismatchError("a", id),
62
+ );
63
+ });
64
+ });