@effect-app/infra 4.0.0-beta.98 → 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 +2891 -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 -515
  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,551 +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) =>
98
- bulkSetInternal(a, ns).pipe(
99
- Effect.orDie,
100
- Effect.delay(Duration.millis(1100))
101
- )
102
- )
103
- ),
104
- Effect.andThen(
105
- Effect.promise(() =>
106
- container.items.create({
107
- _partitionKey: markerId,
108
- id: markerId,
109
- ttl: -1
110
- })
111
- )
112
- ),
113
- Effect.provide(ctx),
114
- 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
+ })
115
179
  )
116
- }),
117
- Effect.withLogSpan(`Cosmos.seedCheck ${name} in ${ns} [effect-app/infra/Store]`),
118
- Effect.withSpan("Cosmos.seed [effect-app/infra/Store]", { attributes: { name, namespace: ns } })
180
+ ),
181
+ Effect.provide(ctx),
182
+ Effect.orDie
119
183
  )
120
- }
121
- const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
122
- if (!seed) return
123
- let cached = seedCache.get(ns)
124
- if (!cached) {
125
- cached = yield* Effect.cached(makeSeedEffect(ns))
126
- seedCache.set(ns, cached)
127
- }
128
- yield* cached
129
- })
130
- const resolveAndSeed = resolveNamespace.pipe(
131
- 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
+ })
132
193
  )
133
-
134
- const bulkSetInternal = (items: NonEmptyReadonlyArray<PM>, ns: string) =>
135
- Effect
136
- .gen(function*() {
137
- // TODO: disable batching if need atomicity
138
- // we delay and batch to keep low amount of RUs
139
- const b = [...items]
140
- .map(
141
- (x) =>
142
- [
143
- x,
144
- Option.match(Option.fromNullishOr(x._etag), {
145
- onNone: () =>
146
- dropUndefinedT({
147
- operationType: "Create" as const,
148
- resourceBody: {
149
- ...Struct.omit(x, ["_etag", idKey]),
150
- id: x[idKey],
151
- _partitionKey: nsPartitionValue(ns, x)
152
- }
153
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
154
- // partitionKey: config?.partitionValue(x)
155
- }),
156
- onSome: (eTag) =>
157
- dropUndefinedT({
158
- operationType: "Replace" as const,
159
- id: x[idKey],
160
- resourceBody: {
161
- ...Struct.omit(x, ["_etag", idKey]),
162
- id: x[idKey],
163
- _partitionKey: nsPartitionValue(ns, x)
164
- },
165
- ifMatch: eTag
166
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
167
- // partitionKey: config?.partitionValue(x)
168
- })
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)
169
238
  })
170
- ] as const
171
- )
172
- const batches = Array.chunksOf(b, config?.maxBulkSize ?? 10)
239
+ })
240
+ ] as const
241
+ )
242
+ const batches = Array.chunksOf(b, config?.maxBulkSize ?? 10)
173
243
 
174
- const batchResult = yield* Effect.forEach(
175
- batches
176
- .map((x, i) => [i, x] as const),
177
- ([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(
178
250
  Effect
179
- .promise(() => bulk(batch.map(([, op]) => op)))
180
- .pipe(
181
- Effect
182
- .delay(Duration.millis(i === 0 ? 0 : 1100)),
183
- Effect
184
- .flatMap((responses) =>
185
- Effect.gen(function*() {
186
- const r = responses.find((x) =>
187
- x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409
188
- )
189
- if (r) {
190
- return yield* Effect.fail(
191
- new OptimisticConcurrencyException(
192
- {
193
- type: name,
194
- id: JSON.stringify(r.resourceBody?.["id"]),
195
- code: r.statusCode,
196
- raw: responses
197
- }
198
- )
199
- )
200
- }
201
- const r2 = responses.find(
202
- (x) => x.statusCode !== 424 && (x.statusCode > 299 || x.statusCode < 200)
203
- )
204
- if (r2) {
205
- return yield* Effect.die(
206
- new CosmosDbOperationError(
207
- "not able to update records: " + r2.statusCode,
208
- responses
209
- )
210
- )
211
- }
212
- const r3 = responses.find(
213
- (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
+ }
214
267
  )
215
- if (r3) {
216
- return yield* Effect.die(
217
- new CosmosDbOperationError(
218
- "not able to update records: " + r3.statusCode,
219
- responses
220
- )
221
- )
222
- }
223
- return batch.map(([e], i) => ({
224
- ...e,
225
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
226
- _etag: responses[i]!.eTag
227
- }))
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
228
278
  })
279
+ }
280
+ const r3 = responses.find(
281
+ (x) => x.statusCode > 299 || x.statusCode < 200
229
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
+ })
230
296
  )
231
- )
232
-
233
- return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
234
- })
235
- .pipe(
236
- Effect.withSpan("Cosmos.bulkSet [effect-app/infra/Store]", {
237
- attributes: { "repository.container_id": containerId, "repository.model_name": name, namespace: ns }
238
- }, { captureStackTrace: false })
239
- )
240
-
241
- const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
242
- resolveAndSeed.pipe(Effect.flatMap((ns) => bulkSetInternal(items, ns)))
297
+ )
298
+ )
243
299
 
244
- const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
245
- return resolveAndSeed
246
- .pipe(Effect.flatMap((ns) =>
247
- Effect
248
- .suspend(() => {
249
- const batch = [...items].map(
250
- (x) =>
251
- [
252
- x,
253
- Option.match(Option.fromNullishOr(x._etag), {
254
- onNone: () => ({
255
- operationType: "Create" as const,
256
- resourceBody: {
257
- ...Struct.omit(x, ["_etag", idKey]),
258
- id: x[idKey],
259
- _partitionKey: nsPartitionValue(ns, x)
260
- }
261
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
262
- // partitionKey: config?.partitionValue(x)
263
- }),
264
- onSome: (eTag) => ({
265
- operationType: "Replace" as const,
266
- id: x[idKey],
267
- resourceBody: {
268
- ...Struct.omit(x, ["_etag", idKey]),
269
- id: x[idKey],
270
- _partitionKey: nsPartitionValue(ns, x)
271
- },
272
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
273
- // partitionKey: config?.partitionValue(x)
274
- ifMatch: eTag
275
- })
276
- })
277
- ] as const
278
- )
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
+ )
279
311
 
280
- const ex = batch.map(([, c]) => c)
312
+ const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
313
+ resolveNamespace.pipe(Effect.flatMap((ns) => bulkSetInternal(items, ns)))
281
314
 
282
- return Effect
283
- .promise(() => execBatch(ex, ex[0]?.resourceBody._partitionKey))
284
- .pipe(Effect.flatMap(Effect.fnUntraced(function*(x) {
285
- const result = x.result ?? []
286
- const firstFailed = result.find(
287
- (x: any) => x.statusCode > 299 || x.statusCode < 200
288
- )
289
- if (firstFailed) {
290
- const code = firstFailed.statusCode ?? 0
291
- if (code === 412 || code === 404 || code === 409) {
292
- 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)
293
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
+ )
294
350
 
295
- return yield* Effect.die(
296
- new CosmosDbOperationError("not able to update record: " + code)
297
- )
298
- }
351
+ const ex = batch.map(([, c]) => c)
299
352
 
300
- return batch.map(([e], i) => ({
301
- ...e,
302
- _etag: result[i]?.eTag
303
- })) as unknown as NonEmptyReadonlyArray<Encoded>
304
- })))
305
- })
306
- .pipe(Effect
307
- .withSpan("Cosmos.batchSet [effect-app/infra/Store]", {
308
- attributes: {
309
- "repository.container_id": containerId,
310
- "repository.model_name": name,
311
- 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 })
312
363
  }
313
- }, { captureStackTrace: false }))
314
- ))
315
- }
316
364
 
317
- const s: Store<IdKey, Encoded> = {
318
- queryRaw: <Out>(query: RawQuery<Encoded, Out>) =>
319
- Effect
320
- .all({ q: Effect.sync(() => query.cosmos({ name })), ns: resolveAndSeed })
321
- .pipe(
322
- Effect.tap(({ q }) => logQuery(q)),
323
- Effect.flatMap(({ ns, q }) =>
324
- Effect
325
- .promise(() =>
326
- container
327
- .items
328
- .query<Out>(q, { partitionKey: nsBasePartitionKey(ns) })
329
- .fetchAll()
330
- .then(({ resources }) =>
331
- resources.map(
332
- (_) => ({ ...defaultValues, ...mapReverseId(_ as any) }) as Out
333
- )
334
- )
335
- )
336
- .pipe(
337
- Effect.withSpan("Cosmos.queryRaw [effect-app/infra/Store]", {
338
- attributes: {
339
- "repository.container_id": containerId,
340
- "repository.model_name": name,
341
- namespace: ns
342
- }
343
- }, { captureStackTrace: false })
344
- )
345
- )
346
- ),
347
- batchRemove: (ids, partitionKey?: string) =>
348
- 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 }) =>
349
397
  Effect
350
- .promise(() =>
351
- execBatch(
352
- mutable(ids.map((id) =>
353
- dropUndefinedT({
354
- operationType: "Delete" as const,
355
- id
356
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
357
- // partitionKey: config?.partitionValue({ [idKey]: id } as Encoded)
358
- })
359
- )),
360
- partitionKey ?? nsBasePartitionKey(ns)
398
+ .gen(function*() {
399
+ const response = yield* tryCosmos(() =>
400
+ container.items.query<Out>(q, { partitionKey: nsBasePartitionKey(ns) }).fetchAll()
361
401
  )
362
- )
402
+ yield* annotateFeed(response)
403
+ return response.resources.map(
404
+ (_) => ({ ...defaultValues, ...mapReverseId(_ as any) }) as Out
405
+ )
406
+ })
363
407
  .pipe(
364
- Effect.withSpan("Cosmos.batchRemove [effect-app/infra/Store]", {
365
- attributes: {
366
- "repository.container_id": containerId,
367
- "repository.model_name": name,
368
- namespace: ns
369
- }
370
- }, { captureStackTrace: false })
408
+ annotateDb({
409
+ operation: "queryRaw",
410
+ system: "cosmosdb",
411
+ collection: containerId,
412
+ namespace: ns,
413
+ entity: name,
414
+ query: q.query
415
+ })
371
416
  )
372
- )),
373
- all: Effect
374
- .all({
375
- q: Effect.sync(() => ({
376
- query: `SELECT * FROM ${name}`,
377
- parameters: []
378
- })),
379
- ns: resolveAndSeed
380
- })
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
+ )
381
434
  .pipe(
382
- Effect.tap(({ q }) => logQuery(q)),
383
- Effect.flatMap(({ ns, q }) =>
384
- Effect
385
- .promise(() =>
386
- container
387
- .items
388
- .query<PMCosmos>(q, { partitionKey: nsBasePartitionKey(ns) })
389
- .fetchAll()
390
- .then(({ resources }) =>
391
- resources.map(
392
- (_) => ({ ...defaultValues, ...mapReverseId(_) })
393
- )
394
- )
395
- )
396
- .pipe(
397
- Effect.withSpan("Cosmos.all [effect-app/infra/Store]", {
398
- attributes: {
399
- "repository.container_id": containerId,
400
- "repository.model_name": name,
401
- namespace: ns
402
- }
403
- }, { captureStackTrace: false })
404
- )
405
- )
406
- ),
407
- /**
408
- * May return duplicate results for "join_find", when matching more than once.
409
- */
410
- filter: <U extends keyof Encoded = never>(
411
- f: FilterArgs<Encoded, U>
412
- ) => {
413
- const skip = f?.skip
414
- const limit = f?.limit
415
- const filter = f.filter
416
- type M = U extends undefined ? Encoded : Pick<Encoded, U>
417
- return Effect
418
- .all({
419
- q: Effect.sync(() =>
420
- buildWhereCosmosQuery3(
421
- idKey,
422
- filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
423
- name,
424
- defaultValues,
425
- f.select as
426
- | NonEmptyReadonlyArray<string | { key: string; subKeys: readonly string[] }>
427
- | undefined,
428
- f.order as NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }> | undefined,
429
- skip,
430
- limit
431
- )
432
- ),
433
- 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
+ )
434
464
  })
435
465
  .pipe(
436
- 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 }) =>
437
518
  Effect
438
- .flatMap(({ ns, q }) =>
439
- Effect
440
- .promise(() =>
441
- f.select
442
- ? container
443
- .items
444
- .query<M>(q, { partitionKey: nsBasePartitionKey(ns) })
445
- .fetchAll()
446
- .then(({ resources }) =>
447
- resources.map((_) => ({
448
- ...pipe(
449
- defaultValues,
450
- Struct.pick(f.select!.filter((_) => typeof _ === "string") as never[])
451
- ),
452
- ...mapReverseId(_ as any)
453
- }))
454
- )
455
- : container
456
- .items
457
- .query<{ f: M }>(q, { partitionKey: nsBasePartitionKey(ns) })
458
- .fetchAll()
459
- .then(({ resources }) =>
460
- resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
461
- )
462
- )
463
- .pipe(
464
- Effect.withSpan("Cosmos.filter [effect-app/infra/Store]", {
465
- attributes: {
466
- "repository.container_id": containerId,
467
- "repository.model_name": name,
468
- namespace: ns
469
- }
470
- }, { captureStackTrace: false })
519
+ .gen(function*() {
520
+ if (f.select) {
521
+ const response = yield* tryCosmos(() =>
522
+ container.items.query<M>(q, { partitionKey: nsBasePartitionKey(ns) }).fetchAll()
471
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
+ })
472
551
  )
473
552
  )
474
- },
475
- find: (id) =>
476
- 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(
477
610
  Effect
478
- .promise(() =>
479
- container
480
- .item(id, nsPartitionValue(ns, { [idKey]: id } as Encoded))
481
- .read<Encoded>()
482
- .then(({ resource }) =>
483
- Option.fromNullishOr(resource).pipe(
484
- Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
485
- )
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 })
486
615
  )
487
- )
488
- .pipe(Effect
489
- .withSpan("Cosmos.find [effect-app/infra/Store]", {
490
- attributes: {
491
- "repository.container_id": containerId,
492
- "repository.model_name": name,
493
- partitionValue: nsPartitionValue(ns, { [idKey]: id } as Encoded),
494
- namespace: ns,
495
- id
496
- }
497
- }, { captureStackTrace: false }))
498
- )),
499
- set: (e) =>
500
- resolveAndSeed.pipe(Effect.flatMap((ns) =>
501
- Option
502
- .match(
503
- Option
504
- .fromNullishOr(e._etag),
505
- {
506
- onNone: () =>
507
- Effect.promise(() =>
508
- container.items.create({
509
- ...mapId(e),
510
- _partitionKey: nsPartitionValue(ns, e)
511
- })
512
- ),
513
- onSome: (eTag) =>
514
- Effect.promise(() =>
515
- container.item(e[idKey], nsPartitionValue(ns, e)).replace(
516
- { ...mapId(e), _partitionKey: nsPartitionValue(ns, e) },
517
- {
518
- accessCondition: {
519
- type: "IfMatch",
520
- condition: eTag
521
- }
522
- }
523
- )
524
- )
525
616
  }
526
- )
527
- .pipe(
528
- Effect
529
- .flatMap((x) => {
530
- if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
531
- return Effect.fail(
532
- new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode })
533
- )
534
- }
535
- if (x.statusCode > 299 || x.statusCode < 200) {
536
- return Effect.die(
537
- new CosmosDbOperationError(
538
- "not able to update record: " + x.statusCode
539
- )
540
- )
541
- }
542
- return Effect.sync(() => ({
543
- ...e,
544
- _etag: x.etag
545
- }))
546
- }),
547
- Effect
548
- .withSpan("Cosmos.set [effect-app/infra/Store]", {
549
- attributes: {
550
- "repository.container_id": containerId,
551
- "repository.model_name": name,
552
- namespace: ns,
553
- id: e[idKey]
554
- }
555
- }, { captureStackTrace: false })
556
- )
557
- )),
558
- batchSet,
559
- bulkSet
560
- }
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
+ }
561
644
 
562
- // Eagerly seed primary namespace on initialization
563
- yield* seedNamespace("primary")
645
+ // Eagerly seed primary namespace on initialization
646
+ yield* seedNamespace("primary")
564
647
 
565
- return s
566
- })
567
- }
568
- })
569
- }
648
+ return s
649
+ })
650
+ }
651
+ })
570
652
 
571
653
  export function CosmosStoreLayer(cfg: StorageConfig) {
572
654
  return StoreMaker