alepha 0.15.0 → 0.15.2

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 (551) hide show
  1. package/README.md +43 -98
  2. package/dist/api/audits/index.d.ts +630 -653
  3. package/dist/api/audits/index.d.ts.map +1 -1
  4. package/dist/api/audits/index.js +12 -35
  5. package/dist/api/audits/index.js.map +1 -1
  6. package/dist/api/files/index.d.ts +365 -358
  7. package/dist/api/files/index.d.ts.map +1 -1
  8. package/dist/api/files/index.js +12 -5
  9. package/dist/api/files/index.js.map +1 -1
  10. package/dist/api/jobs/index.d.ts +255 -248
  11. package/dist/api/jobs/index.d.ts.map +1 -1
  12. package/dist/api/jobs/index.js +10 -3
  13. package/dist/api/jobs/index.js.map +1 -1
  14. package/dist/api/keys/index.d.ts +413 -0
  15. package/dist/api/keys/index.d.ts.map +1 -0
  16. package/dist/api/keys/index.js +476 -0
  17. package/dist/api/keys/index.js.map +1 -0
  18. package/dist/api/notifications/index.browser.js +4 -4
  19. package/dist/api/notifications/index.browser.js.map +1 -1
  20. package/dist/api/notifications/index.d.ts +84 -78
  21. package/dist/api/notifications/index.d.ts.map +1 -1
  22. package/dist/api/notifications/index.js +14 -8
  23. package/dist/api/notifications/index.js.map +1 -1
  24. package/dist/api/parameters/index.d.ts +528 -535
  25. package/dist/api/parameters/index.d.ts.map +1 -1
  26. package/dist/api/parameters/index.js +30 -37
  27. package/dist/api/parameters/index.js.map +1 -1
  28. package/dist/api/users/index.d.ts +1221 -910
  29. package/dist/api/users/index.d.ts.map +1 -1
  30. package/dist/api/users/index.js +2556 -248
  31. package/dist/api/users/index.js.map +1 -1
  32. package/dist/api/verifications/index.d.ts +142 -136
  33. package/dist/api/verifications/index.d.ts.map +1 -1
  34. package/dist/api/verifications/index.js +12 -4
  35. package/dist/api/verifications/index.js.map +1 -1
  36. package/dist/batch/index.d.ts +142 -162
  37. package/dist/batch/index.d.ts.map +1 -1
  38. package/dist/batch/index.js +31 -44
  39. package/dist/batch/index.js.map +1 -1
  40. package/dist/bucket/index.d.ts +595 -171
  41. package/dist/bucket/index.d.ts.map +1 -1
  42. package/dist/bucket/index.js +1856 -12
  43. package/dist/bucket/index.js.map +1 -1
  44. package/dist/cache/core/index.d.ts +225 -53
  45. package/dist/cache/core/index.d.ts.map +1 -1
  46. package/dist/cache/core/index.js +213 -7
  47. package/dist/cache/core/index.js.map +1 -1
  48. package/dist/cache/redis/index.d.ts +1 -0
  49. package/dist/cache/redis/index.d.ts.map +1 -1
  50. package/dist/cache/redis/index.js +6 -2
  51. package/dist/cache/redis/index.js.map +1 -1
  52. package/dist/cli/index.d.ts +834 -226
  53. package/dist/cli/index.d.ts.map +1 -1
  54. package/dist/cli/index.js +2872 -417
  55. package/dist/cli/index.js.map +1 -1
  56. package/dist/command/index.d.ts +458 -310
  57. package/dist/command/index.d.ts.map +1 -1
  58. package/dist/command/index.js +2011 -76
  59. package/dist/command/index.js.map +1 -1
  60. package/dist/core/index.browser.js +309 -97
  61. package/dist/core/index.browser.js.map +1 -1
  62. package/dist/core/index.d.ts +796 -701
  63. package/dist/core/index.d.ts.map +1 -1
  64. package/dist/core/index.js +329 -97
  65. package/dist/core/index.js.map +1 -1
  66. package/dist/core/index.native.js +309 -97
  67. package/dist/core/index.native.js.map +1 -1
  68. package/dist/datetime/index.d.ts +59 -44
  69. package/dist/datetime/index.d.ts.map +1 -1
  70. package/dist/datetime/index.js +15 -0
  71. package/dist/datetime/index.js.map +1 -1
  72. package/dist/email/index.d.ts +314 -19
  73. package/dist/email/index.d.ts.map +1 -1
  74. package/dist/email/index.js +1852 -7
  75. package/dist/email/index.js.map +1 -1
  76. package/dist/fake/index.d.ts +5500 -5418
  77. package/dist/fake/index.d.ts.map +1 -1
  78. package/dist/fake/index.js +113 -42
  79. package/dist/fake/index.js.map +1 -1
  80. package/dist/lock/core/index.d.ts +219 -212
  81. package/dist/lock/core/index.d.ts.map +1 -1
  82. package/dist/lock/core/index.js +11 -4
  83. package/dist/lock/core/index.js.map +1 -1
  84. package/dist/lock/redis/index.d.ts.map +1 -1
  85. package/dist/logger/index.d.ts +41 -90
  86. package/dist/logger/index.d.ts.map +1 -1
  87. package/dist/logger/index.js +15 -68
  88. package/dist/logger/index.js.map +1 -1
  89. package/dist/mcp/index.d.ts +228 -230
  90. package/dist/mcp/index.d.ts.map +1 -1
  91. package/dist/mcp/index.js +32 -31
  92. package/dist/mcp/index.js.map +1 -1
  93. package/dist/orm/index.browser.js +12 -12
  94. package/dist/orm/index.browser.js.map +1 -1
  95. package/dist/orm/index.bun.js +90 -80
  96. package/dist/orm/index.bun.js.map +1 -1
  97. package/dist/orm/index.d.ts +1434 -1459
  98. package/dist/orm/index.d.ts.map +1 -1
  99. package/dist/orm/index.js +112 -130
  100. package/dist/orm/index.js.map +1 -1
  101. package/dist/queue/core/index.d.ts +262 -254
  102. package/dist/queue/core/index.d.ts.map +1 -1
  103. package/dist/queue/core/index.js +14 -6
  104. package/dist/queue/core/index.js.map +1 -1
  105. package/dist/queue/redis/index.d.ts.map +1 -1
  106. package/dist/react/auth/index.browser.js +108 -0
  107. package/dist/react/auth/index.browser.js.map +1 -0
  108. package/dist/react/auth/index.d.ts +100 -0
  109. package/dist/react/auth/index.d.ts.map +1 -0
  110. package/dist/react/auth/index.js +145 -0
  111. package/dist/react/auth/index.js.map +1 -0
  112. package/dist/react/core/index.d.ts +469 -0
  113. package/dist/react/core/index.d.ts.map +1 -0
  114. package/dist/react/core/index.js +464 -0
  115. package/dist/react/core/index.js.map +1 -0
  116. package/dist/react/form/index.d.ts +232 -0
  117. package/dist/react/form/index.d.ts.map +1 -0
  118. package/dist/react/form/index.js +432 -0
  119. package/dist/react/form/index.js.map +1 -0
  120. package/dist/react/head/index.browser.js +423 -0
  121. package/dist/react/head/index.browser.js.map +1 -0
  122. package/dist/react/head/index.d.ts +288 -0
  123. package/dist/react/head/index.d.ts.map +1 -0
  124. package/dist/react/head/index.js +465 -0
  125. package/dist/react/head/index.js.map +1 -0
  126. package/dist/react/i18n/index.d.ts +175 -0
  127. package/dist/react/i18n/index.d.ts.map +1 -0
  128. package/dist/react/i18n/index.js +224 -0
  129. package/dist/react/i18n/index.js.map +1 -0
  130. package/dist/react/router/index.browser.js +1980 -0
  131. package/dist/react/router/index.browser.js.map +1 -0
  132. package/dist/react/router/index.d.ts +2068 -0
  133. package/dist/react/router/index.d.ts.map +1 -0
  134. package/dist/react/router/index.js +4932 -0
  135. package/dist/react/router/index.js.map +1 -0
  136. package/dist/react/websocket/index.d.ts +117 -0
  137. package/dist/react/websocket/index.d.ts.map +1 -0
  138. package/dist/react/websocket/index.js +107 -0
  139. package/dist/react/websocket/index.js.map +1 -0
  140. package/dist/redis/index.bun.js +4 -0
  141. package/dist/redis/index.bun.js.map +1 -1
  142. package/dist/redis/index.d.ts +127 -130
  143. package/dist/redis/index.d.ts.map +1 -1
  144. package/dist/redis/index.js +16 -25
  145. package/dist/redis/index.js.map +1 -1
  146. package/dist/retry/index.d.ts +80 -71
  147. package/dist/retry/index.d.ts.map +1 -1
  148. package/dist/retry/index.js +11 -2
  149. package/dist/retry/index.js.map +1 -1
  150. package/dist/router/index.d.ts +6 -6
  151. package/dist/router/index.d.ts.map +1 -1
  152. package/dist/scheduler/index.d.ts +119 -28
  153. package/dist/scheduler/index.d.ts.map +1 -1
  154. package/dist/scheduler/index.js +404 -3
  155. package/dist/scheduler/index.js.map +1 -1
  156. package/dist/security/index.d.ts +642 -228
  157. package/dist/security/index.d.ts.map +1 -1
  158. package/dist/security/index.js +1579 -37
  159. package/dist/security/index.js.map +1 -1
  160. package/dist/server/auth/index.d.ts +1141 -111
  161. package/dist/server/auth/index.d.ts.map +1 -1
  162. package/dist/server/auth/index.js +1261 -25
  163. package/dist/server/auth/index.js.map +1 -1
  164. package/dist/server/cache/index.d.ts +63 -78
  165. package/dist/server/cache/index.d.ts.map +1 -1
  166. package/dist/server/cache/index.js +7 -22
  167. package/dist/server/cache/index.js.map +1 -1
  168. package/dist/server/compress/index.d.ts +13 -5
  169. package/dist/server/compress/index.d.ts.map +1 -1
  170. package/dist/server/compress/index.js +10 -2
  171. package/dist/server/compress/index.js.map +1 -1
  172. package/dist/server/cookies/index.d.ts +46 -22
  173. package/dist/server/cookies/index.d.ts.map +1 -1
  174. package/dist/server/cookies/index.js +7 -5
  175. package/dist/server/cookies/index.js.map +1 -1
  176. package/dist/server/core/index.d.ts +307 -196
  177. package/dist/server/core/index.d.ts.map +1 -1
  178. package/dist/server/core/index.js +271 -38
  179. package/dist/server/core/index.js.map +1 -1
  180. package/dist/server/cors/index.d.ts +24 -34
  181. package/dist/server/cors/index.d.ts.map +1 -1
  182. package/dist/server/cors/index.js +7 -21
  183. package/dist/server/cors/index.js.map +1 -1
  184. package/dist/server/health/index.d.ts +25 -19
  185. package/dist/server/health/index.d.ts.map +1 -1
  186. package/dist/server/health/index.js +8 -2
  187. package/dist/server/health/index.js.map +1 -1
  188. package/dist/server/helmet/index.d.ts +13 -5
  189. package/dist/server/helmet/index.d.ts.map +1 -1
  190. package/dist/server/helmet/index.js +11 -3
  191. package/dist/server/helmet/index.js.map +1 -1
  192. package/dist/server/links/index.browser.js +9 -1
  193. package/dist/server/links/index.browser.js.map +1 -1
  194. package/dist/server/links/index.d.ts +133 -128
  195. package/dist/server/links/index.d.ts.map +1 -1
  196. package/dist/server/links/index.js +24 -11
  197. package/dist/server/links/index.js.map +1 -1
  198. package/dist/server/metrics/index.d.ts +524 -4
  199. package/dist/server/metrics/index.d.ts.map +1 -1
  200. package/dist/server/metrics/index.js +4472 -7
  201. package/dist/server/metrics/index.js.map +1 -1
  202. package/dist/server/multipart/index.d.ts +15 -9
  203. package/dist/server/multipart/index.d.ts.map +1 -1
  204. package/dist/server/multipart/index.js +9 -3
  205. package/dist/server/multipart/index.js.map +1 -1
  206. package/dist/server/proxy/index.d.ts +110 -104
  207. package/dist/server/proxy/index.d.ts.map +1 -1
  208. package/dist/server/proxy/index.js +8 -2
  209. package/dist/server/proxy/index.js.map +1 -1
  210. package/dist/server/rate-limit/index.d.ts +46 -51
  211. package/dist/server/rate-limit/index.d.ts.map +1 -1
  212. package/dist/server/rate-limit/index.js +18 -55
  213. package/dist/server/rate-limit/index.js.map +1 -1
  214. package/dist/server/static/index.d.ts +181 -48
  215. package/dist/server/static/index.d.ts.map +1 -1
  216. package/dist/server/static/index.js +1848 -5
  217. package/dist/server/static/index.js.map +1 -1
  218. package/dist/server/swagger/index.d.ts +348 -53
  219. package/dist/server/swagger/index.d.ts.map +1 -1
  220. package/dist/server/swagger/index.js +1849 -6
  221. package/dist/server/swagger/index.js.map +1 -1
  222. package/dist/sms/index.d.ts +312 -18
  223. package/dist/sms/index.d.ts.map +1 -1
  224. package/dist/sms/index.js +1854 -10
  225. package/dist/sms/index.js.map +1 -1
  226. package/dist/system/index.browser.js +496 -0
  227. package/dist/system/index.browser.js.map +1 -0
  228. package/dist/system/index.d.ts +1158 -0
  229. package/dist/system/index.d.ts.map +1 -0
  230. package/dist/{file → system}/index.js +412 -20
  231. package/dist/system/index.js.map +1 -0
  232. package/dist/thread/index.d.ts +82 -73
  233. package/dist/thread/index.d.ts.map +1 -1
  234. package/dist/thread/index.js +13 -4
  235. package/dist/thread/index.js.map +1 -1
  236. package/dist/topic/core/index.d.ts +330 -323
  237. package/dist/topic/core/index.d.ts.map +1 -1
  238. package/dist/topic/core/index.js +12 -5
  239. package/dist/topic/core/index.js.map +1 -1
  240. package/dist/topic/redis/index.d.ts +6 -6
  241. package/dist/topic/redis/index.d.ts.map +1 -1
  242. package/dist/vite/index.d.ts +163 -5825
  243. package/dist/vite/index.d.ts.map +1 -1
  244. package/dist/vite/index.js +130 -477
  245. package/dist/vite/index.js.map +1 -1
  246. package/dist/websocket/index.browser.js +3 -3
  247. package/dist/websocket/index.browser.js.map +1 -1
  248. package/dist/websocket/index.d.ts +287 -283
  249. package/dist/websocket/index.d.ts.map +1 -1
  250. package/dist/websocket/index.js +15 -11
  251. package/dist/websocket/index.js.map +1 -1
  252. package/package.json +86 -17
  253. package/src/api/audits/index.ts +10 -33
  254. package/src/api/files/__tests__/$bucket.spec.ts +1 -1
  255. package/src/api/files/controllers/AdminFileStatsController.spec.ts +1 -1
  256. package/src/api/files/controllers/FileController.spec.ts +1 -1
  257. package/src/api/files/index.ts +10 -3
  258. package/src/api/files/jobs/FileJobs.spec.ts +1 -1
  259. package/src/api/files/services/FileService.spec.ts +1 -1
  260. package/src/api/jobs/index.ts +10 -3
  261. package/src/api/keys/controllers/AdminApiKeyController.ts +75 -0
  262. package/src/api/keys/controllers/ApiKeyController.ts +103 -0
  263. package/src/api/keys/entities/apiKeyEntity.ts +41 -0
  264. package/src/api/keys/index.ts +49 -0
  265. package/src/api/keys/schemas/adminApiKeyQuerySchema.ts +7 -0
  266. package/src/api/keys/schemas/adminApiKeyResourceSchema.ts +17 -0
  267. package/src/api/keys/schemas/createApiKeyBodySchema.ts +7 -0
  268. package/src/api/keys/schemas/createApiKeyResponseSchema.ts +11 -0
  269. package/src/api/keys/schemas/listApiKeyResponseSchema.ts +15 -0
  270. package/src/api/keys/schemas/revokeApiKeyParamsSchema.ts +5 -0
  271. package/src/api/keys/schemas/revokeApiKeyResponseSchema.ts +5 -0
  272. package/src/api/keys/services/ApiKeyService.spec.ts +553 -0
  273. package/src/api/keys/services/ApiKeyService.ts +306 -0
  274. package/src/api/logs/TODO.md +52 -0
  275. package/src/api/notifications/index.ts +10 -4
  276. package/src/api/parameters/index.ts +9 -30
  277. package/src/api/parameters/primitives/$config.ts +12 -4
  278. package/src/api/parameters/services/ConfigStore.ts +9 -3
  279. package/src/api/users/__tests__/ApiKeys-integration.spec.ts +1035 -0
  280. package/src/api/users/__tests__/ApiKeys.spec.ts +401 -0
  281. package/src/api/users/index.ts +14 -3
  282. package/src/api/users/primitives/$realm.ts +33 -5
  283. package/src/api/users/providers/RealmProvider.ts +1 -12
  284. package/src/api/users/services/SessionService.ts +1 -11
  285. package/src/api/verifications/controllers/VerificationController.ts +2 -0
  286. package/src/api/verifications/index.ts +10 -4
  287. package/src/batch/index.ts +9 -36
  288. package/src/batch/primitives/$batch.ts +0 -8
  289. package/src/batch/providers/BatchProvider.ts +29 -2
  290. package/src/bucket/__tests__/shared.ts +1 -1
  291. package/src/bucket/index.ts +13 -6
  292. package/src/bucket/primitives/$bucket.ts +1 -1
  293. package/src/bucket/providers/LocalFileStorageProvider.ts +1 -1
  294. package/src/bucket/providers/MemoryFileStorageProvider.ts +1 -1
  295. package/src/cache/core/__tests__/shared.ts +30 -0
  296. package/src/cache/core/index.ts +11 -6
  297. package/src/cache/core/primitives/$cache.spec.ts +5 -0
  298. package/src/cache/core/providers/CacheProvider.ts +17 -0
  299. package/src/cache/core/providers/MemoryCacheProvider.ts +300 -1
  300. package/src/cache/redis/__tests__/cache-redis.spec.ts +5 -0
  301. package/src/cache/redis/providers/RedisCacheProvider.ts +9 -0
  302. package/src/cli/apps/AlephaCli.ts +3 -16
  303. package/src/cli/apps/AlephaPackageBuilderCli.ts +10 -2
  304. package/src/cli/atoms/appEntryOptions.ts +13 -0
  305. package/src/cli/atoms/buildOptions.ts +1 -1
  306. package/src/cli/atoms/changelogOptions.ts +1 -1
  307. package/src/cli/commands/build.ts +64 -52
  308. package/src/cli/commands/db.ts +17 -11
  309. package/src/cli/commands/deploy.ts +1 -1
  310. package/src/cli/commands/dev.ts +13 -49
  311. package/src/cli/commands/gen/env.ts +6 -3
  312. package/src/cli/commands/gen/openapi.ts +5 -2
  313. package/src/cli/commands/init.spec.ts +544 -0
  314. package/src/cli/commands/init.ts +101 -58
  315. package/src/cli/commands/lint.ts +8 -2
  316. package/src/cli/commands/typecheck.ts +11 -0
  317. package/src/cli/defineConfig.ts +9 -0
  318. package/src/cli/index.ts +2 -1
  319. package/src/cli/providers/AppEntryProvider.ts +131 -0
  320. package/src/cli/providers/ViteBuildProvider.ts +40 -0
  321. package/src/cli/providers/ViteDevServerProvider.ts +378 -0
  322. package/src/cli/services/AlephaCliUtils.ts +39 -93
  323. package/src/cli/services/PackageManagerUtils.ts +140 -17
  324. package/src/cli/services/ProjectScaffolder.ts +169 -101
  325. package/src/cli/services/ViteUtils.ts +82 -0
  326. package/src/cli/{assets/claudeMd.ts → templates/agentMd.ts} +41 -28
  327. package/src/cli/{assets → templates}/apiHelloControllerTs.ts +2 -1
  328. package/src/cli/{assets → templates}/biomeJson.ts +2 -1
  329. package/src/cli/{assets → templates}/dummySpecTs.ts +2 -1
  330. package/src/cli/{assets → templates}/editorconfig.ts +2 -1
  331. package/src/cli/templates/gitignore.ts +39 -0
  332. package/src/cli/{assets → templates}/mainBrowserTs.ts +2 -1
  333. package/src/cli/templates/mainCss.ts +33 -0
  334. package/src/cli/templates/mainServerTs.ts +33 -0
  335. package/src/cli/{assets → templates}/tsconfigJson.ts +2 -1
  336. package/src/cli/templates/webAppRouterTs.ts +50 -0
  337. package/src/cli/templates/webHelloComponentTsx.ts +20 -0
  338. package/src/command/helpers/Runner.spec.ts +4 -0
  339. package/src/command/helpers/Runner.ts +3 -21
  340. package/src/command/index.ts +12 -4
  341. package/src/command/providers/CliProvider.spec.ts +1067 -0
  342. package/src/command/providers/CliProvider.ts +203 -40
  343. package/src/core/Alepha.ts +3 -9
  344. package/src/core/__tests__/Alepha-start.spec.ts +4 -4
  345. package/src/core/helpers/jsonSchemaToTypeBox.spec.ts +771 -0
  346. package/src/core/helpers/jsonSchemaToTypeBox.ts +62 -10
  347. package/src/core/index.shared.ts +1 -0
  348. package/src/core/index.ts +20 -0
  349. package/src/core/primitives/$module.ts +12 -0
  350. package/src/core/providers/EventManager.spec.ts +0 -71
  351. package/src/core/providers/EventManager.ts +3 -15
  352. package/src/core/providers/Json.ts +2 -14
  353. package/src/core/providers/KeylessJsonSchemaCodec.spec.ts +257 -0
  354. package/src/core/providers/KeylessJsonSchemaCodec.ts +396 -14
  355. package/src/core/providers/SchemaValidator.spec.ts +236 -0
  356. package/src/datetime/index.ts +15 -0
  357. package/src/email/index.ts +10 -5
  358. package/src/email/providers/LocalEmailProvider.spec.ts +1 -1
  359. package/src/email/providers/LocalEmailProvider.ts +1 -1
  360. package/src/fake/__tests__/keyName.example.ts +1 -1
  361. package/src/fake/__tests__/keyName.spec.ts +5 -5
  362. package/src/fake/index.ts +9 -6
  363. package/src/fake/providers/FakeProvider.spec.ts +258 -40
  364. package/src/fake/providers/FakeProvider.ts +133 -19
  365. package/src/lock/core/index.ts +11 -4
  366. package/src/logger/index.ts +17 -66
  367. package/src/logger/providers/PrettyFormatterProvider.ts +0 -9
  368. package/src/mcp/errors/McpError.ts +30 -0
  369. package/src/mcp/index.ts +13 -27
  370. package/src/mcp/transports/SseMcpTransport.ts +6 -7
  371. package/src/orm/__tests__/PostgresProvider.spec.ts +2 -2
  372. package/src/orm/index.browser.ts +2 -2
  373. package/src/orm/index.bun.ts +4 -2
  374. package/src/orm/index.ts +21 -47
  375. package/src/orm/providers/DrizzleKitProvider.ts +3 -5
  376. package/src/orm/providers/drivers/BunSqliteProvider.ts +1 -0
  377. package/src/orm/services/Repository.ts +18 -3
  378. package/src/queue/core/index.ts +14 -6
  379. package/src/react/auth/__tests__/$auth.spec.ts +202 -0
  380. package/src/react/auth/hooks/useAuth.ts +32 -0
  381. package/src/react/auth/index.browser.ts +13 -0
  382. package/src/react/auth/index.shared.ts +2 -0
  383. package/src/react/auth/index.ts +48 -0
  384. package/src/react/auth/providers/ReactAuthProvider.ts +16 -0
  385. package/src/react/auth/services/ReactAuth.ts +135 -0
  386. package/src/react/core/__tests__/Router.spec.tsx +169 -0
  387. package/src/react/core/components/ClientOnly.tsx +49 -0
  388. package/src/react/core/components/ErrorBoundary.tsx +73 -0
  389. package/src/react/core/contexts/AlephaContext.ts +7 -0
  390. package/src/react/core/contexts/AlephaProvider.tsx +42 -0
  391. package/src/react/core/hooks/useAction.browser.spec.tsx +569 -0
  392. package/src/react/core/hooks/useAction.ts +480 -0
  393. package/src/react/core/hooks/useAlepha.ts +26 -0
  394. package/src/react/core/hooks/useClient.ts +17 -0
  395. package/src/react/core/hooks/useEvents.ts +51 -0
  396. package/src/react/core/hooks/useInject.ts +12 -0
  397. package/src/react/core/hooks/useStore.ts +52 -0
  398. package/src/react/core/index.ts +90 -0
  399. package/src/react/form/components/FormState.tsx +17 -0
  400. package/src/react/form/errors/FormValidationError.ts +18 -0
  401. package/src/react/form/hooks/useForm.browser.spec.tsx +366 -0
  402. package/src/react/form/hooks/useForm.ts +47 -0
  403. package/src/react/form/hooks/useFormState.ts +130 -0
  404. package/src/react/form/index.ts +44 -0
  405. package/src/react/form/services/FormModel.ts +614 -0
  406. package/src/react/head/helpers/SeoExpander.spec.ts +203 -0
  407. package/src/react/head/helpers/SeoExpander.ts +142 -0
  408. package/src/react/head/hooks/useHead.spec.tsx +288 -0
  409. package/src/react/head/hooks/useHead.ts +62 -0
  410. package/src/react/head/index.browser.ts +26 -0
  411. package/src/react/head/index.ts +44 -0
  412. package/src/react/head/interfaces/Head.ts +105 -0
  413. package/src/react/head/primitives/$head.ts +25 -0
  414. package/src/react/head/providers/BrowserHeadProvider.browser.spec.ts +196 -0
  415. package/src/react/head/providers/BrowserHeadProvider.ts +212 -0
  416. package/src/react/head/providers/HeadProvider.ts +168 -0
  417. package/src/react/head/providers/ServerHeadProvider.ts +31 -0
  418. package/src/react/i18n/__tests__/integration.spec.tsx +239 -0
  419. package/src/react/i18n/components/Localize.spec.tsx +357 -0
  420. package/src/react/i18n/components/Localize.tsx +35 -0
  421. package/src/react/i18n/hooks/useI18n.browser.spec.tsx +438 -0
  422. package/src/react/i18n/hooks/useI18n.ts +18 -0
  423. package/src/react/i18n/index.ts +41 -0
  424. package/src/react/i18n/primitives/$dictionary.ts +69 -0
  425. package/src/react/i18n/providers/I18nProvider.spec.ts +389 -0
  426. package/src/react/i18n/providers/I18nProvider.ts +278 -0
  427. package/src/react/router/__tests__/page-head-browser.browser.spec.ts +95 -0
  428. package/src/react/router/__tests__/page-head.spec.ts +48 -0
  429. package/src/react/router/__tests__/seo-head.spec.ts +125 -0
  430. package/src/react/router/atoms/ssrManifestAtom.ts +58 -0
  431. package/src/react/router/components/ErrorViewer.tsx +872 -0
  432. package/src/react/router/components/Link.tsx +23 -0
  433. package/src/react/router/components/NestedView.tsx +223 -0
  434. package/src/react/router/components/NotFound.tsx +30 -0
  435. package/src/react/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  436. package/src/react/router/contexts/RouterLayerContext.ts +12 -0
  437. package/src/react/router/errors/Redirection.ts +28 -0
  438. package/src/react/router/hooks/useActive.ts +52 -0
  439. package/src/react/router/hooks/useQueryParams.ts +63 -0
  440. package/src/react/router/hooks/useRouter.ts +20 -0
  441. package/src/react/router/hooks/useRouterState.ts +11 -0
  442. package/src/react/router/index.browser.ts +45 -0
  443. package/src/react/router/index.shared.ts +19 -0
  444. package/src/react/router/index.ts +142 -0
  445. package/src/react/router/primitives/$page.browser.spec.tsx +851 -0
  446. package/src/react/router/primitives/$page.spec.tsx +708 -0
  447. package/src/react/router/primitives/$page.ts +497 -0
  448. package/src/react/router/providers/ReactBrowserProvider.ts +309 -0
  449. package/src/react/router/providers/ReactBrowserRendererProvider.ts +25 -0
  450. package/src/react/router/providers/ReactBrowserRouterProvider.ts +168 -0
  451. package/src/react/router/providers/ReactPageProvider.ts +726 -0
  452. package/src/react/router/providers/ReactServerProvider.spec.tsx +316 -0
  453. package/src/react/router/providers/ReactServerProvider.ts +558 -0
  454. package/src/react/router/providers/ReactServerTemplateProvider.ts +979 -0
  455. package/src/react/router/providers/SSRManifestProvider.ts +334 -0
  456. package/src/react/router/services/ReactPageServerService.ts +48 -0
  457. package/src/react/router/services/ReactPageService.ts +27 -0
  458. package/src/react/router/services/ReactRouter.ts +262 -0
  459. package/src/react/websocket/hooks/useRoom.tsx +242 -0
  460. package/src/react/websocket/index.ts +7 -0
  461. package/src/redis/__tests__/redis.spec.ts +13 -0
  462. package/src/redis/index.ts +9 -25
  463. package/src/redis/providers/BunRedisProvider.ts +9 -0
  464. package/src/redis/providers/NodeRedisProvider.ts +8 -0
  465. package/src/redis/providers/RedisProvider.ts +16 -0
  466. package/src/retry/index.ts +11 -2
  467. package/src/router/index.ts +15 -0
  468. package/src/scheduler/index.ts +11 -2
  469. package/src/security/__tests__/BasicAuth.spec.ts +2 -0
  470. package/src/security/__tests__/ServerSecurityProvider.spec.ts +13 -5
  471. package/src/security/index.ts +15 -10
  472. package/src/security/interfaces/IssuerResolver.ts +27 -0
  473. package/src/security/primitives/$issuer.ts +55 -0
  474. package/src/security/providers/SecurityProvider.ts +179 -0
  475. package/src/security/providers/ServerBasicAuthProvider.ts +6 -2
  476. package/src/security/providers/ServerSecurityProvider.ts +36 -22
  477. package/src/server/auth/index.ts +12 -7
  478. package/src/server/cache/index.ts +7 -22
  479. package/src/server/compress/index.ts +10 -2
  480. package/src/server/cookies/index.ts +7 -5
  481. package/src/server/cookies/primitives/$cookie.ts +33 -11
  482. package/src/server/core/index.ts +17 -7
  483. package/src/server/core/interfaces/ServerRequest.ts +83 -1
  484. package/src/server/core/primitives/$action.spec.ts +1 -1
  485. package/src/server/core/primitives/$action.ts +8 -3
  486. package/src/server/core/providers/BunHttpServerProvider.ts +1 -1
  487. package/src/server/core/providers/NodeHttpServerProvider.spec.ts +125 -0
  488. package/src/server/core/providers/NodeHttpServerProvider.ts +77 -22
  489. package/src/server/core/providers/ServerLoggerProvider.ts +2 -2
  490. package/src/server/core/providers/ServerProvider.ts +9 -12
  491. package/src/server/core/services/ServerRequestParser.spec.ts +520 -0
  492. package/src/server/core/services/ServerRequestParser.ts +306 -13
  493. package/src/server/cors/index.ts +7 -21
  494. package/src/server/cors/primitives/$cors.ts +6 -2
  495. package/src/server/health/index.ts +8 -2
  496. package/src/server/helmet/index.ts +11 -3
  497. package/src/server/links/atoms/apiLinksAtom.ts +7 -0
  498. package/src/server/links/index.browser.ts +2 -0
  499. package/src/server/links/index.ts +13 -6
  500. package/src/server/metrics/index.ts +10 -3
  501. package/src/server/multipart/index.ts +9 -3
  502. package/src/server/proxy/index.ts +8 -2
  503. package/src/server/rate-limit/index.ts +21 -25
  504. package/src/server/rate-limit/primitives/$rateLimit.ts +6 -2
  505. package/src/server/rate-limit/providers/ServerRateLimitProvider.spec.ts +38 -14
  506. package/src/server/rate-limit/providers/ServerRateLimitProvider.ts +22 -56
  507. package/src/server/static/index.ts +8 -2
  508. package/src/server/static/providers/ServerStaticProvider.ts +1 -1
  509. package/src/server/swagger/index.ts +9 -4
  510. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  511. package/src/sms/index.ts +9 -5
  512. package/src/sms/providers/LocalSmsProvider.spec.ts +1 -1
  513. package/src/sms/providers/LocalSmsProvider.ts +1 -1
  514. package/src/system/index.browser.ts +11 -0
  515. package/src/system/index.ts +62 -0
  516. package/src/{file → system}/providers/FileSystemProvider.ts +16 -0
  517. package/src/{file → system}/providers/MemoryFileSystemProvider.ts +116 -3
  518. package/src/system/providers/MemoryShellProvider.ts +164 -0
  519. package/src/{file → system}/providers/NodeFileSystemProvider.spec.ts +2 -2
  520. package/src/{file → system}/providers/NodeFileSystemProvider.ts +36 -0
  521. package/src/system/providers/NodeShellProvider.ts +184 -0
  522. package/src/system/providers/ShellProvider.ts +74 -0
  523. package/src/{file → system}/services/FileDetector.spec.ts +2 -2
  524. package/src/thread/index.ts +11 -2
  525. package/src/topic/core/index.ts +12 -5
  526. package/src/vite/index.ts +3 -2
  527. package/src/vite/tasks/buildClient.ts +2 -8
  528. package/src/vite/tasks/buildServer.ts +84 -21
  529. package/src/vite/tasks/copyAssets.ts +5 -4
  530. package/src/vite/tasks/generateSitemap.ts +64 -23
  531. package/src/vite/tasks/index.ts +0 -2
  532. package/src/vite/tasks/prerenderPages.ts +49 -24
  533. package/src/websocket/index.ts +12 -8
  534. package/dist/file/index.d.ts +0 -839
  535. package/dist/file/index.d.ts.map +0 -1
  536. package/dist/file/index.js.map +0 -1
  537. package/src/cli/assets/indexHtml.ts +0 -15
  538. package/src/cli/assets/mainServerTs.ts +0 -24
  539. package/src/cli/assets/webAppRouterTs.ts +0 -15
  540. package/src/cli/assets/webHelloComponentTsx.ts +0 -16
  541. package/src/cli/commands/format.ts +0 -23
  542. package/src/file/index.ts +0 -43
  543. package/src/vite/helpers/boot.ts +0 -117
  544. package/src/vite/plugins/viteAlephaDev.ts +0 -177
  545. package/src/vite/tasks/devServer.ts +0 -71
  546. package/src/vite/tasks/runAlepha.ts +0 -270
  547. /package/dist/orm/{chunk-DtkW-qnP.js → chunk-DH6iiROE.js} +0 -0
  548. /package/src/cli/{assets → templates}/apiIndexTs.ts +0 -0
  549. /package/src/cli/{assets → templates}/webIndexTs.ts +0 -0
  550. /package/src/{file → system}/errors/FileError.ts +0 -0
  551. /package/src/{file → system}/services/FileDetector.ts +0 -0
@@ -12,19 +12,19 @@ import * as dayjs_plugin_duration_js0 from "dayjs/plugin/duration.js";
12
12
  */
13
13
  declare abstract class LockProvider {
14
14
  /**
15
- * Set the string value of a key.
16
- *
17
- * @param key The key of the value to set.
18
- * @param value The value to set.
19
- * @param nx If set to true, the key will only be set if it does not already exist.
20
- * @param px Set the specified expire time, in milliseconds.
21
- */
15
+ * Set the string value of a key.
16
+ *
17
+ * @param key The key of the value to set.
18
+ * @param value The value to set.
19
+ * @param nx If set to true, the key will only be set if it does not already exist.
20
+ * @param px Set the specified expire time, in milliseconds.
21
+ */
22
22
  abstract set(key: string, value: string, nx?: boolean, px?: number): Promise<string>;
23
23
  /**
24
- * Remove the specified keys.
25
- *
26
- * @param keys The keys to delete.
27
- */
24
+ * Remove the specified keys.
25
+ *
26
+ * @param keys The keys to delete.
27
+ */
28
28
  abstract del(...keys: string[]): Promise<void>;
29
29
  }
30
30
  //#endregion
@@ -84,205 +84,205 @@ declare const $lock: {
84
84
  };
85
85
  interface LockPrimitiveOptions<TFunc extends AsyncFn> {
86
86
  /**
87
- * The function to execute when the lock is successfully acquired.
88
- *
89
- * This function:
90
- * - Only executes on the instance that successfully acquires the lock
91
- * - Has exclusive access to the protected resource during execution
92
- * - Should contain the critical section logic that must not run concurrently
93
- * - Can be async and perform any operations needed
94
- * - Will automatically release the lock upon completion or error
95
- * - Has access to the full Alepha dependency injection container
96
- *
97
- * **Handler Design Guidelines**:
98
- * - Keep critical sections as short as possible to minimize lock contention
99
- * - Include proper error handling to ensure locks are released
100
- * - Use timeouts for external operations to prevent deadlocks
101
- * - Log important operations for debugging and monitoring
102
- * - Consider idempotency for handlers that might be retried
103
- *
104
- * @param ...args - The arguments passed to the lock execution
105
- * @returns Promise that resolves when the protected operation is complete
106
- *
107
- * @example
108
- * ```ts
109
- * handler: async (batchId: string) => {
110
- * console.log(`Processing batch ${batchId} - only one instance will run this`);
111
- *
112
- * const batch = await this.getBatchData(batchId);
113
- * const results = await this.processBatchItems(batch.items);
114
- * await this.saveBatchResults(batchId, results);
115
- *
116
- * console.log(`Batch ${batchId} completed successfully`);
117
- * }
118
- * ```
119
- */
87
+ * The function to execute when the lock is successfully acquired.
88
+ *
89
+ * This function:
90
+ * - Only executes on the instance that successfully acquires the lock
91
+ * - Has exclusive access to the protected resource during execution
92
+ * - Should contain the critical section logic that must not run concurrently
93
+ * - Can be async and perform any operations needed
94
+ * - Will automatically release the lock upon completion or error
95
+ * - Has access to the full Alepha dependency injection container
96
+ *
97
+ * **Handler Design Guidelines**:
98
+ * - Keep critical sections as short as possible to minimize lock contention
99
+ * - Include proper error handling to ensure locks are released
100
+ * - Use timeouts for external operations to prevent deadlocks
101
+ * - Log important operations for debugging and monitoring
102
+ * - Consider idempotency for handlers that might be retried
103
+ *
104
+ * @param ...args - The arguments passed to the lock execution
105
+ * @returns Promise that resolves when the protected operation is complete
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * handler: async (batchId: string) => {
110
+ * console.log(`Processing batch ${batchId} - only one instance will run this`);
111
+ *
112
+ * const batch = await this.getBatchData(batchId);
113
+ * const results = await this.processBatchItems(batch.items);
114
+ * await this.saveBatchResults(batchId, results);
115
+ *
116
+ * console.log(`Batch ${batchId} completed successfully`);
117
+ * }
118
+ * ```
119
+ */
120
120
  handler: TFunc;
121
121
  /**
122
- * Whether the lock should wait for other instances to complete before giving up.
123
- *
124
- * **wait = false (default)**:
125
- * - Non-blocking behavior - if lock is held, immediately return without executing
126
- * - Perfect for scheduled tasks where you only want one execution per trigger
127
- * - Use when multiple triggers are acceptable but concurrent execution is not
128
- * - Examples: periodic cleanup, cron jobs, background maintenance
129
- *
130
- * **wait = true**:
131
- * - Blocking behavior - wait for the current lock holder to finish
132
- * - All instances will eventually execute (one after another)
133
- * - Perfect for initialization tasks where all instances need the work completed
134
- * - Examples: database migrations, cache warming, resource initialization
135
- *
136
- * **Trade-offs**:
137
- * - Non-waiting: Better performance, may miss executions if timing is off
138
- * - Waiting: Guaranteed execution order, slower overall throughput
139
- *
140
- * @default false
141
- *
142
- * @example
143
- * ```ts
144
- * // Scheduled task - don't wait, just skip if already running
145
- * scheduledCleanup = $lock({
146
- * wait: false, // Skip if cleanup already running
147
- * handler: async () => { } // perform cleanup
148
- * });
149
- *
150
- * // Migration - wait for completion before proceeding
151
- * migration = $lock({
152
- * wait: true, // All instances wait for migration to complete
153
- * handler: async () => { } // perform migration
154
- * });
155
- * ```
156
- */
122
+ * Whether the lock should wait for other instances to complete before giving up.
123
+ *
124
+ * **wait = false (default)**:
125
+ * - Non-blocking behavior - if lock is held, immediately return without executing
126
+ * - Perfect for scheduled tasks where you only want one execution per trigger
127
+ * - Use when multiple triggers are acceptable but concurrent execution is not
128
+ * - Examples: periodic cleanup, cron jobs, background maintenance
129
+ *
130
+ * **wait = true**:
131
+ * - Blocking behavior - wait for the current lock holder to finish
132
+ * - All instances will eventually execute (one after another)
133
+ * - Perfect for initialization tasks where all instances need the work completed
134
+ * - Examples: database migrations, cache warming, resource initialization
135
+ *
136
+ * **Trade-offs**:
137
+ * - Non-waiting: Better performance, may miss executions if timing is off
138
+ * - Waiting: Guaranteed execution order, slower overall throughput
139
+ *
140
+ * @default false
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * // Scheduled task - don't wait, just skip if already running
145
+ * scheduledCleanup = $lock({
146
+ * wait: false, // Skip if cleanup already running
147
+ * handler: async () => { } // perform cleanup
148
+ * });
149
+ *
150
+ * // Migration - wait for completion before proceeding
151
+ * migration = $lock({
152
+ * wait: true, // All instances wait for migration to complete
153
+ * handler: async () => { } // perform migration
154
+ * });
155
+ * ```
156
+ */
157
157
  wait?: boolean;
158
158
  /**
159
- * The unique identifier for the lock.
160
- *
161
- * Can be either:
162
- * - **Static string**: A fixed identifier for the lock
163
- * - **Dynamic function**: A function that generates the lock key based on arguments
164
- *
165
- * **Dynamic Lock Keys**:
166
- * - Enable per-resource locking (e.g., per-user, per-file, per-product)
167
- * - Allow fine-grained concurrency control
168
- * - Prevent unnecessary blocking between unrelated operations
169
- *
170
- * **Key Design Guidelines**:
171
- * - Use descriptive names that indicate the protected resource
172
- * - Include relevant identifiers for dynamic keys
173
- * - Keep keys reasonably short but unique
174
- * - Consider using hierarchical naming (e.g., "service:operation:resource")
175
- *
176
- * If not provided, defaults to `{serviceName}:{propertyKey}`.
177
- *
178
- * @example "user-migration"
179
- * @example "daily-report-generation"
180
- * @example (userId: string) => `user-profile-update:${userId}`
181
- * @example (fileId: string, operation: string) => `file-${operation}:${fileId}`
182
- *
183
- * @example
184
- * ```ts
185
- * // Static lock key - all instances compete for the same lock
186
- * globalCleanup = $lock({
187
- * name: "system-cleanup",
188
- * handler: async () => { } // perform cleanup
189
- * });
190
- *
191
- * // Dynamic lock key - per-user locks, users don't block each other
192
- * updateUserProfile = $lock({
193
- * name: (userId: string) => `user-update:${userId}`,
194
- * handler: async (userId: string, data: UserData) => {
195
- * // Only one update per user at a time, but different users can update concurrently
196
- * }
197
- * });
198
- * ```
199
- */
159
+ * The unique identifier for the lock.
160
+ *
161
+ * Can be either:
162
+ * - **Static string**: A fixed identifier for the lock
163
+ * - **Dynamic function**: A function that generates the lock key based on arguments
164
+ *
165
+ * **Dynamic Lock Keys**:
166
+ * - Enable per-resource locking (e.g., per-user, per-file, per-product)
167
+ * - Allow fine-grained concurrency control
168
+ * - Prevent unnecessary blocking between unrelated operations
169
+ *
170
+ * **Key Design Guidelines**:
171
+ * - Use descriptive names that indicate the protected resource
172
+ * - Include relevant identifiers for dynamic keys
173
+ * - Keep keys reasonably short but unique
174
+ * - Consider using hierarchical naming (e.g., "service:operation:resource")
175
+ *
176
+ * If not provided, defaults to `{serviceName}:{propertyKey}`.
177
+ *
178
+ * @example "user-migration"
179
+ * @example "daily-report-generation"
180
+ * @example (userId: string) => `user-profile-update:${userId}`
181
+ * @example (fileId: string, operation: string) => `file-${operation}:${fileId}`
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * // Static lock key - all instances compete for the same lock
186
+ * globalCleanup = $lock({
187
+ * name: "system-cleanup",
188
+ * handler: async () => { } // perform cleanup
189
+ * });
190
+ *
191
+ * // Dynamic lock key - per-user locks, users don't block each other
192
+ * updateUserProfile = $lock({
193
+ * name: (userId: string) => `user-update:${userId}`,
194
+ * handler: async (userId: string, data: UserData) => {
195
+ * // Only one update per user at a time, but different users can update concurrently
196
+ * }
197
+ * });
198
+ * ```
199
+ */
200
200
  name?: string | ((...args: Parameters<TFunc>) => string);
201
201
  /**
202
- * Maximum duration the lock can be held before it expires automatically.
203
- *
204
- * This prevents deadlocks when a process dies while holding a lock or when
205
- * operations take longer than expected. The lock will be automatically released
206
- * after this duration, allowing other instances to proceed.
207
- *
208
- * **Duration Guidelines**:
209
- * - Set based on expected operation duration plus safety margin
210
- * - Too short: Operations may be interrupted by early expiration
211
- * - Too long: Failed processes block others for extended periods
212
- * - Consider worst-case scenarios and external dependency timeouts
213
- *
214
- * **Typical Values**:
215
- * - Quick operations: 30 seconds - 2 minutes
216
- * - Database operations: 5 - 15 minutes
217
- * - File processing: 10 - 30 minutes
218
- * - Large migrations: 30 minutes - 2 hours
219
- *
220
- * @default [5, "minutes"]
221
- *
222
- * @example [30, "seconds"] // Quick operations
223
- * @example [10, "minutes"] // Database migrations
224
- * @example [1, "hour"] // Long-running batch jobs
225
- *
226
- * @example
227
- * ```ts
228
- * quickTask = $lock({
229
- * maxDuration: [2, "minutes"], // Quick timeout for fast operations
230
- * handler: async () => { } // perform quick task
231
- * });
232
- *
233
- * heavyProcessing = $lock({
234
- * maxDuration: [30, "minutes"], // Longer timeout for heavy work
235
- * handler: async () => { } // perform heavy processing
236
- * });
237
- * ```
238
- */
202
+ * Maximum duration the lock can be held before it expires automatically.
203
+ *
204
+ * This prevents deadlocks when a process dies while holding a lock or when
205
+ * operations take longer than expected. The lock will be automatically released
206
+ * after this duration, allowing other instances to proceed.
207
+ *
208
+ * **Duration Guidelines**:
209
+ * - Set based on expected operation duration plus safety margin
210
+ * - Too short: Operations may be interrupted by early expiration
211
+ * - Too long: Failed processes block others for extended periods
212
+ * - Consider worst-case scenarios and external dependency timeouts
213
+ *
214
+ * **Typical Values**:
215
+ * - Quick operations: 30 seconds - 2 minutes
216
+ * - Database operations: 5 - 15 minutes
217
+ * - File processing: 10 - 30 minutes
218
+ * - Large migrations: 30 minutes - 2 hours
219
+ *
220
+ * @default [5, "minutes"]
221
+ *
222
+ * @example [30, "seconds"] // Quick operations
223
+ * @example [10, "minutes"] // Database migrations
224
+ * @example [1, "hour"] // Long-running batch jobs
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * quickTask = $lock({
229
+ * maxDuration: [2, "minutes"], // Quick timeout for fast operations
230
+ * handler: async () => { } // perform quick task
231
+ * });
232
+ *
233
+ * heavyProcessing = $lock({
234
+ * maxDuration: [30, "minutes"], // Longer timeout for heavy work
235
+ * handler: async () => { } // perform heavy processing
236
+ * });
237
+ * ```
238
+ */
239
239
  maxDuration?: DurationLike;
240
240
  /**
241
- * Additional time to keep the lock active after the handler completes successfully.
242
- *
243
- * This provides a "cooling off" period that can be useful for:
244
- * - Preventing immediate re-execution of the same operation
245
- * - Giving time for related systems to process the results
246
- * - Avoiding race conditions with dependent operations
247
- * - Providing a buffer for cleanup operations
248
- *
249
- * Can be either:
250
- * - **Static duration**: Fixed grace period for all executions
251
- * - **Dynamic function**: Grace period determined by execution arguments
252
- * - **undefined**: No grace period, lock released immediately after completion
253
- *
254
- * **Grace Period Use Cases**:
255
- * - File processing: Prevent immediate reprocessing of uploaded files
256
- * - Cache updates: Allow time for cache propagation
257
- * - Batch operations: Prevent overlapping batch processing
258
- * - External API calls: Respect rate limiting requirements
259
- *
260
- * @default undefined (no grace period)
261
- *
262
- * @example [5, "minutes"] // Fixed 5-minute grace period
263
- * @example [30, "seconds"] // Short grace for quick operations
264
- * @example (userId: string) => userId.startsWith("premium") ? [10, "minutes"] : [2, "minutes"]
265
- *
266
- * @example
267
- * ```ts
268
- * fileProcessor = $lock({
269
- * gracePeriod: [10, "minutes"], // Prevent reprocessing same file immediately
270
- * handler: async (filePath: string) => {
271
- * await this.processFile(filePath);
272
- * }
273
- * });
274
- *
275
- * userOperation = $lock({
276
- * gracePeriod: (userId: string, operation: string) => {
277
- * // Dynamic grace based on operation type
278
- * return operation === 'migration' ? [30, "minutes"] : [5, "minutes"];
279
- * },
280
- * handler: async (userId: string, operation: string) => {
281
- * await this.performUserOperation(userId, operation);
282
- * }
283
- * });
284
- * ```
285
- */
241
+ * Additional time to keep the lock active after the handler completes successfully.
242
+ *
243
+ * This provides a "cooling off" period that can be useful for:
244
+ * - Preventing immediate re-execution of the same operation
245
+ * - Giving time for related systems to process the results
246
+ * - Avoiding race conditions with dependent operations
247
+ * - Providing a buffer for cleanup operations
248
+ *
249
+ * Can be either:
250
+ * - **Static duration**: Fixed grace period for all executions
251
+ * - **Dynamic function**: Grace period determined by execution arguments
252
+ * - **undefined**: No grace period, lock released immediately after completion
253
+ *
254
+ * **Grace Period Use Cases**:
255
+ * - File processing: Prevent immediate reprocessing of uploaded files
256
+ * - Cache updates: Allow time for cache propagation
257
+ * - Batch operations: Prevent overlapping batch processing
258
+ * - External API calls: Respect rate limiting requirements
259
+ *
260
+ * @default undefined (no grace period)
261
+ *
262
+ * @example [5, "minutes"] // Fixed 5-minute grace period
263
+ * @example [30, "seconds"] // Short grace for quick operations
264
+ * @example (userId: string) => userId.startsWith("premium") ? [10, "minutes"] : [2, "minutes"]
265
+ *
266
+ * @example
267
+ * ```ts
268
+ * fileProcessor = $lock({
269
+ * gracePeriod: [10, "minutes"], // Prevent reprocessing same file immediately
270
+ * handler: async (filePath: string) => {
271
+ * await this.processFile(filePath);
272
+ * }
273
+ * });
274
+ *
275
+ * userOperation = $lock({
276
+ * gracePeriod: (userId: string, operation: string) => {
277
+ * // Dynamic grace based on operation type
278
+ * return operation === 'migration' ? [30, "minutes"] : [5, "minutes"];
279
+ * },
280
+ * handler: async (userId: string, operation: string) => {
281
+ * await this.performUserOperation(userId, operation);
282
+ * }
283
+ * });
284
+ * ```
285
+ */
286
286
  gracePeriod?: ((...args: Parameters<TFunc>) => DurationLike | undefined) | DurationLike;
287
287
  }
288
288
  declare const envSchema: alepha1.TObject<{
@@ -307,8 +307,8 @@ declare class LockPrimitive<TFunc extends AsyncFn> extends Primitive<LockPrimiti
307
307
  }>;
308
308
  run(...args: Parameters<TFunc>): Promise<void>;
309
309
  /**
310
- * Set the lock for the given key.
311
- */
310
+ * Set the lock for the given key.
311
+ */
312
312
  protected lock(key: string): Promise<LockResult>;
313
313
  protected setGracePeriod(key: string, lock: LockResult, ...args: Parameters<TFunc>): Promise<void>;
314
314
  protected wait(key: string, maxDuration: DurationLike): Promise<void>;
@@ -333,12 +333,12 @@ declare class MemoryLockProvider implements LockProvider {
333
333
  protected readonly dateTimeProvider: DateTimeProvider;
334
334
  protected readonly log: alepha_logger0.Logger;
335
335
  /**
336
- * The in-memory store.
337
- */
336
+ * The in-memory store.
337
+ */
338
338
  protected store: Record<string, string>;
339
339
  /**
340
- * Timeouts used to expire keys.
341
- */
340
+ * Timeouts used to expire keys.
341
+ */
342
342
  protected storeTimeout: Record<string, Timeout>;
343
343
  set(key: string, value: string, nx?: boolean, px?: number): Promise<string>;
344
344
  del(...keys: string[]): Promise<void>;
@@ -347,12 +347,19 @@ declare class MemoryLockProvider implements LockProvider {
347
347
  //#endregion
348
348
  //#region ../../src/lock/core/index.d.ts
349
349
  /**
350
- * Lock a resource for a certain period of time.
350
+ * | type | quality | stability |
351
+ * |------|---------|-----------|
352
+ * | backend | rare | stable |
351
353
  *
352
- * This module provides a memory implementation of the lock provider.
353
- * You probably want to use an implementation like RedisLockProvider for distributed systems.
354
+ * Resource locking for distributed systems.
355
+ *
356
+ * **Features:**
357
+ * - Distributed locks with timeout
358
+ * - Time-based lock expiration
359
+ * - Automatic release on scope exit
360
+ * - Distributed coordination via Redis
361
+ * - Providers: Memory (dev), Redis (production)
354
362
  *
355
- * @see {@link $lock}
356
363
  * @module alepha.lock
357
364
  */
358
365
  declare const AlephaLock: alepha1.Service<alepha1.Module>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/lock/core/providers/LockProvider.ts","../../../src/lock/core/primitives/$lock.ts","../../../src/lock/core/providers/LockTopicProvider.ts","../../../src/lock/core/providers/MemoryLockProvider.ts","../../../src/lock/core/index.ts"],"mappings":";;;;;;;;;;AAGA;;uBAAsB,YAAA;EAAA;;;;ACkEtB;;;;EDlEsB,SAAA,IAAA,GAAA,UAAA,KAAA,UAAA,EAAA,YAAA,EAAA,YAcjB,OAAA;EAAA;;;;ACoDL;EDpDK,SAAA,IAAA,GAAA,IAAA,aAOqC,OAAA;AAAA;;;;AC6C1C;;;;;;;;;AAQA;;;;;;;;;;;AAgNC;AAIc;;;;;;;;;AAQf;;;;;;;;;;;;;;;;;;cApOa,KAAA;EAAA,eAAuB,OAAA,EAAA,OAAA,EACzB,oBAAA,CAAqB,KAAA,IAC7B,aAAA,CAAc,KAAA;EAAA;;UAMA,oBAAA,eAAmC,OAAA;EAAA;;;;;;;;;;AAgNnD;AAIc;;;;;;;;;AAQf;;;;;;;;;;;;;;EA5NoD,OAAA,EAmCzC,KAAA;EAAA;;;;;;;;;AA6KV;AAIc;;;;;;;;;AAQf;;;;;;;;;;;;;;;;;EAzLW,IAAA;EAAA;;;;;;;;;AA6KV;AAIc;;;;;;;;;AAQf;;;;;;;;;;;;;;;;;;;;;;;EAzLW,IAAA,iBAAA,IAAA,EAkFkB,UAAA,CAAW,KAAA;EAAA;;;;;;;;AA2FvC;AAIc;;;;;;;;;AAQf;;;;;;;;;;;;;;;;;;;;EAvGwC,WAAA,GAwCxB,YAAA;EAAA;;;;;;AAmDf;AAIc;;;;;;;;;AAQf;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkJA;;;EAjNgB,WAAA,QAAA,IAAA,EAiDC,UAAA,CAAW,KAAA,MAAW,YAAA,gBACjC,YAAA;AAAA;AAAA,cAKA,SAAA,EAEJ,OAAA,CAFa,OAAA;EAAA,eAAA,EAEb,OAAA,CAAA,OAAA;AAAA;AAAA;EAAA,UAAA,GAAA,SAGsB,OAAA,CAAQ,MAAA,QAAc,SAAA;AAAA;AAAA,cAGjC,aAAA,eAA4B,OAAA,UAAiB,SAAA,CACxD,oBAAA,CAAqB,KAAA;EAAA,mBAAA,GAAA,EAAD,cAAA,CAEE,MAAA;EAAA,mBAAA,QAAA,EACK,YAAA;EAAA,mBAAA,GAAA;IAAA,eAAA;EAAA;EAAA,mBAAA,gBAAA,EAEQ,gBAAA;EAAA,mBAAA,EAAA;EAAA,SAAA,WAAA,EAAA,yBAAA,CAER,QAAA;EAAA,mBAAA,YAAA,gBAII,cAAA;IAAA,OAAA;YAJJ,OAAA,CAAA,OAAA;IAAA;EAAA;EAAA,IAAA,GAAA,IAAA,EAcD,UAAA,CAAW,KAAA,IAAS,OAAA;EAAA;;;EAAA,UAAA,KAAA,GAAA,WA8CX,OAAA,CAAQ,UAAA;EAAA,UAAA,eAAA,GAAA,UAAA,IAAA,EAanC,UAAA,KAAA,IAAA,EACG,UAAA,CAAW,KAAA,IACnB,OAAA;EAAA,UAAA,KAAA,GAAA,UAAA,WAAA,EAmB4C,YAAA,GAAe,OAAA;EAAA,UAAA,IAAA,GAAA,IAAA,EAWvC,UAAA,CAAW,KAAA;EAAA,UAAA,MAAA,KAAA,WAgBF,UAAA;AAAA;AAAA,UAiBjB,UAAA;EAAA,EAAA;EAAA,SAAA,EAEJ,QAAA;EAAA,OAAA,GACD,QAAA;EAAA,QAAA;AAAA;;;uBC5bU,iBAAA,SAA0B,aAAA;;;;ACMhD;;cAAa,kBAAA,YAA8B,YAAA;EAAA,mBAAA,gBAAA,EACN,gBAAA;EAAA,mBAAA,GAAA,EAAA,cAAA,CACb,MAAA;EAAA;;;EAAA,UAAA,KAAA,EAKL,MAAA;EAAA;;;EAAA,UAAA,YAAA,EAKO,MAAA,SAAe,OAAA;EAAA,IAAA,GAAA,UAAA,KAAA,UAAA,EAAA,YAAA,EAAA,YAOpC,OAAA;EAAA,IAAA,GAAA,IAAA,aAckC,OAAA;EAAA,QAAA,GAAA;AAAA;;;;AChBvC;;;;;;;;cAAa,UAAA,EAAU,OAAA,CAAA,OAAA,CAgBrB,OAAA,CAhBqB,MAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/lock/core/providers/LockProvider.ts","../../../src/lock/core/primitives/$lock.ts","../../../src/lock/core/providers/LockTopicProvider.ts","../../../src/lock/core/providers/MemoryLockProvider.ts","../../../src/lock/core/index.ts"],"mappings":";;;;;;;;;;;;uBAGsB,YAAA;;;;;;AAAtB;;;WASkB,GAAA,CACd,GAAA,UACA,KAAA,UACA,EAAA,YACA,EAAA,YACC,OAAA;EALa;;;;;EAAA,SAYA,GAAA,CAAA,GAAO,IAAA,aAAiB,OAAA;AAAA;;;;;;;;;AArB1C;;;;;;;;;;;;;;;;;;ACkEA;;;;;;;;;;;;;;;;;;;;;;;AAQA;;cARa,KAAA;EAAA,eAAuB,OAAA,EAAO,OAAA,EAChC,oBAAA,CAAqB,KAAA,IAC7B,aAAA,CAAc,KAAA;EAAA;;UAMA,oBAAA,eAAmC,OAAA;EA6JpC;;;;;;;;;;;;;;;;;;;;;;;;;;AAmDf;;;;;;;;EA7KC,OAAA,EAAS,KAAA;;;;;;;;;;;;;;;;AAyLX;;;;;;;;;;;;;;;;;;;;;EAnJE,IAAA;EAyP+C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA7M/C,IAAA,iBAAqB,IAAA,EAAM,UAAA,CAAW,KAAA;EAyLjC;;;;;;;;;;;;;;;;;;AAgEP;;;;;;;;;;;;;;;ACzbA;;;;;EDwOE,WAAA,GAAc,YAAA;;;AElOhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACwBA;;;;;;EH0PE,WAAA,QACS,IAAA,EAAM,UAAA,CAAW,KAAA,MAAW,YAAA,gBACjC,YAAA;AAAA;AAAA,cAKA,SAAA,EAEJ,OAAA,CAFa,OAAA;mBAEb,OAAA,CAAA,OAAA;AAAA;AAAA;EAAA,UAGU,GAAA,SAAY,OAAA,CAAQ,MAAA,QAAc,SAAA;AAAA;AAAA,cAGjC,aAAA,eAA4B,OAAA,UAAiB,SAAA,CACxD,oBAAA,CAAqB,KAAA;EAAA,mBAEF,GAAA,EAFC,cAAA,CAEE,MAAA;EAAA,mBACH,QAAA,EAAQ,YAAA;EAAA,mBACR,GAAA;;;qBACA,gBAAA,EAAgB,gBAAA;EAAA,mBAChB,EAAA;EAAA,SACH,WAAA,EAFmB,yBAAA,CAER,QAAA;EAAA,mBAIR,YAAA,gBAAY,cAAA;;YAJJ,OAAA,CAAA,OAAA;IAAA;EAAA;EAcd,GAAA,CAAA,GAAO,IAAA,EAAM,UAAA,CAAW,KAAA,IAAS,OAAA;;;;YA8C9B,IAAA,CAAK,GAAA,WAAc,OAAA,CAAQ,UAAA;EAAA,UAW3B,cAAA,CACd,GAAA,UACA,IAAA,EAAM,UAAA,KACH,IAAA,EAAM,UAAA,CAAW,KAAA,IACnB,OAAA;EAAA,UAmBa,IAAA,CAAK,GAAA,UAAa,WAAA,EAAa,YAAA,GAAe,OAAA;EAAA,UAWpD,GAAA,CAAA,GAAO,IAAA,EAAM,UAAA,CAAW,KAAA;EAAA,UAgBxB,KAAA,CAAM,KAAA,WAAgB,UAAA;AAAA;AAAA,UAiBjB,UAAA;EACf,EAAA;EACA,SAAA,EAAW,QAAA;EACX,OAAA,GAAU,QAAA;EACV,QAAA;AAAA;;;uBC7boB,iBAAA,SAA0B,aAAA;;;;;;cCMnC,kBAAA,YAA8B,YAAA;EAAA,mBACtB,gBAAA,EAAgB,gBAAA;EAAA,mBAChB,GAAA,EADgB,cAAA,CACb,MAAA;;AHPxB;;YGYY,KAAA,EAAO,MAAA;EHS8B;;;EAAA,UGJrC,YAAA,EAAc,MAAA,SAAe,OAAA;EAE1B,GAAA,CACX,GAAA,UACA,KAAA,UACA,EAAA,YACA,EAAA,YACC,OAAA;EAcU,GAAA,CAAA,GAAO,IAAA,aAAiB,OAAA;EAAA,QAU7B,GAAA;AAAA;;;;;;;;AHhDV;;;;;;;;;;;cI6Ba,UAAA,EAAU,OAAA,CAAA,OAAA,CAgBrB,OAAA,CAhBqB,MAAA"}
@@ -189,12 +189,19 @@ var MemoryLockProvider = class {
189
189
  //#endregion
190
190
  //#region ../../src/lock/core/index.ts
191
191
  /**
192
- * Lock a resource for a certain period of time.
192
+ * | type | quality | stability |
193
+ * |------|---------|-----------|
194
+ * | backend | rare | stable |
193
195
  *
194
- * This module provides a memory implementation of the lock provider.
195
- * You probably want to use an implementation like RedisLockProvider for distributed systems.
196
+ * Resource locking for distributed systems.
197
+ *
198
+ * **Features:**
199
+ * - Distributed locks with timeout
200
+ * - Time-based lock expiration
201
+ * - Automatic release on scope exit
202
+ * - Distributed coordination via Redis
203
+ * - Providers: Memory (dev), Redis (production)
196
204
  *
197
- * @see {@link $lock}
198
205
  * @module alepha.lock
199
206
  */
200
207
  const AlephaLock = $module({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/lock/core/providers/LockProvider.ts","../../../src/lock/core/providers/LockTopicProvider.ts","../../../src/lock/core/primitives/$lock.ts","../../../src/lock/core/providers/MemoryLockProvider.ts","../../../src/lock/core/index.ts"],"sourcesContent":["/**\n * Store Provider Interface\n */\nexport abstract class LockProvider {\n /**\n * Set the string value of a key.\n *\n * @param key The key of the value to set.\n * @param value The value to set.\n * @param nx If set to true, the key will only be set if it does not already exist.\n * @param px Set the specified expire time, in milliseconds.\n */\n public abstract set(\n key: string,\n value: string,\n nx?: boolean,\n px?: number,\n ): Promise<string>;\n\n /**\n * Remove the specified keys.\n *\n * @param keys The keys to delete.\n */\n public abstract del(...keys: string[]): Promise<void>;\n}\n","import { TopicProvider } from \"alepha/topic\";\n\nexport abstract class LockTopicProvider extends TopicProvider {}\n","import {\n $env,\n $inject,\n type AsyncFn,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n t,\n} from \"alepha\";\nimport {\n type DateTime,\n DateTimeProvider,\n type DurationLike,\n} from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $topic, TopicTimeoutError } from \"alepha/topic\";\nimport { LockProvider } from \"../providers/LockProvider.ts\";\nimport { LockTopicProvider } from \"../providers/LockTopicProvider.ts\";\n\n/**\n * Creates a distributed lock primitive for ensuring single-instance execution across processes.\n *\n * Prevents multiple instances of the same operation from running simultaneously, essential for\n * maintaining data consistency and preventing race conditions in distributed applications.\n *\n * **Key Features**\n * - Distributed coordination across multiple processes, servers, and containers\n * - Automatic expiration to prevent deadlocks\n * - Configurable wait behavior (blocking vs. non-blocking)\n * - Optional grace periods for lock extension after completion\n * - Dynamic or static lock keys for fine-grained control\n *\n * **Common Use Cases**\n * - Database migrations and scheduled jobs\n * - File processing and batch operations\n * - Critical section protection and resource initialization\n *\n * @example\n * ```ts\n * class TaskService {\n * // Basic scheduled task - only one server executes\n * dailyReport = $lock({\n * handler: async () => {\n * const report = await this.generateDailyReport();\n * await this.sendReportToManagement(report);\n * }\n * });\n *\n * // Migration with wait - all instances wait for completion\n * migration = $lock({\n * wait: true,\n * maxDuration: [10, \"minutes\"],\n * handler: async (version: string) => {\n * await this.runMigrationScripts(version);\n * }\n * });\n *\n * // Dynamic lock keys for per-resource locking\n * processFile = $lock({\n * name: (fileId: string) => `file-processing:${fileId}`,\n * gracePeriod: [5, \"minutes\"],\n * handler: async (fileId: string) => {\n * await this.processFileData(fileId);\n * }\n * });\n * }\n * ```\n */\nexport const $lock = <TFunc extends AsyncFn>(\n options: LockPrimitiveOptions<TFunc>,\n): LockPrimitive<TFunc> => {\n return createPrimitive(LockPrimitive<TFunc>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface LockPrimitiveOptions<TFunc extends AsyncFn> {\n /**\n * The function to execute when the lock is successfully acquired.\n *\n * This function:\n * - Only executes on the instance that successfully acquires the lock\n * - Has exclusive access to the protected resource during execution\n * - Should contain the critical section logic that must not run concurrently\n * - Can be async and perform any operations needed\n * - Will automatically release the lock upon completion or error\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Design Guidelines**:\n * - Keep critical sections as short as possible to minimize lock contention\n * - Include proper error handling to ensure locks are released\n * - Use timeouts for external operations to prevent deadlocks\n * - Log important operations for debugging and monitoring\n * - Consider idempotency for handlers that might be retried\n *\n * @param ...args - The arguments passed to the lock execution\n * @returns Promise that resolves when the protected operation is complete\n *\n * @example\n * ```ts\n * handler: async (batchId: string) => {\n * console.log(`Processing batch ${batchId} - only one instance will run this`);\n *\n * const batch = await this.getBatchData(batchId);\n * const results = await this.processBatchItems(batch.items);\n * await this.saveBatchResults(batchId, results);\n *\n * console.log(`Batch ${batchId} completed successfully`);\n * }\n * ```\n */\n handler: TFunc;\n\n /**\n * Whether the lock should wait for other instances to complete before giving up.\n *\n * **wait = false (default)**:\n * - Non-blocking behavior - if lock is held, immediately return without executing\n * - Perfect for scheduled tasks where you only want one execution per trigger\n * - Use when multiple triggers are acceptable but concurrent execution is not\n * - Examples: periodic cleanup, cron jobs, background maintenance\n *\n * **wait = true**:\n * - Blocking behavior - wait for the current lock holder to finish\n * - All instances will eventually execute (one after another)\n * - Perfect for initialization tasks where all instances need the work completed\n * - Examples: database migrations, cache warming, resource initialization\n *\n * **Trade-offs**:\n * - Non-waiting: Better performance, may miss executions if timing is off\n * - Waiting: Guaranteed execution order, slower overall throughput\n *\n * @default false\n *\n * @example\n * ```ts\n * // Scheduled task - don't wait, just skip if already running\n * scheduledCleanup = $lock({\n * wait: false, // Skip if cleanup already running\n * handler: async () => { } // perform cleanup\n * });\n *\n * // Migration - wait for completion before proceeding\n * migration = $lock({\n * wait: true, // All instances wait for migration to complete\n * handler: async () => { } // perform migration\n * });\n * ```\n */\n wait?: boolean;\n\n /**\n * The unique identifier for the lock.\n *\n * Can be either:\n * - **Static string**: A fixed identifier for the lock\n * - **Dynamic function**: A function that generates the lock key based on arguments\n *\n * **Dynamic Lock Keys**:\n * - Enable per-resource locking (e.g., per-user, per-file, per-product)\n * - Allow fine-grained concurrency control\n * - Prevent unnecessary blocking between unrelated operations\n *\n * **Key Design Guidelines**:\n * - Use descriptive names that indicate the protected resource\n * - Include relevant identifiers for dynamic keys\n * - Keep keys reasonably short but unique\n * - Consider using hierarchical naming (e.g., \"service:operation:resource\")\n *\n * If not provided, defaults to `{serviceName}:{propertyKey}`.\n *\n * @example \"user-migration\"\n * @example \"daily-report-generation\"\n * @example (userId: string) => `user-profile-update:${userId}`\n * @example (fileId: string, operation: string) => `file-${operation}:${fileId}`\n *\n * @example\n * ```ts\n * // Static lock key - all instances compete for the same lock\n * globalCleanup = $lock({\n * name: \"system-cleanup\",\n * handler: async () => { } // perform cleanup\n * });\n *\n * // Dynamic lock key - per-user locks, users don't block each other\n * updateUserProfile = $lock({\n * name: (userId: string) => `user-update:${userId}`,\n * handler: async (userId: string, data: UserData) => {\n * // Only one update per user at a time, but different users can update concurrently\n * }\n * });\n * ```\n */\n name?: string | ((...args: Parameters<TFunc>) => string);\n\n /**\n * Maximum duration the lock can be held before it expires automatically.\n *\n * This prevents deadlocks when a process dies while holding a lock or when\n * operations take longer than expected. The lock will be automatically released\n * after this duration, allowing other instances to proceed.\n *\n * **Duration Guidelines**:\n * - Set based on expected operation duration plus safety margin\n * - Too short: Operations may be interrupted by early expiration\n * - Too long: Failed processes block others for extended periods\n * - Consider worst-case scenarios and external dependency timeouts\n *\n * **Typical Values**:\n * - Quick operations: 30 seconds - 2 minutes\n * - Database operations: 5 - 15 minutes\n * - File processing: 10 - 30 minutes\n * - Large migrations: 30 minutes - 2 hours\n *\n * @default [5, \"minutes\"]\n *\n * @example [30, \"seconds\"] // Quick operations\n * @example [10, \"minutes\"] // Database migrations\n * @example [1, \"hour\"] // Long-running batch jobs\n *\n * @example\n * ```ts\n * quickTask = $lock({\n * maxDuration: [2, \"minutes\"], // Quick timeout for fast operations\n * handler: async () => { } // perform quick task\n * });\n *\n * heavyProcessing = $lock({\n * maxDuration: [30, \"minutes\"], // Longer timeout for heavy work\n * handler: async () => { } // perform heavy processing\n * });\n * ```\n */\n maxDuration?: DurationLike;\n\n /**\n * Additional time to keep the lock active after the handler completes successfully.\n *\n * This provides a \"cooling off\" period that can be useful for:\n * - Preventing immediate re-execution of the same operation\n * - Giving time for related systems to process the results\n * - Avoiding race conditions with dependent operations\n * - Providing a buffer for cleanup operations\n *\n * Can be either:\n * - **Static duration**: Fixed grace period for all executions\n * - **Dynamic function**: Grace period determined by execution arguments\n * - **undefined**: No grace period, lock released immediately after completion\n *\n * **Grace Period Use Cases**:\n * - File processing: Prevent immediate reprocessing of uploaded files\n * - Cache updates: Allow time for cache propagation\n * - Batch operations: Prevent overlapping batch processing\n * - External API calls: Respect rate limiting requirements\n *\n * @default undefined (no grace period)\n *\n * @example [5, \"minutes\"] // Fixed 5-minute grace period\n * @example [30, \"seconds\"] // Short grace for quick operations\n * @example (userId: string) => userId.startsWith(\"premium\") ? [10, \"minutes\"] : [2, \"minutes\"]\n *\n * @example\n * ```ts\n * fileProcessor = $lock({\n * gracePeriod: [10, \"minutes\"], // Prevent reprocessing same file immediately\n * handler: async (filePath: string) => {\n * await this.processFile(filePath);\n * }\n * });\n *\n * userOperation = $lock({\n * gracePeriod: (userId: string, operation: string) => {\n * // Dynamic grace based on operation type\n * return operation === 'migration' ? [30, \"minutes\"] : [5, \"minutes\"];\n * },\n * handler: async (userId: string, operation: string) => {\n * await this.performUserOperation(userId, operation);\n * }\n * });\n * ```\n */\n gracePeriod?:\n | ((...args: Parameters<TFunc>) => DurationLike | undefined)\n | DurationLike;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n LOCK_PREFIX_KEY: t.text({ default: \"lock\" }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class LockPrimitive<TFunc extends AsyncFn> extends Primitive<\n LockPrimitiveOptions<TFunc>\n> {\n protected readonly log = $logger();\n protected readonly provider = $inject(LockProvider);\n protected readonly env = $env(envSchema);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly id = crypto.randomUUID();\n public readonly maxDuration = this.dateTimeProvider.duration(\n this.options.maxDuration ?? [5, \"minutes\"],\n );\n\n protected readonly topicLockEnd = $topic({\n name: `${this.env.LOCK_PREFIX_KEY}:lock-end`,\n provider: LockTopicProvider,\n schema: {\n payload: t.object({\n name: t.text(),\n }),\n },\n });\n\n public async run(...args: Parameters<TFunc>): Promise<void> {\n const key = this.key(...args);\n const handler = this.options.handler;\n\n const lock = await this.lock(key);\n if (lock.endedAt) {\n return;\n }\n\n if (lock.id !== this.id) {\n if (this.options.wait) {\n try {\n await this.wait(key, this.maxDuration);\n } catch (error) {\n if (error instanceof TopicTimeoutError) {\n this.log.warn(\n `Lock timeout for '${key}' has been reached. Retry...`,\n );\n await this.run(...args);\n } else {\n throw error;\n }\n }\n }\n\n return;\n }\n\n this.log.debug(`Lock '${key}' ...`);\n\n try {\n await handler(...args);\n } finally {\n await this.topicLockEnd.publish({\n name: key,\n });\n\n await this.setGracePeriod(key, lock, ...args);\n\n this.log.debug(`Lock '${key}' OK`);\n }\n }\n\n /**\n * Set the lock for the given key.\n */\n protected async lock(key: string): Promise<LockResult> {\n const value = await this.provider.set(\n key,\n `${this.id},${this.dateTimeProvider.nowISOString()}`,\n true,\n this.maxDuration.as(\"milliseconds\"),\n );\n\n return this.parse(value);\n }\n\n protected async setGracePeriod(\n key: string,\n lock: LockResult,\n ...args: Parameters<TFunc>\n ): Promise<void> {\n const gracePeriod = this.options.gracePeriod\n ? this.dateTimeProvider.isDurationLike(this.options.gracePeriod)\n ? this.options.gracePeriod\n : this.options.gracePeriod(...args)\n : undefined;\n\n if (gracePeriod) {\n await this.provider.set(\n key,\n `${this.id},${lock.createdAt.toISOString()},${this.dateTimeProvider.nowISOString()}`,\n false,\n this.dateTimeProvider.duration(gracePeriod).as(\"milliseconds\"),\n );\n } else {\n await this.provider.del(key);\n }\n }\n\n protected async wait(key: string, maxDuration: DurationLike): Promise<void> {\n this.log.debug(`Wait for lock '${key}' ...`);\n\n await this.topicLockEnd.wait({\n filter: (message) => message.payload.name === key,\n timeout: maxDuration,\n });\n\n this.log.debug(`Wait for lock '${key}' OK`);\n }\n\n protected key(...args: Parameters<TFunc>) {\n let base = \"\";\n\n if (this.options.name) {\n if (typeof this.options.name === \"string\") {\n base = this.options.name;\n } else {\n base = this.options.name(...args);\n }\n } else {\n base = `${this.config.service.name}:${this.config.propertyKey}`;\n }\n\n return `${this.env.LOCK_PREFIX_KEY}:${base}`;\n }\n\n protected parse(value: string): LockResult {\n const [id, createdAtStr, endedAtStr] = value.split(\",\");\n const createdAt = this.dateTimeProvider.of(createdAtStr);\n const endedAt = endedAtStr\n ? this.dateTimeProvider.of(endedAtStr)\n : undefined;\n\n return {\n id,\n createdAt,\n endedAt,\n };\n }\n}\n\n$lock[KIND] = LockPrimitive;\n\nexport interface LockResult {\n id: string;\n createdAt: DateTime;\n endedAt?: DateTime;\n response?: string;\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { LockProvider } from \"./LockProvider.ts\";\n\n/**\n * A simple in-memory store provider.\n */\nexport class MemoryLockProvider implements LockProvider {\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n\n /**\n * The in-memory store.\n */\n protected store: Record<string, string> = {};\n\n /**\n * Timeouts used to expire keys.\n */\n protected storeTimeout: Record<string, Timeout> = {};\n\n public async set(\n key: string,\n value: string,\n nx?: boolean,\n px?: number,\n ): Promise<string> {\n if (nx && this.store[key] != null) {\n return this.store[key];\n }\n\n if (px) {\n this.ttl(key, px);\n }\n\n this.store[key] = value;\n\n return this.store[key];\n }\n\n public async del(...keys: string[]): Promise<void> {\n for (const key of keys) {\n delete this.store[key];\n if (this.storeTimeout[key] != null) {\n this.storeTimeout[key].clear();\n delete this.storeTimeout[key];\n }\n }\n }\n\n private ttl(key: string, ms: number): void {\n if (this.storeTimeout[key] != null) {\n this.storeTimeout[key].clear();\n delete this.storeTimeout[key];\n }\n\n this.storeTimeout[key] = this.dateTimeProvider.createTimeout(() => {\n delete this.store[key];\n delete this.storeTimeout[key];\n }, ms);\n }\n}\n","import { $module } from \"alepha\";\nimport { MemoryTopicProvider } from \"alepha/topic\";\nimport { $lock } from \"./primitives/$lock.ts\";\nimport { LockProvider } from \"./providers/LockProvider.ts\";\nimport { LockTopicProvider } from \"./providers/LockTopicProvider.ts\";\nimport { MemoryLockProvider } from \"./providers/MemoryLockProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$lock.ts\";\nexport * from \"./providers/LockProvider.ts\";\nexport * from \"./providers/LockTopicProvider.ts\";\nexport * from \"./providers/MemoryLockProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Lock a resource for a certain period of time.\n *\n * This module provides a memory implementation of the lock provider.\n * You probably want to use an implementation like RedisLockProvider for distributed systems.\n *\n * @see {@link $lock}\n * @module alepha.lock\n */\nexport const AlephaLock = $module({\n name: \"alepha.lock\",\n primitives: [$lock],\n services: [LockProvider, MemoryLockProvider, LockTopicProvider],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: LockTopicProvider,\n use: MemoryTopicProvider,\n })\n .with({\n optional: true,\n provide: LockProvider,\n use: MemoryLockProvider,\n }),\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/lock/core/providers/LockProvider.ts","../../../src/lock/core/providers/LockTopicProvider.ts","../../../src/lock/core/primitives/$lock.ts","../../../src/lock/core/providers/MemoryLockProvider.ts","../../../src/lock/core/index.ts"],"sourcesContent":["/**\n * Store Provider Interface\n */\nexport abstract class LockProvider {\n /**\n * Set the string value of a key.\n *\n * @param key The key of the value to set.\n * @param value The value to set.\n * @param nx If set to true, the key will only be set if it does not already exist.\n * @param px Set the specified expire time, in milliseconds.\n */\n public abstract set(\n key: string,\n value: string,\n nx?: boolean,\n px?: number,\n ): Promise<string>;\n\n /**\n * Remove the specified keys.\n *\n * @param keys The keys to delete.\n */\n public abstract del(...keys: string[]): Promise<void>;\n}\n","import { TopicProvider } from \"alepha/topic\";\n\nexport abstract class LockTopicProvider extends TopicProvider {}\n","import {\n $env,\n $inject,\n type AsyncFn,\n createPrimitive,\n KIND,\n Primitive,\n type Static,\n t,\n} from \"alepha\";\nimport {\n type DateTime,\n DateTimeProvider,\n type DurationLike,\n} from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { $topic, TopicTimeoutError } from \"alepha/topic\";\nimport { LockProvider } from \"../providers/LockProvider.ts\";\nimport { LockTopicProvider } from \"../providers/LockTopicProvider.ts\";\n\n/**\n * Creates a distributed lock primitive for ensuring single-instance execution across processes.\n *\n * Prevents multiple instances of the same operation from running simultaneously, essential for\n * maintaining data consistency and preventing race conditions in distributed applications.\n *\n * **Key Features**\n * - Distributed coordination across multiple processes, servers, and containers\n * - Automatic expiration to prevent deadlocks\n * - Configurable wait behavior (blocking vs. non-blocking)\n * - Optional grace periods for lock extension after completion\n * - Dynamic or static lock keys for fine-grained control\n *\n * **Common Use Cases**\n * - Database migrations and scheduled jobs\n * - File processing and batch operations\n * - Critical section protection and resource initialization\n *\n * @example\n * ```ts\n * class TaskService {\n * // Basic scheduled task - only one server executes\n * dailyReport = $lock({\n * handler: async () => {\n * const report = await this.generateDailyReport();\n * await this.sendReportToManagement(report);\n * }\n * });\n *\n * // Migration with wait - all instances wait for completion\n * migration = $lock({\n * wait: true,\n * maxDuration: [10, \"minutes\"],\n * handler: async (version: string) => {\n * await this.runMigrationScripts(version);\n * }\n * });\n *\n * // Dynamic lock keys for per-resource locking\n * processFile = $lock({\n * name: (fileId: string) => `file-processing:${fileId}`,\n * gracePeriod: [5, \"minutes\"],\n * handler: async (fileId: string) => {\n * await this.processFileData(fileId);\n * }\n * });\n * }\n * ```\n */\nexport const $lock = <TFunc extends AsyncFn>(\n options: LockPrimitiveOptions<TFunc>,\n): LockPrimitive<TFunc> => {\n return createPrimitive(LockPrimitive<TFunc>, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface LockPrimitiveOptions<TFunc extends AsyncFn> {\n /**\n * The function to execute when the lock is successfully acquired.\n *\n * This function:\n * - Only executes on the instance that successfully acquires the lock\n * - Has exclusive access to the protected resource during execution\n * - Should contain the critical section logic that must not run concurrently\n * - Can be async and perform any operations needed\n * - Will automatically release the lock upon completion or error\n * - Has access to the full Alepha dependency injection container\n *\n * **Handler Design Guidelines**:\n * - Keep critical sections as short as possible to minimize lock contention\n * - Include proper error handling to ensure locks are released\n * - Use timeouts for external operations to prevent deadlocks\n * - Log important operations for debugging and monitoring\n * - Consider idempotency for handlers that might be retried\n *\n * @param ...args - The arguments passed to the lock execution\n * @returns Promise that resolves when the protected operation is complete\n *\n * @example\n * ```ts\n * handler: async (batchId: string) => {\n * console.log(`Processing batch ${batchId} - only one instance will run this`);\n *\n * const batch = await this.getBatchData(batchId);\n * const results = await this.processBatchItems(batch.items);\n * await this.saveBatchResults(batchId, results);\n *\n * console.log(`Batch ${batchId} completed successfully`);\n * }\n * ```\n */\n handler: TFunc;\n\n /**\n * Whether the lock should wait for other instances to complete before giving up.\n *\n * **wait = false (default)**:\n * - Non-blocking behavior - if lock is held, immediately return without executing\n * - Perfect for scheduled tasks where you only want one execution per trigger\n * - Use when multiple triggers are acceptable but concurrent execution is not\n * - Examples: periodic cleanup, cron jobs, background maintenance\n *\n * **wait = true**:\n * - Blocking behavior - wait for the current lock holder to finish\n * - All instances will eventually execute (one after another)\n * - Perfect for initialization tasks where all instances need the work completed\n * - Examples: database migrations, cache warming, resource initialization\n *\n * **Trade-offs**:\n * - Non-waiting: Better performance, may miss executions if timing is off\n * - Waiting: Guaranteed execution order, slower overall throughput\n *\n * @default false\n *\n * @example\n * ```ts\n * // Scheduled task - don't wait, just skip if already running\n * scheduledCleanup = $lock({\n * wait: false, // Skip if cleanup already running\n * handler: async () => { } // perform cleanup\n * });\n *\n * // Migration - wait for completion before proceeding\n * migration = $lock({\n * wait: true, // All instances wait for migration to complete\n * handler: async () => { } // perform migration\n * });\n * ```\n */\n wait?: boolean;\n\n /**\n * The unique identifier for the lock.\n *\n * Can be either:\n * - **Static string**: A fixed identifier for the lock\n * - **Dynamic function**: A function that generates the lock key based on arguments\n *\n * **Dynamic Lock Keys**:\n * - Enable per-resource locking (e.g., per-user, per-file, per-product)\n * - Allow fine-grained concurrency control\n * - Prevent unnecessary blocking between unrelated operations\n *\n * **Key Design Guidelines**:\n * - Use descriptive names that indicate the protected resource\n * - Include relevant identifiers for dynamic keys\n * - Keep keys reasonably short but unique\n * - Consider using hierarchical naming (e.g., \"service:operation:resource\")\n *\n * If not provided, defaults to `{serviceName}:{propertyKey}`.\n *\n * @example \"user-migration\"\n * @example \"daily-report-generation\"\n * @example (userId: string) => `user-profile-update:${userId}`\n * @example (fileId: string, operation: string) => `file-${operation}:${fileId}`\n *\n * @example\n * ```ts\n * // Static lock key - all instances compete for the same lock\n * globalCleanup = $lock({\n * name: \"system-cleanup\",\n * handler: async () => { } // perform cleanup\n * });\n *\n * // Dynamic lock key - per-user locks, users don't block each other\n * updateUserProfile = $lock({\n * name: (userId: string) => `user-update:${userId}`,\n * handler: async (userId: string, data: UserData) => {\n * // Only one update per user at a time, but different users can update concurrently\n * }\n * });\n * ```\n */\n name?: string | ((...args: Parameters<TFunc>) => string);\n\n /**\n * Maximum duration the lock can be held before it expires automatically.\n *\n * This prevents deadlocks when a process dies while holding a lock or when\n * operations take longer than expected. The lock will be automatically released\n * after this duration, allowing other instances to proceed.\n *\n * **Duration Guidelines**:\n * - Set based on expected operation duration plus safety margin\n * - Too short: Operations may be interrupted by early expiration\n * - Too long: Failed processes block others for extended periods\n * - Consider worst-case scenarios and external dependency timeouts\n *\n * **Typical Values**:\n * - Quick operations: 30 seconds - 2 minutes\n * - Database operations: 5 - 15 minutes\n * - File processing: 10 - 30 minutes\n * - Large migrations: 30 minutes - 2 hours\n *\n * @default [5, \"minutes\"]\n *\n * @example [30, \"seconds\"] // Quick operations\n * @example [10, \"minutes\"] // Database migrations\n * @example [1, \"hour\"] // Long-running batch jobs\n *\n * @example\n * ```ts\n * quickTask = $lock({\n * maxDuration: [2, \"minutes\"], // Quick timeout for fast operations\n * handler: async () => { } // perform quick task\n * });\n *\n * heavyProcessing = $lock({\n * maxDuration: [30, \"minutes\"], // Longer timeout for heavy work\n * handler: async () => { } // perform heavy processing\n * });\n * ```\n */\n maxDuration?: DurationLike;\n\n /**\n * Additional time to keep the lock active after the handler completes successfully.\n *\n * This provides a \"cooling off\" period that can be useful for:\n * - Preventing immediate re-execution of the same operation\n * - Giving time for related systems to process the results\n * - Avoiding race conditions with dependent operations\n * - Providing a buffer for cleanup operations\n *\n * Can be either:\n * - **Static duration**: Fixed grace period for all executions\n * - **Dynamic function**: Grace period determined by execution arguments\n * - **undefined**: No grace period, lock released immediately after completion\n *\n * **Grace Period Use Cases**:\n * - File processing: Prevent immediate reprocessing of uploaded files\n * - Cache updates: Allow time for cache propagation\n * - Batch operations: Prevent overlapping batch processing\n * - External API calls: Respect rate limiting requirements\n *\n * @default undefined (no grace period)\n *\n * @example [5, \"minutes\"] // Fixed 5-minute grace period\n * @example [30, \"seconds\"] // Short grace for quick operations\n * @example (userId: string) => userId.startsWith(\"premium\") ? [10, \"minutes\"] : [2, \"minutes\"]\n *\n * @example\n * ```ts\n * fileProcessor = $lock({\n * gracePeriod: [10, \"minutes\"], // Prevent reprocessing same file immediately\n * handler: async (filePath: string) => {\n * await this.processFile(filePath);\n * }\n * });\n *\n * userOperation = $lock({\n * gracePeriod: (userId: string, operation: string) => {\n * // Dynamic grace based on operation type\n * return operation === 'migration' ? [30, \"minutes\"] : [5, \"minutes\"];\n * },\n * handler: async (userId: string, operation: string) => {\n * await this.performUserOperation(userId, operation);\n * }\n * });\n * ```\n */\n gracePeriod?:\n | ((...args: Parameters<TFunc>) => DurationLike | undefined)\n | DurationLike;\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n LOCK_PREFIX_KEY: t.text({ default: \"lock\" }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class LockPrimitive<TFunc extends AsyncFn> extends Primitive<\n LockPrimitiveOptions<TFunc>\n> {\n protected readonly log = $logger();\n protected readonly provider = $inject(LockProvider);\n protected readonly env = $env(envSchema);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly id = crypto.randomUUID();\n public readonly maxDuration = this.dateTimeProvider.duration(\n this.options.maxDuration ?? [5, \"minutes\"],\n );\n\n protected readonly topicLockEnd = $topic({\n name: `${this.env.LOCK_PREFIX_KEY}:lock-end`,\n provider: LockTopicProvider,\n schema: {\n payload: t.object({\n name: t.text(),\n }),\n },\n });\n\n public async run(...args: Parameters<TFunc>): Promise<void> {\n const key = this.key(...args);\n const handler = this.options.handler;\n\n const lock = await this.lock(key);\n if (lock.endedAt) {\n return;\n }\n\n if (lock.id !== this.id) {\n if (this.options.wait) {\n try {\n await this.wait(key, this.maxDuration);\n } catch (error) {\n if (error instanceof TopicTimeoutError) {\n this.log.warn(\n `Lock timeout for '${key}' has been reached. Retry...`,\n );\n await this.run(...args);\n } else {\n throw error;\n }\n }\n }\n\n return;\n }\n\n this.log.debug(`Lock '${key}' ...`);\n\n try {\n await handler(...args);\n } finally {\n await this.topicLockEnd.publish({\n name: key,\n });\n\n await this.setGracePeriod(key, lock, ...args);\n\n this.log.debug(`Lock '${key}' OK`);\n }\n }\n\n /**\n * Set the lock for the given key.\n */\n protected async lock(key: string): Promise<LockResult> {\n const value = await this.provider.set(\n key,\n `${this.id},${this.dateTimeProvider.nowISOString()}`,\n true,\n this.maxDuration.as(\"milliseconds\"),\n );\n\n return this.parse(value);\n }\n\n protected async setGracePeriod(\n key: string,\n lock: LockResult,\n ...args: Parameters<TFunc>\n ): Promise<void> {\n const gracePeriod = this.options.gracePeriod\n ? this.dateTimeProvider.isDurationLike(this.options.gracePeriod)\n ? this.options.gracePeriod\n : this.options.gracePeriod(...args)\n : undefined;\n\n if (gracePeriod) {\n await this.provider.set(\n key,\n `${this.id},${lock.createdAt.toISOString()},${this.dateTimeProvider.nowISOString()}`,\n false,\n this.dateTimeProvider.duration(gracePeriod).as(\"milliseconds\"),\n );\n } else {\n await this.provider.del(key);\n }\n }\n\n protected async wait(key: string, maxDuration: DurationLike): Promise<void> {\n this.log.debug(`Wait for lock '${key}' ...`);\n\n await this.topicLockEnd.wait({\n filter: (message) => message.payload.name === key,\n timeout: maxDuration,\n });\n\n this.log.debug(`Wait for lock '${key}' OK`);\n }\n\n protected key(...args: Parameters<TFunc>) {\n let base = \"\";\n\n if (this.options.name) {\n if (typeof this.options.name === \"string\") {\n base = this.options.name;\n } else {\n base = this.options.name(...args);\n }\n } else {\n base = `${this.config.service.name}:${this.config.propertyKey}`;\n }\n\n return `${this.env.LOCK_PREFIX_KEY}:${base}`;\n }\n\n protected parse(value: string): LockResult {\n const [id, createdAtStr, endedAtStr] = value.split(\",\");\n const createdAt = this.dateTimeProvider.of(createdAtStr);\n const endedAt = endedAtStr\n ? this.dateTimeProvider.of(endedAtStr)\n : undefined;\n\n return {\n id,\n createdAt,\n endedAt,\n };\n }\n}\n\n$lock[KIND] = LockPrimitive;\n\nexport interface LockResult {\n id: string;\n createdAt: DateTime;\n endedAt?: DateTime;\n response?: string;\n}\n","import { $inject } from \"alepha\";\nimport { DateTimeProvider, type Timeout } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport type { LockProvider } from \"./LockProvider.ts\";\n\n/**\n * A simple in-memory store provider.\n */\nexport class MemoryLockProvider implements LockProvider {\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n protected readonly log = $logger();\n\n /**\n * The in-memory store.\n */\n protected store: Record<string, string> = {};\n\n /**\n * Timeouts used to expire keys.\n */\n protected storeTimeout: Record<string, Timeout> = {};\n\n public async set(\n key: string,\n value: string,\n nx?: boolean,\n px?: number,\n ): Promise<string> {\n if (nx && this.store[key] != null) {\n return this.store[key];\n }\n\n if (px) {\n this.ttl(key, px);\n }\n\n this.store[key] = value;\n\n return this.store[key];\n }\n\n public async del(...keys: string[]): Promise<void> {\n for (const key of keys) {\n delete this.store[key];\n if (this.storeTimeout[key] != null) {\n this.storeTimeout[key].clear();\n delete this.storeTimeout[key];\n }\n }\n }\n\n private ttl(key: string, ms: number): void {\n if (this.storeTimeout[key] != null) {\n this.storeTimeout[key].clear();\n delete this.storeTimeout[key];\n }\n\n this.storeTimeout[key] = this.dateTimeProvider.createTimeout(() => {\n delete this.store[key];\n delete this.storeTimeout[key];\n }, ms);\n }\n}\n","import { $module } from \"alepha\";\nimport { MemoryTopicProvider } from \"alepha/topic\";\nimport { $lock } from \"./primitives/$lock.ts\";\nimport { LockProvider } from \"./providers/LockProvider.ts\";\nimport { LockTopicProvider } from \"./providers/LockTopicProvider.ts\";\nimport { MemoryLockProvider } from \"./providers/MemoryLockProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$lock.ts\";\nexport * from \"./providers/LockProvider.ts\";\nexport * from \"./providers/LockTopicProvider.ts\";\nexport * from \"./providers/MemoryLockProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * | type | quality | stability |\n * |------|---------|-----------|\n * | backend | rare | stable |\n *\n * Resource locking for distributed systems.\n *\n * **Features:**\n * - Distributed locks with timeout\n * - Time-based lock expiration\n * - Automatic release on scope exit\n * - Distributed coordination via Redis\n * - Providers: Memory (dev), Redis (production)\n *\n * @module alepha.lock\n */\nexport const AlephaLock = $module({\n name: \"alepha.lock\",\n primitives: [$lock],\n services: [LockProvider, MemoryLockProvider, LockTopicProvider],\n register: (alepha) =>\n alepha\n .with({\n optional: true,\n provide: LockTopicProvider,\n use: MemoryTopicProvider,\n })\n .with({\n optional: true,\n provide: LockProvider,\n use: MemoryLockProvider,\n }),\n});\n"],"mappings":"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"}