@effect-app/infra 4.0.0-beta.20 → 4.0.0-beta.201

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