alepha 0.14.1 → 0.14.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (402) hide show
  1. package/README.md +3 -3
  2. package/dist/api/audits/index.browser.js +5 -5
  3. package/dist/api/audits/index.browser.js.map +1 -1
  4. package/dist/api/audits/index.d.ts +784 -784
  5. package/dist/api/audits/index.d.ts.map +1 -1
  6. package/dist/api/audits/index.js +13 -13
  7. package/dist/api/audits/index.js.map +1 -1
  8. package/dist/api/files/index.browser.js +5 -5
  9. package/dist/api/files/index.browser.js.map +1 -1
  10. package/dist/api/files/index.d.ts +57 -57
  11. package/dist/api/files/index.d.ts.map +1 -1
  12. package/dist/api/files/index.js +71 -71
  13. package/dist/api/files/index.js.map +1 -1
  14. package/dist/api/jobs/index.browser.js +5 -5
  15. package/dist/api/jobs/index.browser.js.map +1 -1
  16. package/dist/api/jobs/index.d.ts +165 -165
  17. package/dist/api/jobs/index.d.ts.map +1 -1
  18. package/dist/api/jobs/index.js +10 -10
  19. package/dist/api/jobs/index.js.map +1 -1
  20. package/dist/api/notifications/index.browser.js +10 -10
  21. package/dist/api/notifications/index.browser.js.map +1 -1
  22. package/dist/api/notifications/index.d.ts +583 -171
  23. package/dist/api/notifications/index.d.ts.map +1 -1
  24. package/dist/api/notifications/index.js +12 -12
  25. package/dist/api/notifications/index.js.map +1 -1
  26. package/dist/api/parameters/index.browser.js +163 -10
  27. package/dist/api/parameters/index.browser.js.map +1 -1
  28. package/dist/api/parameters/index.d.ts +281 -276
  29. package/dist/api/parameters/index.d.ts.map +1 -1
  30. package/dist/api/parameters/index.js +196 -91
  31. package/dist/api/parameters/index.js.map +1 -1
  32. package/dist/api/users/index.browser.js +19 -19
  33. package/dist/api/users/index.browser.js.map +1 -1
  34. package/dist/api/users/index.d.ts +778 -764
  35. package/dist/api/users/index.d.ts.map +1 -1
  36. package/dist/api/users/index.js +831 -596
  37. package/dist/api/users/index.js.map +1 -1
  38. package/dist/api/verifications/index.browser.js +6 -6
  39. package/dist/api/verifications/index.browser.js.map +1 -1
  40. package/dist/api/verifications/index.d.ts +125 -125
  41. package/dist/api/verifications/index.d.ts.map +1 -1
  42. package/dist/api/verifications/index.js +6 -6
  43. package/dist/api/verifications/index.js.map +1 -1
  44. package/dist/batch/index.js.map +1 -1
  45. package/dist/bin/index.d.ts +1 -2
  46. package/dist/bin/index.js +0 -1
  47. package/dist/bin/index.js.map +1 -1
  48. package/dist/cache/core/index.js.map +1 -1
  49. package/dist/cli/index.d.ts +249 -218
  50. package/dist/cli/index.d.ts.map +1 -1
  51. package/dist/cli/index.js +951 -821
  52. package/dist/cli/index.js.map +1 -1
  53. package/dist/command/index.d.ts +40 -0
  54. package/dist/command/index.d.ts.map +1 -1
  55. package/dist/command/index.js +97 -17
  56. package/dist/command/index.js.map +1 -1
  57. package/dist/core/index.browser.js +14 -18
  58. package/dist/core/index.browser.js.map +1 -1
  59. package/dist/core/index.d.ts +29 -0
  60. package/dist/core/index.d.ts.map +1 -1
  61. package/dist/core/index.js +21 -24
  62. package/dist/core/index.js.map +1 -1
  63. package/dist/core/index.native.js +21 -24
  64. package/dist/core/index.native.js.map +1 -1
  65. package/dist/datetime/index.js.map +1 -1
  66. package/dist/fake/index.js +195 -168
  67. package/dist/fake/index.js.map +1 -1
  68. package/dist/file/index.d.ts +8 -0
  69. package/dist/file/index.d.ts.map +1 -1
  70. package/dist/file/index.js +3 -0
  71. package/dist/file/index.js.map +1 -1
  72. package/dist/lock/redis/index.js.map +1 -1
  73. package/dist/logger/index.js.map +1 -1
  74. package/dist/mcp/index.d.ts.map +1 -1
  75. package/dist/mcp/index.js.map +1 -1
  76. package/dist/orm/index.browser.js +26 -5
  77. package/dist/orm/index.browser.js.map +1 -1
  78. package/dist/orm/index.d.ts +146 -121
  79. package/dist/orm/index.d.ts.map +1 -1
  80. package/dist/orm/index.js +49 -24
  81. package/dist/orm/index.js.map +1 -1
  82. package/dist/redis/index.js.map +1 -1
  83. package/dist/retry/index.js.map +1 -1
  84. package/dist/router/index.js.map +1 -1
  85. package/dist/scheduler/index.d.ts +6 -6
  86. package/dist/scheduler/index.js.map +1 -1
  87. package/dist/security/index.d.ts +29 -29
  88. package/dist/security/index.d.ts.map +1 -1
  89. package/dist/security/index.js +1 -1
  90. package/dist/security/index.js.map +1 -1
  91. package/dist/server/auth/index.d.ts +171 -155
  92. package/dist/server/auth/index.d.ts.map +1 -1
  93. package/dist/server/auth/index.js +0 -1
  94. package/dist/server/auth/index.js.map +1 -1
  95. package/dist/server/cache/index.js.map +1 -1
  96. package/dist/server/compress/index.d.ts.map +1 -1
  97. package/dist/server/compress/index.js +2 -0
  98. package/dist/server/compress/index.js.map +1 -1
  99. package/dist/server/cookies/index.browser.js.map +1 -1
  100. package/dist/server/cookies/index.js.map +1 -1
  101. package/dist/server/core/index.browser.js.map +1 -1
  102. package/dist/server/core/index.d.ts.map +1 -1
  103. package/dist/server/core/index.js +1 -1
  104. package/dist/server/core/index.js.map +1 -1
  105. package/dist/server/health/index.d.ts +17 -17
  106. package/dist/server/helmet/index.js.map +1 -1
  107. package/dist/server/links/index.browser.js +22 -6
  108. package/dist/server/links/index.browser.js.map +1 -1
  109. package/dist/server/links/index.d.ts +46 -44
  110. package/dist/server/links/index.d.ts.map +1 -1
  111. package/dist/server/links/index.js +24 -41
  112. package/dist/server/links/index.js.map +1 -1
  113. package/dist/server/multipart/index.js.map +1 -1
  114. package/dist/server/rate-limit/index.js.map +1 -1
  115. package/dist/server/security/index.js.map +1 -1
  116. package/dist/server/swagger/index.d.ts +2 -1
  117. package/dist/server/swagger/index.d.ts.map +1 -1
  118. package/dist/server/swagger/index.js +8 -3
  119. package/dist/server/swagger/index.js.map +1 -1
  120. package/dist/thread/index.js.map +1 -1
  121. package/dist/topic/core/index.js.map +1 -1
  122. package/dist/vite/index.d.ts.map +1 -1
  123. package/dist/vite/index.js +12 -4
  124. package/dist/vite/index.js.map +1 -1
  125. package/dist/websocket/index.browser.js.map +1 -1
  126. package/dist/websocket/index.js.map +1 -1
  127. package/package.json +7 -7
  128. package/src/api/audits/controllers/{AuditController.ts → AdminAuditController.ts} +5 -6
  129. package/src/api/audits/entities/audits.ts +5 -5
  130. package/src/api/audits/index.browser.ts +1 -1
  131. package/src/api/audits/index.ts +3 -3
  132. package/src/api/audits/primitives/$audit.spec.ts +276 -0
  133. package/src/api/audits/services/AuditService.spec.ts +495 -0
  134. package/src/api/files/__tests__/$bucket.spec.ts +91 -0
  135. package/src/api/files/controllers/AdminFileStatsController.spec.ts +166 -0
  136. package/src/api/files/controllers/{StorageStatsController.ts → AdminFileStatsController.ts} +2 -2
  137. package/src/api/files/controllers/FileController.spec.ts +558 -0
  138. package/src/api/files/controllers/FileController.ts +4 -5
  139. package/src/api/files/entities/files.ts +5 -5
  140. package/src/api/files/index.browser.ts +1 -1
  141. package/src/api/files/index.ts +4 -4
  142. package/src/api/files/jobs/FileJobs.spec.ts +52 -0
  143. package/src/api/files/services/FileService.spec.ts +109 -0
  144. package/src/api/jobs/__tests__/JobController.spec.ts +343 -0
  145. package/src/api/jobs/controllers/{JobController.ts → AdminJobController.ts} +2 -2
  146. package/src/api/jobs/entities/jobExecutions.ts +5 -5
  147. package/src/api/jobs/index.ts +3 -3
  148. package/src/api/jobs/primitives/$job.spec.ts +476 -0
  149. package/src/api/notifications/controllers/{NotificationController.ts → AdminNotificationController.ts} +4 -5
  150. package/src/api/notifications/entities/notifications.ts +5 -5
  151. package/src/api/notifications/index.browser.ts +1 -1
  152. package/src/api/notifications/index.ts +4 -4
  153. package/src/api/parameters/controllers/{ConfigController.ts → AdminConfigController.ts} +46 -107
  154. package/src/api/parameters/entities/parameters.ts +7 -17
  155. package/src/api/parameters/index.ts +3 -3
  156. package/src/api/parameters/primitives/$config.spec.ts +356 -0
  157. package/src/api/parameters/schemas/activateConfigBodySchema.ts +12 -0
  158. package/src/api/parameters/schemas/checkScheduledResponseSchema.ts +8 -0
  159. package/src/api/parameters/schemas/configCurrentResponseSchema.ts +13 -0
  160. package/src/api/parameters/schemas/configHistoryResponseSchema.ts +9 -0
  161. package/src/api/parameters/schemas/configNameParamSchema.ts +10 -0
  162. package/src/api/parameters/schemas/configNamesResponseSchema.ts +8 -0
  163. package/src/api/parameters/schemas/configTreeNodeSchema.ts +13 -0
  164. package/src/api/parameters/schemas/configVersionParamSchema.ts +9 -0
  165. package/src/api/parameters/schemas/configVersionResponseSchema.ts +9 -0
  166. package/src/api/parameters/schemas/configsByStatusResponseSchema.ts +9 -0
  167. package/src/api/parameters/schemas/createConfigVersionBodySchema.ts +24 -0
  168. package/src/api/parameters/schemas/index.ts +15 -0
  169. package/src/api/parameters/schemas/parameterResponseSchema.ts +26 -0
  170. package/src/api/parameters/schemas/parameterStatusSchema.ts +13 -0
  171. package/src/api/parameters/schemas/rollbackConfigBodySchema.ts +15 -0
  172. package/src/api/parameters/schemas/statusParamSchema.ts +9 -0
  173. package/src/api/users/__tests__/EmailVerification.spec.ts +369 -0
  174. package/src/api/users/__tests__/PasswordReset.spec.ts +550 -0
  175. package/src/api/users/controllers/AdminIdentityController.spec.ts +365 -0
  176. package/src/api/users/controllers/{IdentityController.ts → AdminIdentityController.ts} +3 -4
  177. package/src/api/users/controllers/AdminSessionController.spec.ts +274 -0
  178. package/src/api/users/controllers/{SessionController.ts → AdminSessionController.ts} +3 -4
  179. package/src/api/users/controllers/AdminUserController.spec.ts +372 -0
  180. package/src/api/users/controllers/AdminUserController.ts +116 -0
  181. package/src/api/users/controllers/UserController.ts +4 -107
  182. package/src/api/users/controllers/UserRealmController.ts +3 -0
  183. package/src/api/users/entities/identities.ts +6 -6
  184. package/src/api/users/entities/sessions.ts +6 -6
  185. package/src/api/users/entities/users.ts +9 -9
  186. package/src/api/users/index.ts +13 -6
  187. package/src/api/users/primitives/$userRealm.ts +13 -8
  188. package/src/api/users/services/CredentialService.spec.ts +509 -0
  189. package/src/api/users/services/CredentialService.ts +46 -0
  190. package/src/api/users/services/IdentityService.ts +15 -0
  191. package/src/api/users/services/RegistrationService.spec.ts +630 -0
  192. package/src/api/users/services/RegistrationService.ts +18 -0
  193. package/src/api/users/services/SessionService.spec.ts +301 -0
  194. package/src/api/users/services/SessionService.ts +110 -1
  195. package/src/api/users/services/UserService.ts +67 -2
  196. package/src/api/verifications/__tests__/CodeVerification.spec.ts +318 -0
  197. package/src/api/verifications/__tests__/LinkVerification.spec.ts +279 -0
  198. package/src/api/verifications/entities/verifications.ts +6 -6
  199. package/src/api/verifications/jobs/VerificationJobs.spec.ts +50 -0
  200. package/src/batch/__tests__/startup-buffering.spec.ts +458 -0
  201. package/src/batch/primitives/$batch.spec.ts +766 -0
  202. package/src/batch/providers/BatchProvider.spec.ts +786 -0
  203. package/src/bin/index.ts +0 -1
  204. package/src/bucket/__tests__/shared.ts +194 -0
  205. package/src/bucket/primitives/$bucket.spec.ts +104 -0
  206. package/src/bucket/providers/FileStorageProvider.spec.ts +13 -0
  207. package/src/bucket/providers/LocalFileStorageProvider.spec.ts +77 -0
  208. package/src/bucket/providers/MemoryFileStorageProvider.spec.ts +82 -0
  209. package/src/cache/core/__tests__/shared.ts +377 -0
  210. package/src/cache/core/primitives/$cache.spec.ts +111 -0
  211. package/src/cache/redis/__tests__/cache-redis.spec.ts +70 -0
  212. package/src/cli/apps/AlephaCli.ts +54 -16
  213. package/src/cli/apps/AlephaPackageBuilderCli.ts +2 -1
  214. package/src/cli/assets/appRouterTs.ts +1 -1
  215. package/src/cli/commands/{ViteCommands.ts → build.ts} +2 -105
  216. package/src/cli/commands/clean.ts +14 -0
  217. package/src/cli/commands/{DrizzleCommands.ts → db.ts} +10 -117
  218. package/src/cli/commands/{DeployCommands.ts → deploy.ts} +1 -1
  219. package/src/cli/commands/dev.ts +69 -0
  220. package/src/cli/commands/format.ts +17 -0
  221. package/src/cli/commands/gen/changelog.spec.ts +315 -0
  222. package/src/cli/commands/{ChangelogCommands.ts → gen/changelog.ts} +16 -31
  223. package/src/cli/commands/gen/openapi.ts +71 -0
  224. package/src/cli/commands/gen.ts +18 -0
  225. package/src/cli/commands/{CoreCommands.ts → init.ts} +4 -40
  226. package/src/cli/commands/lint.ts +17 -0
  227. package/src/cli/commands/root.ts +41 -0
  228. package/src/cli/commands/run.ts +24 -0
  229. package/src/cli/commands/test.ts +42 -0
  230. package/src/cli/commands/typecheck.ts +24 -0
  231. package/src/cli/commands/{VerifyCommands.ts → verify.ts} +1 -13
  232. package/src/cli/defineConfig.ts +10 -1
  233. package/src/cli/index.ts +17 -7
  234. package/src/cli/services/AlephaCliUtils.ts +71 -32
  235. package/src/cli/services/GitMessageParser.ts +1 -1
  236. package/src/command/helpers/Asker.spec.ts +127 -0
  237. package/src/command/helpers/Runner.spec.ts +126 -0
  238. package/src/command/primitives/$command.spec.ts +1588 -0
  239. package/src/command/providers/CliProvider.ts +74 -24
  240. package/src/core/Alepha.ts +52 -4
  241. package/src/core/__tests__/Alepha-emit.spec.ts +22 -0
  242. package/src/core/__tests__/Alepha-graph.spec.ts +93 -0
  243. package/src/core/__tests__/Alepha-has.spec.ts +41 -0
  244. package/src/core/__tests__/Alepha-inject.spec.ts +93 -0
  245. package/src/core/__tests__/Alepha-register.spec.ts +81 -0
  246. package/src/core/__tests__/Alepha-start.spec.ts +176 -0
  247. package/src/core/__tests__/Alepha-with.spec.ts +14 -0
  248. package/src/core/__tests__/TypeBox-usecases.spec.ts +35 -0
  249. package/src/core/__tests__/TypeBoxLocale.spec.ts +15 -0
  250. package/src/core/__tests__/descriptor.spec.ts +34 -0
  251. package/src/core/__tests__/fixtures/A.ts +5 -0
  252. package/src/core/__tests__/pagination.spec.ts +77 -0
  253. package/src/core/helpers/jsonSchemaToTypeBox.ts +2 -2
  254. package/src/core/primitives/$atom.spec.ts +43 -0
  255. package/src/core/primitives/$hook.spec.ts +130 -0
  256. package/src/core/primitives/$inject.spec.ts +175 -0
  257. package/src/core/primitives/$module.spec.ts +115 -0
  258. package/src/core/providers/CodecManager.spec.ts +740 -0
  259. package/src/core/providers/EventManager.spec.ts +762 -0
  260. package/src/core/providers/EventManager.ts +4 -0
  261. package/src/core/providers/StateManager.spec.ts +365 -0
  262. package/src/core/providers/TypeProvider.spec.ts +1607 -0
  263. package/src/core/providers/TypeProvider.ts +20 -26
  264. package/src/datetime/primitives/$interval.spec.ts +103 -0
  265. package/src/datetime/providers/DateTimeProvider.spec.ts +86 -0
  266. package/src/email/primitives/$email.spec.ts +175 -0
  267. package/src/email/providers/LocalEmailProvider.spec.ts +341 -0
  268. package/src/fake/__tests__/keyName.example.ts +40 -0
  269. package/src/fake/__tests__/keyName.spec.ts +152 -0
  270. package/src/fake/__tests__/module.example.ts +32 -0
  271. package/src/fake/providers/FakeProvider.spec.ts +438 -0
  272. package/src/file/providers/FileSystemProvider.ts +8 -0
  273. package/src/file/providers/NodeFileSystemProvider.spec.ts +418 -0
  274. package/src/file/providers/NodeFileSystemProvider.ts +5 -0
  275. package/src/file/services/FileDetector.spec.ts +591 -0
  276. package/src/lock/core/__tests__/shared.ts +190 -0
  277. package/src/lock/core/providers/MemoryLockProvider.spec.ts +25 -0
  278. package/src/lock/redis/providers/RedisLockProvider.spec.ts +25 -0
  279. package/src/logger/__tests__/SimpleFormatterProvider.spec.ts +109 -0
  280. package/src/logger/primitives/$logger.spec.ts +108 -0
  281. package/src/logger/services/Logger.spec.ts +295 -0
  282. package/src/mcp/__tests__/errors.spec.ts +175 -0
  283. package/src/mcp/__tests__/integration.spec.ts +450 -0
  284. package/src/mcp/helpers/jsonrpc.spec.ts +380 -0
  285. package/src/mcp/primitives/$prompt.spec.ts +468 -0
  286. package/src/mcp/primitives/$resource.spec.ts +390 -0
  287. package/src/mcp/primitives/$tool.spec.ts +406 -0
  288. package/src/mcp/providers/McpServerProvider.spec.ts +797 -0
  289. package/src/orm/__tests__/$repository-crud.spec.ts +276 -0
  290. package/src/orm/__tests__/$repository-hooks.spec.ts +325 -0
  291. package/src/orm/__tests__/$repository-orderBy.spec.ts +128 -0
  292. package/src/orm/__tests__/$repository-pagination-sort.spec.ts +149 -0
  293. package/src/orm/__tests__/$repository-save.spec.ts +37 -0
  294. package/src/orm/__tests__/ModelBuilder-integration.spec.ts +490 -0
  295. package/src/orm/__tests__/ModelBuilder-types.spec.ts +186 -0
  296. package/src/orm/__tests__/PostgresProvider.spec.ts +46 -0
  297. package/src/orm/__tests__/delete-returning.spec.ts +256 -0
  298. package/src/orm/__tests__/deletedAt.spec.ts +80 -0
  299. package/src/orm/__tests__/enums.spec.ts +315 -0
  300. package/src/orm/__tests__/execute.spec.ts +72 -0
  301. package/src/orm/__tests__/fixtures/bigEntitySchema.ts +65 -0
  302. package/src/orm/__tests__/fixtures/userEntitySchema.ts +27 -0
  303. package/src/orm/__tests__/joins.spec.ts +1114 -0
  304. package/src/orm/__tests__/page.spec.ts +287 -0
  305. package/src/orm/__tests__/primaryKey.spec.ts +87 -0
  306. package/src/orm/__tests__/query-date-encoding.spec.ts +402 -0
  307. package/src/orm/__tests__/ref-auto-onDelete.spec.ts +156 -0
  308. package/src/orm/__tests__/references.spec.ts +102 -0
  309. package/src/orm/__tests__/security.spec.ts +710 -0
  310. package/src/orm/__tests__/sqlite.spec.ts +111 -0
  311. package/src/orm/__tests__/string-operators.spec.ts +429 -0
  312. package/src/orm/__tests__/timestamps.spec.ts +388 -0
  313. package/src/orm/__tests__/validation.spec.ts +183 -0
  314. package/src/orm/__tests__/version.spec.ts +64 -0
  315. package/src/orm/helpers/parseQueryString.spec.ts +196 -0
  316. package/src/orm/index.browser.ts +1 -1
  317. package/src/orm/index.ts +10 -6
  318. package/src/orm/primitives/$repository.spec.ts +137 -0
  319. package/src/orm/primitives/$sequence.spec.ts +29 -0
  320. package/src/orm/primitives/$transaction.spec.ts +82 -0
  321. package/src/orm/providers/{PostgresTypeProvider.ts → DatabaseTypeProvider.ts} +25 -3
  322. package/src/orm/providers/drivers/BunPostgresProvider.ts +3 -3
  323. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -1
  324. package/src/orm/providers/drivers/CloudflareD1Provider.ts +1 -1
  325. package/src/orm/providers/drivers/DatabaseProvider.ts +1 -1
  326. package/src/orm/providers/drivers/NodePostgresProvider.ts +3 -3
  327. package/src/orm/providers/drivers/NodeSqliteProvider.ts +1 -1
  328. package/src/orm/providers/drivers/PglitePostgresProvider.ts +2 -2
  329. package/src/orm/services/ModelBuilder.spec.ts +575 -0
  330. package/src/orm/services/Repository.spec.ts +137 -0
  331. package/src/queue/core/__tests__/shared.ts +143 -0
  332. package/src/queue/core/providers/MemoryQueueProvider.spec.ts +23 -0
  333. package/src/queue/core/providers/WorkerProvider.spec.ts +378 -0
  334. package/src/queue/redis/providers/RedisQueueProvider.spec.ts +23 -0
  335. package/src/redis/__tests__/redis.spec.ts +58 -0
  336. package/src/retry/primitives/$retry.spec.ts +234 -0
  337. package/src/retry/providers/RetryProvider.spec.ts +438 -0
  338. package/src/router/__tests__/match.spec.ts +252 -0
  339. package/src/router/providers/RouterProvider.spec.ts +197 -0
  340. package/src/scheduler/__tests__/$scheduler-cron.spec.ts +25 -0
  341. package/src/scheduler/__tests__/$scheduler-interval.spec.ts +25 -0
  342. package/src/scheduler/__tests__/shared.ts +77 -0
  343. package/src/security/__tests__/bug-1-wildcard-after-start.spec.ts +229 -0
  344. package/src/security/__tests__/bug-2-password-validation.spec.ts +245 -0
  345. package/src/security/__tests__/bug-3-regex-vulnerability.spec.ts +407 -0
  346. package/src/security/__tests__/bug-4-oauth2-validation.spec.ts +439 -0
  347. package/src/security/__tests__/multi-layer-permissions.spec.ts +522 -0
  348. package/src/security/primitives/$permission.spec.ts +30 -0
  349. package/src/security/primitives/$permission.ts +2 -2
  350. package/src/security/primitives/$realm.spec.ts +101 -0
  351. package/src/security/primitives/$role.spec.ts +52 -0
  352. package/src/security/primitives/$serviceAccount.spec.ts +61 -0
  353. package/src/security/providers/SecurityProvider.spec.ts +350 -0
  354. package/src/server/auth/providers/ServerAuthProvider.ts +0 -2
  355. package/src/server/cache/providers/ServerCacheProvider.spec.ts +942 -0
  356. package/src/server/compress/providers/ServerCompressProvider.spec.ts +31 -0
  357. package/src/server/compress/providers/ServerCompressProvider.ts +2 -0
  358. package/src/server/cookies/providers/ServerCookiesProvider.spec.ts +253 -0
  359. package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +334 -0
  360. package/src/server/core/__tests__/ServerRouterProvider-requestId.spec.ts +129 -0
  361. package/src/server/core/primitives/$action.spec.ts +191 -0
  362. package/src/server/core/primitives/$route.spec.ts +65 -0
  363. package/src/server/core/providers/ServerBodyParserProvider.spec.ts +93 -0
  364. package/src/server/core/providers/ServerLoggerProvider.spec.ts +100 -0
  365. package/src/server/core/providers/ServerProvider.ts +3 -1
  366. package/src/server/core/services/HttpClient.spec.ts +123 -0
  367. package/src/server/core/services/UserAgentParser.spec.ts +111 -0
  368. package/src/server/cors/providers/ServerCorsProvider.spec.ts +481 -0
  369. package/src/server/health/providers/ServerHealthProvider.spec.ts +22 -0
  370. package/src/server/helmet/providers/ServerHelmetProvider.spec.ts +105 -0
  371. package/src/server/links/__tests__/$action.spec.ts +238 -0
  372. package/src/server/links/__tests__/fixtures/CrudApp.ts +122 -0
  373. package/src/server/links/__tests__/requestId.spec.ts +120 -0
  374. package/src/server/links/primitives/$remote.spec.ts +228 -0
  375. package/src/server/links/providers/LinkProvider.spec.ts +54 -0
  376. package/src/server/links/providers/LinkProvider.ts +49 -3
  377. package/src/server/links/providers/ServerLinksProvider.ts +1 -53
  378. package/src/server/links/schemas/apiLinksResponseSchema.ts +7 -0
  379. package/src/server/metrics/providers/ServerMetricsProvider.spec.ts +25 -0
  380. package/src/server/multipart/providers/ServerMultipartProvider.spec.ts +528 -0
  381. package/src/server/proxy/primitives/$proxy.spec.ts +87 -0
  382. package/src/server/rate-limit/__tests__/ActionRateLimit.spec.ts +211 -0
  383. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +344 -0
  384. package/src/server/security/__tests__/BasicAuth.spec.ts +684 -0
  385. package/src/server/security/__tests__/ServerSecurityProvider-realm.spec.ts +388 -0
  386. package/src/server/security/providers/ServerSecurityProvider.spec.ts +123 -0
  387. package/src/server/static/primitives/$serve.spec.ts +193 -0
  388. package/src/server/swagger/__tests__/ui.spec.ts +52 -0
  389. package/src/server/swagger/primitives/$swagger.spec.ts +193 -0
  390. package/src/server/swagger/providers/ServerSwaggerProvider.ts +18 -8
  391. package/src/sms/primitives/$sms.spec.ts +165 -0
  392. package/src/sms/providers/LocalSmsProvider.spec.ts +224 -0
  393. package/src/sms/providers/MemorySmsProvider.spec.ts +193 -0
  394. package/src/thread/primitives/$thread.spec.ts +186 -0
  395. package/src/topic/core/__tests__/shared.ts +144 -0
  396. package/src/topic/core/providers/MemoryTopicProvider.spec.ts +23 -0
  397. package/src/topic/redis/providers/RedisTopicProvider.spec.ts +23 -0
  398. package/src/vite/plugins/viteAlephaDev.ts +16 -4
  399. package/src/vite/tasks/runAlepha.ts +7 -1
  400. package/src/websocket/__tests__/$websocket-new.spec.ts +195 -0
  401. package/src/websocket/primitives/$channel.spec.ts +30 -0
  402. package/src/cli/commands/BiomeCommands.ts +0 -29
@@ -1,35 +1,24 @@
1
1
  import { $inject, t } from "alepha";
2
2
  import { $action } from "alepha/server";
3
3
  import type { ParameterStatus } from "../entities/parameters.ts";
4
+ import {
5
+ activateConfigBodySchema,
6
+ checkScheduledResponseSchema,
7
+ configCurrentResponseSchema,
8
+ configHistoryResponseSchema,
9
+ configNameParamSchema,
10
+ configNamesResponseSchema,
11
+ configsByStatusResponseSchema,
12
+ configTreeNodeSchema,
13
+ configVersionParamSchema,
14
+ configVersionResponseSchema,
15
+ createConfigVersionBodySchema,
16
+ parameterResponseSchema,
17
+ rollbackConfigBodySchema,
18
+ statusParamSchema,
19
+ } from "../schemas/index.ts";
4
20
  import { ConfigStore } from "../services/ConfigStore.ts";
5
21
 
6
- // Define response schemas inline to avoid complex entity schema issues
7
- const parameterResponseSchema = t.object({
8
- id: t.uuid(),
9
- createdAt: t.datetime(),
10
- updatedAt: t.datetime(),
11
- name: t.text(),
12
- content: t.json(),
13
- schemaHash: t.text(),
14
- status: t.enum(["expired", "current", "next", "future"]),
15
- activationDate: t.datetime(),
16
- expiredAt: t.optional(t.datetime()),
17
- version: t.integer(),
18
- changeDescription: t.optional(t.text()),
19
- tags: t.optional(t.array(t.text())),
20
- creatorId: t.optional(t.uuid()),
21
- creatorName: t.optional(t.text()),
22
- previousContent: t.optional(t.json()),
23
- migrationLog: t.optional(t.text()),
24
- });
25
-
26
- const treeNodeSchema: any = t.object({
27
- name: t.text(),
28
- path: t.text(),
29
- isLeaf: t.boolean(),
30
- children: t.array(t.any()),
31
- });
32
-
33
22
  /**
34
23
  * REST API controller for versioned configuration management.
35
24
  *
@@ -41,7 +30,9 @@ const treeNodeSchema: any = t.object({
41
30
  * - Rolling back to previous versions
42
31
  * - Activating scheduled versions immediately
43
32
  */
44
- export class ConfigController {
33
+ export class AdminConfigController {
34
+ protected readonly url = "/configs";
35
+ protected readonly group = "admin:configs";
45
36
  protected readonly store = $inject(ConfigStore);
46
37
 
47
38
  /**
@@ -49,12 +40,13 @@ export class ConfigController {
49
40
  * Useful for admin UI navigation.
50
41
  */
51
42
  getConfigTree = $action({
43
+ group: this.group,
52
44
  description:
53
45
  "Get tree structure of all configuration names for navigation.",
54
46
  path: "/configs/tree",
55
47
  method: "GET",
56
48
  schema: {
57
- response: t.array(treeNodeSchema),
49
+ response: t.array(configTreeNodeSchema),
58
50
  },
59
51
  handler: async () => {
60
52
  return this.store.getConfigTree();
@@ -65,13 +57,12 @@ export class ConfigController {
65
57
  * List all unique configuration names.
66
58
  */
67
59
  listConfigNames = $action({
60
+ group: this.group,
68
61
  description: "List all unique configuration names.",
69
62
  path: "/configs",
70
63
  method: "GET",
71
64
  schema: {
72
- response: t.object({
73
- names: t.array(t.text()),
74
- }),
65
+ response: configNamesResponseSchema,
75
66
  },
76
67
  handler: async () => {
77
68
  const names = await this.store.getConfigNames();
@@ -83,16 +74,13 @@ export class ConfigController {
83
74
  * Get configurations by status.
84
75
  */
85
76
  getByStatus = $action({
77
+ group: this.group,
86
78
  description: "Get all configurations with a specific status.",
87
79
  path: "/configs/status/:status",
88
80
  method: "GET",
89
81
  schema: {
90
- params: t.object({
91
- status: t.enum(["expired", "current", "next", "future"]),
92
- }),
93
- response: t.object({
94
- configs: t.array(parameterResponseSchema),
95
- }),
82
+ params: statusParamSchema,
83
+ response: configsByStatusResponseSchema,
96
84
  },
97
85
  handler: async ({ params }) => {
98
86
  const configs = await this.store.getByStatus(
@@ -106,18 +94,13 @@ export class ConfigController {
106
94
  * Get version history for a specific configuration.
107
95
  */
108
96
  getHistory = $action({
97
+ group: this.group,
109
98
  description: "Get all versions of a specific configuration.",
110
99
  path: "/configs/:name/history",
111
100
  method: "GET",
112
101
  schema: {
113
- params: t.object({
114
- name: t.text({
115
- description: "Configuration name (e.g., app.features.flags)",
116
- }),
117
- }),
118
- response: t.object({
119
- versions: t.array(parameterResponseSchema),
120
- }),
102
+ params: configNameParamSchema,
103
+ response: configHistoryResponseSchema,
121
104
  },
122
105
  handler: async ({ params }) => {
123
106
  const versions = await this.store.getHistory(params.name);
@@ -131,22 +114,13 @@ export class ConfigController {
131
114
  * even if no versions exist in the database yet.
132
115
  */
133
116
  getCurrent = $action({
117
+ group: this.group,
134
118
  description: "Get current and next scheduled values for a configuration.",
135
119
  path: "/configs/:name",
136
120
  method: "GET",
137
121
  schema: {
138
- params: t.object({
139
- name: t.text({
140
- description: "Configuration name (e.g., app.features.flags)",
141
- }),
142
- }),
143
- response: t.object({
144
- current: t.optional(parameterResponseSchema),
145
- next: t.optional(parameterResponseSchema),
146
- defaultValue: t.optional(t.json()),
147
- currentValue: t.optional(t.json()),
148
- schema: t.optional(t.json()),
149
- }),
122
+ params: configNameParamSchema,
123
+ response: configCurrentResponseSchema,
150
124
  },
151
125
  handler: async ({ params }) => {
152
126
  const result = await this.store.getCurrentWithDefault(params.name);
@@ -164,17 +138,13 @@ export class ConfigController {
164
138
  * Get a specific version of a configuration.
165
139
  */
166
140
  getVersion = $action({
141
+ group: this.group,
167
142
  description: "Get a specific version of a configuration.",
168
143
  path: "/configs/:name/versions/:version",
169
144
  method: "GET",
170
145
  schema: {
171
- params: t.object({
172
- name: t.text(),
173
- version: t.integer(),
174
- }),
175
- response: t.object({
176
- config: t.optional(parameterResponseSchema),
177
- }),
146
+ params: configVersionParamSchema,
147
+ response: configVersionResponseSchema,
178
148
  },
179
149
  handler: async ({ params }) => {
180
150
  const config = await this.store.getVersion(params.name, params.version);
@@ -186,31 +156,14 @@ export class ConfigController {
186
156
  * Create a new configuration version.
187
157
  */
188
158
  createVersion = $action({
159
+ group: this.group,
189
160
  description:
190
161
  "Create a new version of a configuration (immediate or scheduled).",
191
162
  path: "/configs/:name",
192
163
  method: "POST",
193
164
  schema: {
194
- params: t.object({
195
- name: t.text({
196
- description: "Configuration name (e.g., app.features.flags)",
197
- }),
198
- }),
199
- body: t.object({
200
- content: t.json({ description: "New configuration content" }),
201
- schemaHash: t.text({
202
- description: "Hash of the schema for migration detection",
203
- }),
204
- activationDate: t.optional(
205
- t.datetime({ description: "When to activate (default: now)" }),
206
- ),
207
- changeDescription: t.optional(
208
- t.text({ description: "Description of changes" }),
209
- ),
210
- tags: t.optional(t.array(t.text())),
211
- creatorId: t.optional(t.uuid()),
212
- creatorName: t.optional(t.text()),
213
- }),
165
+ params: configNameParamSchema,
166
+ body: createConfigVersionBodySchema,
214
167
  response: parameterResponseSchema,
215
168
  },
216
169
  handler: async ({ params, body }) => {
@@ -230,22 +183,14 @@ export class ConfigController {
230
183
  * Rollback to a previous version.
231
184
  */
232
185
  rollback = $action({
186
+ group: this.group,
233
187
  description:
234
188
  "Rollback a configuration to a previous version (creates new version with old content).",
235
189
  path: "/configs/:name/rollback",
236
190
  method: "POST",
237
191
  schema: {
238
- params: t.object({
239
- name: t.text(),
240
- }),
241
- body: t.object({
242
- targetVersion: t.integer({
243
- description: "Version number to rollback to",
244
- }),
245
- changeDescription: t.optional(t.text()),
246
- creatorId: t.optional(t.uuid()),
247
- creatorName: t.optional(t.text()),
248
- }),
192
+ params: configNameParamSchema,
193
+ body: rollbackConfigBodySchema,
249
194
  response: parameterResponseSchema,
250
195
  },
251
196
  handler: async ({ params, body }) => {
@@ -261,18 +206,13 @@ export class ConfigController {
261
206
  * Activate a scheduled version immediately.
262
207
  */
263
208
  activateNow = $action({
209
+ group: this.group,
264
210
  description: "Activate a future/next configuration version immediately.",
265
211
  path: "/configs/:name/activate",
266
212
  method: "POST",
267
213
  schema: {
268
- params: t.object({
269
- name: t.text(),
270
- }),
271
- body: t.object({
272
- version: t.integer({ description: "Version number to activate" }),
273
- creatorId: t.optional(t.uuid()),
274
- creatorName: t.optional(t.text()),
275
- }),
214
+ params: configNameParamSchema,
215
+ body: activateConfigBodySchema,
276
216
  response: parameterResponseSchema,
277
217
  },
278
218
  handler: async ({ params, body }) => {
@@ -307,14 +247,13 @@ export class ConfigController {
307
247
  * Normally called by a scheduler, but exposed for manual triggering.
308
248
  */
309
249
  checkScheduled = $action({
250
+ group: this.group,
310
251
  description:
311
252
  "Manually trigger activation check for all scheduled configurations.",
312
253
  path: "/configs/activate-scheduled",
313
254
  method: "POST",
314
255
  schema: {
315
- response: t.object({
316
- message: t.text(),
317
- }),
256
+ response: checkScheduledResponseSchema,
318
257
  },
319
258
  handler: async () => {
320
259
  await this.store.activateScheduledConfigs();
@@ -1,15 +1,8 @@
1
1
  import { type Static, t } from "alepha";
2
- import { $entity, pg } from "alepha/orm";
2
+ import { $entity, db } from "alepha/orm";
3
+ import { parameterStatusSchema } from "../schemas/index.ts";
3
4
 
4
- /**
5
- * Parameter status values.
6
- *
7
- * - EXPIRED: Past version, no longer active
8
- * - CURRENT: Currently active version
9
- * - NEXT: Scheduled to become active (closest future date)
10
- * - FUTURE: Scheduled for activation after NEXT
11
- */
12
- export type ParameterStatus = "expired" | "current" | "next" | "future";
5
+ export type { ParameterStatus } from "../schemas/index.ts";
13
6
 
14
7
  /**
15
8
  * Configuration parameter entity for versioned configuration management.
@@ -23,9 +16,9 @@ export type ParameterStatus = "expired" | "current" | "next" | "future";
23
16
  export const parameters = $entity({
24
17
  name: "parameters",
25
18
  schema: t.object({
26
- id: pg.primaryKey(t.uuid()),
27
- createdAt: pg.createdAt(),
28
- updatedAt: pg.updatedAt(),
19
+ id: db.primaryKey(t.uuid()),
20
+ createdAt: db.createdAt(),
21
+ updatedAt: db.updatedAt(),
29
22
 
30
23
  /**
31
24
  * Configuration name using dot notation for tree hierarchy.
@@ -47,10 +40,7 @@ export const parameters = $entity({
47
40
  /**
48
41
  * Current status of this parameter version.
49
42
  */
50
- status: pg.default(
51
- t.enum(["expired", "current", "next", "future"]),
52
- "future",
53
- ),
43
+ status: db.default(parameterStatusSchema, "future"),
54
44
 
55
45
  /**
56
46
  * When this version should become active.
@@ -1,12 +1,12 @@
1
1
  import { $module } from "alepha";
2
- import { ConfigController } from "./controllers/ConfigController.ts";
2
+ import { AdminConfigController } from "./controllers/AdminConfigController.ts";
3
3
  import { ConfigActivationScheduler } from "./schedulers/ConfigActivationScheduler.ts";
4
4
  import { ConfigStore } from "./services/ConfigStore.ts";
5
5
 
6
6
  // ---------------------------------------------------------------------------------------------------------------------
7
7
 
8
8
  // Controller exports
9
- export * from "./controllers/ConfigController.ts";
9
+ export * from "./controllers/AdminConfigController.ts";
10
10
  // Entity exports
11
11
  export * from "./entities/parameters.ts";
12
12
  // Primitive exports
@@ -56,5 +56,5 @@ export * from "./services/ConfigStore.ts";
56
56
  */
57
57
  export const AlephaApiParameters = $module({
58
58
  name: "alepha.api.parameters",
59
- services: [ConfigStore, ConfigController, ConfigActivationScheduler],
59
+ services: [ConfigStore, AdminConfigController, ConfigActivationScheduler],
60
60
  });
@@ -0,0 +1,356 @@
1
+ import { Alepha, t } from "alepha";
2
+ import { describe, expect, it } from "vitest";
3
+ import { $config, AlephaApiParameters, ConfigStore } from "../index.ts";
4
+
5
+ const featureSchema = t.object({
6
+ enableBeta: t.boolean(),
7
+ maxUploadSize: t.number(),
8
+ });
9
+
10
+ describe("$config", () => {
11
+ it("should initialize with default value", async () => {
12
+ class AppConfig {
13
+ features = $config({
14
+ name: "app.features.flags",
15
+ schema: featureSchema,
16
+ default: { enableBeta: false, maxUploadSize: 10485760 },
17
+ });
18
+ }
19
+
20
+ const alepha = Alepha.create();
21
+ alepha.with(AlephaApiParameters);
22
+ alepha.with(AppConfig);
23
+ await alepha.start();
24
+
25
+ const config = alepha.inject(AppConfig);
26
+
27
+ expect(config.features.current).toEqual({
28
+ enableBeta: false,
29
+ maxUploadSize: 10485760,
30
+ });
31
+ });
32
+
33
+ it("should set and persist configuration", async () => {
34
+ class AppConfig {
35
+ features = $config({
36
+ name: "app.features.flags",
37
+ schema: featureSchema,
38
+ default: { enableBeta: false, maxUploadSize: 10485760 },
39
+ });
40
+ }
41
+
42
+ const alepha = Alepha.create();
43
+ alepha.with(AlephaApiParameters);
44
+ alepha.with(AppConfig);
45
+ await alepha.start();
46
+
47
+ const config = alepha.inject(AppConfig);
48
+
49
+ await config.features.set({
50
+ enableBeta: true,
51
+ maxUploadSize: 20971520,
52
+ });
53
+
54
+ expect(config.features.current).toEqual({
55
+ enableBeta: true,
56
+ maxUploadSize: 20971520,
57
+ });
58
+
59
+ // Verify persisted to database
60
+ const store = alepha.inject(ConfigStore);
61
+ const history = await store.getHistory("app.features.flags");
62
+ expect(history.length).toBe(1);
63
+ expect(history[0].status).toBe("current");
64
+ expect(history[0].content).toEqual({
65
+ enableBeta: true,
66
+ maxUploadSize: 20971520,
67
+ });
68
+ });
69
+
70
+ it("should load from database on start", async () => {
71
+ // Use a single Alepha instance, seed data via store before registering config
72
+ class AppConfig {
73
+ features = $config({
74
+ name: "app.features.load",
75
+ schema: featureSchema,
76
+ default: { enableBeta: false, maxUploadSize: 10485760 },
77
+ });
78
+ }
79
+
80
+ const alepha = Alepha.create();
81
+ alepha.with(AlephaApiParameters);
82
+ alepha.with(AppConfig);
83
+ await alepha.start();
84
+
85
+ // First, seed the database with a value different from default
86
+ const store = alepha.inject(ConfigStore);
87
+ await store.save(
88
+ "app.features.load",
89
+ { enableBeta: true, maxUploadSize: 5242880 },
90
+ "test-hash",
91
+ );
92
+
93
+ // Manually reload the config primitive
94
+ const config = alepha.inject(AppConfig);
95
+ await config.features.reload();
96
+
97
+ // Should have loaded from database, not default
98
+ expect(config.features.current).toEqual({
99
+ enableBeta: true,
100
+ maxUploadSize: 5242880,
101
+ });
102
+ });
103
+
104
+ it("should maintain version history", async () => {
105
+ class AppConfig {
106
+ features = $config({
107
+ name: "app.features.history",
108
+ schema: featureSchema,
109
+ default: { enableBeta: false, maxUploadSize: 10485760 },
110
+ });
111
+ }
112
+
113
+ const alepha = Alepha.create();
114
+ alepha.with(AlephaApiParameters);
115
+ alepha.with(AppConfig);
116
+ await alepha.start();
117
+
118
+ const config = alepha.inject(AppConfig);
119
+
120
+ // Make multiple changes
121
+ await config.features.set({ enableBeta: true, maxUploadSize: 1 });
122
+ await config.features.set({ enableBeta: false, maxUploadSize: 2 });
123
+ await config.features.set({ enableBeta: true, maxUploadSize: 3 });
124
+
125
+ const history = await config.features.getHistory();
126
+ expect(history.length).toBe(3);
127
+ expect(history[0].version).toBe(3);
128
+ expect(history[1].version).toBe(2);
129
+ expect(history[2].version).toBe(1);
130
+ });
131
+
132
+ it("should support rollback to previous version", async () => {
133
+ class AppConfig {
134
+ features = $config({
135
+ name: "app.features.rollback",
136
+ schema: featureSchema,
137
+ default: { enableBeta: false, maxUploadSize: 10485760 },
138
+ });
139
+ }
140
+
141
+ const alepha = Alepha.create();
142
+ alepha.with(AlephaApiParameters);
143
+ alepha.with(AppConfig);
144
+ await alepha.start();
145
+
146
+ const config = alepha.inject(AppConfig);
147
+
148
+ await config.features.set({ enableBeta: true, maxUploadSize: 100 });
149
+ await config.features.set({ enableBeta: false, maxUploadSize: 200 });
150
+ await config.features.set({ enableBeta: true, maxUploadSize: 300 });
151
+
152
+ // Rollback to version 1
153
+ await config.features.rollback(1);
154
+
155
+ expect(config.features.current).toEqual({
156
+ enableBeta: true,
157
+ maxUploadSize: 100,
158
+ });
159
+
160
+ // Should have created a new version (4)
161
+ const history = await config.features.getHistory();
162
+ expect(history.length).toBe(4);
163
+ });
164
+
165
+ it("should get specific field value", async () => {
166
+ class AppConfig {
167
+ features = $config({
168
+ name: "app.features.field",
169
+ schema: featureSchema,
170
+ default: { enableBeta: false, maxUploadSize: 10485760 },
171
+ });
172
+ }
173
+
174
+ const alepha = Alepha.create();
175
+ alepha.with(AlephaApiParameters);
176
+ alepha.with(AppConfig);
177
+ await alepha.start();
178
+
179
+ const config = alepha.inject(AppConfig);
180
+
181
+ expect(config.features.get("enableBeta")).toBe(false);
182
+ expect(config.features.get("maxUploadSize")).toBe(10485760);
183
+ });
184
+
185
+ it("should support change description", async () => {
186
+ class AppConfig {
187
+ features = $config({
188
+ name: "app.features.description",
189
+ schema: featureSchema,
190
+ default: { enableBeta: false, maxUploadSize: 10485760 },
191
+ });
192
+ }
193
+
194
+ const alepha = Alepha.create();
195
+ alepha.with(AlephaApiParameters);
196
+ alepha.with(AppConfig);
197
+ await alepha.start();
198
+
199
+ const config = alepha.inject(AppConfig);
200
+
201
+ await config.features.set(
202
+ { enableBeta: true, maxUploadSize: 20971520 },
203
+ { changeDescription: "Enable beta features for testing" },
204
+ );
205
+
206
+ const history = await config.features.getHistory();
207
+ expect(history[0].changeDescription).toBe(
208
+ "Enable beta features for testing",
209
+ );
210
+ });
211
+
212
+ it("should support user tracking", async () => {
213
+ class AppConfig {
214
+ features = $config({
215
+ name: "app.features.user",
216
+ schema: featureSchema,
217
+ default: { enableBeta: false, maxUploadSize: 10485760 },
218
+ });
219
+ }
220
+
221
+ const alepha = Alepha.create();
222
+ alepha.with(AlephaApiParameters);
223
+ alepha.with(AppConfig);
224
+ await alepha.start();
225
+
226
+ const config = alepha.inject(AppConfig);
227
+
228
+ const userId = "550e8400-e29b-41d4-a716-446655440000";
229
+ await config.features.set(
230
+ { enableBeta: true, maxUploadSize: 20971520 },
231
+ {
232
+ user: {
233
+ id: userId,
234
+ email: "admin@example.com",
235
+ name: "Admin User",
236
+ },
237
+ },
238
+ );
239
+
240
+ const history = await config.features.getHistory();
241
+ expect(history[0].creatorId).toBe(userId);
242
+ expect(history[0].creatorName).toBe("Admin User");
243
+ });
244
+ });
245
+
246
+ describe("ConfigStore", () => {
247
+ it("should build config tree from names", async () => {
248
+ const alepha = Alepha.create();
249
+ alepha.with(AlephaApiParameters);
250
+ await alepha.start();
251
+
252
+ const store = alepha.inject(ConfigStore);
253
+
254
+ await store.save("app.features.flags", { enabled: true }, "hash1");
255
+ await store.save("app.features.limits", { max: 100 }, "hash2");
256
+ await store.save("app.pricing.tiers", { basic: 10 }, "hash3");
257
+ await store.save("system.logging", { level: "info" }, "hash4");
258
+
259
+ const tree = await store.getConfigTree();
260
+
261
+ expect(tree.length).toBe(2); // app, system
262
+ expect(tree[0].name).toBe("app");
263
+ expect(tree[0].isLeaf).toBe(false);
264
+ expect(tree[0].children.length).toBe(2); // features, pricing
265
+
266
+ const features = tree[0].children.find((c) => c.name === "features");
267
+ expect(features?.children.length).toBe(2); // flags, limits
268
+ });
269
+
270
+ it("should manage status transitions correctly", async () => {
271
+ const alepha = Alepha.create();
272
+ alepha.with(AlephaApiParameters);
273
+ await alepha.start();
274
+
275
+ const store = alepha.inject(ConfigStore);
276
+
277
+ // Create first version (current)
278
+ const v1 = await store.save("test.status.config", { value: 1 }, "hash");
279
+ expect(v1.status).toBe("current");
280
+
281
+ // Create second version (becomes current, first becomes expired)
282
+ const v2 = await store.save("test.status.config", { value: 2 }, "hash");
283
+ expect(v2.status).toBe("current");
284
+
285
+ const history = await store.getHistory("test.status.config");
286
+ const v1Updated = history.find((v) => v.version === 1);
287
+ expect(v1Updated?.status).toBe("expired");
288
+ });
289
+
290
+ it("should get configs by status", async () => {
291
+ const alepha = Alepha.create();
292
+ alepha.with(AlephaApiParameters);
293
+ await alepha.start();
294
+
295
+ const store = alepha.inject(ConfigStore);
296
+
297
+ await store.save("status.a", { a: 1 }, "h1");
298
+ await store.save("status.b", { b: 1 }, "h2");
299
+ await store.save("status.a", { a: 2 }, "h1");
300
+
301
+ const current = await store.getByStatus("current");
302
+ const expired = await store.getByStatus("expired");
303
+
304
+ expect(current.length).toBe(2);
305
+ expect(expired.length).toBe(1);
306
+ });
307
+
308
+ it("should detect schema migration", async () => {
309
+ const alepha = Alepha.create();
310
+ alepha.with(AlephaApiParameters);
311
+ await alepha.start();
312
+
313
+ const store = alepha.inject(ConfigStore);
314
+
315
+ await store.save("migration.config", { v: 1 }, "hash-v1");
316
+ const v2 = await store.save(
317
+ "migration.config",
318
+ { v: 2, extra: true },
319
+ "hash-v2",
320
+ );
321
+
322
+ expect(v2.migrationLog).toContain("Schema changed");
323
+ expect(v2.migrationLog).toContain("hash-v1");
324
+ expect(v2.migrationLog).toContain("hash-v2");
325
+ });
326
+
327
+ it("should store previous content for rollback", async () => {
328
+ const alepha = Alepha.create();
329
+ alepha.with(AlephaApiParameters);
330
+ await alepha.start();
331
+
332
+ const store = alepha.inject(ConfigStore);
333
+
334
+ await store.save("previous.config", { old: true }, "hash");
335
+ const v2 = await store.save("previous.config", { new: true }, "hash");
336
+
337
+ expect(v2.previousContent).toEqual({ old: true });
338
+ });
339
+
340
+ it("should get all config names", async () => {
341
+ const alepha = Alepha.create();
342
+ alepha.with(AlephaApiParameters);
343
+ await alepha.start();
344
+
345
+ const store = alepha.inject(ConfigStore);
346
+
347
+ await store.save("names.alpha", { a: 1 }, "h1");
348
+ await store.save("names.beta", { b: 1 }, "h2");
349
+ await store.save("names.alpha", { a: 2 }, "h1"); // Second version
350
+
351
+ const names = await store.getConfigNames();
352
+
353
+ expect(names).toContain("names.alpha");
354
+ expect(names).toContain("names.beta");
355
+ });
356
+ });