@effect-app/infra 4.0.0-beta.99 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (486) hide show
  1. package/CHANGELOG.md +2884 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +28 -9
  4. package/dist/CUPS.d.ts.map +1 -1
  5. package/dist/CUPS.js +35 -14
  6. package/dist/ClusterCosmos.d.ts +67 -0
  7. package/dist/ClusterCosmos.d.ts.map +1 -0
  8. package/dist/ClusterCosmos.js +588 -0
  9. package/dist/ClusterServiceBus.d.ts +67 -0
  10. package/dist/ClusterServiceBus.d.ts.map +1 -0
  11. package/dist/ClusterServiceBus.js +84 -0
  12. package/dist/{api/ContextProvider.d.ts → ContextProvider.d.ts} +7 -4
  13. package/dist/ContextProvider.d.ts.map +1 -0
  14. package/dist/ContextProvider.js +40 -0
  15. package/dist/Emailer/Sendgrid.d.ts +110 -146
  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 +1 -1
  19. package/dist/Emailer/fake.d.ts.map +1 -1
  20. package/dist/Emailer/fake.js +4 -4
  21. package/dist/MainFiberSet.d.ts +11 -7
  22. package/dist/MainFiberSet.d.ts.map +1 -1
  23. package/dist/MainFiberSet.js +22 -6
  24. package/dist/QueueMaker/errors.d.ts +3 -1
  25. package/dist/QueueMaker/errors.d.ts.map +1 -1
  26. package/dist/QueueMaker/errors.js +4 -2
  27. package/dist/{adapters/SQL → SQL}/Model.d.ts +34 -45
  28. package/dist/SQL/Model.d.ts.map +1 -0
  29. package/dist/SQL/Model.js +469 -0
  30. package/dist/SQL.d.ts.map +1 -0
  31. package/dist/{adapters/SQL.js → SQL.js} +1 -1
  32. package/dist/Store/Cosmos/query.d.ts +16 -4
  33. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  34. package/dist/Store/Cosmos/query.js +238 -42
  35. package/dist/Store/Cosmos.d.ts +2 -2
  36. package/dist/Store/Cosmos.d.ts.map +1 -1
  37. package/dist/Store/Cosmos.js +394 -319
  38. package/dist/Store/Disk.d.ts +4 -4
  39. package/dist/Store/Disk.d.ts.map +1 -1
  40. package/dist/Store/Disk.js +91 -39
  41. package/dist/Store/Memory.d.ts +7 -10
  42. package/dist/Store/Memory.d.ts.map +1 -1
  43. package/dist/Store/Memory.js +348 -78
  44. package/dist/Store/SQL/Pg.d.ts +2 -2
  45. package/dist/Store/SQL/Pg.d.ts.map +1 -1
  46. package/dist/Store/SQL/Pg.js +229 -154
  47. package/dist/Store/SQL/query.d.ts +19 -3
  48. package/dist/Store/SQL/query.d.ts.map +1 -1
  49. package/dist/Store/SQL/query.js +306 -12
  50. package/dist/Store/SQL.d.ts +22 -5
  51. package/dist/Store/SQL.d.ts.map +1 -1
  52. package/dist/Store/SQL.js +369 -98
  53. package/dist/Store/codeFilter.d.ts +4 -4
  54. package/dist/Store/codeFilter.d.ts.map +1 -1
  55. package/dist/Store/codeFilter.js +71 -22
  56. package/dist/Store/index.d.ts +6 -4
  57. package/dist/Store/index.d.ts.map +1 -1
  58. package/dist/Store/index.js +9 -6
  59. package/dist/Store/jsonDocument.d.ts +67 -0
  60. package/dist/Store/jsonDocument.d.ts.map +1 -0
  61. package/dist/Store/jsonDocument.js +107 -0
  62. package/dist/Store/utils.d.ts +21 -2
  63. package/dist/Store/utils.d.ts.map +1 -1
  64. package/dist/Store/utils.js +253 -10
  65. package/dist/WorkflowEngineCosmos.d.ts +30 -0
  66. package/dist/WorkflowEngineCosmos.d.ts.map +1 -0
  67. package/dist/WorkflowEngineCosmos.js +542 -0
  68. package/dist/WorkflowEngineSqlite.d.ts +24 -0
  69. package/dist/WorkflowEngineSqlite.d.ts.map +1 -0
  70. package/dist/WorkflowEngineSqlite.js +550 -0
  71. package/dist/arbs.d.ts +6 -3
  72. package/dist/arbs.d.ts.map +1 -1
  73. package/dist/arbs.js +20 -4
  74. package/dist/codec.d.ts.map +1 -0
  75. package/dist/codec.js +5 -0
  76. package/dist/cosmos-client.d.ts +40 -0
  77. package/dist/cosmos-client.d.ts.map +1 -0
  78. package/dist/cosmos-client.js +41 -0
  79. package/dist/errorReporter.d.ts +6 -4
  80. package/dist/errorReporter.d.ts.map +1 -1
  81. package/dist/errorReporter.js +23 -27
  82. package/dist/fileUtil.d.ts +1 -1
  83. package/dist/fileUtil.d.ts.map +1 -1
  84. package/dist/fileUtil.js +2 -2
  85. package/dist/index.d.ts +2 -1
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +3 -2
  88. package/dist/internal/RequestContextMiddleware.d.ts +5 -0
  89. package/dist/internal/RequestContextMiddleware.d.ts.map +1 -0
  90. package/dist/internal/RequestContextMiddleware.js +45 -0
  91. package/dist/internal/auth.d.ts +53 -0
  92. package/dist/internal/auth.d.ts.map +1 -0
  93. package/dist/internal/auth.js +180 -0
  94. package/dist/internal/events.d.ts +11 -0
  95. package/dist/internal/events.d.ts.map +1 -0
  96. package/dist/internal/events.js +49 -0
  97. package/dist/{api/internal → internal}/health.d.ts +1 -1
  98. package/dist/internal/health.d.ts.map +1 -0
  99. package/dist/internal/health.js +5 -0
  100. package/dist/{api/layerUtils.d.ts → layerUtils.d.ts} +10 -2
  101. package/dist/layerUtils.d.ts.map +1 -0
  102. package/dist/layerUtils.js +17 -0
  103. package/dist/logger/jsonLogger.d.ts +1 -1
  104. package/dist/logger/jsonLogger.d.ts.map +1 -1
  105. package/dist/logger/jsonLogger.js +5 -3
  106. package/dist/logger/logFmtLogger.d.ts +1 -1
  107. package/dist/logger/logFmtLogger.d.ts.map +1 -1
  108. package/dist/logger/logFmtLogger.js +3 -3
  109. package/dist/logger/shared.d.ts +2 -2
  110. package/dist/logger/shared.d.ts.map +1 -1
  111. package/dist/logger/shared.js +6 -6
  112. package/dist/logger.d.ts.map +1 -1
  113. package/dist/middlewares.d.ts.map +1 -0
  114. package/dist/{api/middlewares.js → middlewares.js} +1 -1
  115. package/dist/{adapters/mongo-client.d.ts → mongo-client.d.ts} +2 -1
  116. package/dist/mongo-client.d.ts.map +1 -0
  117. package/dist/mongo-client.js +15 -0
  118. package/dist/otel.d.ts +75 -0
  119. package/dist/otel.d.ts.map +1 -0
  120. package/dist/otel.js +84 -0
  121. package/dist/rateLimit.d.ts +11 -3
  122. package/dist/rateLimit.d.ts.map +1 -1
  123. package/dist/rateLimit.js +7 -12
  124. package/dist/{adapters/redis-client.d.ts → redis-client.d.ts} +4 -1
  125. package/dist/redis-client.d.ts.map +1 -0
  126. package/dist/redis-client.js +98 -0
  127. package/dist/{api/reportError.d.ts → reportError.d.ts} +1 -1
  128. package/dist/reportError.d.ts.map +1 -0
  129. package/dist/reportError.js +28 -0
  130. package/dist/{api/routing → routing}/middleware/RouterMiddleware.d.ts +2 -1
  131. package/dist/routing/middleware/RouterMiddleware.d.ts.map +1 -0
  132. package/dist/{api/routing → routing}/middleware/RouterMiddleware.js +1 -1
  133. package/dist/{api/routing → routing}/middleware/middleware.d.ts +11 -6
  134. package/dist/routing/middleware/middleware.d.ts.map +1 -0
  135. package/dist/routing/middleware/middleware.js +152 -0
  136. package/dist/{api/routing → routing}/middleware.d.ts +0 -1
  137. package/dist/routing/middleware.d.ts.map +1 -0
  138. package/dist/{api/routing → routing}/middleware.js +1 -2
  139. package/dist/routing/schema/jwt.d.ts.map +1 -0
  140. package/dist/routing/schema/jwt.js +12 -0
  141. package/dist/routing/tsort.d.ts.map +1 -0
  142. package/dist/routing/tsort.js +51 -0
  143. package/dist/{api/routing → routing}/utils.d.ts +3 -3
  144. package/dist/routing/utils.d.ts.map +1 -0
  145. package/dist/routing/utils.js +45 -0
  146. package/dist/routing.d.ts +182 -0
  147. package/dist/routing.d.ts.map +1 -0
  148. package/dist/routing.js +256 -0
  149. package/dist/test.d.ts +2 -2
  150. package/dist/test.d.ts.map +1 -1
  151. package/dist/test.js +5 -5
  152. package/dist/util.d.ts.map +1 -0
  153. package/dist/util.js +14 -0
  154. package/docs/cluster-storage.md +26 -0
  155. package/docs/workflow-engine.md +262 -0
  156. package/examples/query.ts +23 -19
  157. package/package.json +36 -364
  158. package/src/CUPS.ts +53 -14
  159. package/src/ClusterCosmos.ts +1170 -0
  160. package/src/ClusterServiceBus.ts +244 -0
  161. package/src/{api/ContextProvider.ts → ContextProvider.ts} +10 -7
  162. package/src/Emailer/Sendgrid.ts +83 -60
  163. package/src/Emailer/fake.ts +4 -4
  164. package/src/MainFiberSet.ts +31 -12
  165. package/src/QueueMaker/errors.ts +3 -1
  166. package/src/{adapters/SQL → SQL}/Model.ts +48 -47
  167. package/src/SQL.ts +1 -0
  168. package/src/Store/Cosmos/query.ts +275 -55
  169. package/src/Store/Cosmos.ts +597 -511
  170. package/src/Store/Disk.ts +145 -71
  171. package/src/Store/Memory.ts +420 -115
  172. package/src/Store/SQL/Pg.ts +352 -268
  173. package/src/Store/SQL/query.ts +366 -20
  174. package/src/Store/SQL.ts +742 -293
  175. package/src/Store/codeFilter.ts +78 -28
  176. package/src/Store/index.ts +18 -13
  177. package/src/Store/jsonDocument.ts +143 -0
  178. package/src/Store/utils.ts +307 -30
  179. package/src/WorkflowEngineCosmos.ts +749 -0
  180. package/src/WorkflowEngineSqlite.ts +813 -0
  181. package/src/arbs.ts +25 -5
  182. package/src/cosmos-client.ts +72 -0
  183. package/src/errorReporter.ts +68 -78
  184. package/src/fileUtil.ts +1 -1
  185. package/src/index.ts +2 -1
  186. package/src/{api/internal → internal}/RequestContextMiddleware.ts +23 -6
  187. package/src/internal/auth.ts +272 -0
  188. package/src/{api/internal → internal}/events.ts +18 -9
  189. package/src/{api/layerUtils.ts → layerUtils.ts} +7 -3
  190. package/src/logger/jsonLogger.ts +5 -3
  191. package/src/logger/logFmtLogger.ts +3 -3
  192. package/src/logger/shared.ts +6 -5
  193. package/src/middlewares.ts +10 -0
  194. package/src/{adapters/mongo-client.ts → mongo-client.ts} +3 -1
  195. package/src/otel.ts +177 -0
  196. package/src/rateLimit.ts +35 -24
  197. package/src/{adapters/redis-client.ts → redis-client.ts} +6 -2
  198. package/src/{api/reportError.ts → reportError.ts} +3 -2
  199. package/src/{api/routing → routing}/middleware/RouterMiddleware.ts +2 -1
  200. package/src/{api/routing → routing}/middleware/middleware.ts +75 -50
  201. package/src/routing/middleware.ts +4 -0
  202. package/src/{api/routing → routing}/schema/jwt.ts +2 -2
  203. package/src/{api/routing → routing}/utils.ts +2 -1
  204. package/src/routing.ts +823 -0
  205. package/src/test.ts +6 -6
  206. package/src/vitest.ts +1 -1
  207. package/test/arbs-magnitude.test.ts +33 -0
  208. package/test/arbs.test.ts +41 -0
  209. package/test/auth.test.ts +101 -0
  210. package/test/cluster-cosmos.test.ts +634 -0
  211. package/test/cluster-servicebus.test.ts +180 -0
  212. package/test/cluster-sqlite.test.ts +207 -0
  213. package/test/contextProvider.test.ts +11 -8
  214. package/test/controller.test.ts +23 -28
  215. package/test/cosmos-container-throughput.test.ts +80 -0
  216. package/test/cosmos-query.test.ts +368 -0
  217. package/test/dist/arbs-magnitude.test.d.ts.map +1 -0
  218. package/test/dist/arbs.test.d.ts.map +1 -0
  219. package/test/dist/auth.test.d.ts.map +1 -0
  220. package/test/dist/cluster-cosmos.test.d.ts.map +1 -0
  221. package/test/dist/cluster-servicebus.test.d.ts.map +1 -0
  222. package/test/dist/cluster-sqlite.test.d.ts.map +1 -0
  223. package/test/dist/contextProvider.test.d.ts.map +1 -1
  224. package/test/dist/controller.test.d.ts.map +1 -1
  225. package/test/dist/cosmos-container-throughput.test.d.ts.map +1 -0
  226. package/test/dist/cosmos-query.test.d.ts.map +1 -0
  227. package/test/dist/fixtures.d.ts +17 -9
  228. package/test/dist/fixtures.d.ts.map +1 -1
  229. package/test/dist/fixtures.js +8 -3
  230. package/test/dist/json-lower.test.d.ts.map +1 -0
  231. package/test/dist/query.test.d.ts.map +1 -1
  232. package/test/dist/rawQuery.test.d.ts.map +1 -1
  233. package/test/dist/repository-ext.test.d.ts.map +1 -0
  234. package/test/dist/repository-legacy-document.test.d.ts.map +1 -0
  235. package/test/dist/requires.test.d.ts.map +1 -1
  236. package/test/dist/router-generator.test.d.ts.map +1 -0
  237. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  238. package/test/dist/rpc-context-map-streaming.test.d.ts.map +1 -0
  239. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  240. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  241. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  242. package/test/dist/workflow-engine-cosmos.test.d.ts.map +1 -0
  243. package/test/dist/workflow-engine-sqlite.test.d.ts.map +1 -0
  244. package/test/fixtures.ts +7 -2
  245. package/test/json-lower.test.ts +129 -0
  246. package/test/layerUtils.test.ts +2 -2
  247. package/test/query.test.ts +1232 -42
  248. package/test/rawQuery.test.ts +432 -7
  249. package/test/repository-ext.test.ts +456 -0
  250. package/test/repository-legacy-document.test.ts +394 -0
  251. package/test/requires.test.ts +7 -3
  252. package/test/router-generator.test.ts +187 -0
  253. package/test/routing-interruptibility.test.ts +66 -0
  254. package/test/rpc-context-map-streaming.test.ts +265 -0
  255. package/test/rpc-e2e-invalidation.test.ts +421 -0
  256. package/test/rpc-multi-middleware.test.ts +17 -11
  257. package/test/rpc-stream-fullstack.test.ts +307 -0
  258. package/test/sql-store.test.ts +1202 -47
  259. package/test/validateSample.test.ts +70 -42
  260. package/test/workflow-engine-cosmos.test.ts +345 -0
  261. package/test/workflow-engine-sqlite.test.ts +297 -0
  262. package/tsconfig.examples.json +1 -1
  263. package/tsconfig.json +9 -1
  264. package/tsconfig.src.json +35 -35
  265. package/tsconfig.test.json +2 -2
  266. package/dist/Emailer/service.d.ts +0 -55
  267. package/dist/Emailer/service.d.ts.map +0 -1
  268. package/dist/Emailer/service.js +0 -6
  269. package/dist/Emailer.d.ts +0 -2
  270. package/dist/Emailer.d.ts.map +0 -1
  271. package/dist/Emailer.js +0 -2
  272. package/dist/Model/Repository/ext.d.ts +0 -41
  273. package/dist/Model/Repository/ext.d.ts.map +0 -1
  274. package/dist/Model/Repository/ext.js +0 -65
  275. package/dist/Model/Repository/internal/internal.d.ts +0 -59
  276. package/dist/Model/Repository/internal/internal.d.ts.map +0 -1
  277. package/dist/Model/Repository/internal/internal.js +0 -320
  278. package/dist/Model/Repository/legacy.d.ts +0 -19
  279. package/dist/Model/Repository/legacy.d.ts.map +0 -1
  280. package/dist/Model/Repository/legacy.js +0 -2
  281. package/dist/Model/Repository/makeRepo.d.ts +0 -49
  282. package/dist/Model/Repository/makeRepo.d.ts.map +0 -1
  283. package/dist/Model/Repository/makeRepo.js +0 -24
  284. package/dist/Model/Repository/service.d.ts +0 -89
  285. package/dist/Model/Repository/service.d.ts.map +0 -1
  286. package/dist/Model/Repository/service.js +0 -2
  287. package/dist/Model/Repository/validation.d.ts +0 -43
  288. package/dist/Model/Repository/validation.d.ts.map +0 -1
  289. package/dist/Model/Repository/validation.js +0 -32
  290. package/dist/Model/Repository.d.ts +0 -6
  291. package/dist/Model/Repository.d.ts.map +0 -1
  292. package/dist/Model/Repository.js +0 -6
  293. package/dist/Model/dsl.d.ts +0 -32
  294. package/dist/Model/dsl.d.ts.map +0 -1
  295. package/dist/Model/dsl.js +0 -44
  296. package/dist/Model/filter/filterApi.d.ts +0 -30
  297. package/dist/Model/filter/filterApi.d.ts.map +0 -1
  298. package/dist/Model/filter/filterApi.js +0 -2
  299. package/dist/Model/filter/types/errors.d.ts +0 -29
  300. package/dist/Model/filter/types/errors.d.ts.map +0 -1
  301. package/dist/Model/filter/types/errors.js +0 -2
  302. package/dist/Model/filter/types/fields.d.ts +0 -15
  303. package/dist/Model/filter/types/fields.d.ts.map +0 -1
  304. package/dist/Model/filter/types/fields.js +0 -2
  305. package/dist/Model/filter/types/path/common.d.ts +0 -316
  306. package/dist/Model/filter/types/path/common.d.ts.map +0 -1
  307. package/dist/Model/filter/types/path/common.js +0 -2
  308. package/dist/Model/filter/types/path/eager.d.ts +0 -95
  309. package/dist/Model/filter/types/path/eager.d.ts.map +0 -1
  310. package/dist/Model/filter/types/path/eager.js +0 -31
  311. package/dist/Model/filter/types/path/index.d.ts +0 -4
  312. package/dist/Model/filter/types/path/index.d.ts.map +0 -1
  313. package/dist/Model/filter/types/path/index.js +0 -3
  314. package/dist/Model/filter/types/utils.d.ts +0 -79
  315. package/dist/Model/filter/types/utils.d.ts.map +0 -1
  316. package/dist/Model/filter/types/utils.js +0 -2
  317. package/dist/Model/filter/types/validator.d.ts +0 -30
  318. package/dist/Model/filter/types/validator.d.ts.map +0 -1
  319. package/dist/Model/filter/types/validator.js +0 -2
  320. package/dist/Model/filter/types.d.ts +0 -5
  321. package/dist/Model/filter/types.d.ts.map +0 -1
  322. package/dist/Model/filter/types.js +0 -7
  323. package/dist/Model/query/dsl.d.ts +0 -248
  324. package/dist/Model/query/dsl.d.ts.map +0 -1
  325. package/dist/Model/query/dsl.js +0 -104
  326. package/dist/Model/query/new-kid-interpreter.d.ts +0 -28
  327. package/dist/Model/query/new-kid-interpreter.d.ts.map +0 -1
  328. package/dist/Model/query/new-kid-interpreter.js +0 -165
  329. package/dist/Model/query.d.ts +0 -15
  330. package/dist/Model/query.d.ts.map +0 -1
  331. package/dist/Model/query.js +0 -3
  332. package/dist/Model.d.ts +0 -4
  333. package/dist/Model.d.ts.map +0 -1
  334. package/dist/Model.js +0 -4
  335. package/dist/Operations.d.ts +0 -55
  336. package/dist/Operations.d.ts.map +0 -1
  337. package/dist/Operations.js +0 -102
  338. package/dist/OperationsRepo.d.ts +0 -41
  339. package/dist/OperationsRepo.d.ts.map +0 -1
  340. package/dist/OperationsRepo.js +0 -14
  341. package/dist/QueueMaker/SQLQueue.d.ts +0 -14
  342. package/dist/QueueMaker/SQLQueue.d.ts.map +0 -1
  343. package/dist/QueueMaker/SQLQueue.js +0 -132
  344. package/dist/QueueMaker/memQueue.d.ts +0 -11
  345. package/dist/QueueMaker/memQueue.d.ts.map +0 -1
  346. package/dist/QueueMaker/memQueue.js +0 -76
  347. package/dist/QueueMaker/sbqueue.d.ts +0 -12
  348. package/dist/QueueMaker/sbqueue.d.ts.map +0 -1
  349. package/dist/QueueMaker/sbqueue.js +0 -91
  350. package/dist/QueueMaker/service.d.ts +0 -11
  351. package/dist/QueueMaker/service.d.ts.map +0 -1
  352. package/dist/QueueMaker/service.js +0 -4
  353. package/dist/RequestContext.d.ts +0 -68
  354. package/dist/RequestContext.d.ts.map +0 -1
  355. package/dist/RequestContext.js +0 -49
  356. package/dist/RequestFiberSet.d.ts +0 -36
  357. package/dist/RequestFiberSet.d.ts.map +0 -1
  358. package/dist/RequestFiberSet.js +0 -78
  359. package/dist/Store/ContextMapContainer.d.ts +0 -25
  360. package/dist/Store/ContextMapContainer.d.ts.map +0 -1
  361. package/dist/Store/ContextMapContainer.js +0 -77
  362. package/dist/Store/service.d.ts +0 -111
  363. package/dist/Store/service.d.ts.map +0 -1
  364. package/dist/Store/service.js +0 -79
  365. package/dist/Store.d.ts +0 -2
  366. package/dist/Store.d.ts.map +0 -1
  367. package/dist/Store.js +0 -2
  368. package/dist/adapters/SQL/Model.d.ts.map +0 -1
  369. package/dist/adapters/SQL/Model.js +0 -478
  370. package/dist/adapters/SQL.d.ts.map +0 -1
  371. package/dist/adapters/ServiceBus.d.ts +0 -58
  372. package/dist/adapters/ServiceBus.d.ts.map +0 -1
  373. package/dist/adapters/ServiceBus.js +0 -99
  374. package/dist/adapters/cosmos-client.d.ts +0 -14
  375. package/dist/adapters/cosmos-client.d.ts.map +0 -1
  376. package/dist/adapters/cosmos-client.js +0 -9
  377. package/dist/adapters/index.d.ts +0 -2
  378. package/dist/adapters/index.d.ts.map +0 -1
  379. package/dist/adapters/index.js +0 -2
  380. package/dist/adapters/logger.d.ts +0 -9
  381. package/dist/adapters/logger.d.ts.map +0 -1
  382. package/dist/adapters/logger.js +0 -3
  383. package/dist/adapters/memQueue.d.ts +0 -13
  384. package/dist/adapters/memQueue.d.ts.map +0 -1
  385. package/dist/adapters/memQueue.js +0 -20
  386. package/dist/adapters/mongo-client.d.ts.map +0 -1
  387. package/dist/adapters/mongo-client.js +0 -13
  388. package/dist/adapters/redis-client.d.ts.map +0 -1
  389. package/dist/adapters/redis-client.js +0 -94
  390. package/dist/api/ContextProvider.d.ts.map +0 -1
  391. package/dist/api/ContextProvider.js +0 -38
  392. package/dist/api/codec.d.ts.map +0 -1
  393. package/dist/api/codec.js +0 -5
  394. package/dist/api/internal/RequestContextMiddleware.d.ts +0 -5
  395. package/dist/api/internal/RequestContextMiddleware.d.ts.map +0 -1
  396. package/dist/api/internal/RequestContextMiddleware.js +0 -35
  397. package/dist/api/internal/auth.d.ts +0 -15
  398. package/dist/api/internal/auth.d.ts.map +0 -1
  399. package/dist/api/internal/auth.js +0 -47
  400. package/dist/api/internal/events.d.ts +0 -9
  401. package/dist/api/internal/events.d.ts.map +0 -1
  402. package/dist/api/internal/events.js +0 -42
  403. package/dist/api/internal/health.d.ts.map +0 -1
  404. package/dist/api/internal/health.js +0 -5
  405. package/dist/api/layerUtils.d.ts.map +0 -1
  406. package/dist/api/layerUtils.js +0 -16
  407. package/dist/api/middlewares.d.ts.map +0 -1
  408. package/dist/api/reportError.d.ts.map +0 -1
  409. package/dist/api/reportError.js +0 -27
  410. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +0 -1
  411. package/dist/api/routing/middleware/middleware.d.ts.map +0 -1
  412. package/dist/api/routing/middleware/middleware.js +0 -130
  413. package/dist/api/routing/middleware.d.ts.map +0 -1
  414. package/dist/api/routing/schema/jwt.d.ts.map +0 -1
  415. package/dist/api/routing/schema/jwt.js +0 -12
  416. package/dist/api/routing/tsort.d.ts.map +0 -1
  417. package/dist/api/routing/tsort.js +0 -51
  418. package/dist/api/routing/utils.d.ts.map +0 -1
  419. package/dist/api/routing/utils.js +0 -44
  420. package/dist/api/routing.d.ts +0 -134
  421. package/dist/api/routing.d.ts.map +0 -1
  422. package/dist/api/routing.js +0 -167
  423. package/dist/api/setupRequest.d.ts +0 -15
  424. package/dist/api/setupRequest.d.ts.map +0 -1
  425. package/dist/api/setupRequest.js +0 -49
  426. package/dist/api/util.d.ts.map +0 -1
  427. package/dist/api/util.js +0 -14
  428. package/eslint.config.mjs +0 -24
  429. package/src/Emailer/service.ts +0 -52
  430. package/src/Emailer.ts +0 -1
  431. package/src/Model/Repository/ext.ts +0 -283
  432. package/src/Model/Repository/internal/internal.ts +0 -580
  433. package/src/Model/Repository/legacy.ts +0 -27
  434. package/src/Model/Repository/makeRepo.ts +0 -139
  435. package/src/Model/Repository/service.ts +0 -627
  436. package/src/Model/Repository/validation.ts +0 -31
  437. package/src/Model/Repository.ts +0 -5
  438. package/src/Model/dsl.ts +0 -128
  439. package/src/Model/filter/filterApi.ts +0 -60
  440. package/src/Model/filter/types/errors.ts +0 -47
  441. package/src/Model/filter/types/fields.ts +0 -50
  442. package/src/Model/filter/types/path/common.ts +0 -404
  443. package/src/Model/filter/types/path/eager.ts +0 -298
  444. package/src/Model/filter/types/path/index.ts +0 -4
  445. package/src/Model/filter/types/utils.ts +0 -128
  446. package/src/Model/filter/types/validator.ts +0 -46
  447. package/src/Model/filter/types.ts +0 -6
  448. package/src/Model/query/dsl.ts +0 -2110
  449. package/src/Model/query/new-kid-interpreter.ts +0 -210
  450. package/src/Model/query.ts +0 -13
  451. package/src/Model.ts +0 -3
  452. package/src/Operations.ts +0 -235
  453. package/src/OperationsRepo.ts +0 -16
  454. package/src/QueueMaker/SQLQueue.ts +0 -190
  455. package/src/QueueMaker/memQueue.ts +0 -130
  456. package/src/QueueMaker/sbqueue.ts +0 -137
  457. package/src/QueueMaker/service.ts +0 -17
  458. package/src/RequestContext.ts +0 -63
  459. package/src/RequestFiberSet.ts +0 -105
  460. package/src/Store/ContextMapContainer.ts +0 -116
  461. package/src/Store/index.test.ts.bak +0 -112
  462. package/src/Store/service.ts +0 -193
  463. package/src/Store.ts +0 -1
  464. package/src/adapters/SQL.ts +0 -1
  465. package/src/adapters/ServiceBus.ts +0 -209
  466. package/src/adapters/cosmos-client.ts +0 -16
  467. package/src/adapters/index.ts +0 -0
  468. package/src/adapters/logger.ts +0 -3
  469. package/src/adapters/memQueue.ts +0 -21
  470. package/src/api/internal/auth.ts +0 -68
  471. package/src/api/middlewares.ts +0 -10
  472. package/src/api/routing/middleware.ts +0 -6
  473. package/src/api/routing.ts +0 -598
  474. package/src/api/setupRequest.ts +0 -104
  475. package/test/dist/date-query.test.d.ts.map +0 -1
  476. package/tsconfig.json.bak +0 -12
  477. /package/dist/{adapters/SQL.d.ts → SQL.d.ts} +0 -0
  478. /package/dist/{api/codec.d.ts → codec.d.ts} +0 -0
  479. /package/dist/{api/middlewares.d.ts → middlewares.d.ts} +0 -0
  480. /package/dist/{api/routing → routing}/schema/jwt.d.ts +0 -0
  481. /package/dist/{api/routing → routing}/tsort.d.ts +0 -0
  482. /package/dist/{api/util.d.ts → util.d.ts} +0 -0
  483. /package/src/{api/codec.ts → codec.ts} +0 -0
  484. /package/src/{api/internal → internal}/health.ts +0 -0
  485. /package/src/{api/routing → routing}/tsort.ts +0 -0
  486. /package/src/{api/util.ts → util.ts} +0 -0
@@ -1,16 +1,26 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
 
3
- import { Array, Duration, Effect, Layer, type NonEmptyReadonlyArray, Option, pipe, Redacted, Struct } from "effect-app"
3
+ import * as Array from "effect-app/Array"
4
+ import type { NonEmptyReadonlyArray } from "effect-app/Array"
4
5
  import { toNonEmptyArray } from "effect-app/Array"
6
+ import * as Effect from "effect-app/Effect"
7
+ import * as Layer from "effect-app/Layer"
8
+ import type { FieldValues } from "effect-app/Model/filter/types"
9
+ import { type ComputedProjectionIrExpression, type RawQuery } from "effect-app/Model/query"
10
+ import * as Option from "effect-app/Option"
11
+ import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, storeId, StoreMaker } from "effect-app/Store"
5
12
  import { dropUndefinedT, mutable } from "effect-app/utils"
6
- import { CosmosClient, CosmosClientLayer } from "../adapters/cosmos-client.js"
7
- import { OptimisticConcurrencyException } from "../errors.js"
8
- import { InfraLogger } from "../logger.js"
9
- import type { FieldValues } from "../Model/filter/types.js"
10
- import { type RawQuery } from "../Model/query.js"
11
- import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.js"
12
- import { storeId } from "./Memory.js"
13
- import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
13
+ import * as Duration from "effect/Duration"
14
+ import { pipe } from "effect/Function"
15
+ import * as Redacted from "effect/Redacted"
16
+ import * as Struct from "effect/Struct"
17
+ import { CosmosClient, CosmosClientLayer, createContainerIfNotExists } from "../cosmos-client.ts"
18
+ import { DatabaseError, OptimisticConcurrencyException } from "../errors.ts"
19
+ import { InfraLogger } from "../logger.ts"
20
+ import { annotateCosmosResponse, annotateDb } from "../otel.ts"
21
+ import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.ts"
22
+ import { decodeStoredMany, decodeStoredOption, makeJsonDocumentCodec, makeStoredDecode } from "./jsonDocument.ts"
23
+ import { makeJsonLower } from "./utils.ts"
14
24
 
15
25
  const makeMapId =
16
26
  <IdKey extends keyof Encoded, Encoded extends FieldValues>(idKey: IdKey) => ({ [idKey]: id, ...e }: Encoded) => ({
@@ -22,547 +32,623 @@ const makeReverseMapId =
22
32
  ({ id, ...t }: PersistenceModelType<Omit<Encoded, IdKey> & { id: string }>) =>
23
33
  ({ ...t, [idKey]: id }) as any as PersistenceModelType<Encoded>
24
34
 
25
- class CosmosDbOperationError {
26
- constructor(readonly message: string, readonly raw?: unknown) {}
27
- } // TODO: Retry operation when running into RU limit.
35
+ // Cosmos statuses worth retrying: request timeout, throttle, retry-with,
36
+ // internal, and service unavailable.
37
+ const RETRYABLE_COSMOS_STATUS = new Set([408, 429, 449, 500, 503])
38
+ const statusIsTransient = (code: number) => RETRYABLE_COSMOS_STATUS.has(code)
28
39
 
29
- function makeCosmosStore({ prefix }: StorageConfig) {
30
- return Effect.gen(function*() {
31
- const { db } = yield* CosmosClient
32
- return {
33
- make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R = never, E = never>(
34
- name: string,
35
- idKey: IdKey,
36
- seed?: Effect.Effect<Iterable<Encoded>, E, R>,
37
- config?: StoreConfig<Encoded>
38
- ) =>
39
- Effect.gen(function*() {
40
- const mapId = makeMapId<IdKey, Encoded>(idKey)
41
- const mapReverseId = makeReverseMapId<IdKey, Encoded>(idKey)
42
- type PM = PersistenceModelType<Encoded>
43
- type PMCosmos = PersistenceModelType<Omit<Encoded, IdKey> & { id: string }>
44
- const containerId = `${prefix}${name}`
45
- yield* Effect.promise(() =>
46
- db.containers.createIfNotExists(dropUndefinedT({
47
- id: containerId,
48
- uniqueKeyPolicy: config?.uniqueKeys
49
- ? { uniqueKeys: config.uniqueKeys }
50
- : undefined
51
- }))
52
- )
40
+ // A thrown SDK error is transient if its status/code is retryable or its message
41
+ // looks like a timeout / throttle / dropped connection.
42
+ const thrownIsTransient = (e: unknown): boolean => {
43
+ const code = (e as any)?.code ?? (e as any)?.statusCode
44
+ if (typeof code === "number" && RETRYABLE_COSMOS_STATUS.has(code)) return true
45
+ const msg = e instanceof Error ? e.message : String(e)
46
+ return /request timed out|timed out|\btimeout\b|ETIMEDOUT|ECONNRESET|ECONNREFUSED|EAI_AGAIN|socket hang up|ServiceUnavailable|RequestTimeout|TooManyRequests|throttl/i
47
+ .test(msg)
48
+ }
49
+
50
+ // Wrap a Cosmos SDK promise so a rejection becomes a typed, serializable
51
+ // `DatabaseError` (transient-classified) instead of a bare-`Error` defect — the
52
+ // latter both bypasses retry and breaks JSON encoding of the failure channel.
53
+ const tryCosmos = <A>(f: () => Promise<A>): Effect.Effect<A, DatabaseError> =>
54
+ Effect.tryPromise({
55
+ try: f,
56
+ catch: (e) =>
57
+ new DatabaseError({
58
+ message: `Cosmos request failed: ${e instanceof Error ? e.message : String(e)}`,
59
+ transient: thrownIsTransient(e),
60
+ cause: e
61
+ })
62
+ })
63
+
64
+ const respBytes = (
65
+ resp: { diagnostics?: { clientSideRequestStatistics?: { totalResponsePayloadLengthInBytes?: number } } }
66
+ ) => resp.diagnostics?.clientSideRequestStatistics?.totalResponsePayloadLengthInBytes ?? 0
67
+
68
+ const annotateFeed = (resp: {
69
+ resources: readonly unknown[]
70
+ requestCharge?: number
71
+ diagnostics?: { clientSideRequestStatistics?: { totalResponsePayloadLengthInBytes?: number } }
72
+ }) =>
73
+ annotateCosmosResponse({
74
+ requestCharge: resp.requestCharge,
75
+ returnedRows: resp.resources.length,
76
+ responseBytes: respBytes(resp)
77
+ })
78
+
79
+ const annotateItem = (resp: {
80
+ requestCharge?: number
81
+ statusCode?: number
82
+ diagnostics?: { clientSideRequestStatistics?: { totalResponsePayloadLengthInBytes?: number } }
83
+ }) =>
84
+ annotateCosmosResponse({
85
+ requestCharge: resp.requestCharge,
86
+ statusCode: resp.statusCode,
87
+ responseBytes: respBytes(resp)
88
+ })
89
+
90
+ const makeCosmosStore = Effect.fnUntraced(function*({ autoscaleMaxThroughput, prefix }: StorageConfig) {
91
+ const { db } = yield* CosmosClient
92
+ return {
93
+ make: Effect.fnUntraced(function*<IdKey extends keyof Encoded, Encoded extends FieldValues, R = never, E = never>(
94
+ name: string,
95
+ idKey: IdKey,
96
+ seed?: Effect.Effect<Iterable<Encoded>, E, R>,
97
+ config?: StoreConfig<Encoded>
98
+ ) {
99
+ const mapId = makeMapId<IdKey, Encoded>(idKey)
100
+ const mapReverseId = makeReverseMapId<IdKey, Encoded>(idKey)
101
+ const codec = makeJsonDocumentCodec<Encoded>(config?.schema)
102
+ const decodeStored = makeStoredDecode<Encoded>(config?.schema, config?.jitM)
103
+ const json = makeJsonLower(config)
104
+ const defaultValues = json.toJson(config?.defaultValues ?? {}) as Partial<Encoded>
105
+ // stored JSON -> defaultValues -> jitM -> JSON→Encoded decode
106
+ const fromStored = (raw: Encoded) => decodeStored({ ...defaultValues, ...mapReverseId(raw as any) })
107
+ type PM = PersistenceModelType<Encoded>
108
+ type PMCosmos = PersistenceModelType<Omit<Encoded, IdKey> & { id: string }>
109
+ const containerId = `${prefix}${name}`
110
+ yield* Effect.promise(() =>
111
+ createContainerIfNotExists(
112
+ db,
113
+ dropUndefinedT({
114
+ id: containerId,
115
+ uniqueKeyPolicy: config?.uniqueKeys
116
+ ? { uniqueKeys: config.uniqueKeys }
117
+ : undefined,
118
+ partitionKey: {
119
+ paths: ["/_partitionKey"],
120
+ version: 2 // support large partitionkeys so that the hash is not based on just the first 100 bytes!
121
+ }
122
+ }),
123
+ { autoscaleMaxThroughput }
124
+ )
125
+ )
53
126
 
54
- const basePartitionKey = config?.partitionValue() ?? "primary"
55
- const nsPrefix = (ns: string) => ns === "primary" ? "" : `${ns}::`
56
- const nsPartitionValue = (ns: string, e?: Encoded) => {
57
- const base = config?.partitionValue(e) ?? "primary"
58
- return `${nsPrefix(ns)}${base}`
127
+ const basePartitionKey = config?.partitionValue() ?? "primary"
128
+ const nsPrefix = (ns: string) => ns === "primary" ? "" : `${ns}::`
129
+ const nsPartitionValue = (ns: string, e?: Encoded) => {
130
+ const base = config?.partitionValue(e) ?? "primary"
131
+ return `${nsPrefix(ns)}${base}`
132
+ }
133
+ const nsBasePartitionKey = (ns: string) => `${nsPrefix(ns)}${basePartitionKey}`
134
+ const resolveNamespace = !config?.allowNamespace
135
+ ? Effect.succeed("primary")
136
+ : storeId.pipe(Effect.map((namespace) => {
137
+ if (namespace !== "primary" && !config.allowNamespace!(namespace)) {
138
+ throw new Error(`Namespace ${namespace} not allowed!`)
59
139
  }
60
- const nsBasePartitionKey = (ns: string) => `${nsPrefix(ns)}${basePartitionKey}`
61
- const resolveNamespace = !config?.allowNamespace
62
- ? Effect.succeed("primary")
63
- : storeId.asEffect().pipe(Effect.map((namespace) => {
64
- if (namespace !== "primary" && !config.allowNamespace!(namespace)) {
65
- throw new Error(`Namespace ${namespace} not allowed!`)
66
- }
67
- return namespace
68
- }))
140
+ return namespace
141
+ }))
69
142
 
70
- const defaultValues = config?.defaultValues ?? {}
71
- const container = db.container(containerId)
72
- const bulk = container.items.bulk.bind(container.items)
73
- const execBatch = container.items.batch.bind(container.items)
74
- // TODO: move the marker to a separate container and get rid of the checks on every query
75
- // then need to clean up the actual data.. perhaps first do with a config toggle to prescribe to it.
76
- const importedMarkerId = containerId
143
+ const container = db.container(containerId)
144
+ const bulk = container.items.bulk.bind(container.items)
145
+ const execBatch = container.items.batch.bind(container.items)
146
+ // TODO: move the marker to a separate container and get rid of the checks on every query
147
+ // then need to clean up the actual data.. perhaps first do with a config toggle to prescribe to it.
148
+ const importedMarkerId = containerId
77
149
 
78
- const ctx = yield* Effect.context<R>()
79
- const seedCache = new Map<string, Effect.Effect<void>>()
80
- const makeSeedEffect = (ns: string) => {
81
- const markerId = ns === "primary" ? importedMarkerId : `${importedMarkerId}::${ns}`
82
- return Effect
83
- .promise(() =>
84
- container
85
- .item(markerId, markerId)
86
- .read<{ id: string }>()
87
- .then(({ resource }) => Option.fromNullishOr(resource))
88
- )
89
- .pipe(
90
- Effect.flatMap((marker) => {
91
- if (Option.isSome(marker)) return Effect.void
92
- return InfraLogger.logInfo(`Creating mock data for ${name} (namespace: ${ns})`).pipe(
93
- Effect.andThen(seed!),
94
- Effect.flatMap((m) =>
95
- Effect.flatMapOption(
96
- Effect.succeed(toNonEmptyArray([...m])),
97
- (a) => bulkSetInternal(a, ns).pipe(Effect.orDie)
98
- )
99
- ),
100
- Effect.andThen(
101
- Effect.promise(() =>
102
- container.items.create({
103
- _partitionKey: markerId,
104
- id: markerId,
105
- ttl: -1
106
- })
107
- )
108
- ),
109
- Effect.provide(ctx),
110
- Effect.orDie
150
+ const ctx = yield* Effect.context<R>()
151
+ const seedCache = new Map<string, Effect.Effect<void>>()
152
+ const makeSeedEffect = (ns: string) => {
153
+ const markerId = ns === "primary" ? importedMarkerId : `${importedMarkerId}::${ns}`
154
+ return Effect
155
+ .promise(() =>
156
+ container
157
+ .item(markerId, markerId)
158
+ .read<{ id: string }>()
159
+ .then(({ resource }) => Option.fromNullishOr(resource))
160
+ )
161
+ .pipe(
162
+ Effect.flatMap((marker) => {
163
+ if (Option.isSome(marker)) return Effect.void
164
+ return InfraLogger.logInfo(`Creating mock data for ${name} (namespace: ${ns})`).pipe(
165
+ Effect.andThen(seed!),
166
+ Effect.flatMap((m) =>
167
+ Effect.flatMapOption(
168
+ Effect.succeed(toNonEmptyArray([...m])),
169
+ (a) => bulkSetInternal(a, ns).pipe(Effect.orDie)
170
+ )
171
+ ),
172
+ Effect.andThen(
173
+ Effect.promise(() =>
174
+ container.items.create({
175
+ _partitionKey: markerId,
176
+ id: markerId,
177
+ ttl: -1
178
+ })
111
179
  )
112
- }),
113
- Effect.withLogSpan(`Cosmos.seedCheck ${name} in ${ns} [effect-app/infra/Store]`),
114
- Effect.withSpan("Cosmos.seed [effect-app/infra/Store]", { attributes: { name, namespace: ns } })
180
+ ),
181
+ Effect.provide(ctx),
182
+ Effect.orDie
115
183
  )
116
- }
117
- const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
118
- if (!seed) return
119
- let cached = seedCache.get(ns)
120
- if (!cached) {
121
- cached = yield* Effect.cached(makeSeedEffect(ns))
122
- seedCache.set(ns, cached)
123
- }
124
- yield* cached
125
- })
126
- const resolveAndSeed = resolveNamespace.pipe(
127
- Effect.tap((ns) => seedNamespace(ns))
184
+ }),
185
+ Effect.withLogSpan(`Cosmos.seedCheck ${name} in ${ns} [effect-app/infra/Store]`),
186
+ annotateDb({
187
+ operation: "seed",
188
+ system: "cosmosdb",
189
+ collection: containerId,
190
+ namespace: ns,
191
+ entity: name
192
+ })
128
193
  )
129
-
130
- const bulkSetInternal = (items: NonEmptyReadonlyArray<PM>, ns: string) =>
131
- Effect
132
- .gen(function*() {
133
- // TODO: disable batching if need atomicity
134
- // we delay and batch to keep low amount of RUs
135
- const b = [...items]
136
- .map(
137
- (x) =>
138
- [
139
- x,
140
- Option.match(Option.fromNullishOr(x._etag), {
141
- onNone: () =>
142
- dropUndefinedT({
143
- operationType: "Create" as const,
144
- resourceBody: {
145
- ...Struct.omit(x, ["_etag", idKey]),
146
- id: x[idKey],
147
- _partitionKey: nsPartitionValue(ns, x)
148
- }
149
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
150
- // partitionKey: config?.partitionValue(x)
151
- }),
152
- onSome: (eTag) =>
153
- dropUndefinedT({
154
- operationType: "Replace" as const,
155
- id: x[idKey],
156
- resourceBody: {
157
- ...Struct.omit(x, ["_etag", idKey]),
158
- id: x[idKey],
159
- _partitionKey: nsPartitionValue(ns, x)
160
- },
161
- ifMatch: eTag
162
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
163
- // partitionKey: config?.partitionValue(x)
164
- })
194
+ }
195
+ const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
196
+ if (!seed) return
197
+ let cached = seedCache.get(ns)
198
+ if (!cached) {
199
+ cached = yield* Effect.cached(Effect.uninterruptible(makeSeedEffect(ns)))
200
+ seedCache.set(ns, cached)
201
+ }
202
+ yield* cached
203
+ })
204
+ const bulkSetInternal = (items: NonEmptyReadonlyArray<PM>, ns: string) =>
205
+ Effect
206
+ .gen(function*() {
207
+ // TODO: disable batching if need atomicity
208
+ // we delay and batch to keep low amount of RUs
209
+ const b = [...items]
210
+ .map(
211
+ (x) =>
212
+ [
213
+ x,
214
+ Option.match(Option.fromNullishOr(x._etag), {
215
+ onNone: () =>
216
+ dropUndefinedT({
217
+ operationType: "Create" as const,
218
+ resourceBody: {
219
+ ...Struct.omit(codec.encode(x), ["_etag", idKey]),
220
+ id: x[idKey],
221
+ _partitionKey: nsPartitionValue(ns, x)
222
+ }
223
+ // don't use this or we get an error that the request and some item partition key dont match - makese no sense
224
+ // partitionKey: config?.partitionValue(x)
225
+ }),
226
+ onSome: (eTag) =>
227
+ dropUndefinedT({
228
+ operationType: "Replace" as const,
229
+ id: x[idKey],
230
+ resourceBody: {
231
+ ...Struct.omit(codec.encode(x), ["_etag", idKey]),
232
+ id: x[idKey],
233
+ _partitionKey: nsPartitionValue(ns, x)
234
+ },
235
+ ifMatch: eTag
236
+ // don't use this or we get an error that the request and some item partition key dont match - makese no sense
237
+ // partitionKey: config?.partitionValue(x)
165
238
  })
166
- ] as const
167
- )
168
- const batches = Array.chunksOf(b, config?.maxBulkSize ?? 10)
239
+ })
240
+ ] as const
241
+ )
242
+ const batches = Array.chunksOf(b, config?.maxBulkSize ?? 10)
169
243
 
170
- const batchResult = yield* Effect.forEach(
171
- batches
172
- .map((x, i) => [i, x] as const),
173
- ([i, batch]) =>
244
+ const batchResult = yield* Effect.forEach(
245
+ batches
246
+ .map((x, i) => [i, x] as const),
247
+ ([i, batch]) =>
248
+ tryCosmos(() => bulk(batch.map(([, op]) => op)))
249
+ .pipe(
174
250
  Effect
175
- .promise(() => bulk(batch.map(([, op]) => op)))
176
- .pipe(
177
- Effect
178
- .delay(Duration.millis(i === 0 ? 0 : 150)),
179
- Effect
180
- .flatMap((responses) =>
181
- Effect.gen(function*() {
182
- const r = responses.find((x) =>
183
- x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409
184
- )
185
- if (r) {
186
- return yield* Effect.fail(
187
- new OptimisticConcurrencyException(
188
- {
189
- type: name,
190
- id: JSON.stringify(r.resourceBody?.["id"]),
191
- code: r.statusCode,
192
- raw: responses
193
- }
194
- )
195
- )
196
- }
197
- const r2 = responses.find(
198
- (x) => x.statusCode !== 424 && (x.statusCode > 299 || x.statusCode < 200)
199
- )
200
- if (r2) {
201
- return yield* Effect.die(
202
- new CosmosDbOperationError(
203
- "not able to update records: " + r2.statusCode,
204
- responses
205
- )
206
- )
207
- }
208
- const r3 = responses.find(
209
- (x) => x.statusCode > 299 || x.statusCode < 200
251
+ .delay(Duration.millis(i === 0 ? 0 : 150)),
252
+ Effect
253
+ .flatMap((responses) =>
254
+ Effect.gen(function*() {
255
+ const r = responses.find((x) =>
256
+ x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409
257
+ )
258
+ if (r) {
259
+ return yield* Effect.fail(
260
+ new OptimisticConcurrencyException(
261
+ {
262
+ type: name,
263
+ id: JSON.stringify(r.resourceBody?.["id"]),
264
+ code: r.statusCode,
265
+ raw: responses
266
+ }
210
267
  )
211
- if (r3) {
212
- return yield* Effect.die(
213
- new CosmosDbOperationError(
214
- "not able to update records: " + r3.statusCode,
215
- responses
216
- )
217
- )
218
- }
219
- return batch.map(([e], i) => ({
220
- ...e,
221
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
222
- _etag: responses[i]!.eTag
223
- }))
268
+ )
269
+ }
270
+ const r2 = responses.find(
271
+ (x) => x.statusCode !== 424 && (x.statusCode > 299 || x.statusCode < 200)
272
+ )
273
+ if (r2) {
274
+ return yield* new DatabaseError({
275
+ message: "not able to update records: " + r2.statusCode,
276
+ transient: statusIsTransient(r2.statusCode),
277
+ cause: responses
224
278
  })
279
+ }
280
+ const r3 = responses.find(
281
+ (x) => x.statusCode > 299 || x.statusCode < 200
225
282
  )
283
+ if (r3) {
284
+ return yield* new DatabaseError({
285
+ message: "not able to update records: " + r3.statusCode,
286
+ transient: statusIsTransient(r3.statusCode),
287
+ cause: responses
288
+ })
289
+ }
290
+ return batch.map(([e], i) => ({
291
+ ...e,
292
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
293
+ _etag: responses[i]!.eTag
294
+ }))
295
+ })
226
296
  )
227
- )
228
-
229
- return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
230
- })
231
- .pipe(
232
- Effect.withSpan("Cosmos.bulkSet [effect-app/infra/Store]", {
233
- attributes: { "repository.container_id": containerId, "repository.model_name": name, namespace: ns }
234
- }, { captureStackTrace: false })
235
- )
236
-
237
- const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
238
- resolveAndSeed.pipe(Effect.flatMap((ns) => bulkSetInternal(items, ns)))
297
+ )
298
+ )
239
299
 
240
- const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
241
- return resolveAndSeed
242
- .pipe(Effect.flatMap((ns) =>
243
- Effect
244
- .suspend(() => {
245
- const batch = [...items].map(
246
- (x) =>
247
- [
248
- x,
249
- Option.match(Option.fromNullishOr(x._etag), {
250
- onNone: () => ({
251
- operationType: "Create" as const,
252
- resourceBody: {
253
- ...Struct.omit(x, ["_etag", idKey]),
254
- id: x[idKey],
255
- _partitionKey: nsPartitionValue(ns, x)
256
- }
257
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
258
- // partitionKey: config?.partitionValue(x)
259
- }),
260
- onSome: (eTag) => ({
261
- operationType: "Replace" as const,
262
- id: x[idKey],
263
- resourceBody: {
264
- ...Struct.omit(x, ["_etag", idKey]),
265
- id: x[idKey],
266
- _partitionKey: nsPartitionValue(ns, x)
267
- },
268
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
269
- // partitionKey: config?.partitionValue(x)
270
- ifMatch: eTag
271
- })
272
- })
273
- ] as const
274
- )
300
+ return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
301
+ })
302
+ .pipe(
303
+ annotateDb({
304
+ operation: "bulkSet",
305
+ system: "cosmosdb",
306
+ collection: containerId,
307
+ namespace: ns,
308
+ entity: name
309
+ })
310
+ )
275
311
 
276
- const ex = batch.map(([, c]) => c)
312
+ const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
313
+ resolveNamespace.pipe(Effect.flatMap((ns) => bulkSetInternal(items, ns)))
277
314
 
278
- return Effect
279
- .promise(() => execBatch(ex, ex[0]?.resourceBody._partitionKey))
280
- .pipe(Effect.flatMap(Effect.fnUntraced(function*(x) {
281
- const result = x.result ?? []
282
- const firstFailed = result.find(
283
- (x: any) => x.statusCode > 299 || x.statusCode < 200
284
- )
285
- if (firstFailed) {
286
- const code = firstFailed.statusCode ?? 0
287
- if (code === 412 || code === 404 || code === 409) {
288
- return yield* new OptimisticConcurrencyException({ type: name, id: "batch", code })
315
+ const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
316
+ return resolveNamespace
317
+ .pipe(Effect.flatMap((ns) =>
318
+ Effect
319
+ .suspend(() => {
320
+ const batch = [...items].map(
321
+ (x) =>
322
+ [
323
+ x,
324
+ Option.match(Option.fromNullishOr(x._etag), {
325
+ onNone: () => ({
326
+ operationType: "Create" as const,
327
+ resourceBody: {
328
+ ...Struct.omit(codec.encode(x), ["_etag", idKey]),
329
+ id: x[idKey],
330
+ _partitionKey: nsPartitionValue(ns, x)
289
331
  }
332
+ // don't use this or we get an error that the request and some item partition key dont match - makese no sense
333
+ // partitionKey: config?.partitionValue(x)
334
+ }),
335
+ onSome: (eTag) => ({
336
+ operationType: "Replace" as const,
337
+ id: x[idKey],
338
+ resourceBody: {
339
+ ...Struct.omit(codec.encode(x), ["_etag", idKey]),
340
+ id: x[idKey],
341
+ _partitionKey: nsPartitionValue(ns, x)
342
+ },
343
+ // don't use this or we get an error that the request and some item partition key dont match - makese no sense
344
+ // partitionKey: config?.partitionValue(x)
345
+ ifMatch: eTag
346
+ })
347
+ })
348
+ ] as const
349
+ )
290
350
 
291
- return yield* Effect.die(
292
- new CosmosDbOperationError("not able to update record: " + code)
293
- )
294
- }
351
+ const ex = batch.map(([, c]) => c)
295
352
 
296
- return batch.map(([e], i) => ({
297
- ...e,
298
- _etag: result[i]?.eTag
299
- })) as unknown as NonEmptyReadonlyArray<Encoded>
300
- })))
301
- })
302
- .pipe(Effect
303
- .withSpan("Cosmos.batchSet [effect-app/infra/Store]", {
304
- attributes: {
305
- "repository.container_id": containerId,
306
- "repository.model_name": name,
307
- namespace: ns
353
+ return tryCosmos(() => execBatch(ex, ex[0]?.resourceBody._partitionKey))
354
+ .pipe(Effect.flatMap(Effect.fnUntraced(function*(x) {
355
+ const result = x.result ?? []
356
+ const firstFailed = result.find(
357
+ (x: any) => x.statusCode > 299 || x.statusCode < 200
358
+ )
359
+ if (firstFailed) {
360
+ const code = firstFailed.statusCode ?? 0
361
+ if (code === 412 || code === 404 || code === 409) {
362
+ return yield* new OptimisticConcurrencyException({ type: name, id: "batch", code })
308
363
  }
309
- }, { captureStackTrace: false }))
310
- ))
311
- }
312
364
 
313
- const s: Store<IdKey, Encoded> = {
314
- queryRaw: <Out>(query: RawQuery<Encoded, Out>) =>
315
- Effect
316
- .all({ q: Effect.sync(() => query.cosmos({ name })), ns: resolveAndSeed })
317
- .pipe(
318
- Effect.tap(({ q }) => logQuery(q)),
319
- Effect.flatMap(({ ns, q }) =>
320
- Effect
321
- .promise(() =>
322
- container
323
- .items
324
- .query<Out>(q, { partitionKey: nsBasePartitionKey(ns) })
325
- .fetchAll()
326
- .then(({ resources }) =>
327
- resources.map(
328
- (_) => ({ ...defaultValues, ...mapReverseId(_ as any) }) as Out
329
- )
330
- )
331
- )
332
- .pipe(
333
- Effect.withSpan("Cosmos.queryRaw [effect-app/infra/Store]", {
334
- attributes: {
335
- "repository.container_id": containerId,
336
- "repository.model_name": name,
337
- namespace: ns
338
- }
339
- }, { captureStackTrace: false })
340
- )
341
- )
342
- ),
343
- batchRemove: (ids, partitionKey?: string) =>
344
- resolveAndSeed.pipe(Effect.flatMap((ns) =>
365
+ return yield* new DatabaseError({
366
+ message: "not able to update record: " + code,
367
+ transient: statusIsTransient(code),
368
+ cause: x
369
+ })
370
+ }
371
+
372
+ return batch.map(([e], i) => ({
373
+ ...e,
374
+ _etag: result[i]?.eTag
375
+ })) as unknown as NonEmptyReadonlyArray<Encoded>
376
+ })))
377
+ })
378
+ .pipe(annotateDb({
379
+ operation: "batchSet",
380
+ system: "cosmosdb",
381
+ collection: containerId,
382
+ namespace: ns,
383
+ entity: name
384
+ }))
385
+ ))
386
+ }
387
+
388
+ const s: Store<IdKey, Encoded> = {
389
+ seedNamespace: (ns) => seedNamespace(ns),
390
+
391
+ queryRaw: <Out>(query: RawQuery<Encoded, Out>) =>
392
+ Effect
393
+ .all({ q: Effect.sync(() => query.cosmos({ name })), ns: resolveNamespace })
394
+ .pipe(
395
+ Effect.tap(({ q }) => logQuery(q)),
396
+ Effect.flatMap(({ ns, q }) =>
345
397
  Effect
346
- .promise(() =>
347
- execBatch(
348
- mutable(ids.map((id) =>
349
- dropUndefinedT({
350
- operationType: "Delete" as const,
351
- id
352
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
353
- // partitionKey: config?.partitionValue({ [idKey]: id } as Encoded)
354
- })
355
- )),
356
- partitionKey ?? nsBasePartitionKey(ns)
398
+ .gen(function*() {
399
+ const response = yield* tryCosmos(() =>
400
+ container.items.query<Out>(q, { partitionKey: nsBasePartitionKey(ns) }).fetchAll()
357
401
  )
358
- )
402
+ yield* annotateFeed(response)
403
+ return response.resources.map(
404
+ (_) => ({ ...defaultValues, ...mapReverseId(_ as any) }) as Out
405
+ )
406
+ })
359
407
  .pipe(
360
- Effect.withSpan("Cosmos.batchRemove [effect-app/infra/Store]", {
361
- attributes: {
362
- "repository.container_id": containerId,
363
- "repository.model_name": name,
364
- namespace: ns
365
- }
366
- }, { captureStackTrace: false })
408
+ annotateDb({
409
+ operation: "queryRaw",
410
+ system: "cosmosdb",
411
+ collection: containerId,
412
+ namespace: ns,
413
+ entity: name,
414
+ query: q.query
415
+ })
367
416
  )
368
- )),
369
- all: Effect
370
- .all({
371
- q: Effect.sync(() => ({
372
- query: `SELECT * FROM ${name}`,
373
- parameters: []
374
- })),
375
- ns: resolveAndSeed
376
- })
417
+ )
418
+ ),
419
+ batchRemove: (ids, partitionKey?: string) =>
420
+ resolveNamespace.pipe(Effect.flatMap((ns) =>
421
+ tryCosmos(() =>
422
+ execBatch(
423
+ mutable(ids.map((id) =>
424
+ dropUndefinedT({
425
+ operationType: "Delete" as const,
426
+ id
427
+ // don't use this or we get an error that the request and some item partition key dont match - makese no sense
428
+ // partitionKey: config?.partitionValue({ [idKey]: id } as Encoded)
429
+ })
430
+ )),
431
+ partitionKey ?? nsBasePartitionKey(ns)
432
+ )
433
+ )
377
434
  .pipe(
378
- Effect.tap(({ q }) => logQuery(q)),
379
- Effect.flatMap(({ ns, q }) =>
380
- Effect
381
- .promise(() =>
382
- container
383
- .items
384
- .query<PMCosmos>(q, { partitionKey: nsBasePartitionKey(ns) })
385
- .fetchAll()
386
- .then(({ resources }) =>
387
- resources.map(
388
- (_) => ({ ...defaultValues, ...mapReverseId(_) })
389
- )
390
- )
391
- )
392
- .pipe(
393
- Effect.withSpan("Cosmos.all [effect-app/infra/Store]", {
394
- attributes: {
395
- "repository.container_id": containerId,
396
- "repository.model_name": name,
397
- namespace: ns
398
- }
399
- }, { captureStackTrace: false })
400
- )
401
- )
402
- ),
403
- /**
404
- * May return duplicate results for "join_find", when matching more than once.
405
- */
406
- filter: <U extends keyof Encoded = never>(
407
- f: FilterArgs<Encoded, U>
408
- ) => {
409
- const skip = f?.skip
410
- const limit = f?.limit
411
- const filter = f.filter
412
- type M = U extends undefined ? Encoded : Pick<Encoded, U>
413
- return Effect
414
- .all({
415
- q: Effect.sync(() =>
416
- buildWhereCosmosQuery3(
417
- idKey,
418
- filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
419
- name,
420
- defaultValues,
421
- f.select as
422
- | NonEmptyReadonlyArray<string | { key: string; subKeys: readonly string[] }>
423
- | undefined,
424
- f.order as NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }> | undefined,
425
- skip,
426
- limit
427
- )
428
- ),
429
- ns: resolveAndSeed
435
+ annotateDb({
436
+ operation: "batchRemove",
437
+ system: "cosmosdb",
438
+ collection: containerId,
439
+ namespace: ns,
440
+ entity: name
441
+ })
442
+ )
443
+ )),
444
+ all: Effect
445
+ .all({
446
+ q: Effect.sync(() => ({
447
+ query: `SELECT * FROM ${name}`,
448
+ parameters: []
449
+ })),
450
+ ns: resolveNamespace
451
+ })
452
+ .pipe(
453
+ Effect.tap(({ q }) => logQuery(q)),
454
+ Effect.flatMap(({ ns, q }) =>
455
+ Effect
456
+ .gen(function*() {
457
+ const response = yield* tryCosmos(() =>
458
+ container.items.query<PMCosmos>(q, { partitionKey: nsBasePartitionKey(ns) }).fetchAll()
459
+ )
460
+ yield* annotateFeed(response)
461
+ return yield* Effect.fromResult(
462
+ decodeStoredMany(response.resources, (_) => fromStored(_ as unknown as Encoded))
463
+ )
430
464
  })
431
465
  .pipe(
432
- Effect.tap(({ q }) => logQuery(q)),
466
+ annotateDb({
467
+ operation: "all",
468
+ system: "cosmosdb",
469
+ collection: containerId,
470
+ namespace: ns,
471
+ entity: name,
472
+ query: q.query
473
+ })
474
+ )
475
+ )
476
+ ),
477
+ /**
478
+ * May return duplicate results for "join_find", when matching more than once.
479
+ */
480
+ filter: <U extends keyof Encoded = never>(
481
+ f: FilterArgs<Encoded, U>
482
+ ) => {
483
+ const skip = f?.skip
484
+ const limit = f?.limit
485
+ const filter = f.filter
486
+ type M = U extends undefined ? Encoded : Pick<Encoded, U>
487
+ return Effect
488
+ .all({
489
+ q: Effect.sync(() =>
490
+ buildWhereCosmosQuery3(
491
+ idKey,
492
+ filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
493
+ name,
494
+ defaultValues,
495
+ f.select as
496
+ | NonEmptyReadonlyArray<
497
+ string | {
498
+ key: string
499
+ subKeys: readonly string[]
500
+ } | {
501
+ key: string
502
+ computed: ComputedProjectionIrExpression
503
+ }
504
+ >
505
+ | undefined,
506
+ f.order as NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }> | undefined,
507
+ skip,
508
+ limit,
509
+ json
510
+ )
511
+ ),
512
+ ns: resolveNamespace
513
+ })
514
+ .pipe(
515
+ Effect.tap(({ q }) => logQuery(q)),
516
+ Effect
517
+ .flatMap(({ ns, q }) =>
433
518
  Effect
434
- .flatMap(({ ns, q }) =>
435
- Effect
436
- .promise(() =>
437
- f.select
438
- ? container
439
- .items
440
- .query<M>(q, { partitionKey: nsBasePartitionKey(ns) })
441
- .fetchAll()
442
- .then(({ resources }) =>
443
- resources.map((_) => ({
444
- ...pipe(
445
- defaultValues,
446
- Struct.pick(f.select!.filter((_) => typeof _ === "string") as never[])
447
- ),
448
- ...mapReverseId(_ as any)
449
- }))
450
- )
451
- : container
452
- .items
453
- .query<{ f: M }>(q, { partitionKey: nsBasePartitionKey(ns) })
454
- .fetchAll()
455
- .then(({ resources }) =>
456
- resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
457
- )
458
- )
459
- .pipe(
460
- Effect.withSpan("Cosmos.filter [effect-app/infra/Store]", {
461
- attributes: {
462
- "repository.container_id": containerId,
463
- "repository.model_name": name,
464
- namespace: ns
465
- }
466
- }, { captureStackTrace: false })
519
+ .gen(function*() {
520
+ if (f.select) {
521
+ const response = yield* tryCosmos(() =>
522
+ container.items.query<M>(q, { partitionKey: nsBasePartitionKey(ns) }).fetchAll()
467
523
  )
524
+ yield* annotateFeed(response)
525
+ return response.resources.map((_) => ({
526
+ ...pipe(
527
+ defaultValues,
528
+ Struct.pick(f.select!.filter((_) => typeof _ === "string") as never[])
529
+ ),
530
+ ...mapReverseId(_ as any)
531
+ }))
532
+ }
533
+ const response = yield* tryCosmos(() =>
534
+ container.items.query<{ f: M }>(q, { partitionKey: nsBasePartitionKey(ns) }).fetchAll()
535
+ )
536
+ yield* annotateFeed(response)
537
+ const decoded = yield* Effect.fromResult(
538
+ decodeStoredMany(response.resources, ({ f }) => fromStored(f as Encoded))
539
+ )
540
+ return decoded as any
541
+ })
542
+ .pipe(
543
+ annotateDb({
544
+ operation: "filter",
545
+ system: "cosmosdb",
546
+ collection: containerId,
547
+ namespace: ns,
548
+ entity: name,
549
+ query: q.query
550
+ })
468
551
  )
469
552
  )
470
- },
471
- find: (id) =>
472
- resolveAndSeed.pipe(Effect.flatMap((ns) =>
553
+ )
554
+ },
555
+ find: (id) =>
556
+ resolveNamespace.pipe(Effect.flatMap((ns) =>
557
+ Effect
558
+ .gen(function*() {
559
+ const response = yield* tryCosmos(() =>
560
+ container
561
+ .item(id, nsPartitionValue(ns, { [idKey]: id } as Encoded))
562
+ .read<Encoded>()
563
+ )
564
+ yield* annotateItem(response)
565
+ return yield* Effect.fromResult(
566
+ decodeStoredOption(Option.fromNullishOr(response.resource), fromStored)
567
+ )
568
+ })
569
+ .pipe(annotateDb({
570
+ operation: "find",
571
+ system: "cosmosdb",
572
+ collection: containerId,
573
+ namespace: ns,
574
+ entity: name,
575
+ extra: {
576
+ "azure.cosmosdb.operation.partition_key": nsPartitionValue(ns, { [idKey]: id } as Encoded),
577
+ "app.entity.id": id
578
+ }
579
+ }))
580
+ )),
581
+ set: (e) =>
582
+ resolveNamespace.pipe(Effect.flatMap((ns) =>
583
+ // Annotate the result so the create/replace promise union collapses to
584
+ // one type (otherwise `A` infers as `unknown` and the chain degrades).
585
+ tryCosmos((): Promise<{ statusCode: number; etag: string }> =>
586
+ Option
587
+ .match(
588
+ Option
589
+ .fromNullishOr(e._etag),
590
+ {
591
+ onNone: () =>
592
+ container.items.create({
593
+ ...mapId(codec.encode(e)),
594
+ _partitionKey: nsPartitionValue(ns, e)
595
+ }),
596
+ onSome: (eTag) =>
597
+ container.item(e[idKey], nsPartitionValue(ns, e)).replace(
598
+ { ...mapId(codec.encode(e)), _partitionKey: nsPartitionValue(ns, e) },
599
+ {
600
+ accessCondition: {
601
+ type: "IfMatch",
602
+ condition: eTag
603
+ }
604
+ }
605
+ )
606
+ }
607
+ )
608
+ )
609
+ .pipe(
473
610
  Effect
474
- .promise(() =>
475
- container
476
- .item(id, nsPartitionValue(ns, { [idKey]: id } as Encoded))
477
- .read<Encoded>()
478
- .then(({ resource }) =>
479
- Option.fromNullishOr(resource).pipe(
480
- Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
481
- )
611
+ .flatMap((x): Effect.Effect<PM, OptimisticConcurrencyException | DatabaseError> => {
612
+ if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
613
+ return Effect.fail(
614
+ new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode })
482
615
  )
483
- )
484
- .pipe(Effect
485
- .withSpan("Cosmos.find [effect-app/infra/Store]", {
486
- attributes: {
487
- "repository.container_id": containerId,
488
- "repository.model_name": name,
489
- partitionValue: nsPartitionValue(ns, { [idKey]: id } as Encoded),
490
- namespace: ns,
491
- id
492
- }
493
- }, { captureStackTrace: false }))
494
- )),
495
- set: (e) =>
496
- resolveAndSeed.pipe(Effect.flatMap((ns) =>
497
- Option
498
- .match(
499
- Option
500
- .fromNullishOr(e._etag),
501
- {
502
- onNone: () =>
503
- Effect.promise(() =>
504
- container.items.create({
505
- ...mapId(e),
506
- _partitionKey: nsPartitionValue(ns, e)
507
- })
508
- ),
509
- onSome: (eTag) =>
510
- Effect.promise(() =>
511
- container.item(e[idKey], nsPartitionValue(ns, e)).replace(
512
- { ...mapId(e), _partitionKey: nsPartitionValue(ns, e) },
513
- {
514
- accessCondition: {
515
- type: "IfMatch",
516
- condition: eTag
517
- }
518
- }
519
- )
520
- )
521
616
  }
522
- )
523
- .pipe(
524
- Effect
525
- .flatMap((x) => {
526
- if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
527
- return Effect.fail(
528
- new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode })
529
- )
530
- }
531
- if (x.statusCode > 299 || x.statusCode < 200) {
532
- return Effect.die(
533
- new CosmosDbOperationError(
534
- "not able to update record: " + x.statusCode
535
- )
536
- )
537
- }
538
- return Effect.sync(() => ({
539
- ...e,
540
- _etag: x.etag
541
- }))
542
- }),
543
- Effect
544
- .withSpan("Cosmos.set [effect-app/infra/Store]", {
545
- attributes: {
546
- "repository.container_id": containerId,
547
- "repository.model_name": name,
548
- namespace: ns,
549
- id: e[idKey]
550
- }
551
- }, { captureStackTrace: false })
552
- )
553
- )),
554
- batchSet,
555
- bulkSet
556
- }
617
+ if (x.statusCode > 299 || x.statusCode < 200) {
618
+ return Effect.fail(
619
+ new DatabaseError({
620
+ message: "not able to update record: " + x.statusCode,
621
+ transient: statusIsTransient(x.statusCode),
622
+ cause: x
623
+ })
624
+ )
625
+ }
626
+ return Effect.sync(() => ({
627
+ ...e,
628
+ _etag: x.etag
629
+ }))
630
+ }),
631
+ annotateDb({
632
+ operation: "set",
633
+ system: "cosmosdb",
634
+ collection: containerId,
635
+ namespace: ns,
636
+ entity: name,
637
+ extra: { "app.entity.id": e[idKey] }
638
+ })
639
+ )
640
+ )),
641
+ batchSet,
642
+ bulkSet
643
+ }
557
644
 
558
- // Eagerly seed primary namespace on initialization
559
- yield* seedNamespace("primary")
645
+ // Eagerly seed primary namespace on initialization
646
+ yield* seedNamespace("primary")
560
647
 
561
- return s
562
- })
563
- }
564
- })
565
- }
648
+ return s
649
+ })
650
+ }
651
+ })
566
652
 
567
653
  export function CosmosStoreLayer(cfg: StorageConfig) {
568
654
  return StoreMaker