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

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