alepha 0.23.0 → 0.24.0

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 (845) hide show
  1. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  2. package/assets/swagger-ui/swagger-ui-standalone-preset.js +1 -1
  3. package/dist/api/audits/index.browser.js +49 -52
  4. package/dist/api/audits/index.browser.js.map +1 -1
  5. package/dist/api/audits/index.d.ts +629 -565
  6. package/dist/api/audits/index.d.ts.map +1 -1
  7. package/dist/api/audits/index.js +206 -98
  8. package/dist/api/audits/index.js.map +1 -1
  9. package/dist/api/files/index.browser.js +44 -44
  10. package/dist/api/files/index.browser.js.map +1 -1
  11. package/dist/api/files/index.d.ts +292 -293
  12. package/dist/api/files/index.d.ts.map +1 -1
  13. package/dist/api/files/index.js +61 -64
  14. package/dist/api/files/index.js.map +1 -1
  15. package/dist/api/jobs/index.browser.js +61 -59
  16. package/dist/api/jobs/index.browser.js.map +1 -1
  17. package/dist/api/jobs/index.d.ts +546 -347
  18. package/dist/api/jobs/index.d.ts.map +1 -1
  19. package/dist/api/jobs/index.js +165 -86
  20. package/dist/api/jobs/index.js.map +1 -1
  21. package/dist/api/keys/index.d.ts +247 -235
  22. package/dist/api/keys/index.d.ts.map +1 -1
  23. package/dist/api/keys/index.js +63 -66
  24. package/dist/api/keys/index.js.map +1 -1
  25. package/dist/api/notifications/index.browser.js +40 -38
  26. package/dist/api/notifications/index.browser.js.map +1 -1
  27. package/dist/api/notifications/index.d.ts +339 -209
  28. package/dist/api/notifications/index.d.ts.map +1 -1
  29. package/dist/api/notifications/index.js +108 -60
  30. package/dist/api/notifications/index.js.map +1 -1
  31. package/dist/api/oauth/index.d.ts +190 -78
  32. package/dist/api/oauth/index.d.ts.map +1 -1
  33. package/dist/api/oauth/index.js +290 -68
  34. package/dist/api/oauth/index.js.map +1 -1
  35. package/dist/api/organizations/index.browser.js +14 -14
  36. package/dist/api/organizations/index.browser.js.map +1 -1
  37. package/dist/api/organizations/index.d.ts +158 -134
  38. package/dist/api/organizations/index.d.ts.map +1 -1
  39. package/dist/api/organizations/index.js +18 -18
  40. package/dist/api/organizations/index.js.map +1 -1
  41. package/dist/api/parameters/index.browser.js +74 -53
  42. package/dist/api/parameters/index.browser.js.map +1 -1
  43. package/dist/api/parameters/index.d.ts +607 -513
  44. package/dist/api/parameters/index.d.ts.map +1 -1
  45. package/dist/api/parameters/index.js +205 -127
  46. package/dist/api/parameters/index.js.map +1 -1
  47. package/dist/api/payments/index.d.ts +652 -449
  48. package/dist/api/payments/index.d.ts.map +1 -1
  49. package/dist/api/payments/index.js +159 -93
  50. package/dist/api/payments/index.js.map +1 -1
  51. package/dist/api/subscriptions/index.d.ts +1032 -762
  52. package/dist/api/subscriptions/index.d.ts.map +1 -1
  53. package/dist/api/subscriptions/index.js +166 -185
  54. package/dist/api/subscriptions/index.js.map +1 -1
  55. package/dist/api/users/index.browser.js +48 -71
  56. package/dist/api/users/index.browser.js.map +1 -1
  57. package/dist/api/users/index.d.ts +1297 -1143
  58. package/dist/api/users/index.d.ts.map +1 -1
  59. package/dist/api/users/index.js +456 -392
  60. package/dist/api/users/index.js.map +1 -1
  61. package/dist/api/verifications/index.browser.js +17 -17
  62. package/dist/api/verifications/index.browser.js.map +1 -1
  63. package/dist/api/verifications/index.d.ts +167 -155
  64. package/dist/api/verifications/index.d.ts.map +1 -1
  65. package/dist/api/verifications/index.js +47 -95
  66. package/dist/api/verifications/index.js.map +1 -1
  67. package/dist/background/index.d.ts +0 -1
  68. package/dist/background/index.d.ts.map +1 -1
  69. package/dist/background/index.js.map +1 -1
  70. package/dist/background/index.workerd.js.map +1 -1
  71. package/dist/batch/index.d.ts +0 -1
  72. package/dist/batch/index.d.ts.map +1 -1
  73. package/dist/batch/index.js.map +1 -1
  74. package/dist/bin/index.js +1 -1
  75. package/dist/bin/index.js.map +1 -1
  76. package/dist/bucket/index.d.ts +27 -32
  77. package/dist/bucket/index.d.ts.map +1 -1
  78. package/dist/bucket/index.js +56 -26
  79. package/dist/bucket/index.js.map +1 -1
  80. package/dist/bucket/index.workerd.js +52 -24
  81. package/dist/bucket/index.workerd.js.map +1 -1
  82. package/dist/cache/core/index.d.ts +4 -6
  83. package/dist/cache/core/index.d.ts.map +1 -1
  84. package/dist/cache/core/index.js +4 -10
  85. package/dist/cache/core/index.js.map +1 -1
  86. package/dist/cache/core/index.workerd.js +4 -10
  87. package/dist/cache/core/index.workerd.js.map +1 -1
  88. package/dist/cache/database/index.d.ts +22 -23
  89. package/dist/cache/database/index.d.ts.map +1 -1
  90. package/dist/cache/database/index.js +11 -20
  91. package/dist/cache/database/index.js.map +1 -1
  92. package/dist/cache/redis/index.d.ts +3 -4
  93. package/dist/cache/redis/index.d.ts.map +1 -1
  94. package/dist/cache/redis/index.js +2 -2
  95. package/dist/cache/redis/index.js.map +1 -1
  96. package/dist/captcha/index.d.ts.map +1 -1
  97. package/dist/captcha/index.js +4 -4
  98. package/dist/captcha/index.js.map +1 -1
  99. package/dist/cli/config/index.d.ts +0 -1
  100. package/dist/cli/config/index.d.ts.map +1 -1
  101. package/dist/cli/core/index.d.ts +205 -442
  102. package/dist/cli/core/index.d.ts.map +1 -1
  103. package/dist/cli/core/index.js +259 -284
  104. package/dist/cli/core/index.js.map +1 -1
  105. package/dist/cli/devtools/index.d.ts +3 -9
  106. package/dist/cli/devtools/index.d.ts.map +1 -1
  107. package/dist/cli/devtools/index.js +3 -3
  108. package/dist/cli/devtools/index.js.map +1 -1
  109. package/dist/cli/i18n/index.d.ts +8 -34
  110. package/dist/cli/i18n/index.d.ts.map +1 -1
  111. package/dist/cli/i18n/index.js +6 -6
  112. package/dist/cli/i18n/index.js.map +1 -1
  113. package/dist/cli/platform/index.d.ts +89 -82
  114. package/dist/cli/platform/index.d.ts.map +1 -1
  115. package/dist/cli/platform/index.js +27 -33
  116. package/dist/cli/platform/index.js.map +1 -1
  117. package/dist/cli/platform-lib/index.d.ts +351 -404
  118. package/dist/cli/platform-lib/index.d.ts.map +1 -1
  119. package/dist/cli/platform-lib/index.js +218 -239
  120. package/dist/cli/platform-lib/index.js.map +1 -1
  121. package/dist/cli/vendor/index.d.ts +17 -41
  122. package/dist/cli/vendor/index.d.ts.map +1 -1
  123. package/dist/cli/vendor/index.js +10 -13
  124. package/dist/cli/vendor/index.js.map +1 -1
  125. package/dist/command/index.d.ts +83 -195
  126. package/dist/command/index.d.ts.map +1 -1
  127. package/dist/command/index.js +160 -563
  128. package/dist/command/index.js.map +1 -1
  129. package/dist/core/index.browser.js +845 -859
  130. package/dist/core/index.browser.js.map +1 -1
  131. package/dist/core/index.d.ts +787 -527
  132. package/dist/core/index.d.ts.map +1 -1
  133. package/dist/core/index.js +845 -859
  134. package/dist/core/index.js.map +1 -1
  135. package/dist/core/index.native.js +845 -859
  136. package/dist/core/index.native.js.map +1 -1
  137. package/dist/core/index.workerd.js +845 -859
  138. package/dist/core/index.workerd.js.map +1 -1
  139. package/dist/crypto/index.browser.js +1 -1
  140. package/dist/crypto/index.browser.js.map +1 -1
  141. package/dist/crypto/index.d.ts +3 -4
  142. package/dist/crypto/index.d.ts.map +1 -1
  143. package/dist/crypto/index.js +6 -3
  144. package/dist/crypto/index.js.map +1 -1
  145. package/dist/datetime/index.d.ts +12 -2
  146. package/dist/datetime/index.d.ts.map +1 -1
  147. package/dist/datetime/index.js +6 -1
  148. package/dist/datetime/index.js.map +1 -1
  149. package/dist/email/brevo/index.d.ts +0 -1
  150. package/dist/email/brevo/index.d.ts.map +1 -1
  151. package/dist/email/brevo/index.js +4 -4
  152. package/dist/email/brevo/index.js.map +1 -1
  153. package/dist/email/cloudflare/index.d.ts +0 -1
  154. package/dist/email/cloudflare/index.d.ts.map +1 -1
  155. package/dist/email/cloudflare/index.js +2 -2
  156. package/dist/email/cloudflare/index.js.map +1 -1
  157. package/dist/email/core/index.d.ts +15 -7
  158. package/dist/email/core/index.d.ts.map +1 -1
  159. package/dist/email/core/index.js +14 -5
  160. package/dist/email/core/index.js.map +1 -1
  161. package/dist/email/core/index.workerd.js +5 -1
  162. package/dist/email/core/index.workerd.js.map +1 -1
  163. package/dist/email/smtp/index.d.ts +8 -9
  164. package/dist/email/smtp/index.d.ts.map +1 -1
  165. package/dist/email/smtp/index.js +702 -479
  166. package/dist/email/smtp/index.js.map +1 -1
  167. package/dist/fake/index.d.ts +1580 -1238
  168. package/dist/fake/index.d.ts.map +1 -1
  169. package/dist/fake/index.js +11728 -32131
  170. package/dist/fake/index.js.map +1 -1
  171. package/dist/lock/core/index.d.ts +13 -9
  172. package/dist/lock/core/index.d.ts.map +1 -1
  173. package/dist/lock/core/index.js +5 -5
  174. package/dist/lock/core/index.js.map +1 -1
  175. package/dist/lock/redis/index.d.ts +0 -1
  176. package/dist/lock/redis/index.d.ts.map +1 -1
  177. package/dist/lock/redis/index.js.map +1 -1
  178. package/dist/logger/index.d.ts +81 -69
  179. package/dist/logger/index.d.ts.map +1 -1
  180. package/dist/logger/index.js +123 -56
  181. package/dist/logger/index.js.map +1 -1
  182. package/dist/mcp/index.d.ts +27 -90
  183. package/dist/mcp/index.d.ts.map +1 -1
  184. package/dist/mcp/index.js +42 -70
  185. package/dist/mcp/index.js.map +1 -1
  186. package/dist/orm/core/index.browser.js +54 -40
  187. package/dist/orm/core/index.browser.js.map +1 -1
  188. package/dist/orm/core/index.bun.js +140 -106
  189. package/dist/orm/core/index.bun.js.map +1 -1
  190. package/dist/orm/core/index.d.ts +145 -150
  191. package/dist/orm/core/index.d.ts.map +1 -1
  192. package/dist/orm/core/index.js +144 -109
  193. package/dist/orm/core/index.js.map +1 -1
  194. package/dist/orm/postgres/index.bun.js +64 -54
  195. package/dist/orm/postgres/index.bun.js.map +1 -1
  196. package/dist/orm/postgres/index.d.ts +36 -76
  197. package/dist/orm/postgres/index.d.ts.map +1 -1
  198. package/dist/orm/postgres/index.js +67 -57
  199. package/dist/orm/postgres/index.js.map +1 -1
  200. package/dist/queue/core/index.d.ts +62 -75
  201. package/dist/queue/core/index.d.ts.map +1 -1
  202. package/dist/queue/core/index.js +48 -71
  203. package/dist/queue/core/index.js.map +1 -1
  204. package/dist/queue/core/index.workerd.js +48 -71
  205. package/dist/queue/core/index.workerd.js.map +1 -1
  206. package/dist/queue/redis/index.d.ts +3 -4
  207. package/dist/queue/redis/index.d.ts.map +1 -1
  208. package/dist/queue/redis/index.js +2 -2
  209. package/dist/queue/redis/index.js.map +1 -1
  210. package/dist/react/auth/index.browser.js.map +1 -1
  211. package/dist/react/auth/index.d.ts +7 -4
  212. package/dist/react/auth/index.d.ts.map +1 -1
  213. package/dist/react/auth/index.js.map +1 -1
  214. package/dist/react/core/index.d.ts +87 -5
  215. package/dist/react/core/index.d.ts.map +1 -1
  216. package/dist/react/core/index.js +170 -23
  217. package/dist/react/core/index.js.map +1 -1
  218. package/dist/react/form/index.d.ts +4 -5
  219. package/dist/react/form/index.d.ts.map +1 -1
  220. package/dist/react/form/index.js +91 -56
  221. package/dist/react/form/index.js.map +1 -1
  222. package/dist/react/head/index.browser.js.map +1 -1
  223. package/dist/react/head/index.d.ts +0 -1
  224. package/dist/react/head/index.d.ts.map +1 -1
  225. package/dist/react/head/index.js.map +1 -1
  226. package/dist/react/i18n/index.d.ts +4 -5
  227. package/dist/react/i18n/index.d.ts.map +1 -1
  228. package/dist/react/i18n/index.js +2 -2
  229. package/dist/react/i18n/index.js.map +1 -1
  230. package/dist/react/intro/index.d.ts +1 -4
  231. package/dist/react/intro/index.d.ts.map +1 -1
  232. package/dist/react/intro/index.js.map +1 -1
  233. package/dist/react/router/index.browser.js +60 -24
  234. package/dist/react/router/index.browser.js.map +1 -1
  235. package/dist/react/router/index.d.ts +378 -377
  236. package/dist/react/router/index.d.ts.map +1 -1
  237. package/dist/react/router/index.js +77 -41
  238. package/dist/react/router/index.js.map +1 -1
  239. package/dist/react/sitemap/index.d.ts +0 -1
  240. package/dist/react/sitemap/index.d.ts.map +1 -1
  241. package/dist/react/sitemap/index.js.map +1 -1
  242. package/dist/react/testing/index.d.ts +4 -5
  243. package/dist/react/testing/index.d.ts.map +1 -1
  244. package/dist/react/testing/index.js +41 -15
  245. package/dist/react/testing/index.js.map +1 -1
  246. package/dist/react/testing/{chunk-BpyX8vjI.js → rolldown-runtime-Bl3dcgcQ.js} +1 -1
  247. package/dist/react/ui/index.d.ts +35 -34
  248. package/dist/react/ui/index.d.ts.map +1 -1
  249. package/dist/react/ui/index.js +12 -17
  250. package/dist/react/ui/index.js.map +1 -1
  251. package/dist/redis/index.bun.js +5 -5
  252. package/dist/redis/index.bun.js.map +1 -1
  253. package/dist/redis/index.d.ts +6 -7
  254. package/dist/redis/index.d.ts.map +1 -1
  255. package/dist/redis/index.js +6 -6
  256. package/dist/redis/index.js.map +1 -1
  257. package/dist/retry/index.d.ts +0 -1
  258. package/dist/retry/index.d.ts.map +1 -1
  259. package/dist/retry/index.js.map +1 -1
  260. package/dist/router/index.d.ts.map +1 -1
  261. package/dist/router/index.js.map +1 -1
  262. package/dist/scheduler/index.d.ts +4 -12
  263. package/dist/scheduler/index.d.ts.map +1 -1
  264. package/dist/scheduler/index.js +9 -4
  265. package/dist/scheduler/index.js.map +1 -1
  266. package/dist/scheduler/index.workerd.js +9 -4
  267. package/dist/scheduler/index.workerd.js.map +1 -1
  268. package/dist/security/index.browser.js +30 -28
  269. package/dist/security/index.browser.js.map +1 -1
  270. package/dist/security/index.d.ts +113 -44
  271. package/dist/security/index.d.ts.map +1 -1
  272. package/dist/security/index.js +452 -37
  273. package/dist/security/index.js.map +1 -1
  274. package/dist/server/auth/index.browser.js +19 -19
  275. package/dist/server/auth/index.browser.js.map +1 -1
  276. package/dist/server/auth/index.d.ts +198 -274
  277. package/dist/server/auth/index.d.ts.map +1 -1
  278. package/dist/server/auth/index.js +83 -621
  279. package/dist/server/auth/index.js.map +1 -1
  280. package/dist/server/cookies/index.browser.js +103 -5
  281. package/dist/server/cookies/index.browser.js.map +1 -1
  282. package/dist/server/cookies/index.d.ts +72 -5
  283. package/dist/server/cookies/index.d.ts.map +1 -1
  284. package/dist/server/cookies/index.js +103 -9
  285. package/dist/server/cookies/index.js.map +1 -1
  286. package/dist/server/core/index.browser.js +22 -27
  287. package/dist/server/core/index.browser.js.map +1 -1
  288. package/dist/server/core/index.d.ts +116 -85
  289. package/dist/server/core/index.d.ts.map +1 -1
  290. package/dist/server/core/index.js +105 -118
  291. package/dist/server/core/index.js.map +1 -1
  292. package/dist/server/cors/index.d.ts +9 -10
  293. package/dist/server/cors/index.d.ts.map +1 -1
  294. package/dist/server/cors/index.js +14 -26
  295. package/dist/server/cors/index.js.map +1 -1
  296. package/dist/server/etag/index.d.ts +2 -3
  297. package/dist/server/etag/index.d.ts.map +1 -1
  298. package/dist/server/etag/index.js.map +1 -1
  299. package/dist/server/health/index.d.ts +18 -19
  300. package/dist/server/health/index.d.ts.map +1 -1
  301. package/dist/server/health/index.js +6 -6
  302. package/dist/server/health/index.js.map +1 -1
  303. package/dist/server/links/index.browser.js +15 -18
  304. package/dist/server/links/index.browser.js.map +1 -1
  305. package/dist/server/links/index.d.ts +66 -67
  306. package/dist/server/links/index.d.ts.map +1 -1
  307. package/dist/server/links/index.js +29 -32
  308. package/dist/server/links/index.js.map +1 -1
  309. package/dist/server/metrics/index.d.ts +0 -1
  310. package/dist/server/metrics/index.d.ts.map +1 -1
  311. package/dist/server/metrics/index.js +10 -3
  312. package/dist/server/metrics/index.js.map +1 -1
  313. package/dist/server/proxy/index.d.ts +0 -1
  314. package/dist/server/proxy/index.d.ts.map +1 -1
  315. package/dist/server/proxy/index.js.map +1 -1
  316. package/dist/server/rate-limit/index.d.ts +8 -9
  317. package/dist/server/rate-limit/index.d.ts.map +1 -1
  318. package/dist/server/rate-limit/index.js +6 -12
  319. package/dist/server/rate-limit/index.js.map +1 -1
  320. package/dist/server/static/index.d.ts +0 -1
  321. package/dist/server/static/index.d.ts.map +1 -1
  322. package/dist/server/static/index.js.map +1 -1
  323. package/dist/server/swagger/index.d.ts +4 -4
  324. package/dist/server/swagger/index.d.ts.map +1 -1
  325. package/dist/server/swagger/index.js +37 -25
  326. package/dist/server/swagger/index.js.map +1 -1
  327. package/dist/sms/index.d.ts +0 -1
  328. package/dist/sms/index.d.ts.map +1 -1
  329. package/dist/sms/index.js.map +1 -1
  330. package/dist/system/index.browser.js.map +1 -1
  331. package/dist/system/index.d.ts +0 -1
  332. package/dist/system/index.d.ts.map +1 -1
  333. package/dist/system/index.js +2 -1
  334. package/dist/system/index.js.map +1 -1
  335. package/dist/system/index.workerd.js.map +1 -1
  336. package/dist/topic/core/index.d.ts +19 -20
  337. package/dist/topic/core/index.d.ts.map +1 -1
  338. package/dist/topic/core/index.js +8 -8
  339. package/dist/topic/core/index.js.map +1 -1
  340. package/dist/topic/redis/index.d.ts +3 -4
  341. package/dist/topic/redis/index.d.ts.map +1 -1
  342. package/dist/topic/redis/index.js +2 -2
  343. package/dist/topic/redis/index.js.map +1 -1
  344. package/package.json +22 -35
  345. package/src/api/audits/__tests__/AuditJobs.spec.ts +83 -0
  346. package/src/api/audits/__tests__/AuditService.spec.ts +139 -1
  347. package/src/api/audits/controllers/AdminAuditController.ts +41 -41
  348. package/src/api/audits/entities/audits.ts +22 -22
  349. package/src/api/audits/index.ts +6 -1
  350. package/src/api/audits/jobs/AuditJobs.ts +29 -0
  351. package/src/api/audits/parameters/AuditParameters.ts +51 -0
  352. package/src/api/audits/primitives/$audit.ts +24 -0
  353. package/src/api/audits/schemas/auditQuerySchema.ts +13 -13
  354. package/src/api/audits/schemas/createAuditSchema.ts +18 -18
  355. package/src/api/audits/services/AuditService.ts +79 -7
  356. package/src/api/files/__tests__/FileController.spec.ts +5 -5
  357. package/src/api/files/__tests__/FileJobs.spec.ts +6 -0
  358. package/src/api/files/controllers/FileController.ts +25 -25
  359. package/src/api/files/entities/files.ts +15 -15
  360. package/src/api/files/schemas/fileCreatorSummarySchema.ts +7 -7
  361. package/src/api/files/schemas/fileQuerySchema.ts +9 -9
  362. package/src/api/files/schemas/fileResourceSchema.ts +7 -9
  363. package/src/api/files/schemas/storageStatsSchema.ts +13 -13
  364. package/src/api/jobs/__tests__/$job-hardening.spec.ts +289 -0
  365. package/src/api/jobs/__tests__/$job.spec.ts +18 -18
  366. package/src/api/jobs/controllers/AdminJobController.ts +8 -8
  367. package/src/api/jobs/entities/jobExecutionEntity.ts +29 -19
  368. package/src/api/jobs/providers/JobProvider.ts +128 -12
  369. package/src/api/jobs/providers/JobQueueProvider.ts +2 -2
  370. package/src/api/jobs/schemas/jobConfigAtom.ts +22 -22
  371. package/src/api/jobs/schemas/jobExecutionQuerySchema.ts +6 -6
  372. package/src/api/jobs/schemas/jobExecutionResourceSchema.ts +11 -15
  373. package/src/api/jobs/schemas/jobRegistrationSchema.ts +20 -19
  374. package/src/api/jobs/schemas/triggerJobSchema.ts +3 -3
  375. package/src/api/jobs/services/JobService.ts +6 -6
  376. package/src/api/keys/__tests__/ApiKeyService.spec.ts +2 -2
  377. package/src/api/keys/controllers/AdminApiKeyController.ts +10 -10
  378. package/src/api/keys/entities/apiKeyEntity.ts +15 -15
  379. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +4 -4
  380. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +15 -15
  381. package/src/api/keys/schemas/createApiKeyBodySchema.ts +5 -5
  382. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +9 -9
  383. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +13 -13
  384. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +3 -3
  385. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +3 -3
  386. package/src/api/notifications/__tests__/AlephaApiNotifications.spec.ts +2 -2
  387. package/src/api/notifications/controllers/AdminNotificationController.ts +38 -14
  388. package/src/api/notifications/jobs/NotificationJobs.ts +7 -6
  389. package/src/api/notifications/primitives/$notification.ts +43 -4
  390. package/src/api/notifications/schemas/notificationContactPreferencesSchema.ts +4 -4
  391. package/src/api/notifications/schemas/notificationContactSchema.ts +7 -7
  392. package/src/api/notifications/schemas/notificationDetailResourceSchema.ts +9 -11
  393. package/src/api/notifications/schemas/notificationPayloadSchema.ts +11 -9
  394. package/src/api/notifications/schemas/notificationQuerySchema.ts +6 -6
  395. package/src/api/notifications/schemas/notificationResourceSchema.ts +14 -14
  396. package/src/api/notifications/services/NotificationSenderService.ts +38 -2
  397. package/src/api/oauth/__tests__/OAuthClientService.spec.ts +42 -0
  398. package/src/api/oauth/__tests__/OAuthController.spec.ts +128 -0
  399. package/src/api/oauth/controllers/OAuthController.ts +132 -12
  400. package/src/api/oauth/entities/oauthClientEntity.ts +29 -12
  401. package/src/api/oauth/helpers/oauthMetadata.ts +1 -0
  402. package/src/api/oauth/helpers/oidcMetadata.ts +19 -0
  403. package/src/api/oauth/index.ts +1 -0
  404. package/src/api/oauth/schemas/authorizeDecisionBodySchema.ts +12 -11
  405. package/src/api/oauth/schemas/authorizeQuerySchema.ts +12 -10
  406. package/src/api/oauth/schemas/registerClientBodySchema.ts +10 -11
  407. package/src/api/oauth/schemas/tokenRequestBodySchema.ts +9 -8
  408. package/src/api/oauth/services/OAuthClientService.ts +228 -15
  409. package/src/api/organizations/controllers/AdminOrganizationController.ts +8 -8
  410. package/src/api/organizations/entities/organizations.ts +6 -6
  411. package/src/api/organizations/schemas/createOrganizationSchema.ts +5 -5
  412. package/src/api/organizations/schemas/organizationQuerySchema.ts +4 -4
  413. package/src/api/organizations/schemas/updateOrganizationSchema.ts +1 -2
  414. package/src/api/parameters/__tests__/$parameter.spec.ts +229 -50
  415. package/src/api/parameters/controllers/AdminParameterController.ts +9 -12
  416. package/src/api/parameters/entities/parameters.ts +29 -16
  417. package/src/api/parameters/primitives/$parameter.ts +3 -3
  418. package/src/api/parameters/schemas/activateParameterBodySchema.ts +4 -4
  419. package/src/api/parameters/schemas/createParameterVersionBodySchema.ts +15 -15
  420. package/src/api/parameters/schemas/parameterCreatorSummarySchema.ts +7 -7
  421. package/src/api/parameters/schemas/parameterCurrentResponseSchema.ts +7 -7
  422. package/src/api/parameters/schemas/parameterHistoryResponseSchema.ts +3 -3
  423. package/src/api/parameters/schemas/parameterNameParamSchema.ts +2 -3
  424. package/src/api/parameters/schemas/parameterNamesResponseSchema.ts +3 -3
  425. package/src/api/parameters/schemas/parameterResponseSchema.ts +3 -3
  426. package/src/api/parameters/schemas/parameterStatusSchema.ts +2 -2
  427. package/src/api/parameters/schemas/parameterTreeNodeSchema.ts +6 -6
  428. package/src/api/parameters/schemas/parameterVersionParamSchema.ts +5 -6
  429. package/src/api/parameters/schemas/parameterVersionResponseSchema.ts +3 -3
  430. package/src/api/parameters/schemas/rollbackParameterBodySchema.ts +6 -9
  431. package/src/api/parameters/services/ParameterProvider.ts +152 -65
  432. package/src/api/payments/controllers/AdminPaymentController.ts +7 -7
  433. package/src/api/payments/controllers/MockCheckoutController.ts +79 -28
  434. package/src/api/payments/controllers/PaymentController.ts +4 -4
  435. package/src/api/payments/entities/paymentIntents.ts +11 -11
  436. package/src/api/payments/entities/paymentMethods.ts +11 -11
  437. package/src/api/payments/entities/refunds.ts +9 -9
  438. package/src/api/payments/providers/PaymentProvider.ts +18 -0
  439. package/src/api/payments/schemas/intentSchemas.ts +25 -25
  440. package/src/api/payments/schemas/paymentMethodSchemas.ts +3 -3
  441. package/src/api/payments/services/PaymentService.ts +60 -3
  442. package/src/api/subscriptions/__tests__/SubscriptionService.spec.ts +14 -0
  443. package/src/api/subscriptions/controllers/AdminSubscriptionController.ts +11 -11
  444. package/src/api/subscriptions/controllers/SubscriptionController.ts +5 -4
  445. package/src/api/subscriptions/entities/subscriptionEvents.ts +15 -15
  446. package/src/api/subscriptions/entities/subscriptions.ts +22 -22
  447. package/src/api/subscriptions/notifications/SubscriptionNotifications.ts +22 -22
  448. package/src/api/subscriptions/schemas/cancelSubscriptionSchema.ts +4 -4
  449. package/src/api/subscriptions/schemas/changePlanSchema.ts +5 -5
  450. package/src/api/subscriptions/schemas/createSubscriptionSchema.ts +14 -7
  451. package/src/api/subscriptions/schemas/entitlementsSchema.ts +10 -10
  452. package/src/api/subscriptions/schemas/mrrSchema.ts +9 -9
  453. package/src/api/subscriptions/schemas/planDefinitionSchema.ts +21 -21
  454. package/src/api/subscriptions/schemas/planResourceSchema.ts +19 -19
  455. package/src/api/subscriptions/schemas/subscriptionQuerySchema.ts +8 -8
  456. package/src/api/subscriptions/schemas/subscriptionSettingsSchema.ts +7 -7
  457. package/src/api/subscriptions/schemas/subscriptionStatsSchema.ts +17 -17
  458. package/src/api/subscriptions/services/SubscriptionConfig.ts +2 -2
  459. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +11 -11
  460. package/src/api/users/atoms/realmAuthSettingsAtom.ts +111 -119
  461. package/src/api/users/controllers/AdminIdentityController.ts +29 -19
  462. package/src/api/users/controllers/AdminSessionController.ts +37 -27
  463. package/src/api/users/controllers/AdminUserController.ts +75 -53
  464. package/src/api/users/controllers/MySessionController.ts +125 -0
  465. package/src/api/users/controllers/RealmController.ts +9 -10
  466. package/src/api/users/controllers/UserController.ts +55 -55
  467. package/src/api/users/entities/identities.ts +8 -8
  468. package/src/api/users/entities/sessions.ts +17 -17
  469. package/src/api/users/entities/users.ts +17 -17
  470. package/src/api/users/index.ts +3 -0
  471. package/src/api/users/notifications/UserNotifications.ts +76 -24
  472. package/src/api/users/primitives/$realm.ts +12 -26
  473. package/src/api/users/providers/RealmProvider.ts +0 -15
  474. package/src/api/users/schemas/completePasswordResetRequestSchema.ts +8 -12
  475. package/src/api/users/schemas/completeRegistrationRequestSchema.ts +17 -20
  476. package/src/api/users/schemas/createUserSchema.ts +2 -2
  477. package/src/api/users/schemas/identityQuerySchema.ts +4 -4
  478. package/src/api/users/schemas/identityResourceSchema.ts +3 -2
  479. package/src/api/users/schemas/loginSchema.ts +4 -4
  480. package/src/api/users/schemas/passwordResetIntentResponseSchema.ts +6 -8
  481. package/src/api/users/schemas/realmConfigSchema.ts +10 -24
  482. package/src/api/users/schemas/registerQuerySchema.ts +6 -6
  483. package/src/api/users/schemas/registerRequestSchema.ts +21 -43
  484. package/src/api/users/schemas/registerResponseSchema.ts +2 -2
  485. package/src/api/users/schemas/registerSchema.ts +13 -31
  486. package/src/api/users/schemas/registrationIntentResponseSchema.ts +19 -23
  487. package/src/api/users/schemas/resetPasswordSchema.ts +10 -17
  488. package/src/api/users/schemas/sessionQuerySchema.ts +3 -3
  489. package/src/api/users/schemas/sessionResourceSchema.ts +25 -25
  490. package/src/api/users/schemas/updateUserSchema.ts +3 -4
  491. package/src/api/users/schemas/userQuerySchema.ts +7 -7
  492. package/src/api/users/services/RegistrationService.ts +25 -11
  493. package/src/api/users/services/UserService.ts +12 -6
  494. package/src/api/verifications/__tests__/CodeVerification.spec.ts +63 -101
  495. package/src/api/verifications/__tests__/LinkVerification.spec.ts +23 -92
  496. package/src/api/verifications/controllers/VerificationController.ts +13 -27
  497. package/src/api/verifications/entities/verifications.ts +13 -14
  498. package/src/api/verifications/schemas/requestVerificationCodeResponseSchema.ts +17 -14
  499. package/src/api/verifications/schemas/validateVerificationCodeResponseSchema.ts +9 -10
  500. package/src/api/verifications/schemas/verificationSettingsSchema.ts +65 -66
  501. package/src/api/verifications/schemas/verificationTypeEnumSchema.ts +2 -2
  502. package/src/api/verifications/services/VerificationService.ts +12 -0
  503. package/src/background/__tests__/BackgroundTaskProvider.spec.ts +36 -0
  504. package/src/batch/__tests__/$batch.spec.ts +22 -22
  505. package/src/batch/__tests__/startup-buffering.spec.ts +9 -9
  506. package/src/bin/index.ts +1 -1
  507. package/src/bucket/__tests__/TenantScopedBucket.spec.ts +60 -0
  508. package/src/bucket/providers/LocalFileStorageProvider.ts +14 -6
  509. package/src/bucket/providers/MemoryFileStorageProvider.ts +18 -7
  510. package/src/bucket/providers/R2FileStorageProvider.ts +14 -6
  511. package/src/bucket/providers/S3FileStorageProvider.ts +31 -15
  512. package/src/cache/core/primitives/$cache.ts +7 -10
  513. package/src/cache/database/entities/cacheEntries.ts +16 -27
  514. package/src/cache/database/providers/DatabaseCacheProvider.ts +15 -13
  515. package/src/cache/redis/providers/RedisCacheProvider.ts +6 -6
  516. package/src/captcha/providers/TurnstileCaptchaProvider.ts +4 -4
  517. package/src/cli/core/__tests__/BuildCloudflareTask.spec.ts +178 -0
  518. package/src/cli/core/__tests__/BuildDockerTask.spec.ts +0 -2
  519. package/src/cli/core/__tests__/DbCommand.spec.ts +108 -0
  520. package/src/cli/core/atoms/appEntryOptions.ts +5 -5
  521. package/src/cli/core/atoms/buildOptions.ts +76 -76
  522. package/src/cli/core/atoms/changelogOptions.ts +3 -3
  523. package/src/cli/core/atoms/devOptions.ts +3 -3
  524. package/src/cli/core/commands/build.ts +36 -39
  525. package/src/cli/core/commands/db.ts +171 -83
  526. package/src/cli/core/commands/dev.ts +8 -8
  527. package/src/cli/core/commands/gen/changelog.ts +12 -14
  528. package/src/cli/core/commands/gen/env.ts +6 -6
  529. package/src/cli/core/commands/gen/openapi.ts +6 -6
  530. package/src/cli/core/commands/init.ts +28 -33
  531. package/src/cli/core/commands/pack.ts +10 -10
  532. package/src/cli/core/commands/root.ts +7 -8
  533. package/src/cli/core/commands/test.ts +17 -20
  534. package/src/cli/core/tasks/BuildCloudflareTask.ts +103 -230
  535. package/src/cli/core/templates/apiHelloResponseSchemaTs.ts +4 -4
  536. package/src/cli/devtools/atoms/devtoolsOptions.ts +6 -6
  537. package/src/cli/i18n/atoms/i18nOptions.ts +8 -8
  538. package/src/cli/platform/commands/SecretsCommand.ts +24 -25
  539. package/src/cli/platform/commands/platform.ts +40 -46
  540. package/src/cli/platform/index.ts +1 -1
  541. package/src/cli/platform-lib/__tests__/CloudflareAdapter.spec.ts +0 -4
  542. package/src/cli/platform-lib/__tests__/PlatformInspector.spec.ts +10 -3
  543. package/src/cli/platform-lib/__tests__/VercelAdapter.spec.ts +0 -2
  544. package/src/cli/platform-lib/adapters/CloudflareAdapter.ts +13 -14
  545. package/src/cli/platform-lib/adapters/VercelAdapter.ts +6 -11
  546. package/src/cli/platform-lib/atoms/platformOptions.ts +46 -25
  547. package/src/cli/platform-lib/schemas/cloudflare.ts +87 -87
  548. package/src/cli/platform-lib/schemas/platform.ts +51 -51
  549. package/src/cli/platform-lib/schemas/vercel.ts +28 -28
  550. package/src/cli/platform-lib/services/CloudflareApi.ts +5 -5
  551. package/src/cli/platform-lib/services/VercelApi.ts +4 -4
  552. package/src/cli/platform-lib/services/VercelCli.ts +6 -15
  553. package/src/cli/platform-lib/services/WranglerApi.ts +6 -15
  554. package/src/cli/vendor/atoms/vendorOptions.ts +9 -9
  555. package/src/cli/vendor/commands/VendorCommand.ts +11 -12
  556. package/src/command/__tests__/$command.spec.ts +82 -83
  557. package/src/command/__tests__/Asker.spec.ts +6 -13
  558. package/src/command/__tests__/CliProvider.spec.ts +136 -149
  559. package/src/command/__tests__/Runner.spec.ts +86 -110
  560. package/src/command/helpers/Asker.ts +23 -58
  561. package/src/command/helpers/Runner.ts +34 -110
  562. package/src/command/index.ts +4 -9
  563. package/src/command/primitives/$command.ts +10 -10
  564. package/src/command/providers/CliProvider.ts +183 -161
  565. package/src/core/Alepha.ts +62 -13
  566. package/src/core/__tests__/$atom.spec.ts +52 -3
  567. package/src/core/__tests__/$computed.spec.ts +129 -0
  568. package/src/core/__tests__/$env.spec.ts +13 -13
  569. package/src/core/__tests__/$inject.spec.ts +4 -4
  570. package/src/core/__tests__/$module.spec.ts +1 -1
  571. package/src/core/__tests__/Alepha-graph.spec.ts +1 -1
  572. package/src/core/__tests__/Alepha-parseEnv.spec.ts +32 -32
  573. package/src/core/__tests__/Alepha-start.spec.ts +3 -3
  574. package/src/core/__tests__/CodecManager.spec.ts +113 -94
  575. package/src/core/__tests__/KeylessJsonSchemaCodec.spec.ts +138 -138
  576. package/src/core/__tests__/SchemaValidator.spec.ts +43 -44
  577. package/src/core/__tests__/StateManager.persist.browser.spec.ts +87 -0
  578. package/src/core/__tests__/StateManager.spec.ts +423 -0
  579. package/src/core/__tests__/TypeBox-usecases.spec.ts +8 -8
  580. package/src/core/__tests__/TypeProvider.spec.ts +314 -309
  581. package/src/core/__tests__/dump.spec.ts +6 -6
  582. package/src/core/__tests__/pagination.spec.ts +4 -4
  583. package/src/core/__tests__/scoped-after-start.spec.ts +43 -0
  584. package/src/core/errors/TypeBoxError.ts +10 -4
  585. package/src/core/helpers/FileLike.ts +5 -5
  586. package/src/core/helpers/coerceStrings.ts +70 -0
  587. package/src/core/helpers/jsonSchemaToZod.ts +112 -0
  588. package/src/core/index.shared.ts +4 -1
  589. package/src/core/primitives/$atom.ts +95 -27
  590. package/src/core/primitives/$computed.ts +148 -0
  591. package/src/core/primitives/$env.ts +5 -5
  592. package/src/core/primitives/$state.ts +1 -1
  593. package/src/core/providers/AlsProvider.ts +26 -0
  594. package/src/core/providers/CodecManager.ts +1 -2
  595. package/src/core/providers/JsonSchemaCodec.ts +1 -2
  596. package/src/core/providers/KeylessJsonSchemaCodec.ts +82 -57
  597. package/src/core/providers/SchemaValidator.ts +48 -158
  598. package/src/core/providers/StateManager.ts +383 -9
  599. package/src/core/providers/TypeProvider.ts +80 -734
  600. package/src/core/providers/ZodProvider.ts +380 -0
  601. package/src/core/providers/zodAugment.ts +69 -0
  602. package/src/core/schemas/pageQuerySchema.ts +19 -21
  603. package/src/core/schemas/pageSchema.ts +47 -57
  604. package/src/crypto/__tests__/SecretProvider.spec.ts +31 -0
  605. package/src/crypto/providers/SecretProvider.ts +16 -10
  606. package/src/datetime/primitives/$throttle.ts +6 -1
  607. package/src/email/brevo/providers/BrevoEmailProvider.ts +4 -4
  608. package/src/email/cloudflare/providers/CloudflareEmailProvider.ts +6 -6
  609. package/src/email/core/index.ts +7 -2
  610. package/src/email/core/primitives/$email.ts +5 -1
  611. package/src/email/core/providers/LocalEmailProvider.ts +5 -5
  612. package/src/email/smtp/providers/NodemailerEmailProvider.ts +41 -52
  613. package/src/fake/__tests__/FakeProvider.spec.ts +110 -110
  614. package/src/fake/__tests__/keyName.example.ts +21 -21
  615. package/src/fake/__tests__/keyName.spec.ts +41 -41
  616. package/src/fake/__tests__/module.example.ts +8 -8
  617. package/src/fake/providers/FakeProvider.ts +122 -121
  618. package/src/lock/core/__tests__/lock-mutual-exclusion.spec.ts +44 -0
  619. package/src/lock/core/index.ts +1 -0
  620. package/src/lock/core/primitives/$lock.ts +13 -6
  621. package/src/logger/__tests__/CliFormatterProvider.spec.ts +76 -0
  622. package/src/logger/__tests__/Logger.spec.ts +49 -0
  623. package/src/logger/index.ts +25 -18
  624. package/src/logger/providers/CliFormatterProvider.ts +75 -0
  625. package/src/logger/schemas/logEntrySchema.ts +10 -10
  626. package/src/logger/services/Logger.ts +18 -0
  627. package/src/mcp/__tests__/$prompt.spec.ts +21 -23
  628. package/src/mcp/__tests__/$tool.spec.ts +22 -22
  629. package/src/mcp/__tests__/McpServerProvider.spec.ts +11 -11
  630. package/src/mcp/__tests__/StreamableHttpMcpTransport.spec.ts +2 -2
  631. package/src/mcp/__tests__/integration.spec.ts +33 -33
  632. package/src/mcp/primitives/$prompt.ts +11 -9
  633. package/src/mcp/primitives/$tool.ts +27 -86
  634. package/src/mcp/transports/StreamableHttpMcpTransport.ts +7 -7
  635. package/src/orm/__tests__/$repository-tests.ts +133 -27
  636. package/src/orm/__tests__/$repository.spec.ts +23 -0
  637. package/src/orm/__tests__/$transactional-tests.ts +4 -4
  638. package/src/orm/__tests__/ModelBuilder-tests.ts +28 -28
  639. package/src/orm/__tests__/ModelBuilder.spec.ts +124 -106
  640. package/src/orm/__tests__/QueryManager.spec.ts +5 -5
  641. package/src/orm/__tests__/delete-returning-tests.ts +11 -11
  642. package/src/orm/__tests__/deletedAt-tests.ts +3 -3
  643. package/src/orm/__tests__/enums.spec.ts +29 -25
  644. package/src/orm/__tests__/execute-tests.ts +8 -8
  645. package/src/orm/__tests__/fixtures/bigEntitySchema.ts +48 -48
  646. package/src/orm/__tests__/fixtures/userEntitySchema.ts +6 -6
  647. package/src/orm/__tests__/joins-tests.ts +30 -30
  648. package/src/orm/__tests__/organization-tests.ts +105 -3
  649. package/src/orm/__tests__/organization.spec.ts +46 -0
  650. package/src/orm/__tests__/orm-errors.spec.ts +10 -10
  651. package/src/orm/__tests__/orm-next-tests.ts +29 -29
  652. package/src/orm/__tests__/orm-showcase-tests.ts +9 -9
  653. package/src/orm/__tests__/primaryKey.spec.ts +4 -4
  654. package/src/orm/__tests__/query-date-encoding-tests.ts +17 -17
  655. package/src/orm/__tests__/ref-auto-onDelete.spec.ts +14 -14
  656. package/src/orm/__tests__/references.spec.ts +7 -7
  657. package/src/orm/__tests__/security.spec.ts +17 -17
  658. package/src/orm/__tests__/string-operators-tests.ts +5 -5
  659. package/src/orm/__tests__/timestamps-tests.ts +14 -14
  660. package/src/orm/__tests__/validation-tests.ts +5 -5
  661. package/src/orm/core/__tests__/BunSqliteProvider.bun.spec.ts +8 -8
  662. package/src/orm/core/constants/PG_SYMBOLS.ts +13 -1
  663. package/src/orm/core/entities/alephaSequences.ts +8 -8
  664. package/src/orm/core/helpers/pgAttr.ts +4 -4
  665. package/src/orm/core/primitives/$entity.ts +10 -6
  666. package/src/orm/core/providers/DatabaseTypeProvider.ts +46 -53
  667. package/src/orm/core/providers/drivers/BunSqliteProvider.ts +8 -8
  668. package/src/orm/core/providers/drivers/CloudflareD1Provider.ts +3 -5
  669. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +8 -8
  670. package/src/orm/core/schemas/databaseEnvSchema.ts +4 -4
  671. package/src/orm/core/schemas/insertSchema.ts +14 -18
  672. package/src/orm/core/schemas/legacyIdSchema.ts +2 -2
  673. package/src/orm/core/schemas/updateSchema.ts +10 -16
  674. package/src/orm/core/services/PgRelationManager.ts +2 -2
  675. package/src/orm/core/services/QueryManager.ts +13 -2
  676. package/src/orm/core/services/Repository.ts +88 -33
  677. package/src/orm/core/services/SqliteModelBuilder.ts +38 -45
  678. package/src/orm/postgres/__tests__/BunPostgresProvider.bun.spec.ts +4 -4
  679. package/src/orm/postgres/providers/CloudflareHyperdriveProvider.ts +4 -6
  680. package/src/orm/postgres/schemas/postgresEnvSchema.ts +6 -6
  681. package/src/orm/postgres/services/PostgresModelBuilder.ts +127 -114
  682. package/src/queue/core/__tests__/$consumer.spec.ts +4 -4
  683. package/src/queue/core/__tests__/WorkerProvider.spec.ts +4 -4
  684. package/src/queue/core/__tests__/shared.ts +6 -6
  685. package/src/queue/core/primitives/$consumer.ts +5 -5
  686. package/src/queue/core/primitives/$queue.ts +44 -61
  687. package/src/queue/core/providers/CloudflareQueueProvider.ts +6 -0
  688. package/src/queue/core/providers/WorkerProvider.ts +17 -14
  689. package/src/queue/redis/providers/RedisQueueProvider.ts +3 -3
  690. package/src/react/core/__tests__/ErrorBoundary.browser.spec.tsx +92 -0
  691. package/src/react/core/__tests__/Router.spec.tsx +4 -4
  692. package/src/react/core/__tests__/useComputed.browser.spec.tsx +192 -0
  693. package/src/react/core/__tests__/useQuery.browser.spec.tsx +48 -0
  694. package/src/react/core/__tests__/useSelector.browser.spec.tsx +105 -0
  695. package/src/react/core/__tests__/useStore.browser.spec.tsx +113 -0
  696. package/src/react/core/components/ErrorBoundary.tsx +42 -0
  697. package/src/react/core/hooks/useAction.ts +56 -19
  698. package/src/react/core/hooks/useComputed.ts +91 -0
  699. package/src/react/core/hooks/useSelector.ts +67 -0
  700. package/src/react/core/hooks/useStore.ts +36 -17
  701. package/src/react/core/index.ts +3 -0
  702. package/src/react/core/utils/shallowEqual.ts +36 -0
  703. package/src/react/form/__tests__/FormModel-submit-loading.spec.ts +2 -2
  704. package/src/react/form/__tests__/form-submitting-reactive.browser.spec.tsx +5 -5
  705. package/src/react/form/__tests__/useForm.browser.spec.tsx +54 -54
  706. package/src/react/form/errors/FormValidationError.ts +0 -2
  707. package/src/react/form/hooks/useForm.ts +19 -4
  708. package/src/react/form/hooks/useFormQuerySync.ts +0 -0
  709. package/src/react/form/services/FormModel.ts +67 -47
  710. package/src/react/form/services/parseField.ts +66 -27
  711. package/src/react/i18n/__tests__/Localize.spec.tsx +1 -1
  712. package/src/react/i18n/providers/I18nProvider.ts +2 -2
  713. package/src/react/router/__tests__/$page.browser.spec.tsx +19 -19
  714. package/src/react/router/__tests__/$page.spec.tsx +21 -21
  715. package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +87 -1
  716. package/src/react/router/atoms/ssrManifestAtom.ts +16 -16
  717. package/src/react/router/components/NestedView.tsx +11 -3
  718. package/src/react/router/hooks/useQueryParams.ts +1 -1
  719. package/src/react/router/primitives/$page.ts +2 -2
  720. package/src/react/router/providers/ReactBrowserProvider.ts +64 -13
  721. package/src/react/router/providers/ReactPageProvider.ts +26 -12
  722. package/src/react/router/providers/ReactServerProvider.ts +13 -15
  723. package/src/react/sitemap/__tests__/$sitemap.spec.ts +2 -2
  724. package/src/react/ui/atoms/uiAtom.ts +6 -6
  725. package/src/react/ui/atoms/uiThemeListAtom.ts +7 -7
  726. package/src/react/ui/hooks/useColorMode.ts +11 -4
  727. package/src/react/ui/services/SchemaControl.ts +4 -4
  728. package/src/redis/providers/BunRedisProvider.ts +19 -11
  729. package/src/redis/providers/NodeRedisProvider.ts +3 -3
  730. package/src/scheduler/primitives/$scheduler.ts +21 -8
  731. package/src/security/__tests__/assertRealmScope.spec.ts +29 -0
  732. package/src/security/atoms/currentTenantAtom.ts +6 -6
  733. package/src/security/atoms/currentUserAtom.ts +2 -2
  734. package/src/security/primitives/$issuer.ts +24 -2
  735. package/src/security/providers/JwtProvider.ts +100 -0
  736. package/src/security/providers/SecurityProvider.ts +43 -2
  737. package/src/security/schemas/permissionSchema.ts +19 -19
  738. package/src/security/schemas/roleSchema.ts +22 -28
  739. package/src/security/schemas/userAccountInfoSchema.ts +47 -37
  740. package/src/server/auth/index.ts +0 -3
  741. package/src/server/auth/primitives/$auth.ts +6 -6
  742. package/src/server/auth/primitives/$authApple.ts +10 -10
  743. package/src/server/auth/primitives/$authFacebook.ts +10 -10
  744. package/src/server/auth/primitives/$authFranceConnect.ts +10 -10
  745. package/src/server/auth/primitives/$authGithub.ts +10 -10
  746. package/src/server/auth/primitives/$authGoogle.ts +10 -10
  747. package/src/server/auth/primitives/$authMicrosoft.ts +14 -14
  748. package/src/server/auth/providers/ServerAuthProvider.ts +49 -48
  749. package/src/server/auth/schemas/authenticationProviderSchema.ts +9 -12
  750. package/src/server/auth/schemas/tokenResponseSchema.ts +2 -2
  751. package/src/server/auth/schemas/tokensSchema.ts +16 -16
  752. package/src/server/auth/schemas/userinfoResponseSchema.ts +3 -3
  753. package/src/server/cookies/__tests__/AtomCookieBinding.spec.ts +80 -0
  754. package/src/server/cookies/__tests__/AtomCookiePersistence.browser.spec.ts +169 -0
  755. package/src/server/cookies/__tests__/AtomCookiePersistence.spec.ts +294 -0
  756. package/src/server/cookies/__tests__/ServerCookiesProvider.spec.ts +14 -14
  757. package/src/server/cookies/index.browser.ts +4 -1
  758. package/src/server/cookies/index.ts +8 -1
  759. package/src/server/cookies/primitives/$cookie.browser.ts +1 -1
  760. package/src/server/cookies/primitives/$cookie.ts +16 -0
  761. package/src/server/cookies/providers/AtomCookiePersistence.browser.ts +133 -0
  762. package/src/server/cookies/providers/AtomCookiePersistence.ts +148 -0
  763. package/src/server/cookies/providers/ServerCookiesProvider.ts +13 -5
  764. package/src/server/cookies/services/CookieParser.ts +6 -1
  765. package/src/server/core/__tests__/$action.spec.ts +13 -13
  766. package/src/server/core/__tests__/$middleware.spec.ts +10 -10
  767. package/src/server/core/__tests__/$route.spec.ts +9 -9
  768. package/src/server/core/__tests__/$sse.spec.ts +26 -26
  769. package/src/server/core/__tests__/BunHttpServerProvider.bun.spec.ts +6 -6
  770. package/src/server/core/__tests__/HttpClient.spec.ts +2 -2
  771. package/src/server/core/__tests__/NodeHttpServerProvider.spec.ts +15 -3
  772. package/src/server/core/__tests__/ServerBodyParserProvider.spec.ts +14 -14
  773. package/src/server/core/__tests__/ServerMultipartProvider.spec.ts +33 -33
  774. package/src/server/core/__tests__/ServerRouterProvider-errorHandler.spec.ts +5 -1
  775. package/src/server/core/__tests__/ServerRouterProvider-getRoutes.spec.ts +25 -25
  776. package/src/server/core/__tests__/ServerRouterProvider-serializationError.spec.ts +9 -5
  777. package/src/server/core/__tests__/ServerRouterProvider-validationError.spec.ts +56 -24
  778. package/src/server/core/errors/ValidationError.ts +6 -3
  779. package/src/server/core/helpers/isMultipart.ts +2 -1
  780. package/src/server/core/index.ts +5 -2
  781. package/src/server/core/interfaces/ServerRequest.ts +6 -14
  782. package/src/server/core/primitives/$action.ts +26 -36
  783. package/src/server/core/primitives/$sse.ts +3 -3
  784. package/src/server/core/providers/BunHttpServerProvider.ts +8 -9
  785. package/src/server/core/providers/NodeHttpServerProvider.ts +8 -9
  786. package/src/server/core/providers/ServerBodyParserProvider.ts +20 -12
  787. package/src/server/core/providers/ServerCompressProvider.ts +9 -10
  788. package/src/server/core/providers/ServerHelmetProvider.ts +26 -27
  789. package/src/server/core/providers/ServerMultipartProvider.ts +19 -19
  790. package/src/server/core/providers/ServerRouterProvider.ts +44 -15
  791. package/src/server/core/schemas/errorSchema.ts +20 -25
  792. package/src/server/core/schemas/okSchema.ts +9 -14
  793. package/src/server/core/services/HttpClient.ts +13 -1
  794. package/src/server/core/services/ServerRequestParser.ts +6 -6
  795. package/src/server/cors/providers/ServerCorsProvider.ts +26 -28
  796. package/src/server/health/schemas/healthSchema.ts +6 -6
  797. package/src/server/links/__tests__/$remote.spec.ts +3 -3
  798. package/src/server/links/__tests__/BatchEndpoint.spec.ts +9 -9
  799. package/src/server/links/__tests__/LinkProvider.spec.ts +9 -9
  800. package/src/server/links/__tests__/ServerLinksProvider.spec.ts +23 -23
  801. package/src/server/links/__tests__/fixtures/CrudApp.ts +21 -21
  802. package/src/server/links/atoms/apiLinksAtom.ts +2 -2
  803. package/src/server/links/atoms/linkOptionsAtom.ts +6 -6
  804. package/src/server/links/primitives/$remote.ts +3 -3
  805. package/src/server/links/providers/LinkProvider.ts +3 -3
  806. package/src/server/links/providers/ServerLinksProvider.ts +20 -20
  807. package/src/server/links/schemas/apiLinksResponseSchema.ts +23 -23
  808. package/src/server/proxy/__tests__/$proxy.spec.ts +9 -9
  809. package/src/server/proxy/providers/ServerProxyProvider.ts +5 -0
  810. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +18 -20
  811. package/src/server/swagger/__tests__/$swagger.spec.ts +91 -94
  812. package/src/server/swagger/__tests__/ui.spec.ts +3 -3
  813. package/src/server/swagger/providers/ServerSwaggerProvider.ts +66 -43
  814. package/src/system/__tests__/NodeShellProvider.spec.ts +46 -0
  815. package/src/system/providers/NodeShellProvider.ts +4 -1
  816. package/src/topic/core/__tests__/$subscriber.spec.ts +4 -4
  817. package/src/topic/core/__tests__/shared.ts +8 -8
  818. package/src/topic/core/primitives/$subscriber.ts +5 -5
  819. package/src/topic/core/primitives/$topic.ts +14 -14
  820. package/src/topic/redis/providers/RedisTopicProvider.ts +3 -3
  821. package/dist/container/core/index.d.ts +0 -236
  822. package/dist/container/core/index.d.ts.map +0 -1
  823. package/dist/container/core/index.js +0 -231
  824. package/dist/container/core/index.js.map +0 -1
  825. package/dist/container/core/index.workerd.js +0 -192
  826. package/dist/container/core/index.workerd.js.map +0 -1
  827. package/src/command/helpers/PrettyAsker.ts +0 -256
  828. package/src/command/helpers/PrettyPrint.ts +0 -332
  829. package/src/container/core/__tests__/$container.spec.ts +0 -83
  830. package/src/container/core/index.ts +0 -50
  831. package/src/container/core/index.workerd.ts +0 -53
  832. package/src/container/core/interfaces/ContainerOptions.ts +0 -69
  833. package/src/container/core/primitives/$container.ts +0 -100
  834. package/src/container/core/providers/CloudflareContainerProvider.ts +0 -70
  835. package/src/container/core/providers/ContainerProvider.ts +0 -92
  836. package/src/container/core/providers/MockContainerProvider.ts +0 -62
  837. package/src/container/core/providers/NodeContainerProvider.ts +0 -53
  838. package/src/core/__tests__/jsonSchemaToTypeBox.spec.ts +0 -771
  839. package/src/core/helpers/jsonSchemaToTypeBox.ts +0 -359
  840. package/src/server/auth/__tests__/authFederationClient.spec.ts +0 -40
  841. package/src/server/auth/__tests__/federationAssertion.spec.ts +0 -146
  842. package/src/server/auth/helpers/federationAssertion.ts +0 -74
  843. package/src/server/auth/primitives/$authFederationBroker.ts +0 -273
  844. package/src/server/auth/primitives/$authFederationClient.ts +0 -89
  845. /package/dist/orm/core/{chunk-B4FMCO8f.js → rolldown-runtime-B4FMCO8f.js} +0 -0
@@ -1,4 +1,4 @@
1
- import { $inject, $module, Alepha, AlephaError, KIND, Primitive, createPrimitive } from "alepha";
1
+ import { $hook, $inject, $module, Alepha, AlephaError, KIND, Primitive, createPrimitive } from "alepha";
2
2
  import { AlephaServer } from "alepha/server";
3
3
  import { DateTimeProvider } from "alepha/datetime";
4
4
  //#region ../../src/server/cookies/services/CookieParser.ts
@@ -32,7 +32,7 @@ var CookieParser = class {
32
32
  const parts = [];
33
33
  parts.push(`${name}=${cookie.value}`);
34
34
  if (cookie.path) parts.push(`Path=${cookie.path}`);
35
- if (cookie.maxAge) parts.push(`Max-Age=${cookie.maxAge}`);
35
+ if (cookie.maxAge != null) parts.push(`Max-Age=${cookie.maxAge}`);
36
36
  if (cookie.secure !== false && isHttps) parts.push("Secure");
37
37
  if (cookie.httpOnly) parts.push("HttpOnly");
38
38
  if (cookie.sameSite) parts.push(`SameSite=${cookie.sameSite}`);
@@ -41,6 +41,100 @@ var CookieParser = class {
41
41
  }
42
42
  };
43
43
  //#endregion
44
+ //#region ../../src/server/cookies/providers/AtomCookiePersistence.browser.ts
45
+ /**
46
+ * Browser variant of AtomCookiePersistence: reads `document.cookie` for every
47
+ * `persist: "cookie"` atom, and writes it back on every mutation.
48
+ *
49
+ * Like the server variant, atoms are resolved from the `StateManager`
50
+ * registry (`alepha.store.listAtoms()`) rather than from a map fed by
51
+ * `state:register` — that event fires once, is never replayed, and
52
+ * `$module.register()` registers `atoms[]` before the module's `services[]`
53
+ * exist, so an event-sourced map would miss every atom declared the
54
+ * documented way. The `configure` hook sweeps everything registered up to
55
+ * boot; `state:register` covers atoms that first register later (lazily, on
56
+ * first read).
57
+ *
58
+ * Cookie names are NOT APP_NAME-prefixed here, because `APP_NAME` is not
59
+ * reachable in the browser: `BuildClientTask` only `define`s
60
+ * `process.env.NODE_ENV` for the client bundle, and the SSR hydration
61
+ * payload (`ReactServerTemplateProvider.buildHydrationData`) only carries
62
+ * registered atom values + router layers — never the raw `env` store key.
63
+ * The server variant (`AtomCookiePersistence.ts`) matches this by passing
64
+ * `prefix: false` to `ServerCookiesProvider`, specifically for atom-cookie
65
+ * traffic — every other server cookie ($cookie primitive) still gets the
66
+ * APP_NAME namespace. See `ServerCookiesProvider.prefixName`.
67
+ */
68
+ var AtomCookiePersistence = class {
69
+ alepha = $inject(Alepha);
70
+ cookieParser = $inject(CookieParser);
71
+ dateTime = $inject(DateTimeProvider);
72
+ onConfigure = $hook({
73
+ on: "configure",
74
+ handler: () => {
75
+ for (const atom of this.cookieAtoms()) this.read(atom);
76
+ }
77
+ });
78
+ onRegister = $hook({
79
+ on: "state:register",
80
+ handler: ({ atom }) => {
81
+ if (atom.options.persist !== "cookie") return;
82
+ this.read(atom);
83
+ }
84
+ });
85
+ onMutate = $hook({
86
+ on: "state:mutate",
87
+ handler: ({ key, value }) => {
88
+ const atom = this.alepha.store.getAtom(String(key));
89
+ if (!atom || atom.options.persist !== "cookie") return;
90
+ if (value === void 0) {
91
+ this.clearCookie(atom.key);
92
+ return;
93
+ }
94
+ const cookie = {
95
+ value: encodeURIComponent(JSON.stringify(value)),
96
+ path: "/",
97
+ sameSite: "lax",
98
+ maxAge: this.dateTime.duration([365, "days"]).as("seconds")
99
+ };
100
+ document.cookie = this.cookieParser.cookieToString(atom.key, cookie);
101
+ }
102
+ });
103
+ /**
104
+ * Every registered atom declared with `persist: "cookie"`, read live from
105
+ * the state manager's registry.
106
+ */
107
+ cookieAtoms() {
108
+ return this.alepha.store.listAtoms().filter((atom) => atom.options.persist === "cookie");
109
+ }
110
+ /**
111
+ * Seed an atom's state from `document.cookie`, if a cookie exists for it.
112
+ * Idempotent: re-reading an atom that already holds the cookie's value is
113
+ * a no-op write of the same data.
114
+ */
115
+ read(atom) {
116
+ const raw = this.cookieParser.parseRequestCookies(document.cookie)[atom.key];
117
+ if (!raw) return;
118
+ try {
119
+ this.alepha.store.set(atom.key, JSON.parse(decodeURIComponent(raw)), { skipEvents: true });
120
+ } catch {
121
+ this.clearCookie(atom.key);
122
+ }
123
+ }
124
+ /**
125
+ * Expire a cookie immediately, mirroring
126
+ * `ServerCookiesProvider.deleteCookie`'s `Max-Age=0` semantics.
127
+ */
128
+ clearCookie(name) {
129
+ document.cookie = this.cookieParser.cookieToString(name, {
130
+ value: "",
131
+ path: "/",
132
+ sameSite: "lax",
133
+ maxAge: 0
134
+ });
135
+ }
136
+ };
137
+ //#endregion
44
138
  //#region ../../src/server/cookies/primitives/$cookie.browser.ts
45
139
  /**
46
140
  * Creates a browser-side cookie primitive for client-side cookie management.
@@ -56,7 +150,7 @@ var CookieParser = class {
56
150
  * class ClientCookies {
57
151
  * preferences = $cookie({
58
152
  * name: "user-prefs",
59
- * schema: t.object({ theme: t.text(), language: t.text() }),
153
+ * schema: z.object({ theme: z.text(), language: z.text() }),
60
154
  * ttl: [30, "days"]
61
155
  * });
62
156
  *
@@ -148,9 +242,13 @@ $cookie[KIND] = BrowserCookiePrimitive;
148
242
  const AlephaServerCookies = $module({
149
243
  name: "alepha.server.cookies",
150
244
  primitives: [],
151
- services: [AlephaServer]
245
+ services: [
246
+ AlephaServer,
247
+ CookieParser,
248
+ AtomCookiePersistence
249
+ ]
152
250
  });
153
251
  //#endregion
154
- export { $cookie, AlephaServerCookies, BrowserCookiePrimitive };
252
+ export { $cookie, AlephaServerCookies, AtomCookiePersistence, BrowserCookiePrimitive };
155
253
 
156
254
  //# sourceMappingURL=index.browser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.browser.js","names":[],"sources":["../../../src/server/cookies/services/CookieParser.ts","../../../src/server/cookies/primitives/$cookie.browser.ts","../../../src/server/cookies/index.browser.ts"],"sourcesContent":["import type { Cookie } from \"../primitives/$cookie.ts\";\n\nexport class CookieParser {\n public parseRequestCookies(header: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n const parts = header.split(\";\");\n for (const part of parts) {\n const eqIndex = part.indexOf(\"=\");\n if (eqIndex === -1) {\n continue;\n }\n\n const key = part.slice(0, eqIndex).trim();\n const value = part.slice(eqIndex + 1).trim();\n\n if (!key || !value) {\n continue;\n }\n\n cookies[key] = value;\n }\n\n return cookies;\n }\n\n public serializeResponseCookies(\n cookies: Record<string, Cookie | null>,\n isHttps: boolean,\n ): string[] {\n const headers = [];\n\n for (const [name, cookie] of Object.entries(cookies)) {\n // If the cookie is null, we need to delete it\n if (cookie == null) {\n headers.push(`${name}=; Path=/; Max-Age=0`);\n continue;\n }\n\n if (!cookie.value) {\n continue;\n }\n\n headers.push(this.cookieToString(name, cookie, isHttps));\n }\n\n return headers;\n }\n\n public cookieToString(\n name: string,\n cookie: Cookie,\n isHttps?: boolean,\n ): string {\n const parts: string[] = [];\n\n parts.push(`${name}=${cookie.value}`);\n\n if (cookie.path) {\n parts.push(`Path=${cookie.path}`);\n }\n if (cookie.maxAge) {\n parts.push(`Max-Age=${cookie.maxAge}`);\n }\n if (cookie.secure !== false && isHttps) {\n parts.push(\"Secure\");\n }\n if (cookie.httpOnly) {\n parts.push(\"HttpOnly\");\n }\n if (cookie.sameSite) {\n parts.push(`SameSite=${cookie.sameSite}`);\n }\n if (cookie.domain) {\n parts.push(`Domain=${cookie.domain}`);\n }\n\n return parts.join(\"; \");\n }\n}\n","import {\n $inject,\n Alepha,\n AlephaError,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { CookieParser } from \"../services/CookieParser.ts\";\nimport type {\n AbstractCookiePrimitive,\n Cookie,\n CookiePrimitiveOptions,\n Cookies,\n} from \"./$cookie.ts\";\n\n/**\n * Creates a browser-side cookie primitive for client-side cookie management.\n *\n * Browser-specific version of $cookie that uses document.cookie API. Supports type-safe\n * cookie operations with schema validation but excludes encryption/signing (use server-side\n * $cookie for secure operations).\n *\n * **Note**: This is the browser version - encryption, signing, and compression are not supported.\n *\n * @example\n * ```ts\n * class ClientCookies {\n * preferences = $cookie({\n * name: \"user-prefs\",\n * schema: t.object({ theme: t.text(), language: t.text() }),\n * ttl: [30, \"days\"]\n * });\n *\n * savePreferences() {\n * this.preferences.set({ theme: \"dark\", language: \"en\" });\n * }\n *\n * getPreferences() {\n * return this.preferences.get() ?? { theme: \"light\", language: \"en\" };\n * }\n * }\n * ```\n */\nexport const $cookie = <T extends TSchema>(\n options: CookiePrimitiveOptions<T>,\n extendedOptions?: Omit<CookiePrimitiveOptions<T>, \"schema\">,\n): AbstractCookiePrimitive<T> => {\n if (extendedOptions) {\n options = {\n key: options.key,\n schema: options.schema,\n ...extendedOptions,\n };\n }\n return createPrimitive(BrowserCookiePrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class BrowserCookiePrimitive<T extends TSchema>\n extends Primitive<CookiePrimitiveOptions<T>>\n implements AbstractCookiePrimitive<T>\n{\n protected cookieParser = $inject(CookieParser);\n protected alepha = $inject(Alepha);\n protected dateTimeProvider = $inject(DateTimeProvider);\n protected cookie?: Cookie;\n\n protected onInit() {\n if (this.options.key) {\n this.alepha.events.on(\"state:mutate\", ({ key, value }) => {\n if (key === this.options.key) {\n this.set(value);\n }\n });\n this.alepha.events.on(\"configure\", () => {\n try {\n const value = this.get();\n if (value !== undefined) {\n this.alepha.set(this.options.key as any, value);\n }\n } catch {}\n });\n }\n }\n\n public get schema(): T {\n return this.options.schema;\n }\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n public set(data: Static<T>): void {\n const value = JSON.stringify(this.alepha.codec.decode(this.schema, data));\n const options = this.options;\n\n if (options.compress) {\n throw new AlephaError(\"Compression is not supported in browser cookies.\");\n }\n\n if (options.encrypt) {\n throw new AlephaError(\"Encryption is not supported in browser cookies.\");\n }\n\n if (options.sign) {\n throw new AlephaError(\"Signing is not supported in browser cookies.\");\n }\n\n const cookie: Cookie = {\n value: encodeURIComponent(value),\n path: options.path ?? \"/\",\n sameSite: options.sameSite ?? \"lax\",\n secure: false,\n httpOnly: false,\n domain: options.domain,\n };\n\n if (options.ttl) {\n cookie.maxAge = this.dateTimeProvider.duration(options.ttl).as(\"seconds\");\n }\n\n // biome-ignore lint/suspicious/noDocumentCookie: ...\n document.cookie = this.cookieParser.cookieToString(this.name, cookie);\n }\n\n public get(options?: { cookies?: Cookies }): Static<T> | undefined {\n const cookie = this.cookieParser.parseRequestCookies(document.cookie)[\n this.name\n ];\n if (!cookie) {\n return undefined;\n }\n\n const rawValue = decodeURIComponent(cookie);\n\n if (this.options.compress) {\n throw new AlephaError(\"Compression is not supported in browser cookies.\");\n }\n\n if (this.options.encrypt) {\n throw new AlephaError(\"Encryption is not supported in browser cookies.\");\n }\n\n if (this.options.sign) {\n throw new AlephaError(\"Signing is not supported in browser cookies.\");\n }\n\n return this.alepha.codec.decode(this.schema, JSON.parse(rawValue));\n }\n\n public del(): void {\n const options = this.options;\n const cookie: Cookie = {\n value: \"\",\n path: options.path ?? \"/\",\n sameSite: options.sameSite ?? \"lax\",\n secure: false,\n httpOnly: false,\n domain: options.domain,\n maxAge: 0, // Set maxAge to 0 to delete the cookie\n };\n\n // biome-ignore lint/suspicious/noDocumentCookie: ...\n document.cookie = this.cookieParser.cookieToString(this.name, cookie);\n }\n}\n\n$cookie[KIND] = BrowserCookiePrimitive;\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$cookie.browser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaServerCookies = $module({\n name: \"alepha.server.cookies\",\n primitives: [],\n services: [AlephaServer],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.browser.js","names":[],"sources":["../../../src/server/cookies/services/CookieParser.ts","../../../src/server/cookies/providers/AtomCookiePersistence.browser.ts","../../../src/server/cookies/primitives/$cookie.browser.ts","../../../src/server/cookies/index.browser.ts"],"sourcesContent":["import type { Cookie } from \"../primitives/$cookie.ts\";\n\nexport class CookieParser {\n public parseRequestCookies(header: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n const parts = header.split(\";\");\n for (const part of parts) {\n const eqIndex = part.indexOf(\"=\");\n if (eqIndex === -1) {\n continue;\n }\n\n const key = part.slice(0, eqIndex).trim();\n const value = part.slice(eqIndex + 1).trim();\n\n if (!key || !value) {\n continue;\n }\n\n cookies[key] = value;\n }\n\n return cookies;\n }\n\n public serializeResponseCookies(\n cookies: Record<string, Cookie | null>,\n isHttps: boolean,\n ): string[] {\n const headers = [];\n\n for (const [name, cookie] of Object.entries(cookies)) {\n // If the cookie is null, we need to delete it\n if (cookie == null) {\n headers.push(`${name}=; Path=/; Max-Age=0`);\n continue;\n }\n\n if (!cookie.value) {\n continue;\n }\n\n headers.push(this.cookieToString(name, cookie, isHttps));\n }\n\n return headers;\n }\n\n public cookieToString(\n name: string,\n cookie: Cookie,\n isHttps?: boolean,\n ): string {\n const parts: string[] = [];\n\n parts.push(`${name}=${cookie.value}`);\n\n if (cookie.path) {\n parts.push(`Path=${cookie.path}`);\n }\n // `!= null`, not truthy: `maxAge: 0` is the standard \"delete this\n // cookie now\" value (used by AtomCookiePersistence.browser.ts's\n // clearCookie() and BrowserCookiePrimitive.del()). A truthy check would\n // silently drop `Max-Age` for exactly that value, turning a deletion\n // into a plain (non-expiring-until-session-end) write.\n if (cookie.maxAge != null) {\n parts.push(`Max-Age=${cookie.maxAge}`);\n }\n if (cookie.secure !== false && isHttps) {\n parts.push(\"Secure\");\n }\n if (cookie.httpOnly) {\n parts.push(\"HttpOnly\");\n }\n if (cookie.sameSite) {\n parts.push(`SameSite=${cookie.sameSite}`);\n }\n if (cookie.domain) {\n parts.push(`Domain=${cookie.domain}`);\n }\n\n return parts.join(\"; \");\n }\n}\n","import { $hook, $inject, Alepha, type Atom, type State } from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport type { Cookie } from \"../primitives/$cookie.ts\";\nimport { CookieParser } from \"../services/CookieParser.ts\";\n\n/**\n * Browser variant of AtomCookiePersistence: reads `document.cookie` for every\n * `persist: \"cookie\"` atom, and writes it back on every mutation.\n *\n * Like the server variant, atoms are resolved from the `StateManager`\n * registry (`alepha.store.listAtoms()`) rather than from a map fed by\n * `state:register` — that event fires once, is never replayed, and\n * `$module.register()` registers `atoms[]` before the module's `services[]`\n * exist, so an event-sourced map would miss every atom declared the\n * documented way. The `configure` hook sweeps everything registered up to\n * boot; `state:register` covers atoms that first register later (lazily, on\n * first read).\n *\n * Cookie names are NOT APP_NAME-prefixed here, because `APP_NAME` is not\n * reachable in the browser: `BuildClientTask` only `define`s\n * `process.env.NODE_ENV` for the client bundle, and the SSR hydration\n * payload (`ReactServerTemplateProvider.buildHydrationData`) only carries\n * registered atom values + router layers — never the raw `env` store key.\n * The server variant (`AtomCookiePersistence.ts`) matches this by passing\n * `prefix: false` to `ServerCookiesProvider`, specifically for atom-cookie\n * traffic — every other server cookie ($cookie primitive) still gets the\n * APP_NAME namespace. See `ServerCookiesProvider.prefixName`.\n */\nexport class AtomCookiePersistence {\n protected readonly alepha = $inject(Alepha);\n protected readonly cookieParser = $inject(CookieParser);\n protected readonly dateTime = $inject(DateTimeProvider);\n\n protected readonly onConfigure = $hook({\n on: \"configure\",\n handler: () => {\n for (const atom of this.cookieAtoms()) {\n this.read(atom);\n }\n },\n });\n\n protected readonly onRegister = $hook({\n on: \"state:register\",\n handler: ({ atom }) => {\n if (atom.options.persist !== \"cookie\") {\n return;\n }\n this.read(atom);\n },\n });\n\n protected readonly onMutate = $hook({\n on: \"state:mutate\",\n handler: ({ key, value }) => {\n const atom = this.alepha.store.getAtom(String(key));\n if (!atom || atom.options.persist !== \"cookie\") {\n return;\n }\n\n if (value === undefined) {\n // Without this branch, `store.del(atom)` would write the literal\n // string \"undefined\", and the next read's `JSON.parse(\"undefined\")`\n // would throw into the catch in `read()` on every page load.\n this.clearCookie(atom.key);\n return;\n }\n\n const cookie: Cookie = {\n value: encodeURIComponent(JSON.stringify(value)),\n path: \"/\",\n sameSite: \"lax\",\n maxAge: this.dateTime.duration([365, \"days\"]).as(\"seconds\"),\n };\n // biome-ignore lint/suspicious/noDocumentCookie: cookie persistence adapter\n document.cookie = this.cookieParser.cookieToString(atom.key, cookie);\n },\n });\n\n /**\n * Every registered atom declared with `persist: \"cookie\"`, read live from\n * the state manager's registry.\n */\n protected cookieAtoms(): Array<Atom<any, any>> {\n return this.alepha.store\n .listAtoms()\n .filter((atom) => atom.options.persist === \"cookie\");\n }\n\n /**\n * Seed an atom's state from `document.cookie`, if a cookie exists for it.\n * Idempotent: re-reading an atom that already holds the cookie's value is\n * a no-op write of the same data.\n */\n protected read(atom: Atom<any, any>): void {\n const raw = this.cookieParser.parseRequestCookies(document.cookie)[\n atom.key\n ];\n if (!raw) {\n return;\n }\n\n try {\n this.alepha.store.set(\n atom.key as keyof State,\n JSON.parse(decodeURIComponent(raw)),\n { skipEvents: true },\n );\n } catch {\n // Corrupted cookie (invalid JSON/encoding, or a value that no longer\n // matches the atom's schema — `store.set` validates against it) —\n // clear it instead of leaving it to keep throwing on every future\n // read. Mirrors the web-storage adapter's parity\n // (StateManager.bindWebStorage removes the bad entry too).\n this.clearCookie(atom.key);\n }\n }\n\n /**\n * Expire a cookie immediately, mirroring\n * `ServerCookiesProvider.deleteCookie`'s `Max-Age=0` semantics.\n */\n protected clearCookie(name: string): void {\n const cookie: Cookie = {\n value: \"\",\n path: \"/\",\n sameSite: \"lax\",\n maxAge: 0,\n };\n // biome-ignore lint/suspicious/noDocumentCookie: cookie persistence adapter\n document.cookie = this.cookieParser.cookieToString(name, cookie);\n }\n}\n","import {\n $inject,\n Alepha,\n AlephaError,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { CookieParser } from \"../services/CookieParser.ts\";\nimport type {\n AbstractCookiePrimitive,\n Cookie,\n CookiePrimitiveOptions,\n Cookies,\n} from \"./$cookie.ts\";\n\n/**\n * Creates a browser-side cookie primitive for client-side cookie management.\n *\n * Browser-specific version of $cookie that uses document.cookie API. Supports type-safe\n * cookie operations with schema validation but excludes encryption/signing (use server-side\n * $cookie for secure operations).\n *\n * **Note**: This is the browser version - encryption, signing, and compression are not supported.\n *\n * @example\n * ```ts\n * class ClientCookies {\n * preferences = $cookie({\n * name: \"user-prefs\",\n * schema: z.object({ theme: z.text(), language: z.text() }),\n * ttl: [30, \"days\"]\n * });\n *\n * savePreferences() {\n * this.preferences.set({ theme: \"dark\", language: \"en\" });\n * }\n *\n * getPreferences() {\n * return this.preferences.get() ?? { theme: \"light\", language: \"en\" };\n * }\n * }\n * ```\n */\nexport const $cookie = <T extends TSchema>(\n options: CookiePrimitiveOptions<T>,\n extendedOptions?: Omit<CookiePrimitiveOptions<T>, \"schema\">,\n): AbstractCookiePrimitive<T> => {\n if (extendedOptions) {\n options = {\n key: options.key,\n schema: options.schema,\n ...extendedOptions,\n };\n }\n return createPrimitive(BrowserCookiePrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class BrowserCookiePrimitive<T extends TSchema>\n extends Primitive<CookiePrimitiveOptions<T>>\n implements AbstractCookiePrimitive<T>\n{\n protected cookieParser = $inject(CookieParser);\n protected alepha = $inject(Alepha);\n protected dateTimeProvider = $inject(DateTimeProvider);\n protected cookie?: Cookie;\n\n protected onInit() {\n if (this.options.key) {\n this.alepha.events.on(\"state:mutate\", ({ key, value }) => {\n if (key === this.options.key) {\n this.set(value);\n }\n });\n this.alepha.events.on(\"configure\", () => {\n try {\n const value = this.get();\n if (value !== undefined) {\n this.alepha.set(this.options.key as any, value);\n }\n } catch {}\n });\n }\n }\n\n public get schema(): T {\n return this.options.schema;\n }\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n public set(data: Static<T>): void {\n const value = JSON.stringify(this.alepha.codec.decode(this.schema, data));\n const options = this.options;\n\n if (options.compress) {\n throw new AlephaError(\"Compression is not supported in browser cookies.\");\n }\n\n if (options.encrypt) {\n throw new AlephaError(\"Encryption is not supported in browser cookies.\");\n }\n\n if (options.sign) {\n throw new AlephaError(\"Signing is not supported in browser cookies.\");\n }\n\n const cookie: Cookie = {\n value: encodeURIComponent(value),\n path: options.path ?? \"/\",\n sameSite: options.sameSite ?? \"lax\",\n secure: false,\n httpOnly: false,\n domain: options.domain,\n };\n\n if (options.ttl) {\n cookie.maxAge = this.dateTimeProvider.duration(options.ttl).as(\"seconds\");\n }\n\n // biome-ignore lint/suspicious/noDocumentCookie: ...\n document.cookie = this.cookieParser.cookieToString(this.name, cookie);\n }\n\n public get(options?: { cookies?: Cookies }): Static<T> | undefined {\n const cookie = this.cookieParser.parseRequestCookies(document.cookie)[\n this.name\n ];\n if (!cookie) {\n return undefined;\n }\n\n const rawValue = decodeURIComponent(cookie);\n\n if (this.options.compress) {\n throw new AlephaError(\"Compression is not supported in browser cookies.\");\n }\n\n if (this.options.encrypt) {\n throw new AlephaError(\"Encryption is not supported in browser cookies.\");\n }\n\n if (this.options.sign) {\n throw new AlephaError(\"Signing is not supported in browser cookies.\");\n }\n\n return this.alepha.codec.decode(this.schema, JSON.parse(rawValue));\n }\n\n public del(): void {\n const options = this.options;\n const cookie: Cookie = {\n value: \"\",\n path: options.path ?? \"/\",\n sameSite: options.sameSite ?? \"lax\",\n secure: false,\n httpOnly: false,\n domain: options.domain,\n maxAge: 0, // Set maxAge to 0 to delete the cookie\n };\n\n // biome-ignore lint/suspicious/noDocumentCookie: ...\n document.cookie = this.cookieParser.cookieToString(this.name, cookie);\n }\n}\n\n$cookie[KIND] = BrowserCookiePrimitive;\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { AtomCookiePersistence } from \"./providers/AtomCookiePersistence.browser.ts\";\nimport { CookieParser } from \"./services/CookieParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$cookie.browser.ts\";\nexport * from \"./providers/AtomCookiePersistence.browser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaServerCookies = $module({\n name: \"alepha.server.cookies\",\n primitives: [],\n services: [AlephaServer, CookieParser, AtomCookiePersistence],\n});\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
- import { Alepha, KIND, Primitive, Static, TSchema } from "alepha";
1
+ import { Alepha, Atom, KIND, Primitive, Static, TSchema } from "alepha";
2
2
  import { SecretProvider } from "alepha/crypto";
3
3
  import { DateTimeProvider, DurationLike } from "alepha/datetime";
4
-
5
4
  //#region ../../src/server/cookies/services/CookieParser.d.ts
6
5
  declare class CookieParser {
7
6
  parseRequestCookies(header: string): Record<string, string>;
@@ -37,11 +36,16 @@ declare class ServerCookiesProvider {
37
36
  * When `APP_NAME` is unset the name is returned unchanged, so existing
38
37
  * single-app deployments keep their current cookie names. Same convention as
39
38
  * the `APP_NAME` prefix used by R2 storage and server-timing.
39
+ *
40
+ * Pass `prefix = false` to skip the namespace entirely — used for
41
+ * `persist: "cookie"` atoms, whose browser-side counterpart cannot see
42
+ * `APP_NAME` and therefore always uses the bare name. See
43
+ * `CookiePrimitiveOptions.prefix`.
40
44
  */
41
- protected prefixName(name: string): string;
45
+ protected prefixName(name: string, prefix?: boolean): string;
42
46
  getCookie<T extends TSchema>(name: string, options: CookiePrimitiveOptions<T>, contextCookies?: Cookies): Static<T> | undefined;
43
47
  setCookie<T extends TSchema>(name: string, options: CookiePrimitiveOptions<T>, data: Static<T>, contextCookies?: Cookies): void;
44
- deleteCookie<T extends TSchema>(name: string, contextCookies?: Cookies): void;
48
+ deleteCookie<T extends TSchema>(name: string, contextCookies?: Cookies, prefix?: boolean): void;
45
49
  protected encrypt(text: string): string;
46
50
  protected decrypt(encryptedText: string): string;
47
51
  /**
@@ -111,6 +115,21 @@ interface CookiePrimitiveOptions<T extends TSchema> {
111
115
  * Optional key to link this cookie to an Atom, enabling automatic synchronization between the cookie and the Atom's state.
112
116
  */
113
117
  key?: string;
118
+ /**
119
+ * Internal escape hatch: when explicitly set to `false`, the server skips
120
+ * the `APP_NAME` cookie-name namespace applied by
121
+ * `ServerCookiesProvider.prefixName()`.
122
+ *
123
+ * Used by `AtomCookiePersistence` so the cookie name the server
124
+ * reads/writes for a `persist: "cookie"` atom matches the bare name its
125
+ * browser variant uses — `APP_NAME` is not reachable client-side (not
126
+ * baked into the client bundle, not part of SSR hydration), so the
127
+ * browser can only ever use the bare atom key.
128
+ *
129
+ * Application code declaring `$cookie(...)` directly should leave this
130
+ * unset — it defaults to `true` (prefixed), matching every other cookie.
131
+ */
132
+ prefix?: boolean;
114
133
  }
115
134
  interface AbstractCookiePrimitive<T extends TSchema> {
116
135
  readonly name: string;
@@ -165,6 +184,54 @@ interface Cookie {
165
184
  domain?: string;
166
185
  }
167
186
  //#endregion
187
+ //#region ../../src/server/cookies/providers/AtomCookiePersistence.d.ts
188
+ /**
189
+ * Binds every atom declared with `persist: "cookie"` to an HTTP cookie.
190
+ *
191
+ * - `server:onRequest` — seeds the request-scoped state from the cookie, so
192
+ * SSR renders with the persisted value.
193
+ * - `state:mutate` — writes the new value back as a Set-Cookie header.
194
+ * - `state:register` — an atom registering lazily *during* a request (first
195
+ * touched by an SSR render, after `server:onRequest` already ran) reads
196
+ * its cookie right away, so that render still sees the persisted value.
197
+ *
198
+ * Atoms are resolved from the `StateManager` registry
199
+ * (`alepha.store.listAtoms()`) on every request and every mutation, never
200
+ * from a map built up from `state:register` events. That event fires exactly
201
+ * once per atom and is never replayed, while `$module.register()` registers
202
+ * `atoms[]` BEFORE it wires `imports[]` and injects `services[]` — so the
203
+ * documented `$module({ atoms, imports: [AlephaServerCookies], services })`
204
+ * shape registers every atom before this provider exists. An event-sourced
205
+ * map would stay empty forever there, making `persist: "cookie"` a silent
206
+ * no-op in both directions. Reading the registry on demand is
207
+ * order-independent.
208
+ *
209
+ * The cookie is named after the atom key, lives 365 days, SameSite=lax,
210
+ * path "/". For custom cookie options (encryption, signing, custom TTL),
211
+ * declare an explicit `$cookie({ key: atom.key, ... })` binding instead.
212
+ */
213
+ declare class AtomCookiePersistence {
214
+ protected readonly alepha: Alepha;
215
+ protected readonly log: import("alepha/logger").Logger;
216
+ protected readonly serverCookies: ServerCookiesProvider;
217
+ protected readonly onRequest: import("alepha").HookPrimitive<"server:onRequest">;
218
+ protected readonly onRegister: import("alepha").HookPrimitive<"state:register">;
219
+ protected readonly onMutate: import("alepha").HookPrimitive<"state:mutate">;
220
+ /**
221
+ * Every registered atom declared with `persist: "cookie"`, read live from
222
+ * the state manager's registry.
223
+ */
224
+ protected cookieAtoms(): Array<Atom<any, any>>;
225
+ protected read(atom: Atom<any, any>, cookies?: Cookies): void;
226
+ protected cookieOptions(atom: Atom<any, any>): {
227
+ schema: any;
228
+ path: string;
229
+ sameSite: "lax";
230
+ ttl: DurationLike;
231
+ prefix: boolean;
232
+ };
233
+ }
234
+ //#endregion
168
235
  //#region ../../src/server/cookies/index.d.ts
169
236
  declare module "alepha/server" {
170
237
  interface ServerRequest {
@@ -181,5 +248,5 @@ declare module "alepha/server" {
181
248
  */
182
249
  declare const AlephaServerCookies: import("alepha").Service<import("alepha").Module>;
183
250
  //#endregion
184
- export { $cookie, AbstractCookiePrimitive, AlephaServerCookies, Cookie, CookiePrimitive, CookiePrimitiveOptions, Cookies, ServerCookiesProvider };
251
+ export { $cookie, AbstractCookiePrimitive, AlephaServerCookies, AtomCookiePersistence, Cookie, CookiePrimitive, CookiePrimitiveOptions, Cookies, ServerCookiesProvider };
185
252
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/cookies/services/CookieParser.ts","../../../src/server/cookies/providers/ServerCookiesProvider.ts","../../../src/server/cookies/primitives/$cookie.ts","../../../src/server/cookies/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/server/cookies/services/CookieParser.ts","../../../src/server/cookies/providers/ServerCookiesProvider.ts","../../../src/server/cookies/primitives/$cookie.ts","../../../src/server/cookies/providers/AtomCookiePersistence.ts","../../../src/server/cookies/index.ts"],"mappings":";;;;cAEa;EACJ,oBAAoB,iBAAiB;EAsBrC,yBACL,SAAS,eAAe,gBACxB;EAqBK,eACL,cACA,QAAQ,QACR;;;;cCxBS;qBACQ,QAAM;qBACN,6BAAG;qBACH,cAAY;qBACZ,kBAAgB;qBAChB,gBAAc;qBAGd;qBACA;qBACA;qBACA;WAEH,4BAAS;WAYT,2BAAQ;WAYR,yBAAM;YAeZ,sBAAsB,UAAU,UAAU;;;;;;;;;;;;;;;;;;;;YA8B1C,WAAW,cAAc;EAO5B,UAAU,UAAU,SACzB,cACA,SAAS,uBAAuB,IAChC,iBAAiB,UAChB,OAAO;EA8CH,UAAU,UAAU,SACzB,cACA,SAAS,uBAAuB,IAChC,MAAM,OAAO,IACb,iBAAiB;EAoCZ,aAAa,UAAU,SAC5B,cACA,iBAAiB,SACjB;YAQQ,QAAQ;YAWR,QAAQ;;;;;YAyBR,aAAa;YAIb,KAAK;;;;;;;;;cCjPJ;GAAW,UAAU,SAAO,SAC9B,uBAAuB,IAAE,kBAChB,KAAK,uBAAuB,gBAC7C,wBAAwB;;;UAaV,uBAAuB,UAAU;;;;EAIhD,QAAQ;;;;EAKR;;;;EAKA;;;;EAKA,MAAM;;;;EAKN;;;;EAKA;;;;EAKA;;;;EAKA;;;;EAKA;;;;EAKA;;;;EAKA;;;;EAKA;;;;;;;;;;;;;;;EAgBA;;UAGe,wBAAwB,UAAU;WACxC;WACA,SAAS,uBAAuB;EACzC,IACE,OAAO,OAAO,IACd;IAAY,UAAU;IAAS,MAAM;;EAEvC,IAAI;IAAY,UAAU;MAAY,OAAO;EAC7C,IAAI;IAAY,UAAU;;;cAGf,gBAAgB,UAAU,iBAC7B,UAAU,uBAAuB,eAC9B,wBAAwB;qBAEhB,uBAAqB;YAE9B;MAoBC,UAAU;MAIV;;;;EAOJ,IACL,OAAO,OAAO,IACd;IAAY,UAAU;IAAS,MAAM;;;;;EAgBhC,IAAI;IAAY,UAAU;MAAY,OAAO;;;;EAW7C,IAAI;IAAY,UAAU;;;UASlB;EACf,KAAK;EACL,KAAK,eAAe;;UAGL;EACf;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cCjLW;qBACQ,QAAM;qBACN,6BAAG;qBACH,eAAa;qBAEb,4BAAS;qBAmBT,6BAAU;qBAeV,2BAAQ;;;;;YAwBjB,eAAe,MAAM;YAMrB,KAAK,MAAM,gBAAgB,UAAU;YA+BrC,cAAc,MAAM;IAE1B;IACA;IACA;IACA,KAAsB;IAQtB;;;;;;YChIM;IACR,SAAS;;;;;;;;;;;cAYA,sCAAmB,yBAAA"}
@@ -36,7 +36,7 @@ var CookieParser = class {
36
36
  const parts = [];
37
37
  parts.push(`${name}=${cookie.value}`);
38
38
  if (cookie.path) parts.push(`Path=${cookie.path}`);
39
- if (cookie.maxAge) parts.push(`Max-Age=${cookie.maxAge}`);
39
+ if (cookie.maxAge != null) parts.push(`Max-Age=${cookie.maxAge}`);
40
40
  if (cookie.secure !== false && isHttps) parts.push("Secure");
41
41
  if (cookie.httpOnly) parts.push("HttpOnly");
42
42
  if (cookie.sameSite) parts.push(`SameSite=${cookie.sameSite}`);
@@ -102,15 +102,22 @@ var ServerCookiesProvider = class {
102
102
  * When `APP_NAME` is unset the name is returned unchanged, so existing
103
103
  * single-app deployments keep their current cookie names. Same convention as
104
104
  * the `APP_NAME` prefix used by R2 storage and server-timing.
105
+ *
106
+ * Pass `prefix = false` to skip the namespace entirely — used for
107
+ * `persist: "cookie"` atoms, whose browser-side counterpart cannot see
108
+ * `APP_NAME` and therefore always uses the bare name. See
109
+ * `CookiePrimitiveOptions.prefix`.
105
110
  */
106
- prefixName(name) {
111
+ prefixName(name, prefix = true) {
112
+ if (!prefix) return name;
107
113
  const app = this.alepha.env.APP_NAME;
108
114
  if (!app) return name;
109
115
  return `${String(app).toLowerCase()}.${name}`;
110
116
  }
111
117
  getCookie(name, options, contextCookies) {
112
118
  const cookies = this.getCookiesFromContext(contextCookies);
113
- let rawValue = cookies.req[this.prefixName(name)];
119
+ const prefixed = options.prefix !== false;
120
+ let rawValue = cookies.req[this.prefixName(name, prefixed)];
114
121
  if (!rawValue) return void 0;
115
122
  try {
116
123
  rawValue = decodeURIComponent(rawValue);
@@ -129,7 +136,7 @@ var ServerCookiesProvider = class {
129
136
  return this.alepha.codec.decode(options.schema, JSON.parse(rawValue));
130
137
  } catch (error) {
131
138
  this.log.warn(`Failed to parse cookie "${name}"`, error);
132
- this.deleteCookie(name, cookies);
139
+ this.deleteCookie(name, cookies, prefixed);
133
140
  return;
134
141
  }
135
142
  }
@@ -148,11 +155,11 @@ var ServerCookiesProvider = class {
148
155
  domain: options.domain
149
156
  };
150
157
  if (options.ttl) cookie.maxAge = this.dateTimeProvider.duration(options.ttl).as("seconds");
151
- cookies.res[this.prefixName(name)] = cookie;
158
+ cookies.res[this.prefixName(name, options.prefix !== false)] = cookie;
152
159
  }
153
- deleteCookie(name, contextCookies) {
160
+ deleteCookie(name, contextCookies, prefix = true) {
154
161
  const cookies = this.getCookiesFromContext(contextCookies);
155
- cookies.res[this.prefixName(name)] = null;
162
+ cookies.res[this.prefixName(name, prefix)] = null;
156
163
  }
157
164
  encrypt(text) {
158
165
  const iv = randomBytes(this.IV_LENGTH);
@@ -245,6 +252,92 @@ var CookiePrimitive = class extends Primitive {
245
252
  };
246
253
  $cookie[KIND] = CookiePrimitive;
247
254
  //#endregion
255
+ //#region ../../src/server/cookies/providers/AtomCookiePersistence.ts
256
+ /**
257
+ * Binds every atom declared with `persist: "cookie"` to an HTTP cookie.
258
+ *
259
+ * - `server:onRequest` — seeds the request-scoped state from the cookie, so
260
+ * SSR renders with the persisted value.
261
+ * - `state:mutate` — writes the new value back as a Set-Cookie header.
262
+ * - `state:register` — an atom registering lazily *during* a request (first
263
+ * touched by an SSR render, after `server:onRequest` already ran) reads
264
+ * its cookie right away, so that render still sees the persisted value.
265
+ *
266
+ * Atoms are resolved from the `StateManager` registry
267
+ * (`alepha.store.listAtoms()`) on every request and every mutation, never
268
+ * from a map built up from `state:register` events. That event fires exactly
269
+ * once per atom and is never replayed, while `$module.register()` registers
270
+ * `atoms[]` BEFORE it wires `imports[]` and injects `services[]` — so the
271
+ * documented `$module({ atoms, imports: [AlephaServerCookies], services })`
272
+ * shape registers every atom before this provider exists. An event-sourced
273
+ * map would stay empty forever there, making `persist: "cookie"` a silent
274
+ * no-op in both directions. Reading the registry on demand is
275
+ * order-independent.
276
+ *
277
+ * The cookie is named after the atom key, lives 365 days, SameSite=lax,
278
+ * path "/". For custom cookie options (encryption, signing, custom TTL),
279
+ * declare an explicit `$cookie({ key: atom.key, ... })` binding instead.
280
+ */
281
+ var AtomCookiePersistence = class {
282
+ alepha = $inject(Alepha);
283
+ log = $logger();
284
+ serverCookies = $inject(ServerCookiesProvider);
285
+ onRequest = $hook({
286
+ on: "server:onRequest",
287
+ after: [this.serverCookies],
288
+ handler: ({ request }) => {
289
+ for (const atom of this.cookieAtoms()) this.read(atom, request.cookies);
290
+ }
291
+ });
292
+ onRegister = $hook({
293
+ on: "state:register",
294
+ handler: ({ atom }) => {
295
+ if (atom.options.persist !== "cookie") return;
296
+ if (this.alepha.store.get("alepha.http.request")) this.read(atom);
297
+ }
298
+ });
299
+ onMutate = $hook({
300
+ on: "state:mutate",
301
+ handler: ({ key, value }) => {
302
+ const atom = this.alepha.store.getAtom(String(key));
303
+ if (!atom || atom.options.persist !== "cookie") return;
304
+ try {
305
+ this.serverCookies.setCookie(atom.key, this.cookieOptions(atom), value);
306
+ } catch (error) {
307
+ this.log.debug(`Cannot persist atom "${atom.key}" to cookie`, { error });
308
+ }
309
+ }
310
+ });
311
+ /**
312
+ * Every registered atom declared with `persist: "cookie"`, read live from
313
+ * the state manager's registry.
314
+ */
315
+ cookieAtoms() {
316
+ return this.alepha.store.listAtoms().filter((atom) => atom.options.persist === "cookie");
317
+ }
318
+ read(atom, cookies) {
319
+ if (!this.alepha.context.exists()) {
320
+ this.log.debug(`Skipping cookie read for atom "${atom.key}": no active request context.`);
321
+ return;
322
+ }
323
+ try {
324
+ const value = this.serverCookies.getCookie(atom.key, this.cookieOptions(atom), cookies);
325
+ if (value !== void 0) this.alepha.store.set(atom.key, value, { skipEvents: true });
326
+ } catch (error) {
327
+ this.log.debug(`Cannot read cookie for atom "${atom.key}"`, { error });
328
+ }
329
+ }
330
+ cookieOptions(atom) {
331
+ return {
332
+ schema: atom.schema,
333
+ path: "/",
334
+ sameSite: "lax",
335
+ ttl: [365, "days"],
336
+ prefix: false
337
+ };
338
+ }
339
+ };
340
+ //#endregion
248
341
  //#region ../../src/server/cookies/index.ts
249
342
  /**
250
343
  * Server and browser-safe cookie handling.
@@ -260,10 +353,11 @@ const AlephaServerCookies = $module({
260
353
  services: [
261
354
  AlephaServer,
262
355
  ServerCookiesProvider,
263
- CookieParser
356
+ CookieParser,
357
+ AtomCookiePersistence
264
358
  ]
265
359
  });
266
360
  //#endregion
267
- export { $cookie, AlephaServerCookies, CookiePrimitive, ServerCookiesProvider };
361
+ export { $cookie, AlephaServerCookies, AtomCookiePersistence, CookiePrimitive, ServerCookiesProvider };
268
362
 
269
363
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/server/cookies/services/CookieParser.ts","../../../src/server/cookies/providers/ServerCookiesProvider.ts","../../../src/server/cookies/primitives/$cookie.ts","../../../src/server/cookies/index.ts"],"sourcesContent":["import type { Cookie } from \"../primitives/$cookie.ts\";\n\nexport class CookieParser {\n public parseRequestCookies(header: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n const parts = header.split(\";\");\n for (const part of parts) {\n const eqIndex = part.indexOf(\"=\");\n if (eqIndex === -1) {\n continue;\n }\n\n const key = part.slice(0, eqIndex).trim();\n const value = part.slice(eqIndex + 1).trim();\n\n if (!key || !value) {\n continue;\n }\n\n cookies[key] = value;\n }\n\n return cookies;\n }\n\n public serializeResponseCookies(\n cookies: Record<string, Cookie | null>,\n isHttps: boolean,\n ): string[] {\n const headers = [];\n\n for (const [name, cookie] of Object.entries(cookies)) {\n // If the cookie is null, we need to delete it\n if (cookie == null) {\n headers.push(`${name}=; Path=/; Max-Age=0`);\n continue;\n }\n\n if (!cookie.value) {\n continue;\n }\n\n headers.push(this.cookieToString(name, cookie, isHttps));\n }\n\n return headers;\n }\n\n public cookieToString(\n name: string,\n cookie: Cookie,\n isHttps?: boolean,\n ): string {\n const parts: string[] = [];\n\n parts.push(`${name}=${cookie.value}`);\n\n if (cookie.path) {\n parts.push(`Path=${cookie.path}`);\n }\n if (cookie.maxAge) {\n parts.push(`Max-Age=${cookie.maxAge}`);\n }\n if (cookie.secure !== false && isHttps) {\n parts.push(\"Secure\");\n }\n if (cookie.httpOnly) {\n parts.push(\"HttpOnly\");\n }\n if (cookie.sameSite) {\n parts.push(`SameSite=${cookie.sameSite}`);\n }\n if (cookie.domain) {\n parts.push(`Domain=${cookie.domain}`);\n }\n\n return parts.join(\"; \");\n }\n}\n","import {\n createCipheriv,\n createDecipheriv,\n createHash,\n createHmac,\n randomBytes,\n timingSafeEqual,\n} from \"node:crypto\";\nimport { deflateRawSync, inflateRawSync } from \"node:zlib\";\nimport {\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport { SecretProvider } from \"alepha/crypto\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type {\n Cookie,\n CookiePrimitiveOptions,\n Cookies,\n} from \"../primitives/$cookie.ts\";\nimport { CookieParser } from \"../services/CookieParser.ts\";\n\nexport class ServerCookiesProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly cookieParser = $inject(CookieParser);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly secretProvider = $inject(SecretProvider);\n\n // crypto constants\n protected readonly ALGORITHM = \"aes-256-gcm\";\n protected readonly IV_LENGTH = 16; // For GCM\n protected readonly AUTH_TAG_LENGTH = 16;\n protected readonly SIGNATURE_LENGTH = 32; // For SHA256\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: ({ request }) => {\n request.cookies = {\n req: this.cookieParser.parseRequestCookies(\n request.headers.cookie ?? \"\",\n ),\n res: {},\n };\n },\n });\n\n public readonly onAction = $hook({\n on: \"action:onRequest\",\n handler: ({ request }) => {\n request.cookies = {\n req: this.cookieParser.parseRequestCookies(\n request.headers.cookie ?? \"\",\n ),\n res: {},\n };\n },\n });\n\n public readonly onSend = $hook({\n on: \"server:onSend\",\n handler: ({ request }) => {\n if (request.cookies && Object.keys(request.cookies.res).length > 0) {\n const setCookieHeaders = this.cookieParser.serializeResponseCookies(\n request.cookies.res,\n request.url.protocol === \"https:\",\n );\n if (setCookieHeaders.length > 0) {\n request.reply.headers[\"set-cookie\"] = setCookieHeaders;\n }\n }\n },\n });\n\n protected getCookiesFromContext(cookies?: Cookies): Cookies {\n const contextCookies = this.alepha.store.get(\n \"alepha.http.request\",\n )?.cookies;\n if (cookies) return cookies;\n if (contextCookies) return contextCookies;\n throw new AlephaError(\n \"Cookie context is not available. This method must be called within a server request cycle.\",\n );\n }\n\n /**\n * Namespaces a cookie name with the app's `APP_NAME` (lowercased) so that\n * two Alepha apps sharing a cookie jar do not collide.\n *\n * Cookies are scoped by host + path only — never by port — so two apps on\n * `localhost:3000` and `localhost:4000` would otherwise both read/write a\n * cookie literally named `tokens` and silently clobber each other (and,\n * because each encrypts with its own `APP_SECRET`, the foreign cookie fails\n * to decrypt and gets deleted, logging the user out).\n *\n * When `APP_NAME` is unset the name is returned unchanged, so existing\n * single-app deployments keep their current cookie names. Same convention as\n * the `APP_NAME` prefix used by R2 storage and server-timing.\n */\n protected prefixName(name: string): string {\n const app = this.alepha.env.APP_NAME;\n if (!app) return name;\n return `${String(app).toLowerCase()}.${name}`;\n }\n\n public getCookie<T extends TSchema>(\n name: string,\n options: CookiePrimitiveOptions<T>,\n contextCookies?: Cookies,\n ): Static<T> | undefined {\n const cookies = this.getCookiesFromContext(contextCookies);\n let rawValue = cookies.req[this.prefixName(name)];\n\n if (!rawValue) return undefined;\n\n try {\n rawValue = decodeURIComponent(rawValue);\n\n if (options.sign) {\n const signature = rawValue.substring(0, this.SIGNATURE_LENGTH * 2);\n const value = rawValue.substring(this.SIGNATURE_LENGTH * 2);\n const expectedSignature = this.sign(value);\n\n if (\n !timingSafeEqual(\n Buffer.from(signature, \"hex\"),\n Buffer.from(expectedSignature, \"hex\"),\n )\n ) {\n this.log.warn(`Invalid signature for cookie \"${name}\".`);\n return undefined;\n }\n rawValue = value;\n }\n\n if (options.encrypt) {\n rawValue = this.decrypt(rawValue);\n }\n\n if (options.compress) {\n rawValue = inflateRawSync(Buffer.from(rawValue, \"base64\")).toString(\n \"utf8\",\n );\n }\n\n return this.alepha.codec.decode(options.schema, JSON.parse(rawValue));\n } catch (error) {\n this.log.warn(`Failed to parse cookie \"${name}\"`, error);\n // corrupted or invalid cookie, instruct browser to delete it on next response\n this.deleteCookie(name, cookies);\n return undefined;\n }\n }\n\n public setCookie<T extends TSchema>(\n name: string,\n options: CookiePrimitiveOptions<T>,\n data: Static<T>,\n contextCookies?: Cookies,\n ): void {\n const cookies = this.getCookiesFromContext(contextCookies);\n let value = JSON.stringify(this.alepha.codec.decode(options.schema, data));\n\n if (options.compress) {\n value = deflateRawSync(value).toString(\"base64\");\n }\n\n if (options.encrypt) {\n value = this.encrypt(value);\n }\n\n if (options.sign) {\n value = this.sign(value) + value;\n }\n\n const cookie: Cookie = {\n value: encodeURIComponent(value),\n path: options.path ?? \"/\",\n // \"lax\" is correct here — \"strict\" would break OAuth flows because OAuth callbacks\n // are top-level redirects from the IdP, and strict cookies are not sent on cross-site\n // top-level navigations. \"lax\" is Chrome's own default and the industry standard.\n sameSite: options.sameSite ?? \"lax\",\n secure: options.secure ?? this.alepha.isProduction(),\n httpOnly: options.httpOnly,\n domain: options.domain,\n };\n\n if (options.ttl) {\n cookie.maxAge = this.dateTimeProvider.duration(options.ttl).as(\"seconds\");\n }\n\n cookies.res[this.prefixName(name)] = cookie;\n }\n\n public deleteCookie<T extends TSchema>(\n name: string,\n contextCookies?: Cookies,\n ): void {\n const cookies = this.getCookiesFromContext(contextCookies);\n cookies.res[this.prefixName(name)] = null;\n }\n\n // --- Crypto & Parsing ---\n\n protected encrypt(text: string): string {\n const iv = randomBytes(this.IV_LENGTH);\n const cipher = createCipheriv(this.ALGORITHM, this.deriveKey(), iv);\n const encrypted = Buffer.concat([\n cipher.update(text, \"utf8\"),\n cipher.final(),\n ]);\n const authTag = cipher.getAuthTag();\n return Buffer.concat([iv, authTag, encrypted]).toString(\"base64\");\n }\n\n protected decrypt(encryptedText: string): string {\n const data = Buffer.from(encryptedText, \"base64\");\n const iv = data.subarray(0, this.IV_LENGTH);\n const authTag = data.subarray(\n this.IV_LENGTH,\n this.IV_LENGTH + this.AUTH_TAG_LENGTH,\n );\n\n const encrypted = data.subarray(this.IV_LENGTH + this.AUTH_TAG_LENGTH);\n const decipher = createDecipheriv(this.ALGORITHM, this.deriveKey(), iv);\n\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([\n decipher.update(encrypted),\n decipher.final(),\n ]);\n\n return decrypted.toString(\"utf8\");\n }\n\n /**\n * Derives a 32-byte key from APP_SECRET via SHA-256.\n * Accepts any string length — no padding or truncation needed.\n */\n protected deriveKey(): Buffer {\n return createHash(\"sha256\").update(this.secretProvider.secretKey).digest();\n }\n\n protected sign(data: string): string {\n return createHmac(\"sha256\", this.deriveKey()).update(data).digest(\"hex\");\n }\n}\n","import {\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { ServerCookiesProvider } from \"../providers/ServerCookiesProvider.ts\";\n\n/**\n * Declares a type-safe, configurable HTTP cookie.\n * This primitive provides methods to get, set, and delete the cookie\n * within the server request/response cycle.\n */\nexport const $cookie = <T extends TSchema>(\n options: CookiePrimitiveOptions<T>,\n extendedOptions?: Omit<CookiePrimitiveOptions<T>, \"schema\">,\n): AbstractCookiePrimitive<T> => {\n if (extendedOptions) {\n options = {\n key: options.key,\n schema: options.schema,\n ...extendedOptions,\n };\n }\n return createPrimitive(CookiePrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface CookiePrimitiveOptions<T extends TSchema> {\n /**\n * The schema for the cookie's value, used for validation and type safety.\n */\n schema: T;\n\n /**\n * The name of the cookie.\n */\n name?: string;\n\n /**\n * The cookie's path. Defaults to \"/\".\n */\n path?: string;\n\n /**\n * Time-to-live for the cookie. Maps to `Max-Age`.\n */\n ttl?: DurationLike;\n\n /**\n * If true, the cookie is only sent over HTTPS. Defaults to true in production.\n */\n secure?: boolean;\n\n /**\n * If true, the cookie cannot be accessed by client-side scripts.\n */\n httpOnly?: boolean;\n\n /**\n * SameSite policy for the cookie. Defaults to \"lax\".\n */\n sameSite?: \"strict\" | \"lax\" | \"none\";\n\n /**\n * The domain for the cookie.\n */\n domain?: string;\n\n /**\n * If true, the cookie value will be compressed using zlib.\n */\n compress?: boolean;\n\n /**\n * If true, the cookie value will be encrypted. Requires `COOKIE_SECRET` env var.\n */\n encrypt?: boolean;\n\n /**\n * If true, the cookie will be signed to prevent tampering. Requires `COOKIE_SECRET` env var.\n */\n sign?: boolean;\n\n /**\n * Optional key to link this cookie to an Atom, enabling automatic synchronization between the cookie and the Atom's state.\n */\n key?: string;\n}\n\nexport interface AbstractCookiePrimitive<T extends TSchema> {\n readonly name: string;\n readonly options: CookiePrimitiveOptions<T>;\n set(\n value: Static<T>,\n options?: { cookies?: Cookies; ttl?: DurationLike },\n ): void;\n get(options?: { cookies?: Cookies }): Static<T> | undefined;\n del(options?: { cookies?: Cookies }): void;\n}\n\nexport class CookiePrimitive<T extends TSchema>\n extends Primitive<CookiePrimitiveOptions<T>>\n implements AbstractCookiePrimitive<T>\n{\n protected readonly serverCookiesProvider = $inject(ServerCookiesProvider);\n\n protected onInit() {\n if (this.options.key) {\n this.alepha.events.on(\"state:mutate\", ({ key, value }) => {\n if (key === this.options.key) {\n this.set(value);\n }\n });\n this.alepha.events.on(\"server:onRequest\", ({ request }) => {\n try {\n const value = this.get(request);\n if (value !== undefined) {\n this.alepha.store.set(this.options.key as any, value, {\n skipEvents: true,\n });\n }\n } catch {}\n });\n }\n }\n\n public get schema(): T {\n return this.options.schema;\n }\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Sets the cookie with the given value in the current request's response.\n */\n public set(\n value: Static<T>,\n options?: { cookies?: Cookies; ttl?: DurationLike },\n ): void {\n this.serverCookiesProvider.setCookie(\n this.name,\n {\n ...this.options,\n ttl: options?.ttl ?? this.options.ttl,\n },\n value,\n options?.cookies,\n );\n }\n\n /**\n * Gets the cookie value from the current request. Returns undefined if not found or invalid.\n */\n public get(options?: { cookies?: Cookies }): Static<T> | undefined {\n return this.serverCookiesProvider.getCookie(\n this.name,\n this.options,\n options?.cookies,\n );\n }\n\n /**\n * Deletes the cookie in the current request's response.\n */\n public del(options?: { cookies?: Cookies }): void {\n this.serverCookiesProvider.deleteCookie(this.name, options?.cookies);\n }\n}\n\n$cookie[KIND] = CookiePrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface Cookies {\n req: Record<string, string>;\n res: Record<string, Cookie | null>;\n}\n\nexport interface Cookie {\n value: string;\n path?: string;\n maxAge?: number;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: \"strict\" | \"lax\" | \"none\";\n domain?: string;\n}\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { $cookie, type Cookies } from \"./primitives/$cookie.ts\";\nimport { ServerCookiesProvider } from \"./providers/ServerCookiesProvider.ts\";\nimport { CookieParser } from \"./services/CookieParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$cookie.ts\";\nexport * from \"./providers/ServerCookiesProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ServerRequest {\n cookies: Cookies;\n }\n}\n\n/**\n * Server and browser-safe cookie handling.\n *\n * **Features:**\n * - Cookie management on server and browser\n *\n * @module alepha.server.cookies\n */\nexport const AlephaServerCookies = $module({\n name: \"alepha.server.cookies\",\n primitives: [$cookie],\n services: [AlephaServer, ServerCookiesProvider, CookieParser],\n});\n"],"mappings":";;;;;;;;AAEA,IAAa,eAAb,MAA0B;CACxB,oBAA2B,QAAwC;EACjE,MAAM,UAAkC,CAAC;EACzC,MAAM,QAAQ,OAAO,MAAM,GAAG;EAC9B,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,UAAU,KAAK,QAAQ,GAAG;GAChC,IAAI,YAAY,IACd;GAGF,MAAM,MAAM,KAAK,MAAM,GAAG,OAAO,EAAE,KAAK;GACxC,MAAM,QAAQ,KAAK,MAAM,UAAU,CAAC,EAAE,KAAK;GAE3C,IAAI,CAAC,OAAO,CAAC,OACX;GAGF,QAAQ,OAAO;EACjB;EAEA,OAAO;CACT;CAEA,yBACE,SACA,SACU;EACV,MAAM,UAAU,CAAC;EAEjB,KAAK,MAAM,CAAC,MAAM,WAAW,OAAO,QAAQ,OAAO,GAAG;GAEpD,IAAI,UAAU,MAAM;IAClB,QAAQ,KAAK,GAAG,KAAK,qBAAqB;IAC1C;GACF;GAEA,IAAI,CAAC,OAAO,OACV;GAGF,QAAQ,KAAK,KAAK,eAAe,MAAM,QAAQ,OAAO,CAAC;EACzD;EAEA,OAAO;CACT;CAEA,eACE,MACA,QACA,SACQ;EACR,MAAM,QAAkB,CAAC;EAEzB,MAAM,KAAK,GAAG,KAAK,GAAG,OAAO,OAAO;EAEpC,IAAI,OAAO,MACT,MAAM,KAAK,QAAQ,OAAO,MAAM;EAElC,IAAI,OAAO,QACT,MAAM,KAAK,WAAW,OAAO,QAAQ;EAEvC,IAAI,OAAO,WAAW,SAAS,SAC7B,MAAM,KAAK,QAAQ;EAErB,IAAI,OAAO,UACT,MAAM,KAAK,UAAU;EAEvB,IAAI,OAAO,UACT,MAAM,KAAK,YAAY,OAAO,UAAU;EAE1C,IAAI,OAAO,QACT,MAAM,KAAK,UAAU,OAAO,QAAQ;EAGtC,OAAO,MAAM,KAAK,IAAI;CACxB;AACF;;;ACnDA,IAAa,wBAAb,MAAmC;CACjC,SAA4B,QAAQ,MAAM;CAC1C,MAAyB,QAAQ;CACjC,eAAkC,QAAQ,YAAY;CACtD,mBAAsC,QAAQ,gBAAgB;CAC9D,iBAAoC,QAAQ,cAAc;CAG1D,YAA+B;CAC/B,YAA+B;CAC/B,kBAAqC;CACrC,mBAAsC;CAEtC,YAA4B,MAAM;EAChC,IAAI;EACJ,UAAU,EAAE,cAAc;GACxB,QAAQ,UAAU;IAChB,KAAK,KAAK,aAAa,oBACrB,QAAQ,QAAQ,UAAU,EAC5B;IACA,KAAK,CAAC;GACR;EACF;CACF,CAAC;CAED,WAA2B,MAAM;EAC/B,IAAI;EACJ,UAAU,EAAE,cAAc;GACxB,QAAQ,UAAU;IAChB,KAAK,KAAK,aAAa,oBACrB,QAAQ,QAAQ,UAAU,EAC5B;IACA,KAAK,CAAC;GACR;EACF;CACF,CAAC;CAED,SAAyB,MAAM;EAC7B,IAAI;EACJ,UAAU,EAAE,cAAc;GACxB,IAAI,QAAQ,WAAW,OAAO,KAAK,QAAQ,QAAQ,GAAG,EAAE,SAAS,GAAG;IAClE,MAAM,mBAAmB,KAAK,aAAa,yBACzC,QAAQ,QAAQ,KAChB,QAAQ,IAAI,aAAa,QAC3B;IACA,IAAI,iBAAiB,SAAS,GAC5B,QAAQ,MAAM,QAAQ,gBAAgB;GAE1C;EACF;CACF,CAAC;CAED,sBAAgC,SAA4B;EAC1D,MAAM,iBAAiB,KAAK,OAAO,MAAM,IACvC,qBACF,GAAG;EACH,IAAI,SAAS,OAAO;EACpB,IAAI,gBAAgB,OAAO;EAC3B,MAAM,IAAI,YACR,4FACF;CACF;;;;;;;;;;;;;;;CAgBA,WAAqB,MAAsB;EACzC,MAAM,MAAM,KAAK,OAAO,IAAI;EAC5B,IAAI,CAAC,KAAK,OAAO;EACjB,OAAO,GAAG,OAAO,GAAG,EAAE,YAAY,EAAE,GAAG;CACzC;CAEA,UACE,MACA,SACA,gBACuB;EACvB,MAAM,UAAU,KAAK,sBAAsB,cAAc;EACzD,IAAI,WAAW,QAAQ,IAAI,KAAK,WAAW,IAAI;EAE/C,IAAI,CAAC,UAAU,OAAO,KAAA;EAEtB,IAAI;GACF,WAAW,mBAAmB,QAAQ;GAEtC,IAAI,QAAQ,MAAM;IAChB,MAAM,YAAY,SAAS,UAAU,GAAG,KAAK,mBAAmB,CAAC;IACjE,MAAM,QAAQ,SAAS,UAAU,KAAK,mBAAmB,CAAC;IAC1D,MAAM,oBAAoB,KAAK,KAAK,KAAK;IAEzC,IACE,CAAC,gBACC,OAAO,KAAK,WAAW,KAAK,GAC5B,OAAO,KAAK,mBAAmB,KAAK,CACtC,GACA;KACA,KAAK,IAAI,KAAK,iCAAiC,KAAK,GAAG;KACvD;IACF;IACA,WAAW;GACb;GAEA,IAAI,QAAQ,SACV,WAAW,KAAK,QAAQ,QAAQ;GAGlC,IAAI,QAAQ,UACV,WAAW,eAAe,OAAO,KAAK,UAAU,QAAQ,CAAC,EAAE,SACzD,MACF;GAGF,OAAO,KAAK,OAAO,MAAM,OAAO,QAAQ,QAAQ,KAAK,MAAM,QAAQ,CAAC;EACtE,SAAS,OAAO;GACd,KAAK,IAAI,KAAK,2BAA2B,KAAK,IAAI,KAAK;GAEvD,KAAK,aAAa,MAAM,OAAO;GAC/B;EACF;CACF;CAEA,UACE,MACA,SACA,MACA,gBACM;EACN,MAAM,UAAU,KAAK,sBAAsB,cAAc;EACzD,IAAI,QAAQ,KAAK,UAAU,KAAK,OAAO,MAAM,OAAO,QAAQ,QAAQ,IAAI,CAAC;EAEzE,IAAI,QAAQ,UACV,QAAQ,eAAe,KAAK,EAAE,SAAS,QAAQ;EAGjD,IAAI,QAAQ,SACV,QAAQ,KAAK,QAAQ,KAAK;EAG5B,IAAI,QAAQ,MACV,QAAQ,KAAK,KAAK,KAAK,IAAI;EAG7B,MAAM,SAAiB;GACrB,OAAO,mBAAmB,KAAK;GAC/B,MAAM,QAAQ,QAAQ;GAItB,UAAU,QAAQ,YAAY;GAC9B,QAAQ,QAAQ,UAAU,KAAK,OAAO,aAAa;GACnD,UAAU,QAAQ;GAClB,QAAQ,QAAQ;EAClB;EAEA,IAAI,QAAQ,KACV,OAAO,SAAS,KAAK,iBAAiB,SAAS,QAAQ,GAAG,EAAE,GAAG,SAAS;EAG1E,QAAQ,IAAI,KAAK,WAAW,IAAI,KAAK;CACvC;CAEA,aACE,MACA,gBACM;EACN,MAAM,UAAU,KAAK,sBAAsB,cAAc;EACzD,QAAQ,IAAI,KAAK,WAAW,IAAI,KAAK;CACvC;CAIA,QAAkB,MAAsB;EACtC,MAAM,KAAK,YAAY,KAAK,SAAS;EACrC,MAAM,SAAS,eAAe,KAAK,WAAW,KAAK,UAAU,GAAG,EAAE;EAClE,MAAM,YAAY,OAAO,OAAO,CAC9B,OAAO,OAAO,MAAM,MAAM,GAC1B,OAAO,MAAM,CACf,CAAC;EACD,MAAM,UAAU,OAAO,WAAW;EAClC,OAAO,OAAO,OAAO;GAAC;GAAI;GAAS;EAAS,CAAC,EAAE,SAAS,QAAQ;CAClE;CAEA,QAAkB,eAA+B;EAC/C,MAAM,OAAO,OAAO,KAAK,eAAe,QAAQ;EAChD,MAAM,KAAK,KAAK,SAAS,GAAG,KAAK,SAAS;EAC1C,MAAM,UAAU,KAAK,SACnB,KAAK,WACL,KAAK,YAAY,KAAK,eACxB;EAEA,MAAM,YAAY,KAAK,SAAS,KAAK,YAAY,KAAK,eAAe;EACrE,MAAM,WAAW,iBAAiB,KAAK,WAAW,KAAK,UAAU,GAAG,EAAE;EAEtE,SAAS,WAAW,OAAO;EAO3B,OALkB,OAAO,OAAO,CAC9B,SAAS,OAAO,SAAS,GACzB,SAAS,MAAM,CACjB,CAEe,EAAE,SAAS,MAAM;CAClC;;;;;CAMA,YAA8B;EAC5B,OAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,eAAe,SAAS,EAAE,OAAO;CAC3E;CAEA,KAAe,MAAsB;EACnC,OAAO,WAAW,UAAU,KAAK,UAAU,CAAC,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;CACzE;AACF;;;;;;;;AC5OA,MAAa,WACX,SACA,oBAC+B;CAC/B,IAAI,iBACF,UAAU;EACR,KAAK,QAAQ;EACb,QAAQ,QAAQ;EAChB,GAAG;CACL;CAEF,OAAO,gBAAgB,iBAAoB,OAAO;AACpD;AA6EA,IAAa,kBAAb,cACU,UAEV;CACE,wBAA2C,QAAQ,qBAAqB;CAExE,SAAmB;EACjB,IAAI,KAAK,QAAQ,KAAK;GACpB,KAAK,OAAO,OAAO,GAAG,iBAAiB,EAAE,KAAK,YAAY;IACxD,IAAI,QAAQ,KAAK,QAAQ,KACvB,KAAK,IAAI,KAAK;GAElB,CAAC;GACD,KAAK,OAAO,OAAO,GAAG,qBAAqB,EAAE,cAAc;IACzD,IAAI;KACF,MAAM,QAAQ,KAAK,IAAI,OAAO;KAC9B,IAAI,UAAU,KAAA,GACZ,KAAK,OAAO,MAAM,IAAI,KAAK,QAAQ,KAAY,OAAO,EACpD,YAAY,KACd,CAAC;IAEL,QAAQ,CAAC;GACX,CAAC;EACH;CACF;CAEA,IAAW,SAAY;EACrB,OAAO,KAAK,QAAQ;CACtB;CAEA,IAAW,OAAe;EACxB,OAAO,KAAK,QAAQ,QAAQ,GAAG,KAAK,OAAO;CAC7C;;;;CAKA,IACE,OACA,SACM;EACN,KAAK,sBAAsB,UACzB,KAAK,MACL;GACE,GAAG,KAAK;GACR,KAAK,SAAS,OAAO,KAAK,QAAQ;EACpC,GACA,OACA,SAAS,OACX;CACF;;;;CAKA,IAAW,SAAwD;EACjE,OAAO,KAAK,sBAAsB,UAChC,KAAK,MACL,KAAK,SACL,SAAS,OACX;CACF;;;;CAKA,IAAW,SAAuC;EAChD,KAAK,sBAAsB,aAAa,KAAK,MAAM,SAAS,OAAO;CACrE;AACF;AAEA,QAAQ,QAAQ;;;;;;;;;;;ACrJhB,MAAa,sBAAsB,QAAQ;CACzC,MAAM;CACN,YAAY,CAAC,OAAO;CACpB,UAAU;EAAC;EAAc;EAAuB;CAAY;AAC9D,CAAC"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/server/cookies/services/CookieParser.ts","../../../src/server/cookies/providers/ServerCookiesProvider.ts","../../../src/server/cookies/primitives/$cookie.ts","../../../src/server/cookies/providers/AtomCookiePersistence.ts","../../../src/server/cookies/index.ts"],"sourcesContent":["import type { Cookie } from \"../primitives/$cookie.ts\";\n\nexport class CookieParser {\n public parseRequestCookies(header: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n const parts = header.split(\";\");\n for (const part of parts) {\n const eqIndex = part.indexOf(\"=\");\n if (eqIndex === -1) {\n continue;\n }\n\n const key = part.slice(0, eqIndex).trim();\n const value = part.slice(eqIndex + 1).trim();\n\n if (!key || !value) {\n continue;\n }\n\n cookies[key] = value;\n }\n\n return cookies;\n }\n\n public serializeResponseCookies(\n cookies: Record<string, Cookie | null>,\n isHttps: boolean,\n ): string[] {\n const headers = [];\n\n for (const [name, cookie] of Object.entries(cookies)) {\n // If the cookie is null, we need to delete it\n if (cookie == null) {\n headers.push(`${name}=; Path=/; Max-Age=0`);\n continue;\n }\n\n if (!cookie.value) {\n continue;\n }\n\n headers.push(this.cookieToString(name, cookie, isHttps));\n }\n\n return headers;\n }\n\n public cookieToString(\n name: string,\n cookie: Cookie,\n isHttps?: boolean,\n ): string {\n const parts: string[] = [];\n\n parts.push(`${name}=${cookie.value}`);\n\n if (cookie.path) {\n parts.push(`Path=${cookie.path}`);\n }\n // `!= null`, not truthy: `maxAge: 0` is the standard \"delete this\n // cookie now\" value (used by AtomCookiePersistence.browser.ts's\n // clearCookie() and BrowserCookiePrimitive.del()). A truthy check would\n // silently drop `Max-Age` for exactly that value, turning a deletion\n // into a plain (non-expiring-until-session-end) write.\n if (cookie.maxAge != null) {\n parts.push(`Max-Age=${cookie.maxAge}`);\n }\n if (cookie.secure !== false && isHttps) {\n parts.push(\"Secure\");\n }\n if (cookie.httpOnly) {\n parts.push(\"HttpOnly\");\n }\n if (cookie.sameSite) {\n parts.push(`SameSite=${cookie.sameSite}`);\n }\n if (cookie.domain) {\n parts.push(`Domain=${cookie.domain}`);\n }\n\n return parts.join(\"; \");\n }\n}\n","import {\n createCipheriv,\n createDecipheriv,\n createHash,\n createHmac,\n randomBytes,\n timingSafeEqual,\n} from \"node:crypto\";\nimport { deflateRawSync, inflateRawSync } from \"node:zlib\";\nimport {\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport { SecretProvider } from \"alepha/crypto\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type {\n Cookie,\n CookiePrimitiveOptions,\n Cookies,\n} from \"../primitives/$cookie.ts\";\nimport { CookieParser } from \"../services/CookieParser.ts\";\n\nexport class ServerCookiesProvider {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly cookieParser = $inject(CookieParser);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly secretProvider = $inject(SecretProvider);\n\n // crypto constants\n protected readonly ALGORITHM = \"aes-256-gcm\";\n protected readonly IV_LENGTH = 16; // For GCM\n protected readonly AUTH_TAG_LENGTH = 16;\n protected readonly SIGNATURE_LENGTH = 32; // For SHA256\n\n public readonly onRequest = $hook({\n on: \"server:onRequest\",\n handler: ({ request }) => {\n request.cookies = {\n req: this.cookieParser.parseRequestCookies(\n request.headers.cookie ?? \"\",\n ),\n res: {},\n };\n },\n });\n\n public readonly onAction = $hook({\n on: \"action:onRequest\",\n handler: ({ request }) => {\n request.cookies = {\n req: this.cookieParser.parseRequestCookies(\n request.headers.cookie ?? \"\",\n ),\n res: {},\n };\n },\n });\n\n public readonly onSend = $hook({\n on: \"server:onSend\",\n handler: ({ request }) => {\n if (request.cookies && Object.keys(request.cookies.res).length > 0) {\n const setCookieHeaders = this.cookieParser.serializeResponseCookies(\n request.cookies.res,\n request.url.protocol === \"https:\",\n );\n if (setCookieHeaders.length > 0) {\n request.reply.headers[\"set-cookie\"] = setCookieHeaders;\n }\n }\n },\n });\n\n protected getCookiesFromContext(cookies?: Cookies): Cookies {\n const contextCookies = this.alepha.store.get(\n \"alepha.http.request\",\n )?.cookies;\n if (cookies) return cookies;\n if (contextCookies) return contextCookies;\n throw new AlephaError(\n \"Cookie context is not available. This method must be called within a server request cycle.\",\n );\n }\n\n /**\n * Namespaces a cookie name with the app's `APP_NAME` (lowercased) so that\n * two Alepha apps sharing a cookie jar do not collide.\n *\n * Cookies are scoped by host + path only — never by port — so two apps on\n * `localhost:3000` and `localhost:4000` would otherwise both read/write a\n * cookie literally named `tokens` and silently clobber each other (and,\n * because each encrypts with its own `APP_SECRET`, the foreign cookie fails\n * to decrypt and gets deleted, logging the user out).\n *\n * When `APP_NAME` is unset the name is returned unchanged, so existing\n * single-app deployments keep their current cookie names. Same convention as\n * the `APP_NAME` prefix used by R2 storage and server-timing.\n *\n * Pass `prefix = false` to skip the namespace entirely — used for\n * `persist: \"cookie\"` atoms, whose browser-side counterpart cannot see\n * `APP_NAME` and therefore always uses the bare name. See\n * `CookiePrimitiveOptions.prefix`.\n */\n protected prefixName(name: string, prefix = true): string {\n if (!prefix) return name;\n const app = this.alepha.env.APP_NAME;\n if (!app) return name;\n return `${String(app).toLowerCase()}.${name}`;\n }\n\n public getCookie<T extends TSchema>(\n name: string,\n options: CookiePrimitiveOptions<T>,\n contextCookies?: Cookies,\n ): Static<T> | undefined {\n const cookies = this.getCookiesFromContext(contextCookies);\n const prefixed = options.prefix !== false;\n let rawValue = cookies.req[this.prefixName(name, prefixed)];\n\n if (!rawValue) return undefined;\n\n try {\n rawValue = decodeURIComponent(rawValue);\n\n if (options.sign) {\n const signature = rawValue.substring(0, this.SIGNATURE_LENGTH * 2);\n const value = rawValue.substring(this.SIGNATURE_LENGTH * 2);\n const expectedSignature = this.sign(value);\n\n if (\n !timingSafeEqual(\n Buffer.from(signature, \"hex\"),\n Buffer.from(expectedSignature, \"hex\"),\n )\n ) {\n this.log.warn(`Invalid signature for cookie \"${name}\".`);\n return undefined;\n }\n rawValue = value;\n }\n\n if (options.encrypt) {\n rawValue = this.decrypt(rawValue);\n }\n\n if (options.compress) {\n rawValue = inflateRawSync(Buffer.from(rawValue, \"base64\")).toString(\n \"utf8\",\n );\n }\n\n return this.alepha.codec.decode(options.schema, JSON.parse(rawValue));\n } catch (error) {\n this.log.warn(`Failed to parse cookie \"${name}\"`, error);\n // corrupted or invalid cookie, instruct browser to delete it on next response\n this.deleteCookie(name, cookies, prefixed);\n return undefined;\n }\n }\n\n public setCookie<T extends TSchema>(\n name: string,\n options: CookiePrimitiveOptions<T>,\n data: Static<T>,\n contextCookies?: Cookies,\n ): void {\n const cookies = this.getCookiesFromContext(contextCookies);\n let value = JSON.stringify(this.alepha.codec.decode(options.schema, data));\n\n if (options.compress) {\n value = deflateRawSync(value).toString(\"base64\");\n }\n\n if (options.encrypt) {\n value = this.encrypt(value);\n }\n\n if (options.sign) {\n value = this.sign(value) + value;\n }\n\n const cookie: Cookie = {\n value: encodeURIComponent(value),\n path: options.path ?? \"/\",\n // \"lax\" is correct here — \"strict\" would break OAuth flows because OAuth callbacks\n // are top-level redirects from the IdP, and strict cookies are not sent on cross-site\n // top-level navigations. \"lax\" is Chrome's own default and the industry standard.\n sameSite: options.sameSite ?? \"lax\",\n secure: options.secure ?? this.alepha.isProduction(),\n httpOnly: options.httpOnly,\n domain: options.domain,\n };\n\n if (options.ttl) {\n cookie.maxAge = this.dateTimeProvider.duration(options.ttl).as(\"seconds\");\n }\n\n cookies.res[this.prefixName(name, options.prefix !== false)] = cookie;\n }\n\n public deleteCookie<T extends TSchema>(\n name: string,\n contextCookies?: Cookies,\n prefix = true,\n ): void {\n const cookies = this.getCookiesFromContext(contextCookies);\n cookies.res[this.prefixName(name, prefix)] = null;\n }\n\n // --- Crypto & Parsing ---\n\n protected encrypt(text: string): string {\n const iv = randomBytes(this.IV_LENGTH);\n const cipher = createCipheriv(this.ALGORITHM, this.deriveKey(), iv);\n const encrypted = Buffer.concat([\n cipher.update(text, \"utf8\"),\n cipher.final(),\n ]);\n const authTag = cipher.getAuthTag();\n return Buffer.concat([iv, authTag, encrypted]).toString(\"base64\");\n }\n\n protected decrypt(encryptedText: string): string {\n const data = Buffer.from(encryptedText, \"base64\");\n const iv = data.subarray(0, this.IV_LENGTH);\n const authTag = data.subarray(\n this.IV_LENGTH,\n this.IV_LENGTH + this.AUTH_TAG_LENGTH,\n );\n\n const encrypted = data.subarray(this.IV_LENGTH + this.AUTH_TAG_LENGTH);\n const decipher = createDecipheriv(this.ALGORITHM, this.deriveKey(), iv);\n\n decipher.setAuthTag(authTag);\n\n const decrypted = Buffer.concat([\n decipher.update(encrypted),\n decipher.final(),\n ]);\n\n return decrypted.toString(\"utf8\");\n }\n\n /**\n * Derives a 32-byte key from APP_SECRET via SHA-256.\n * Accepts any string length — no padding or truncation needed.\n */\n protected deriveKey(): Buffer {\n return createHash(\"sha256\").update(this.secretProvider.secretKey).digest();\n }\n\n protected sign(data: string): string {\n return createHmac(\"sha256\", this.deriveKey()).update(data).digest(\"hex\");\n }\n}\n","import {\n $inject,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { ServerCookiesProvider } from \"../providers/ServerCookiesProvider.ts\";\n\n/**\n * Declares a type-safe, configurable HTTP cookie.\n * This primitive provides methods to get, set, and delete the cookie\n * within the server request/response cycle.\n */\nexport const $cookie = <T extends TSchema>(\n options: CookiePrimitiveOptions<T>,\n extendedOptions?: Omit<CookiePrimitiveOptions<T>, \"schema\">,\n): AbstractCookiePrimitive<T> => {\n if (extendedOptions) {\n options = {\n key: options.key,\n schema: options.schema,\n ...extendedOptions,\n };\n }\n return createPrimitive(CookiePrimitive<T>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface CookiePrimitiveOptions<T extends TSchema> {\n /**\n * The schema for the cookie's value, used for validation and type safety.\n */\n schema: T;\n\n /**\n * The name of the cookie.\n */\n name?: string;\n\n /**\n * The cookie's path. Defaults to \"/\".\n */\n path?: string;\n\n /**\n * Time-to-live for the cookie. Maps to `Max-Age`.\n */\n ttl?: DurationLike;\n\n /**\n * If true, the cookie is only sent over HTTPS. Defaults to true in production.\n */\n secure?: boolean;\n\n /**\n * If true, the cookie cannot be accessed by client-side scripts.\n */\n httpOnly?: boolean;\n\n /**\n * SameSite policy for the cookie. Defaults to \"lax\".\n */\n sameSite?: \"strict\" | \"lax\" | \"none\";\n\n /**\n * The domain for the cookie.\n */\n domain?: string;\n\n /**\n * If true, the cookie value will be compressed using zlib.\n */\n compress?: boolean;\n\n /**\n * If true, the cookie value will be encrypted. Requires `COOKIE_SECRET` env var.\n */\n encrypt?: boolean;\n\n /**\n * If true, the cookie will be signed to prevent tampering. Requires `COOKIE_SECRET` env var.\n */\n sign?: boolean;\n\n /**\n * Optional key to link this cookie to an Atom, enabling automatic synchronization between the cookie and the Atom's state.\n */\n key?: string;\n\n /**\n * Internal escape hatch: when explicitly set to `false`, the server skips\n * the `APP_NAME` cookie-name namespace applied by\n * `ServerCookiesProvider.prefixName()`.\n *\n * Used by `AtomCookiePersistence` so the cookie name the server\n * reads/writes for a `persist: \"cookie\"` atom matches the bare name its\n * browser variant uses — `APP_NAME` is not reachable client-side (not\n * baked into the client bundle, not part of SSR hydration), so the\n * browser can only ever use the bare atom key.\n *\n * Application code declaring `$cookie(...)` directly should leave this\n * unset — it defaults to `true` (prefixed), matching every other cookie.\n */\n prefix?: boolean;\n}\n\nexport interface AbstractCookiePrimitive<T extends TSchema> {\n readonly name: string;\n readonly options: CookiePrimitiveOptions<T>;\n set(\n value: Static<T>,\n options?: { cookies?: Cookies; ttl?: DurationLike },\n ): void;\n get(options?: { cookies?: Cookies }): Static<T> | undefined;\n del(options?: { cookies?: Cookies }): void;\n}\n\nexport class CookiePrimitive<T extends TSchema>\n extends Primitive<CookiePrimitiveOptions<T>>\n implements AbstractCookiePrimitive<T>\n{\n protected readonly serverCookiesProvider = $inject(ServerCookiesProvider);\n\n protected onInit() {\n if (this.options.key) {\n this.alepha.events.on(\"state:mutate\", ({ key, value }) => {\n if (key === this.options.key) {\n this.set(value);\n }\n });\n this.alepha.events.on(\"server:onRequest\", ({ request }) => {\n try {\n const value = this.get(request);\n if (value !== undefined) {\n this.alepha.store.set(this.options.key as any, value, {\n skipEvents: true,\n });\n }\n } catch {}\n });\n }\n }\n\n public get schema(): T {\n return this.options.schema;\n }\n\n public get name(): string {\n return this.options.name ?? `${this.config.propertyKey}`;\n }\n\n /**\n * Sets the cookie with the given value in the current request's response.\n */\n public set(\n value: Static<T>,\n options?: { cookies?: Cookies; ttl?: DurationLike },\n ): void {\n this.serverCookiesProvider.setCookie(\n this.name,\n {\n ...this.options,\n ttl: options?.ttl ?? this.options.ttl,\n },\n value,\n options?.cookies,\n );\n }\n\n /**\n * Gets the cookie value from the current request. Returns undefined if not found or invalid.\n */\n public get(options?: { cookies?: Cookies }): Static<T> | undefined {\n return this.serverCookiesProvider.getCookie(\n this.name,\n this.options,\n options?.cookies,\n );\n }\n\n /**\n * Deletes the cookie in the current request's response.\n */\n public del(options?: { cookies?: Cookies }): void {\n this.serverCookiesProvider.deleteCookie(this.name, options?.cookies);\n }\n}\n\n$cookie[KIND] = CookiePrimitive;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface Cookies {\n req: Record<string, string>;\n res: Record<string, Cookie | null>;\n}\n\nexport interface Cookie {\n value: string;\n path?: string;\n maxAge?: number;\n secure?: boolean;\n httpOnly?: boolean;\n sameSite?: \"strict\" | \"lax\" | \"none\";\n domain?: string;\n}\n","import { $hook, $inject, Alepha, type Atom, type State } from \"alepha\";\nimport type { DurationLike } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { Cookies } from \"../primitives/$cookie.ts\";\nimport { ServerCookiesProvider } from \"./ServerCookiesProvider.ts\";\n\n/**\n * Binds every atom declared with `persist: \"cookie\"` to an HTTP cookie.\n *\n * - `server:onRequest` — seeds the request-scoped state from the cookie, so\n * SSR renders with the persisted value.\n * - `state:mutate` — writes the new value back as a Set-Cookie header.\n * - `state:register` — an atom registering lazily *during* a request (first\n * touched by an SSR render, after `server:onRequest` already ran) reads\n * its cookie right away, so that render still sees the persisted value.\n *\n * Atoms are resolved from the `StateManager` registry\n * (`alepha.store.listAtoms()`) on every request and every mutation, never\n * from a map built up from `state:register` events. That event fires exactly\n * once per atom and is never replayed, while `$module.register()` registers\n * `atoms[]` BEFORE it wires `imports[]` and injects `services[]` — so the\n * documented `$module({ atoms, imports: [AlephaServerCookies], services })`\n * shape registers every atom before this provider exists. An event-sourced\n * map would stay empty forever there, making `persist: \"cookie\"` a silent\n * no-op in both directions. Reading the registry on demand is\n * order-independent.\n *\n * The cookie is named after the atom key, lives 365 days, SameSite=lax,\n * path \"/\". For custom cookie options (encryption, signing, custom TTL),\n * declare an explicit `$cookie({ key: atom.key, ... })` binding instead.\n */\nexport class AtomCookiePersistence {\n protected readonly alepha = $inject(Alepha);\n protected readonly log = $logger();\n protected readonly serverCookies = $inject(ServerCookiesProvider);\n\n protected readonly onRequest = $hook({\n on: \"server:onRequest\",\n // Explicit dependency instead of relying on `services` array order in\n // `AlephaServerCookies`: this hook needs `request.cookies`, which is\n // only populated by `ServerCookiesProvider.onRequest`. If that ever ran\n // second, `getCookiesFromContext` would throw, get caught, and log at\n // debug — cookies silently ignored in production with no visible error.\n after: [this.serverCookies],\n handler: ({ request }) => {\n // `alepha.http.request` is not set in the store yet at this point in\n // the request lifecycle (`ServerRouterProvider` sets it only after\n // `server:onRequest` resolves), so the request-scoped cookie jar must\n // be passed explicitly rather than resolved from context.\n for (const atom of this.cookieAtoms()) {\n this.read(atom, request.cookies);\n }\n },\n });\n\n protected readonly onRegister = $hook({\n on: \"state:register\",\n handler: ({ atom }) => {\n if (atom.options.persist !== \"cookie\") {\n return;\n }\n\n // Only the lazy, mid-request case is handled here — every atom already\n // registered when the request started was swept by `onRequest` above.\n if (this.alepha.store.get(\"alepha.http.request\")) {\n this.read(atom);\n }\n },\n });\n\n protected readonly onMutate = $hook({\n on: \"state:mutate\",\n handler: ({ key, value }) => {\n const atom = this.alepha.store.getAtom(String(key));\n if (!atom || atom.options.persist !== \"cookie\") {\n return;\n }\n try {\n this.serverCookies.setCookie(atom.key, this.cookieOptions(atom), value);\n } catch (error) {\n // Outside a request cycle there is no response to attach the\n // Set-Cookie header to; browser-side writes are handled by the\n // browser variant of this provider.\n this.log.debug(`Cannot persist atom \"${atom.key}\" to cookie`, {\n error,\n });\n }\n },\n });\n\n /**\n * Every registered atom declared with `persist: \"cookie\"`, read live from\n * the state manager's registry.\n */\n protected cookieAtoms(): Array<Atom<any, any>> {\n return this.alepha.store\n .listAtoms()\n .filter((atom) => atom.options.persist === \"cookie\");\n }\n\n protected read(atom: Atom<any, any>, cookies?: Cookies): void {\n // Safety net, not a live bug today: an ALS provider is always installed\n // by `core/index.ts` / `core/index.workerd.ts`, so every request already\n // runs inside its own fork. But `store.set(..., { skipEvents: true })`\n // below has no `skipContext`, so if no fork were active,\n // `StateManager.set()` would fall through to writing the APP-LEVEL\n // store — turning one user's cookie value into what every subsequent\n // cookieless request reads. Degrade to \"cookie ignored\" instead.\n if (!this.alepha.context.exists()) {\n this.log.debug(\n `Skipping cookie read for atom \"${atom.key}\": no active request context.`,\n );\n return;\n }\n\n try {\n const value = this.serverCookies.getCookie(\n atom.key,\n this.cookieOptions(atom),\n cookies,\n );\n if (value !== undefined) {\n this.alepha.store.set(atom.key as keyof State, value, {\n skipEvents: true,\n });\n }\n } catch (error) {\n this.log.debug(`Cannot read cookie for atom \"${atom.key}\"`, { error });\n }\n }\n\n protected cookieOptions(atom: Atom<any, any>) {\n return {\n schema: atom.schema,\n path: \"/\",\n sameSite: \"lax\" as const,\n ttl: [365, \"days\"] as DurationLike,\n // The browser variant of this adapter cannot see APP_NAME (not baked\n // into the client bundle, not part of SSR hydration — see\n // AtomCookiePersistence.browser.ts), so it always reads/writes the\n // bare atom key. Skip the APP_NAME prefix here too — specifically for\n // atom-persisted cookies — so both sides agree on the same cookie\n // name. Every other server cookie ($cookie primitive) still gets the\n // namespace; see ServerCookiesProvider.prefixName.\n prefix: false,\n };\n }\n}\n","import { $module } from \"alepha\";\nimport { AlephaServer } from \"alepha/server\";\nimport { $cookie, type Cookies } from \"./primitives/$cookie.ts\";\nimport { AtomCookiePersistence } from \"./providers/AtomCookiePersistence.ts\";\nimport { ServerCookiesProvider } from \"./providers/ServerCookiesProvider.ts\";\nimport { CookieParser } from \"./services/CookieParser.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$cookie.ts\";\nexport * from \"./providers/AtomCookiePersistence.ts\";\nexport * from \"./providers/ServerCookiesProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\ndeclare module \"alepha/server\" {\n interface ServerRequest {\n cookies: Cookies;\n }\n}\n\n/**\n * Server and browser-safe cookie handling.\n *\n * **Features:**\n * - Cookie management on server and browser\n *\n * @module alepha.server.cookies\n */\nexport const AlephaServerCookies = $module({\n name: \"alepha.server.cookies\",\n primitives: [$cookie],\n services: [\n AlephaServer,\n ServerCookiesProvider,\n CookieParser,\n AtomCookiePersistence,\n ],\n});\n"],"mappings":"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"}