@effect-app/infra 4.0.0-beta.26 → 4.0.0-beta.261

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 (505) hide show
  1. package/CHANGELOG.md +1973 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +30 -11
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +35 -14
  6. package/dist/ClusterCosmos.d.ts +64 -0
  7. package/dist/ClusterCosmos.d.ts.map +1 -0
  8. package/dist/ClusterCosmos.js +501 -0
  9. package/dist/ClusterServiceBus.d.ts +67 -0
  10. package/dist/ClusterServiceBus.d.ts.map +1 -0
  11. package/dist/ClusterServiceBus.js +82 -0
  12. package/dist/ContextProvider.d.ts +34 -0
  13. package/dist/ContextProvider.d.ts.map +1 -0
  14. package/dist/ContextProvider.js +40 -0
  15. package/dist/Emailer/Sendgrid.d.ts +111 -147
  16. package/dist/Emailer/Sendgrid.d.ts.map +1 -1
  17. package/dist/Emailer/Sendgrid.js +24 -19
  18. package/dist/Emailer/fake.d.ts +2 -2
  19. package/dist/Emailer/fake.d.ts.map +1 -1
  20. package/dist/Emailer/fake.js +4 -4
  21. package/dist/MainFiberSet.d.ts +12 -9
  22. package/dist/MainFiberSet.d.ts.map +1 -1
  23. package/dist/MainFiberSet.js +10 -6
  24. package/dist/QueueMaker/SQLQueue.d.ts +8 -9
  25. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  26. package/dist/QueueMaker/SQLQueue.js +138 -120
  27. package/dist/QueueMaker/errors.d.ts +5 -3
  28. package/dist/QueueMaker/errors.d.ts.map +1 -1
  29. package/dist/QueueMaker/errors.js +4 -2
  30. package/dist/QueueMaker/memQueue.d.ts +10 -6
  31. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  32. package/dist/QueueMaker/memQueue.js +84 -68
  33. package/dist/QueueMaker/sbqueue.d.ts +9 -5
  34. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  35. package/dist/QueueMaker/sbqueue.js +60 -58
  36. package/dist/RequestFiberSet.d.ts +10 -7
  37. package/dist/RequestFiberSet.d.ts.map +1 -1
  38. package/dist/RequestFiberSet.js +13 -8
  39. package/dist/SQL/Model.d.ts +468 -0
  40. package/dist/SQL/Model.d.ts.map +1 -0
  41. package/dist/SQL/Model.js +469 -0
  42. package/dist/SQL.d.ts +2 -0
  43. package/dist/SQL.d.ts.map +1 -0
  44. package/dist/{adapters/SQL.js → SQL.js} +1 -1
  45. package/dist/ServiceBus.d.ts +61 -0
  46. package/dist/ServiceBus.d.ts.map +1 -0
  47. package/dist/ServiceBus.js +108 -0
  48. package/dist/Store/Cosmos/query.d.ts +15 -4
  49. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  50. package/dist/Store/Cosmos/query.js +179 -41
  51. package/dist/Store/Cosmos.d.ts +3 -3
  52. package/dist/Store/Cosmos.d.ts.map +1 -1
  53. package/dist/Store/Cosmos.js +344 -246
  54. package/dist/Store/Disk.d.ts +5 -5
  55. package/dist/Store/Disk.d.ts.map +1 -1
  56. package/dist/Store/Disk.js +78 -38
  57. package/dist/Store/Memory.d.ts +7 -10
  58. package/dist/Store/Memory.d.ts.map +1 -1
  59. package/dist/Store/Memory.js +326 -66
  60. package/dist/Store/SQL/Pg.d.ts +4 -0
  61. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  62. package/dist/Store/SQL/Pg.js +232 -0
  63. package/dist/Store/SQL/query.d.ts +49 -0
  64. package/dist/Store/SQL/query.d.ts.map +1 -0
  65. package/dist/Store/SQL/query.js +527 -0
  66. package/dist/Store/SQL.d.ts +21 -0
  67. package/dist/Store/SQL.d.ts.map +1 -0
  68. package/dist/Store/SQL.js +449 -0
  69. package/dist/Store/codeFilter.d.ts +5 -5
  70. package/dist/Store/codeFilter.d.ts.map +1 -1
  71. package/dist/Store/codeFilter.js +6 -3
  72. package/dist/Store/index.d.ts +7 -5
  73. package/dist/Store/index.d.ts.map +1 -1
  74. package/dist/Store/index.js +18 -5
  75. package/dist/Store/utils.d.ts +4 -3
  76. package/dist/Store/utils.d.ts.map +1 -1
  77. package/dist/Store/utils.js +5 -5
  78. package/dist/WorkflowEngineCosmos.d.ts +29 -0
  79. package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
  80. package/dist/WorkflowEngineCosmos.js +521 -0
  81. package/dist/WorkflowEngineSqlite.d.ts +24 -0
  82. package/dist/WorkflowEngineSqlite.d.ts.map +1 -0
  83. package/dist/WorkflowEngineSqlite.js +550 -0
  84. package/dist/arbs.d.ts +2 -2
  85. package/dist/arbs.d.ts.map +1 -1
  86. package/dist/arbs.js +5 -3
  87. package/dist/codec.d.ts +5 -0
  88. package/dist/codec.d.ts.map +1 -0
  89. package/dist/codec.js +5 -0
  90. package/dist/cosmos-client.d.ts +16 -0
  91. package/dist/cosmos-client.d.ts.map +1 -0
  92. package/dist/cosmos-client.js +11 -0
  93. package/dist/errorReporter.d.ts +7 -5
  94. package/dist/errorReporter.d.ts.map +1 -1
  95. package/dist/errorReporter.js +23 -27
  96. package/dist/errors.d.ts +1 -1
  97. package/dist/fileUtil.d.ts +2 -2
  98. package/dist/fileUtil.d.ts.map +1 -1
  99. package/dist/fileUtil.js +2 -2
  100. package/dist/index.d.ts +3 -2
  101. package/dist/index.d.ts.map +1 -1
  102. package/dist/index.js +3 -2
  103. package/dist/internal/RequestContextMiddleware.d.ts +5 -0
  104. package/dist/internal/RequestContextMiddleware.d.ts.map +1 -0
  105. package/dist/internal/RequestContextMiddleware.js +45 -0
  106. package/dist/internal/auth.d.ts +53 -0
  107. package/dist/internal/auth.d.ts.map +1 -0
  108. package/dist/internal/auth.js +180 -0
  109. package/dist/internal/events.d.ts +11 -0
  110. package/dist/internal/events.d.ts.map +1 -0
  111. package/dist/internal/events.js +49 -0
  112. package/dist/internal/health.d.ts +3 -0
  113. package/dist/internal/health.d.ts.map +1 -0
  114. package/dist/internal/health.js +5 -0
  115. package/dist/layerUtils.d.ts +32 -0
  116. package/dist/layerUtils.d.ts.map +1 -0
  117. package/dist/layerUtils.js +17 -0
  118. package/dist/logger/jsonLogger.d.ts +2 -2
  119. package/dist/logger/jsonLogger.d.ts.map +1 -1
  120. package/dist/logger/jsonLogger.js +5 -3
  121. package/dist/logger/logFmtLogger.d.ts +2 -2
  122. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  123. package/dist/logger/logFmtLogger.js +3 -3
  124. package/dist/logger/shared.d.ts +3 -3
  125. package/dist/logger/shared.d.ts.map +1 -1
  126. package/dist/logger/shared.js +5 -5
  127. package/dist/logger.d.ts +1 -1
  128. package/dist/logger.d.ts.map +1 -1
  129. package/dist/memQueue.d.ts +15 -0
  130. package/dist/memQueue.d.ts.map +1 -0
  131. package/dist/memQueue.js +21 -0
  132. package/dist/middlewares.d.ts +10 -0
  133. package/dist/middlewares.d.ts.map +1 -0
  134. package/dist/{api/middlewares.js → middlewares.js} +1 -1
  135. package/dist/mongo-client.d.ts +11 -0
  136. package/dist/mongo-client.d.ts.map +1 -0
  137. package/dist/mongo-client.js +15 -0
  138. package/dist/otel.d.ts +75 -0
  139. package/dist/otel.d.ts.map +1 -0
  140. package/dist/otel.js +65 -0
  141. package/dist/rateLimit.d.ts +12 -4
  142. package/dist/rateLimit.d.ts.map +1 -1
  143. package/dist/rateLimit.js +7 -12
  144. package/dist/redis-client.d.ts +42 -0
  145. package/dist/redis-client.d.ts.map +1 -0
  146. package/dist/redis-client.js +98 -0
  147. package/dist/reportError.d.ts +4 -0
  148. package/dist/reportError.d.ts.map +1 -0
  149. package/dist/reportError.js +28 -0
  150. package/dist/routing/middleware/RouterMiddleware.d.ts +16 -0
  151. package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
  152. package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
  153. package/dist/routing/middleware/middleware.d.ts +48 -0
  154. package/dist/routing/middleware/middleware.d.ts.map +1 -0
  155. package/dist/routing/middleware/middleware.js +128 -0
  156. package/dist/routing/middleware.d.ts +3 -0
  157. package/dist/routing/middleware.d.ts.map +1 -0
  158. package/dist/{api/routing → routing}/middleware.js +1 -2
  159. package/dist/routing/schema/jwt.d.ts +4 -0
  160. package/dist/routing/schema/jwt.d.ts.map +1 -0
  161. package/dist/routing/schema/jwt.js +13 -0
  162. package/dist/routing/tsort.d.ts +8 -0
  163. package/dist/routing/tsort.d.ts.map +1 -0
  164. package/dist/routing/tsort.js +51 -0
  165. package/dist/routing/utils.d.ts +19 -0
  166. package/dist/routing/utils.d.ts.map +1 -0
  167. package/dist/routing/utils.js +45 -0
  168. package/dist/routing.d.ts +184 -0
  169. package/dist/routing.d.ts.map +1 -0
  170. package/dist/routing.js +236 -0
  171. package/dist/test.d.ts +3 -3
  172. package/dist/test.d.ts.map +1 -1
  173. package/dist/test.js +2 -2
  174. package/dist/util.d.ts +3 -0
  175. package/dist/util.d.ts.map +1 -0
  176. package/dist/util.js +14 -0
  177. package/dist/vitest.d.ts +1 -1
  178. package/docs/cluster-storage.md +26 -0
  179. package/docs/workflow-engine.md +262 -0
  180. package/examples/query.ts +47 -39
  181. package/package.json +31 -345
  182. package/run.sh +1 -0
  183. package/src/CUPS.ts +52 -13
  184. package/src/ClusterCosmos.ts +984 -0
  185. package/src/ClusterServiceBus.ts +242 -0
  186. package/src/{api/ContextProvider.ts → ContextProvider.ts} +19 -16
  187. package/src/Emailer/Sendgrid.ts +82 -59
  188. package/src/Emailer/fake.ts +3 -3
  189. package/src/MainFiberSet.ts +12 -10
  190. package/src/QueueMaker/SQLQueue.ts +153 -156
  191. package/src/QueueMaker/errors.ts +3 -1
  192. package/src/QueueMaker/memQueue.ts +113 -107
  193. package/src/QueueMaker/sbqueue.ts +78 -90
  194. package/src/RequestFiberSet.ts +13 -8
  195. package/src/{adapters/SQL → SQL}/Model.ts +42 -41
  196. package/src/ServiceBus.ts +219 -0
  197. package/src/Store/Cosmos/query.ts +216 -52
  198. package/src/Store/Cosmos.ts +493 -353
  199. package/src/Store/Disk.ts +109 -69
  200. package/src/Store/Memory.ts +365 -96
  201. package/src/Store/SQL/Pg.ts +363 -0
  202. package/src/Store/SQL/query.ts +603 -0
  203. package/src/Store/SQL.ts +735 -0
  204. package/src/Store/codeFilter.ts +8 -5
  205. package/src/Store/index.ts +21 -6
  206. package/src/Store/utils.ts +26 -24
  207. package/src/WorkflowEngineCosmos.ts +719 -0
  208. package/src/WorkflowEngineSqlite.ts +813 -0
  209. package/src/arbs.ts +5 -3
  210. package/src/{adapters/cosmos-client.ts → cosmos-client.ts} +5 -3
  211. package/src/errorReporter.ts +66 -76
  212. package/src/fileUtil.ts +1 -1
  213. package/src/index.ts +2 -1
  214. package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
  215. package/src/internal/auth.ts +272 -0
  216. package/src/{api/internal → internal}/events.ts +22 -13
  217. package/src/{api/layerUtils.ts → layerUtils.ts} +14 -10
  218. package/src/logger/jsonLogger.ts +4 -2
  219. package/src/logger/logFmtLogger.ts +2 -2
  220. package/src/logger/shared.ts +5 -4
  221. package/src/{adapters/memQueue.ts → memQueue.ts} +5 -4
  222. package/src/{adapters/mongo-client.ts → mongo-client.ts} +4 -2
  223. package/src/otel.ts +152 -0
  224. package/src/rateLimit.ts +34 -23
  225. package/src/{adapters/redis-client.ts → redis-client.ts} +7 -3
  226. package/src/{api/reportError.ts → reportError.ts} +3 -2
  227. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +5 -4
  228. package/src/{api/routing → routing}/middleware/middleware.ts +62 -17
  229. package/src/routing/middleware.ts +4 -0
  230. package/src/{api/routing → routing}/schema/jwt.ts +2 -1
  231. package/src/{api/routing → routing}/utils.ts +2 -1
  232. package/src/routing.ts +768 -0
  233. package/src/test.ts +2 -2
  234. package/test/auth.test.ts +101 -0
  235. package/test/cluster-cosmos.test.ts +590 -0
  236. package/test/cluster-servicebus.test.ts +180 -0
  237. package/test/cluster-sqlite.test.ts +207 -0
  238. package/test/contextProvider.test.ts +15 -12
  239. package/test/controller.test.ts +28 -32
  240. package/test/cosmos-query.test.ts +159 -0
  241. package/test/dist/_check-agg-infer.test-d.d.ts +2 -0
  242. package/test/dist/_check-agg-infer.test-d.d.ts.map +1 -0
  243. package/test/dist/_check-agg-infer.test-d.js +19 -0
  244. package/test/dist/_check-proj-infer.test-d.d.ts +2 -0
  245. package/test/dist/_check-proj-infer.test-d.d.ts.map +1 -0
  246. package/test/dist/_check-proj-infer.test-d.js +16 -0
  247. package/test/dist/_check-tighten.test-d.d.ts +2 -0
  248. package/test/dist/_check-tighten.test-d.d.ts.map +1 -0
  249. package/test/dist/_check-tighten.test-d.js +21 -0
  250. package/test/dist/auth.test.d.ts.map +1 -0
  251. package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
  252. package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
  253. package/test/dist/cluster-sqlite.test.d.ts.map +1 -0
  254. package/test/dist/contextProvider.test.d.ts.map +1 -1
  255. package/test/dist/controller.test.d.ts.map +1 -1
  256. package/test/dist/cosmos-query.test.d.ts.map +1 -0
  257. package/test/dist/date-query.test.d.ts.map +1 -0
  258. package/test/dist/fixtures.d.ts +30 -12
  259. package/test/dist/fixtures.d.ts.map +1 -1
  260. package/test/dist/fixtures.js +17 -10
  261. package/test/dist/query.test.d.ts.map +1 -1
  262. package/test/dist/rawQuery.test.d.ts.map +1 -1
  263. package/test/dist/repository-ext.test.d.ts.map +1 -0
  264. package/test/dist/requires.test.d.ts.map +1 -1
  265. package/test/dist/router-generator.test.d.ts.map +1 -0
  266. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  267. package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
  268. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  269. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  270. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  271. package/test/dist/sql-store.test.d.ts.map +1 -0
  272. package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
  273. package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
  274. package/test/fixtures.ts +16 -9
  275. package/test/layerUtils.test.ts +2 -2
  276. package/test/query.test.ts +905 -40
  277. package/test/rawQuery.test.ts +340 -22
  278. package/test/repository-ext.test.ts +62 -0
  279. package/test/requires.test.ts +10 -5
  280. package/test/router-generator.test.ts +187 -0
  281. package/test/routing-interruptibility.test.ts +66 -0
  282. package/test/rpc-context-map-streaming.test.ts +262 -0
  283. package/test/rpc-e2e-invalidation.test.ts +256 -0
  284. package/test/rpc-multi-middleware.test.ts +85 -10
  285. package/test/rpc-stream-fullstack.test.ts +304 -0
  286. package/test/sql-store.test.ts +1711 -0
  287. package/test/validateSample.test.ts +26 -14
  288. package/test/workflow-engine-cosmos.test.ts +354 -0
  289. package/test/workflow-engine-sqlite.test.ts +299 -0
  290. package/tsconfig.examples.json +1 -1
  291. package/tsconfig.json +2 -1
  292. package/tsconfig.json.bak +2 -2
  293. package/tsconfig.src.json +35 -35
  294. package/tsconfig.test.json +2 -2
  295. package/dist/Emailer/service.d.ts +0 -55
  296. package/dist/Emailer/service.d.ts.map +0 -1
  297. package/dist/Emailer/service.js +0 -6
  298. package/dist/Emailer.d.ts +0 -2
  299. package/dist/Emailer.d.ts.map +0 -1
  300. package/dist/Emailer.js +0 -2
  301. package/dist/Model/Repository/ext.d.ts +0 -41
  302. package/dist/Model/Repository/ext.d.ts.map +0 -1
  303. package/dist/Model/Repository/ext.js +0 -65
  304. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  305. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  306. package/dist/Model/Repository/internal/internal.js +0 -316
  307. package/dist/Model/Repository/legacy.d.ts +0 -19
  308. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  309. package/dist/Model/Repository/legacy.js +0 -2
  310. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  311. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  312. package/dist/Model/Repository/makeRepo.js +0 -24
  313. package/dist/Model/Repository/service.d.ts +0 -89
  314. package/dist/Model/Repository/service.d.ts.map +0 -1
  315. package/dist/Model/Repository/service.js +0 -2
  316. package/dist/Model/Repository/validation.d.ts +0 -42
  317. package/dist/Model/Repository/validation.d.ts.map +0 -1
  318. package/dist/Model/Repository/validation.js +0 -32
  319. package/dist/Model/Repository.d.ts +0 -6
  320. package/dist/Model/Repository.d.ts.map +0 -1
  321. package/dist/Model/Repository.js +0 -6
  322. package/dist/Model/dsl.d.ts +0 -32
  323. package/dist/Model/dsl.d.ts.map +0 -1
  324. package/dist/Model/dsl.js +0 -44
  325. package/dist/Model/filter/filterApi.d.ts +0 -30
  326. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  327. package/dist/Model/filter/filterApi.js +0 -2
  328. package/dist/Model/filter/types/errors.d.ts +0 -29
  329. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  330. package/dist/Model/filter/types/errors.js +0 -2
  331. package/dist/Model/filter/types/fields.d.ts +0 -15
  332. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  333. package/dist/Model/filter/types/fields.js +0 -2
  334. package/dist/Model/filter/types/path/common.d.ts +0 -316
  335. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  336. package/dist/Model/filter/types/path/common.js +0 -2
  337. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  338. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  339. package/dist/Model/filter/types/path/eager.js +0 -31
  340. package/dist/Model/filter/types/path/index.d.ts +0 -4
  341. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  342. package/dist/Model/filter/types/path/index.js +0 -3
  343. package/dist/Model/filter/types/utils.d.ts +0 -79
  344. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  345. package/dist/Model/filter/types/utils.js +0 -2
  346. package/dist/Model/filter/types/validator.d.ts +0 -30
  347. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  348. package/dist/Model/filter/types/validator.js +0 -2
  349. package/dist/Model/filter/types.d.ts +0 -5
  350. package/dist/Model/filter/types.d.ts.map +0 -1
  351. package/dist/Model/filter/types.js +0 -7
  352. package/dist/Model/query/dsl.d.ts +0 -248
  353. package/dist/Model/query/dsl.d.ts.map +0 -1
  354. package/dist/Model/query/dsl.js +0 -104
  355. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  356. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  357. package/dist/Model/query/new-kid-interpreter.js +0 -165
  358. package/dist/Model/query.d.ts +0 -15
  359. package/dist/Model/query.d.ts.map +0 -1
  360. package/dist/Model/query.js +0 -3
  361. package/dist/Model.d.ts +0 -4
  362. package/dist/Model.d.ts.map +0 -1
  363. package/dist/Model.js +0 -4
  364. package/dist/Operations.d.ts +0 -55
  365. package/dist/Operations.d.ts.map +0 -1
  366. package/dist/Operations.js +0 -102
  367. package/dist/OperationsRepo.d.ts +0 -41
  368. package/dist/OperationsRepo.d.ts.map +0 -1
  369. package/dist/OperationsRepo.js +0 -14
  370. package/dist/QueueMaker/service.d.ts +0 -11
  371. package/dist/QueueMaker/service.d.ts.map +0 -1
  372. package/dist/QueueMaker/service.js +0 -4
  373. package/dist/RequestContext.d.ts +0 -63
  374. package/dist/RequestContext.d.ts.map +0 -1
  375. package/dist/RequestContext.js +0 -49
  376. package/dist/Store/ContextMapContainer.d.ts +0 -14
  377. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  378. package/dist/Store/ContextMapContainer.js +0 -16
  379. package/dist/Store/service.d.ts +0 -108
  380. package/dist/Store/service.d.ts.map +0 -1
  381. package/dist/Store/service.js +0 -71
  382. package/dist/Store.d.ts +0 -2
  383. package/dist/Store.d.ts.map +0 -1
  384. package/dist/Store.js +0 -2
  385. package/dist/adapters/SQL/Model.d.ts +0 -479
  386. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  387. package/dist/adapters/SQL/Model.js +0 -478
  388. package/dist/adapters/SQL.d.ts +0 -2
  389. package/dist/adapters/SQL.d.ts.map +0 -1
  390. package/dist/adapters/ServiceBus.d.ts +0 -58
  391. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  392. package/dist/adapters/ServiceBus.js +0 -99
  393. package/dist/adapters/cosmos-client.d.ts +0 -14
  394. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  395. package/dist/adapters/cosmos-client.js +0 -9
  396. package/dist/adapters/index.d.ts +0 -2
  397. package/dist/adapters/index.d.ts.map +0 -1
  398. package/dist/adapters/index.js +0 -2
  399. package/dist/adapters/logger.d.ts +0 -9
  400. package/dist/adapters/logger.d.ts.map +0 -1
  401. package/dist/adapters/logger.js +0 -3
  402. package/dist/adapters/memQueue.d.ts +0 -13
  403. package/dist/adapters/memQueue.d.ts.map +0 -1
  404. package/dist/adapters/memQueue.js +0 -20
  405. package/dist/adapters/mongo-client.d.ts +0 -10
  406. package/dist/adapters/mongo-client.d.ts.map +0 -1
  407. package/dist/adapters/mongo-client.js +0 -13
  408. package/dist/adapters/redis-client.d.ts +0 -39
  409. package/dist/adapters/redis-client.d.ts.map +0 -1
  410. package/dist/adapters/redis-client.js +0 -94
  411. package/dist/api/ContextProvider.d.ts +0 -31
  412. package/dist/api/ContextProvider.d.ts.map +0 -1
  413. package/dist/api/ContextProvider.js +0 -38
  414. package/dist/api/codec.d.ts +0 -5
  415. package/dist/api/codec.d.ts.map +0 -1
  416. package/dist/api/codec.js +0 -5
  417. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  418. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  419. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  420. package/dist/api/internal/auth.d.ts +0 -15
  421. package/dist/api/internal/auth.d.ts.map +0 -1
  422. package/dist/api/internal/auth.js +0 -47
  423. package/dist/api/internal/events.d.ts +0 -9
  424. package/dist/api/internal/events.d.ts.map +0 -1
  425. package/dist/api/internal/events.js +0 -42
  426. package/dist/api/internal/health.d.ts +0 -3
  427. package/dist/api/internal/health.d.ts.map +0 -1
  428. package/dist/api/internal/health.js +0 -5
  429. package/dist/api/layerUtils.d.ts +0 -24
  430. package/dist/api/layerUtils.d.ts.map +0 -1
  431. package/dist/api/layerUtils.js +0 -16
  432. package/dist/api/middlewares.d.ts +0 -10
  433. package/dist/api/middlewares.d.ts.map +0 -1
  434. package/dist/api/reportError.d.ts +0 -4
  435. package/dist/api/reportError.d.ts.map +0 -1
  436. package/dist/api/reportError.js +0 -27
  437. package/dist/api/routing/middleware/RouterMiddleware.d.ts +0 -15
  438. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  439. package/dist/api/routing/middleware/middleware.d.ts +0 -9
  440. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  441. package/dist/api/routing/middleware/middleware.js +0 -92
  442. package/dist/api/routing/middleware.d.ts +0 -4
  443. package/dist/api/routing/middleware.d.ts.map +0 -1
  444. package/dist/api/routing/schema/jwt.d.ts +0 -4
  445. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  446. package/dist/api/routing/schema/jwt.js +0 -12
  447. package/dist/api/routing/tsort.d.ts +0 -8
  448. package/dist/api/routing/tsort.d.ts.map +0 -1
  449. package/dist/api/routing/tsort.js +0 -51
  450. package/dist/api/routing/utils.d.ts +0 -19
  451. package/dist/api/routing/utils.d.ts.map +0 -1
  452. package/dist/api/routing/utils.js +0 -44
  453. package/dist/api/routing.d.ts +0 -138
  454. package/dist/api/routing.d.ts.map +0 -1
  455. package/dist/api/routing.js +0 -166
  456. package/dist/api/setupRequest.d.ts +0 -12
  457. package/dist/api/setupRequest.d.ts.map +0 -1
  458. package/dist/api/setupRequest.js +0 -44
  459. package/dist/api/util.d.ts +0 -3
  460. package/dist/api/util.d.ts.map +0 -1
  461. package/dist/api/util.js +0 -14
  462. package/eslint.config.mjs +0 -24
  463. package/src/Emailer/service.ts +0 -52
  464. package/src/Emailer.ts +0 -1
  465. package/src/Model/Repository/ext.ts +0 -283
  466. package/src/Model/Repository/internal/internal.ts +0 -577
  467. package/src/Model/Repository/legacy.ts +0 -27
  468. package/src/Model/Repository/makeRepo.ts +0 -139
  469. package/src/Model/Repository/service.ts +0 -627
  470. package/src/Model/Repository/validation.ts +0 -31
  471. package/src/Model/Repository.ts +0 -5
  472. package/src/Model/dsl.ts +0 -128
  473. package/src/Model/filter/filterApi.ts +0 -60
  474. package/src/Model/filter/types/errors.ts +0 -47
  475. package/src/Model/filter/types/fields.ts +0 -50
  476. package/src/Model/filter/types/path/common.ts +0 -404
  477. package/src/Model/filter/types/path/eager.ts +0 -298
  478. package/src/Model/filter/types/path/index.ts +0 -4
  479. package/src/Model/filter/types/utils.ts +0 -128
  480. package/src/Model/filter/types/validator.ts +0 -46
  481. package/src/Model/filter/types.ts +0 -6
  482. package/src/Model/query/dsl.ts +0 -2110
  483. package/src/Model/query/new-kid-interpreter.ts +0 -210
  484. package/src/Model/query.ts +0 -13
  485. package/src/Model.ts +0 -3
  486. package/src/Operations.ts +0 -235
  487. package/src/OperationsRepo.ts +0 -16
  488. package/src/QueueMaker/service.ts +0 -17
  489. package/src/RequestContext.ts +0 -63
  490. package/src/Store/ContextMapContainer.ts +0 -20
  491. package/src/Store/service.ts +0 -184
  492. package/src/Store.ts +0 -1
  493. package/src/adapters/ServiceBus.ts +0 -209
  494. package/src/adapters/index.ts +0 -0
  495. package/src/adapters/logger.ts +0 -3
  496. package/src/api/internal/auth.ts +0 -68
  497. package/src/api/routing/middleware.ts +0 -6
  498. package/src/api/routing.ts +0 -598
  499. package/src/api/setupRequest.ts +0 -84
  500. /package/src/{adapters/SQL.ts → SQL.ts} +0 -0
  501. /package/src/{api/codec.ts → codec.ts} +0 -0
  502. /package/src/{api/internal → internal}/health.ts +0 -0
  503. /package/src/{api/middlewares.ts → middlewares.ts} +0 -0
  504. /package/src/{api/routing → routing}/tsort.ts +0 -0
  505. /package/src/{api/util.ts → util.ts} +0 -0
@@ -0,0 +1,984 @@
1
+ import * as Arr from "effect-app/Array"
2
+ import * as Effect from "effect-app/Effect"
3
+ import * as Layer from "effect-app/Layer"
4
+ import * as Option from "effect-app/Option"
5
+ import * as Cause from "effect/Cause"
6
+ import * as Duration from "effect/Duration"
7
+ import * as Redacted from "effect/Redacted"
8
+ import { PersistenceError } from "effect/unstable/cluster/ClusterError"
9
+ import type * as Envelope from "effect/unstable/cluster/Envelope"
10
+ import * as MessageStorage from "effect/unstable/cluster/MessageStorage"
11
+ import { SaveResultEncoded } from "effect/unstable/cluster/MessageStorage"
12
+ import type * as Reply from "effect/unstable/cluster/Reply"
13
+ import * as RunnerStorage from "effect/unstable/cluster/RunnerStorage"
14
+ import * as ShardId from "effect/unstable/cluster/ShardId"
15
+ import * as ShardingConfig from "effect/unstable/cluster/ShardingConfig"
16
+ import * as Snowflake from "effect/unstable/cluster/Snowflake"
17
+ import { CosmosClient, CosmosClientLayer } from "./cosmos-client.js"
18
+ import { annotateCosmosResponse, annotateDb } from "./otel.js"
19
+
20
+ export interface ClusterCosmosConfig {
21
+ readonly url: Redacted.Redacted<string>
22
+ readonly dbName: string
23
+ readonly prefix?: string
24
+ }
25
+
26
+ type MessageKind = "Request" | "AckChunk" | "Interrupt"
27
+ type CosmosQueryValue = string | number | boolean | null | Array<string | number | boolean | null>
28
+ type CosmosParameter = { readonly name: string; readonly value: CosmosQueryValue }
29
+
30
+ interface MessageDoc {
31
+ readonly id: string
32
+ readonly _partitionKey: string
33
+ readonly type: "message"
34
+ readonly rowid: string
35
+ readonly messageId: string | null
36
+ readonly shardId: string
37
+ readonly entityType: string
38
+ readonly entityId: string
39
+ readonly kind: MessageKind
40
+ readonly tag: string | null
41
+ readonly payload: unknown
42
+ readonly headers: Record<string, string> | null
43
+ readonly traceId?: string | undefined
44
+ readonly spanId?: string | undefined
45
+ readonly sampled?: boolean | undefined
46
+ processed: boolean
47
+ readonly requestId: string
48
+ readonly replyId: string | null
49
+ lastReplyId: string | null
50
+ lastRead: number | null
51
+ readonly deliverAt: number | null
52
+ readonly _etag?: string
53
+ }
54
+
55
+ type ReplyDoc = WithExitReplyDoc | ChunkReplyDoc
56
+
57
+ interface ReplyDocBase {
58
+ readonly id: string
59
+ readonly _partitionKey: string
60
+ readonly type: "reply"
61
+ readonly rowid: string
62
+ readonly requestId: string
63
+ acked: boolean
64
+ }
65
+
66
+ interface WithExitReplyDoc extends ReplyDocBase {
67
+ readonly kind: "WithExit"
68
+ readonly payload: Reply.WithExitEncoded["exit"]
69
+ readonly sequence: null
70
+ }
71
+
72
+ interface ChunkReplyDoc extends ReplyDocBase {
73
+ readonly kind: "Chunk"
74
+ readonly payload: Reply.ChunkEncoded["values"]
75
+ readonly sequence: number
76
+ }
77
+
78
+ interface RunnerDoc {
79
+ readonly id: string
80
+ readonly _partitionKey: "runner"
81
+ readonly type: "runner"
82
+ readonly address: string
83
+ runner: string
84
+ healthy: boolean
85
+ lastHeartbeat: number
86
+ }
87
+
88
+ interface LockDoc {
89
+ readonly id: string
90
+ readonly _partitionKey: "lock"
91
+ readonly type: "lock"
92
+ readonly shardId: string
93
+ address: string
94
+ acquiredAt: number
95
+ readonly _etag?: string
96
+ }
97
+
98
+ const withTracerDisabled = Effect.withTracerEnabled(false)
99
+ const refailPersistence = <A, E, R>(effect: Effect.Effect<A, E, R>) => PersistenceError.refail(effect)
100
+ const cosmosId = (id: string) => encodeURIComponent(id)
101
+ const messagePartition = (shardId: string) => `message::${shardId}`
102
+ const messageDocId = (envelope: Envelope.Encoded, primaryKey: string | null) =>
103
+ cosmosId(primaryKey === null ? envelopeId(envelope) : `primary::${primaryKey}`)
104
+ const replyPartition = (requestId: string) => `reply::${requestId}`
105
+ const runnerDocId = (address: string) => cosmosId(`runner::${address}`)
106
+ const lockDocId = (shardId: string) => cosmosId(`lock::${shardId}`)
107
+ const tenMinutes = Duration.toMillis(Duration.minutes(10))
108
+
109
+ const isCosmosStatus = (u: unknown, code: number): boolean =>
110
+ Cause.isUnknownError(u)
111
+ ? isCosmosStatus(u.cause, code)
112
+ : typeof u === "object" && u !== null && "code" in u && u.code === code
113
+
114
+ const isConflict = (u: unknown) => isCosmosStatus(u, 409)
115
+ const isNotFound = (u: unknown) => isCosmosStatus(u, 404)
116
+ const isPreconditionFailed = (u: unknown) => isCosmosStatus(u, 412)
117
+
118
+ const respBytes = (
119
+ resp: { diagnostics?: { clientSideRequestStatistics?: { totalResponsePayloadLengthInBytes?: number } } }
120
+ ) => resp.diagnostics?.clientSideRequestStatistics?.totalResponsePayloadLengthInBytes ?? 0
121
+
122
+ const annotateItem = (resp: {
123
+ readonly requestCharge?: number
124
+ readonly statusCode?: number
125
+ readonly diagnostics?: {
126
+ readonly clientSideRequestStatistics?: { readonly totalResponsePayloadLengthInBytes?: number }
127
+ }
128
+ }) =>
129
+ annotateCosmosResponse({
130
+ requestCharge: resp.requestCharge,
131
+ statusCode: resp.statusCode,
132
+ responseBytes: respBytes(resp)
133
+ })
134
+
135
+ const annotateFeed = (resp: {
136
+ readonly resources: readonly unknown[]
137
+ readonly requestCharge?: number
138
+ readonly diagnostics?: {
139
+ readonly clientSideRequestStatistics?: { readonly totalResponsePayloadLengthInBytes?: number }
140
+ }
141
+ }) =>
142
+ annotateCosmosResponse({
143
+ requestCharge: resp.requestCharge,
144
+ returnedRows: resp.resources.length,
145
+ responseBytes: respBytes(resp)
146
+ })
147
+
148
+ const envelopeId = (envelope: Envelope.Encoded) => envelope._tag === "Request" ? envelope.requestId : envelope.id
149
+
150
+ const envelopeToDoc = (
151
+ envelope: Envelope.Encoded,
152
+ primaryKey: string | null,
153
+ deliverAt: number | null
154
+ ): MessageDoc => {
155
+ switch (envelope._tag) {
156
+ case "Request":
157
+ return {
158
+ id: messageDocId(envelope, primaryKey),
159
+ _partitionKey: messagePartition(ShardId.toString(envelope.address.shardId)),
160
+ type: "message",
161
+ rowid: envelope.requestId,
162
+ messageId: primaryKey,
163
+ shardId: ShardId.toString(envelope.address.shardId),
164
+ entityType: envelope.address.entityType,
165
+ entityId: envelope.address.entityId,
166
+ kind: "Request",
167
+ tag: envelope.tag,
168
+ payload: envelope.payload,
169
+ headers: envelope.headers,
170
+ traceId: envelope.traceId,
171
+ spanId: envelope.spanId,
172
+ sampled: envelope.sampled,
173
+ processed: false,
174
+ requestId: envelope.requestId,
175
+ replyId: null,
176
+ lastReplyId: null,
177
+ lastRead: null,
178
+ deliverAt
179
+ }
180
+ case "AckChunk":
181
+ return {
182
+ id: cosmosId(envelope.id),
183
+ _partitionKey: messagePartition(ShardId.toString(envelope.address.shardId)),
184
+ type: "message",
185
+ rowid: envelope.id,
186
+ messageId: primaryKey,
187
+ shardId: ShardId.toString(envelope.address.shardId),
188
+ entityType: envelope.address.entityType,
189
+ entityId: envelope.address.entityId,
190
+ kind: "AckChunk",
191
+ tag: null,
192
+ payload: null,
193
+ headers: null,
194
+ processed: false,
195
+ requestId: envelope.requestId,
196
+ replyId: envelope.replyId,
197
+ lastReplyId: null,
198
+ lastRead: null,
199
+ deliverAt
200
+ }
201
+ case "Interrupt":
202
+ return {
203
+ id: cosmosId(envelope.id),
204
+ _partitionKey: messagePartition(ShardId.toString(envelope.address.shardId)),
205
+ type: "message",
206
+ rowid: envelope.id,
207
+ messageId: primaryKey,
208
+ shardId: ShardId.toString(envelope.address.shardId),
209
+ entityType: envelope.address.entityType,
210
+ entityId: envelope.address.entityId,
211
+ kind: "Interrupt",
212
+ tag: null,
213
+ payload: null,
214
+ headers: null,
215
+ processed: false,
216
+ requestId: envelope.requestId,
217
+ replyId: null,
218
+ lastReplyId: null,
219
+ lastRead: null,
220
+ deliverAt
221
+ }
222
+ }
223
+ }
224
+
225
+ const envelopeFromDoc = (
226
+ doc: MessageDoc,
227
+ lastSentReply: Option.Option<Reply.Encoded>
228
+ ): {
229
+ readonly envelope: Envelope.Encoded
230
+ readonly lastSentReply: Option.Option<Reply.Encoded>
231
+ } => {
232
+ switch (doc.kind) {
233
+ case "Request": {
234
+ const envelope: Envelope.PartialRequestEncoded = {
235
+ _tag: "Request",
236
+ requestId: doc.requestId,
237
+ address: {
238
+ shardId: shardIdFromString(doc.shardId),
239
+ entityType: doc.entityType,
240
+ entityId: doc.entityId
241
+ },
242
+ tag: doc.tag ?? "",
243
+ payload: doc.payload,
244
+ headers: doc.headers ?? {},
245
+ ...(doc.traceId !== undefined && { traceId: doc.traceId }),
246
+ ...(doc.spanId !== undefined && { spanId: doc.spanId }),
247
+ ...(doc.sampled !== undefined && { sampled: doc.sampled })
248
+ }
249
+ return {
250
+ envelope,
251
+ lastSentReply
252
+ }
253
+ }
254
+ case "AckChunk":
255
+ return {
256
+ envelope: {
257
+ _tag: "AckChunk",
258
+ id: doc.rowid,
259
+ requestId: doc.requestId,
260
+ replyId: doc.replyId ?? "",
261
+ address: {
262
+ shardId: shardIdFromString(doc.shardId),
263
+ entityType: doc.entityType,
264
+ entityId: doc.entityId
265
+ }
266
+ },
267
+ lastSentReply: Option.none()
268
+ }
269
+ case "Interrupt":
270
+ return {
271
+ envelope: {
272
+ _tag: "Interrupt",
273
+ id: doc.rowid,
274
+ requestId: doc.requestId,
275
+ address: {
276
+ shardId: shardIdFromString(doc.shardId),
277
+ entityType: doc.entityType,
278
+ entityId: doc.entityId
279
+ }
280
+ },
281
+ lastSentReply: Option.none()
282
+ }
283
+ }
284
+ }
285
+
286
+ const replyToDoc = (reply: Reply.Encoded): ReplyDoc =>
287
+ reply._tag === "WithExit"
288
+ ? {
289
+ id: cosmosId(reply.id),
290
+ _partitionKey: replyPartition(reply.requestId),
291
+ type: "reply",
292
+ rowid: reply.id,
293
+ kind: "WithExit",
294
+ requestId: reply.requestId,
295
+ payload: reply.exit,
296
+ sequence: null,
297
+ acked: false
298
+ }
299
+ : {
300
+ id: cosmosId(reply.id),
301
+ _partitionKey: replyPartition(reply.requestId),
302
+ type: "reply",
303
+ rowid: reply.id,
304
+ kind: "Chunk",
305
+ requestId: reply.requestId,
306
+ payload: reply.values,
307
+ sequence: reply.sequence,
308
+ acked: false
309
+ }
310
+
311
+ const replyFromDoc = (doc: ReplyDoc): Reply.Encoded =>
312
+ doc.kind === "WithExit"
313
+ ? {
314
+ _tag: "WithExit",
315
+ id: doc.rowid,
316
+ requestId: doc.requestId,
317
+ exit: doc.payload
318
+ }
319
+ : {
320
+ _tag: "Chunk",
321
+ id: doc.rowid,
322
+ requestId: doc.requestId,
323
+ values: doc.payload,
324
+ sequence: doc.sequence ?? 0
325
+ }
326
+
327
+ const shardIdFromString = (shardId: string): Envelope.Encoded["address"]["shardId"] =>
328
+ ShardId.fromStringEncoded(shardId)
329
+
330
+ const makeMachineId = (address: string) => {
331
+ let hash = 0
332
+ for (let i = 0; i < address.length; i++) {
333
+ hash = Math.imul(31, hash) + address.charCodeAt(i) | 0
334
+ }
335
+ return Math.abs(hash)
336
+ }
337
+
338
+ const createContainer = (prefix: string) =>
339
+ Effect.fnUntraced(function*() {
340
+ const { db } = yield* CosmosClient
341
+ const containerId = `${prefix}cluster`
342
+ yield* Effect
343
+ .tryPromise(() =>
344
+ db.containers.create({
345
+ id: containerId,
346
+ partitionKey: { paths: ["/_partitionKey"], version: 2 }
347
+ })
348
+ )
349
+ .pipe(Effect.catchIf(isConflict, () => Effect.void))
350
+ return db.container(containerId)
351
+ })
352
+
353
+ export const makeMessageStorage = Effect.fnUntraced(function*(options?: {
354
+ readonly prefix?: string | undefined
355
+ }) {
356
+ const prefix = options?.prefix ?? "cluster-"
357
+ const container = yield* createContainer(prefix)().pipe(Effect.orDie)
358
+ const containerId = `${prefix}cluster`
359
+ const annotate = (operation: string) =>
360
+ annotateDb({ operation, system: "cosmosdb", collection: containerId, entity: "cluster-message-storage" })
361
+
362
+ const readMessage = (id: string, partitionKey: string) =>
363
+ Effect.tryPromise(() => container.item(id, partitionKey).read<MessageDoc>()).pipe(
364
+ Effect.tap(annotateItem),
365
+ Effect.map((resp) => Option.fromNullishOr(resp.resource)),
366
+ Effect.catchIf(isNotFound, () => Effect.succeed(Option.none()))
367
+ )
368
+
369
+ const queryMessages = (query: string, parameters: ReadonlyArray<CosmosParameter>) =>
370
+ Effect
371
+ .tryPromise(() => container.items.query<MessageDoc>({ query, parameters: Array.from(parameters) }).fetchAll())
372
+ .pipe(
373
+ Effect.tap(annotateFeed),
374
+ Effect.map((resp) => resp.resources)
375
+ )
376
+
377
+ const queryReplies = (query: string, parameters: ReadonlyArray<CosmosParameter>) =>
378
+ Effect
379
+ .tryPromise(() => container.items.query<ReplyDoc>({ query, parameters: Array.from(parameters) }).fetchAll())
380
+ .pipe(
381
+ Effect.tap(annotateFeed),
382
+ Effect.map((resp) => resp.resources)
383
+ )
384
+
385
+ const lastReply = (replyId: string | null) =>
386
+ replyId === null
387
+ ? Effect.succeed(Option.none<Reply.Encoded>())
388
+ : queryReplies("SELECT * FROM c WHERE c.type = 'reply' AND c.rowid = @id", [{ name: "@id", value: replyId }])
389
+ .pipe(
390
+ Effect.map((docs) => Option.map(Option.fromNullishOr(docs[0]), replyFromDoc))
391
+ )
392
+
393
+ const markReplyAcked = (requestId: string, replyId: string) =>
394
+ Effect.tryPromise(() => container.item(cosmosId(replyId), replyPartition(requestId)).read<ReplyDoc>()).pipe(
395
+ Effect.flatMap((resp) => {
396
+ const doc = resp.resource
397
+ if (!doc) return Effect.void
398
+ doc.acked = true
399
+ return Effect
400
+ .tryPromise(() =>
401
+ container.item(cosmosId(replyId), replyPartition(requestId)).replace(doc, {
402
+ accessCondition: { type: "IfMatch", condition: doc._etag ?? "" }
403
+ })
404
+ )
405
+ .pipe(Effect.tap(annotateItem), Effect.asVoid)
406
+ }),
407
+ Effect.catchIf(isNotFound, () => Effect.void),
408
+ Effect.catchIf(isPreconditionFailed, () => Effect.void)
409
+ )
410
+
411
+ const replaceMessage = (doc: MessageDoc) =>
412
+ Effect
413
+ .tryPromise(() =>
414
+ container.item(doc.id, doc._partitionKey).replace(doc, {
415
+ accessCondition: { type: "IfMatch", condition: doc._etag ?? "" }
416
+ })
417
+ )
418
+ .pipe(
419
+ Effect.tap(annotateItem),
420
+ Effect.as(true),
421
+ Effect.catchIf(isPreconditionFailed, () => Effect.succeed(false))
422
+ )
423
+
424
+ const updateMessage = (
425
+ doc: MessageDoc,
426
+ update: (doc: MessageDoc) => boolean
427
+ ): Effect.Effect<void, unknown> =>
428
+ Effect.suspend(function loop(current = doc): Effect.Effect<void, unknown> {
429
+ if (!update(current)) return Effect.void
430
+ return replaceMessage(current).pipe(
431
+ Effect.flatMap((replaced) =>
432
+ replaced
433
+ ? Effect.void
434
+ : readMessage(current.id, current._partitionKey).pipe(
435
+ Effect.flatMap((found) =>
436
+ Option.match(found, {
437
+ onNone: () => Effect.void,
438
+ onSome: loop
439
+ })
440
+ )
441
+ )
442
+ )
443
+ )
444
+ })
445
+
446
+ return yield* MessageStorage.makeEncoded({
447
+ saveEnvelope: ({ deliverAt, envelope, primaryKey }) =>
448
+ Effect
449
+ .gen(function*() {
450
+ const doc = envelopeToDoc(envelope, primaryKey, deliverAt)
451
+ if (envelope._tag === "AckChunk") {
452
+ yield* markReplyAcked(envelope.requestId, envelope.replyId)
453
+ const pendingAcks = yield* queryMessages(
454
+ "SELECT * FROM c WHERE c.type = 'message' AND c.kind = 'AckChunk' AND c.processed = false AND c.requestId = @requestId",
455
+ [{ name: "@requestId", value: envelope.requestId }]
456
+ )
457
+ yield* Effect.forEach(pendingAcks, (ack) => {
458
+ ack.processed = true
459
+ return replaceMessage(ack)
460
+ }, { discard: true })
461
+ }
462
+ return yield* Effect.tryPromise(() => container.items.create(doc)).pipe(
463
+ Effect.tap(annotateItem),
464
+ Effect.as<MessageStorage.SaveResult.Encoded>(SaveResultEncoded.Success()),
465
+ Effect.catchIf(isConflict, () =>
466
+ readMessage(doc.id, doc._partitionKey).pipe(
467
+ Effect.flatMap((found) =>
468
+ Option.match(found, {
469
+ onNone: () => Effect.succeed<MessageStorage.SaveResult.Encoded>(SaveResultEncoded.Success()),
470
+ onSome: (existing) =>
471
+ lastReply(existing.lastReplyId).pipe(
472
+ Effect.map((lastReceivedReply) =>
473
+ SaveResultEncoded.Duplicate({
474
+ originalId: Snowflake.Snowflake(existing.requestId),
475
+ lastReceivedReply
476
+ })
477
+ )
478
+ )
479
+ })
480
+ )
481
+ ))
482
+ )
483
+ })
484
+ .pipe(annotate("saveEnvelope"), refailPersistence, withTracerDisabled),
485
+
486
+ saveReply: (reply) =>
487
+ Effect
488
+ .gen(function*() {
489
+ const doc = replyToDoc(reply)
490
+ yield* Effect.tryPromise(() => container.items.create(doc)).pipe(
491
+ Effect.tap(annotateItem),
492
+ Effect.catchIf(isConflict, () => Effect.void)
493
+ )
494
+ const messages = yield* queryMessages(
495
+ "SELECT * FROM c WHERE c.type = 'message' AND c.requestId = @requestId",
496
+ [{ name: "@requestId", value: reply.requestId }]
497
+ )
498
+ yield* Effect.forEach(messages, (message) => {
499
+ return updateMessage(message, (doc) => {
500
+ if (reply._tag === "WithExit") {
501
+ doc.processed = true
502
+ } else if (doc.id !== reply.requestId && doc.kind !== "Request") {
503
+ return false
504
+ }
505
+ doc.lastReplyId = reply.id
506
+ return true
507
+ })
508
+ }, { discard: true })
509
+ })
510
+ .pipe(annotate("saveReply"), refailPersistence, withTracerDisabled),
511
+
512
+ clearReplies: (requestId) =>
513
+ Effect
514
+ .gen(function*() {
515
+ const id = String(requestId)
516
+ const replies = yield* queryReplies(
517
+ "SELECT * FROM c WHERE c.type = 'reply' AND c.requestId = @requestId AND c.kind = 'WithExit'",
518
+ [
519
+ { name: "@requestId", value: id }
520
+ ]
521
+ )
522
+ yield* Effect.forEach(replies, (reply) =>
523
+ Effect
524
+ .tryPromise(() => container.item(reply.id, reply._partitionKey).delete())
525
+ .pipe(
526
+ Effect.tap(annotateItem),
527
+ Effect.catchIf(isNotFound, () => Effect.void)
528
+ ), { discard: true })
529
+ const messages = yield* queryMessages(
530
+ "SELECT * FROM c WHERE c.type = 'message' AND c.requestId = @requestId",
531
+ [{ name: "@requestId", value: id }]
532
+ )
533
+ yield* Effect.forEach(messages, (message) => {
534
+ if (message.kind === "Interrupt") {
535
+ return Effect.tryPromise(() => container.item(message.id, message._partitionKey).delete()).pipe(
536
+ Effect.tap(annotateItem),
537
+ Effect.catchIf(isNotFound, () => Effect.void)
538
+ )
539
+ }
540
+ message.processed = false
541
+ message.lastReplyId = null
542
+ message.lastRead = null
543
+ return replaceMessage(message).pipe(Effect.asVoid)
544
+ }, { discard: true })
545
+ })
546
+ .pipe(annotate("clearReplies"), refailPersistence, withTracerDisabled),
547
+
548
+ requestIdForPrimaryKey: (primaryKey) =>
549
+ queryMessages("SELECT * FROM c WHERE c.type = 'message' AND c.messageId = @primaryKey", [
550
+ { name: "@primaryKey", value: primaryKey }
551
+ ])
552
+ .pipe(
553
+ Effect.map((docs) => Option.map(Option.fromNullishOr(docs[0]?.requestId), Snowflake.Snowflake)),
554
+ annotate("requestIdForPrimaryKey"),
555
+ refailPersistence,
556
+ withTracerDisabled
557
+ ),
558
+
559
+ repliesFor: (requestIds) =>
560
+ queryReplies(
561
+ "SELECT * FROM c WHERE c.type = 'reply' AND ARRAY_CONTAINS(@requestIds, c.requestId) AND (c.kind = 'WithExit' OR (c.kind = 'Chunk' AND c.acked = false)) ORDER BY c.rowid",
562
+ [{ name: "@requestIds", value: Array.from(requestIds) }]
563
+ )
564
+ .pipe(
565
+ Effect.map(Arr.map(replyFromDoc)),
566
+ annotate("repliesFor"),
567
+ refailPersistence,
568
+ withTracerDisabled
569
+ ),
570
+
571
+ repliesForUnfiltered: (requestIds) =>
572
+ queryReplies(
573
+ "SELECT * FROM c WHERE c.type = 'reply' AND ARRAY_CONTAINS(@requestIds, c.requestId) ORDER BY c.rowid",
574
+ [{ name: "@requestIds", value: Array.from(requestIds) }]
575
+ )
576
+ .pipe(
577
+ Effect.map(Arr.map(replyFromDoc)),
578
+ annotate("repliesForUnfiltered"),
579
+ refailPersistence,
580
+ withTracerDisabled
581
+ ),
582
+
583
+ unprocessedMessages: (shardIds, now) =>
584
+ queryMessages(
585
+ "SELECT * FROM c WHERE c.type = 'message' AND ARRAY_CONTAINS(@shardIds, c.shardId) AND c.processed = false AND (NOT IS_DEFINED(c.lastRead) OR IS_NULL(c.lastRead) OR c.lastRead < @lastReadBefore) AND (NOT IS_DEFINED(c.deliverAt) OR IS_NULL(c.deliverAt) OR c.deliverAt <= @now) ORDER BY c.rowid",
586
+ [
587
+ { name: "@shardIds", value: Array.from(shardIds) },
588
+ { name: "@lastReadBefore", value: now - tenMinutes },
589
+ { name: "@now", value: now }
590
+ ]
591
+ )
592
+ .pipe(
593
+ Effect.flatMap((docs) => collectUnprocessed(docs, now, lastReply, replaceMessage, queryReplies)),
594
+ annotate("unprocessedMessages"),
595
+ refailPersistence,
596
+ withTracerDisabled
597
+ ),
598
+
599
+ unprocessedMessagesById: (messageIds, now) =>
600
+ queryMessages(
601
+ "SELECT * FROM c WHERE c.type = 'message' AND (ARRAY_CONTAINS(@messageIds, c.id) OR ARRAY_CONTAINS(@messageIds, c.requestId)) AND c.processed = false AND (NOT IS_DEFINED(c.deliverAt) OR IS_NULL(c.deliverAt) OR c.deliverAt <= @now) ORDER BY c.rowid",
602
+ [
603
+ { name: "@messageIds", value: Array.from(messageIds, String) },
604
+ { name: "@now", value: now }
605
+ ]
606
+ )
607
+ .pipe(
608
+ Effect.flatMap((docs) => collectUnprocessed(docs, now, lastReply, replaceMessage, queryReplies)),
609
+ annotate("unprocessedMessagesById"),
610
+ refailPersistence,
611
+ withTracerDisabled
612
+ ),
613
+
614
+ resetAddress: (address) =>
615
+ queryMessages(
616
+ "SELECT * FROM c WHERE c.type = 'message' AND c.processed = false AND c.shardId = @shardId AND c.entityType = @entityType AND c.entityId = @entityId",
617
+ [
618
+ { name: "@shardId", value: ShardId.toString(address.shardId) },
619
+ { name: "@entityType", value: address.entityType },
620
+ { name: "@entityId", value: address.entityId }
621
+ ]
622
+ )
623
+ .pipe(
624
+ Effect.flatMap((docs) =>
625
+ Effect.forEach(docs, (doc) => {
626
+ doc.lastRead = null
627
+ return replaceMessage(doc).pipe(Effect.asVoid)
628
+ }, { discard: true })
629
+ ),
630
+ annotate("resetAddress"),
631
+ refailPersistence,
632
+ withTracerDisabled
633
+ ),
634
+
635
+ clearAddress: (address) =>
636
+ queryMessages(
637
+ "SELECT * FROM c WHERE c.type = 'message' AND c.entityType = @entityType AND c.entityId = @entityId",
638
+ [
639
+ { name: "@entityType", value: address.entityType },
640
+ { name: "@entityId", value: address.entityId }
641
+ ]
642
+ )
643
+ .pipe(
644
+ Effect.flatMap((messages) =>
645
+ Effect.forEach(messages, (message) =>
646
+ queryReplies("SELECT * FROM c WHERE c.type = 'reply' AND c.requestId = @requestId", [
647
+ {
648
+ name: "@requestId",
649
+ value: message
650
+ .requestId
651
+ }
652
+ ])
653
+ .pipe(
654
+ Effect
655
+ .flatMap((replies) =>
656
+ Effect
657
+ .forEach(replies, (reply) =>
658
+ Effect
659
+ .tryPromise(() =>
660
+ container.item(reply.id, reply._partitionKey).delete()
661
+ )
662
+ .pipe(
663
+ Effect.tap(annotateItem),
664
+ Effect.catchIf(isNotFound, () => Effect.void)
665
+ ), { discard: true })
666
+ ),
667
+ Effect.andThen(
668
+ Effect.tryPromise(() => container.item(message.id, message._partitionKey).delete()).pipe(
669
+ Effect.tap(annotateItem),
670
+ Effect.catchIf(isNotFound, () => Effect.void)
671
+ )
672
+ )
673
+ ), { discard: true })
674
+ ),
675
+ annotate("clearAddress"),
676
+ refailPersistence,
677
+ withTracerDisabled
678
+ ),
679
+
680
+ resetShards: (shardIds) =>
681
+ queryMessages(
682
+ "SELECT * FROM c WHERE c.type = 'message' AND c.processed = false AND ARRAY_CONTAINS(@shardIds, c.shardId)",
683
+ [{ name: "@shardIds", value: Array.from(shardIds) }]
684
+ )
685
+ .pipe(
686
+ Effect.flatMap((docs) =>
687
+ Effect.forEach(docs, (doc) => {
688
+ doc.lastRead = null
689
+ return replaceMessage(doc).pipe(Effect.asVoid)
690
+ }, { discard: true })
691
+ ),
692
+ annotate("resetShards"),
693
+ refailPersistence,
694
+ withTracerDisabled
695
+ ),
696
+
697
+ withTransaction: (effect) => effect
698
+ })
699
+ })
700
+
701
+ const collectUnprocessed = <E>(
702
+ docs: ReadonlyArray<MessageDoc>,
703
+ now: number,
704
+ lastReply: (replyId: string | null) => Effect.Effect<Option.Option<Reply.Encoded>, E>,
705
+ replaceMessage: (doc: MessageDoc) => Effect.Effect<boolean, E>,
706
+ queryReplies: (
707
+ query: string,
708
+ parameters: ReadonlyArray<CosmosParameter>
709
+ ) => Effect.Effect<Array<ReplyDoc>, E>
710
+ ) =>
711
+ Effect.gen(function*() {
712
+ const messages: Array<{
713
+ readonly envelope: Envelope.Encoded
714
+ readonly lastSentReply: Option.Option<Reply.Encoded>
715
+ }> = []
716
+ for (const doc of docs) {
717
+ const replies = yield* queryReplies(
718
+ "SELECT * FROM c WHERE c.type = 'reply' AND c.requestId = @requestId AND (c.kind = 'WithExit' OR (c.kind = 'Chunk' AND c.acked = false))",
719
+ [{ name: "@requestId", value: doc.requestId }]
720
+ )
721
+ if (Arr.isArrayNonEmpty(replies)) continue
722
+ const sentReply = yield* lastReply(doc.lastReplyId)
723
+ doc.lastRead = now
724
+ const replaced = yield* replaceMessage(doc)
725
+ if (replaced) {
726
+ messages.push(envelopeFromDoc(doc, sentReply))
727
+ }
728
+ }
729
+ return messages
730
+ })
731
+
732
+ export const makeRunnerStorage = Effect.fnUntraced(function*(options?: {
733
+ readonly prefix?: string | undefined
734
+ }) {
735
+ const prefix = options?.prefix ?? "cluster-"
736
+ const container = yield* createContainer(prefix)().pipe(Effect.orDie)
737
+ const config = yield* ShardingConfig.ShardingConfig
738
+ const expires = Duration.toMillis(Duration.fromInputUnsafe(config.shardLockExpiration))
739
+ const containerId = `${prefix}cluster`
740
+ const annotate = (operation: string) =>
741
+ annotateDb({ operation, system: "cosmosdb", collection: containerId, entity: "cluster-runner-storage" })
742
+
743
+ const queryRunners = (query: string, parameters: ReadonlyArray<CosmosParameter>) =>
744
+ Effect
745
+ .tryPromise(() =>
746
+ container
747
+ .items
748
+ .query<RunnerDoc>({ query, parameters: Array.from(parameters) }, { partitionKey: "runner" })
749
+ .fetchAll()
750
+ )
751
+ .pipe(Effect.tap(annotateFeed), Effect.map((resp) => resp.resources))
752
+
753
+ const readLock = (shardId: string) =>
754
+ Effect.tryPromise(() => container.item(lockDocId(shardId), "lock").read<LockDoc>()).pipe(
755
+ Effect.tap(annotateItem),
756
+ Effect.map((resp) => Option.fromNullishOr(resp.resource)),
757
+ Effect.catchIf(isNotFound, () => Effect.succeed(Option.none()))
758
+ )
759
+
760
+ const writeLock = (doc: LockDoc) =>
761
+ Effect
762
+ .tryPromise(() =>
763
+ container.item(doc.id, "lock").replace(doc, {
764
+ accessCondition: { type: "IfMatch", condition: doc._etag ?? "" }
765
+ })
766
+ )
767
+ .pipe(
768
+ Effect.tap(annotateItem),
769
+ Effect.as(true),
770
+ Effect.catchIf(isPreconditionFailed, () => Effect.succeed(false))
771
+ )
772
+
773
+ const createLock = (address: string, shardId: string, now: number) =>
774
+ Effect
775
+ .tryPromise(() =>
776
+ container.items.create<LockDoc>({
777
+ id: lockDocId(shardId),
778
+ _partitionKey: "lock",
779
+ type: "lock",
780
+ shardId,
781
+ address,
782
+ acquiredAt: now
783
+ })
784
+ )
785
+ .pipe(
786
+ Effect.tap(annotateItem),
787
+ Effect.as(true),
788
+ Effect.catchIf(isConflict, () => Effect.succeed(false))
789
+ )
790
+
791
+ const tryAcquire = (address: string, shardId: string, now: number) =>
792
+ readLock(shardId).pipe(
793
+ Effect.flatMap((lock) =>
794
+ Option.match(lock, {
795
+ onNone: () => createLock(address, shardId, now),
796
+ onSome: (doc) => {
797
+ if (doc.address !== address && now - doc.acquiredAt <= expires) {
798
+ return Effect.succeed(false)
799
+ }
800
+ doc.address = address
801
+ doc.acquiredAt = now
802
+ return writeLock(doc)
803
+ }
804
+ })
805
+ ),
806
+ Effect.map((acquired) => acquired ? Option.some(shardId) : Option.none())
807
+ )
808
+
809
+ return RunnerStorage.makeEncoded({
810
+ getRunners: Effect.sync(() => Date.now()).pipe(
811
+ Effect.flatMap((now) =>
812
+ queryRunners("SELECT * FROM c WHERE c.type = 'runner' AND c.lastHeartbeat > @expiresAt", [
813
+ { name: "@expiresAt", value: now - expires }
814
+ ])
815
+ ),
816
+ Effect.map((docs) => docs.map((doc) => [doc.runner, doc.healthy] as const)),
817
+ annotate("getRunners"),
818
+ refailPersistence,
819
+ withTracerDisabled
820
+ ),
821
+
822
+ register: (address, runner, healthy) =>
823
+ Effect.sync(() => Date.now()).pipe(
824
+ Effect.flatMap((now) =>
825
+ Effect
826
+ .tryPromise(() =>
827
+ container.items.upsert<RunnerDoc>({
828
+ id: runnerDocId(address),
829
+ _partitionKey: "runner",
830
+ type: "runner",
831
+ address,
832
+ runner,
833
+ healthy,
834
+ lastHeartbeat: now
835
+ })
836
+ )
837
+ .pipe(Effect.tap(annotateItem))
838
+ ),
839
+ Effect.as(makeMachineId(address)),
840
+ annotate("register"),
841
+ refailPersistence,
842
+ withTracerDisabled
843
+ ),
844
+
845
+ unregister: (address) =>
846
+ Effect.tryPromise(() => container.item(runnerDocId(address), "runner").delete()).pipe(
847
+ Effect.tap(annotateItem),
848
+ Effect.catchIf(isNotFound, () => Effect.void),
849
+ annotate("unregister"),
850
+ refailPersistence,
851
+ withTracerDisabled
852
+ ),
853
+
854
+ setRunnerHealth: (address, healthy) =>
855
+ Effect.tryPromise(() => container.item(runnerDocId(address), "runner").read<RunnerDoc>()).pipe(
856
+ Effect.flatMap((resp) => {
857
+ const doc = resp.resource
858
+ if (!doc) return Effect.void
859
+ doc.healthy = healthy
860
+ return Effect.tryPromise(() => container.item(doc.id, "runner").replace(doc)).pipe(Effect.tap(annotateItem))
861
+ }),
862
+ Effect.asVoid,
863
+ Effect.catchIf(isNotFound, () => Effect.void),
864
+ annotate("setRunnerHealth"),
865
+ refailPersistence,
866
+ withTracerDisabled
867
+ ),
868
+
869
+ acquire: (address, shardIds) =>
870
+ Effect.sync(() => Date.now()).pipe(
871
+ Effect.flatMap((now) => Effect.forEach(shardIds, (shardId) => tryAcquire(address, shardId, now))),
872
+ Effect.map(Arr.getSomes),
873
+ annotate("acquire"),
874
+ refailPersistence,
875
+ withTracerDisabled
876
+ ),
877
+
878
+ refresh: (address, shardIds) =>
879
+ Effect
880
+ .gen(function*() {
881
+ const now = Date.now()
882
+ yield* Effect.tryPromise(() => container.item(runnerDocId(address), "runner").read<RunnerDoc>()).pipe(
883
+ Effect.flatMap((resp) => {
884
+ const doc = resp.resource
885
+ if (!doc) return Effect.void
886
+ doc.lastHeartbeat = now
887
+ return Effect.tryPromise(() => container.item(doc.id, "runner").replace(doc)).pipe(
888
+ Effect.tap(annotateItem)
889
+ )
890
+ }),
891
+ Effect.catchIf(isNotFound, () => Effect.void)
892
+ )
893
+ const refreshed = yield* Effect.forEach(shardIds, (shardId) =>
894
+ readLock(shardId).pipe(
895
+ Effect.flatMap((lock) =>
896
+ Option.match(lock, {
897
+ onNone: () => Effect.succeed(Option.none<string>()),
898
+ onSome: (doc) => {
899
+ if (doc.address !== address) return Effect.succeed(Option.none<string>())
900
+ doc.acquiredAt = now
901
+ return writeLock(doc).pipe(Effect.map((ok) => ok ? Option.some(shardId) : Option.none<string>()))
902
+ }
903
+ })
904
+ )
905
+ ))
906
+ return Arr.getSomes(refreshed)
907
+ })
908
+ .pipe(annotate("refresh"), refailPersistence, withTracerDisabled),
909
+
910
+ release: (address, shardId) =>
911
+ readLock(shardId).pipe(
912
+ Effect.flatMap((lock) =>
913
+ Option.match(lock, {
914
+ onNone: () => Effect.void,
915
+ onSome: (doc) =>
916
+ doc.address === address
917
+ ? Effect.tryPromise(() => container.item(doc.id, "lock").delete()).pipe(
918
+ Effect.tap(annotateItem),
919
+ Effect.catchIf(isNotFound, () => Effect.void),
920
+ Effect.asVoid
921
+ )
922
+ : Effect.void
923
+ })
924
+ ),
925
+ annotate("release"),
926
+ refailPersistence,
927
+ withTracerDisabled
928
+ ),
929
+
930
+ releaseAll: (address) =>
931
+ Effect
932
+ .tryPromise(() =>
933
+ container
934
+ .items
935
+ .query<LockDoc>({
936
+ query: "SELECT * FROM c WHERE c.type = 'lock' AND c.address = @address",
937
+ parameters: [{ name: "@address", value: address }]
938
+ }, { partitionKey: "lock" })
939
+ .fetchAll()
940
+ )
941
+ .pipe(
942
+ Effect.tap(annotateFeed),
943
+ Effect.flatMap((resp) =>
944
+ Effect.forEach(resp.resources, (doc) =>
945
+ Effect.tryPromise(() => container.item(doc.id, "lock").delete()).pipe(
946
+ Effect.tap(annotateItem),
947
+ Effect.catchIf(isNotFound, () => Effect.void)
948
+ ), { discard: true })
949
+ ),
950
+ annotate("releaseAll"),
951
+ refailPersistence,
952
+ withTracerDisabled
953
+ )
954
+ })
955
+ })
956
+
957
+ export const layerMessageStorage = (options?: {
958
+ readonly prefix?: string | undefined
959
+ }): Layer.Layer<MessageStorage.MessageStorage, never, CosmosClient | ShardingConfig.ShardingConfig> =>
960
+ Layer.effect(MessageStorage.MessageStorage, makeMessageStorage(options)).pipe(
961
+ Layer.provide(Snowflake.layerGenerator)
962
+ )
963
+
964
+ export const layerRunnerStorage = (options?: {
965
+ readonly prefix?: string | undefined
966
+ }): Layer.Layer<RunnerStorage.RunnerStorage, never, CosmosClient | ShardingConfig.ShardingConfig> =>
967
+ Layer.effect(RunnerStorage.RunnerStorage, makeRunnerStorage(options))
968
+
969
+ export const layerStorage = (options?: {
970
+ readonly prefix?: string | undefined
971
+ }): Layer.Layer<
972
+ MessageStorage.MessageStorage | RunnerStorage.RunnerStorage,
973
+ never,
974
+ CosmosClient | ShardingConfig.ShardingConfig
975
+ > => Layer.merge(layerMessageStorage(options), layerRunnerStorage(options))
976
+
977
+ export const layerCosmos = (config: ClusterCosmosConfig): Layer.Layer<
978
+ MessageStorage.MessageStorage | RunnerStorage.RunnerStorage,
979
+ never,
980
+ ShardingConfig.ShardingConfig
981
+ > =>
982
+ layerStorage({ prefix: config.prefix }).pipe(
983
+ Layer.provide(CosmosClientLayer(Redacted.value(config.url), config.dbName))
984
+ )