@effect-app/infra 4.0.0-beta.21 → 4.0.0-beta.211

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 (309) hide show
  1. package/CHANGELOG.md +1556 -0
  2. package/_check.sh +1 -1
  3. package/dist/CUPS.d.ts +7 -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 +98 -50
  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 +46 -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 +130 -116
  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 +75 -63
  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 +52 -53
  72. package/dist/QueueMaker/service.d.ts +1 -1
  73. package/dist/RequestContext.d.ts +74 -35
  74. package/dist/RequestContext.d.ts.map +1 -1
  75. package/dist/RequestContext.js +13 -14
  76. package/dist/RequestFiberSet.d.ts +7 -7
  77. package/dist/RequestFiberSet.d.ts.map +1 -1
  78. package/dist/RequestFiberSet.js +3 -3
  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 +335 -243
  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 +72 -35
  91. package/dist/Store/Memory.d.ts +6 -4
  92. package/dist/Store/Memory.d.ts.map +1 -1
  93. package/dist/Store/Memory.js +90 -57
  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 +231 -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 +464 -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 +4 -2
  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 +31 -42
  117. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  118. package/dist/adapters/SQL/Model.js +29 -38
  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 +25 -21
  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 +10 -8
  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 +48 -16
  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 +80 -37
  174. package/dist/api/routing.d.ts.map +1 -1
  175. package/dist/api/routing.js +112 -40
  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/otel.d.ts +75 -0
  197. package/dist/otel.d.ts.map +1 -0
  198. package/dist/otel.js +65 -0
  199. package/dist/rateLimit.d.ts +9 -3
  200. package/dist/rateLimit.d.ts.map +1 -1
  201. package/dist/rateLimit.js +5 -11
  202. package/dist/test.d.ts +2 -2
  203. package/dist/test.d.ts.map +1 -1
  204. package/dist/test.js +1 -1
  205. package/dist/vitest.d.ts +1 -1
  206. package/examples/query.ts +39 -35
  207. package/package.json +45 -37
  208. package/src/CUPS.ts +9 -11
  209. package/src/Emailer/Sendgrid.ts +17 -14
  210. package/src/Emailer/service.ts +9 -3
  211. package/src/MainFiberSet.ts +5 -6
  212. package/src/Model/Repository/Registry.ts +33 -0
  213. package/src/Model/Repository/ext.ts +96 -10
  214. package/src/Model/Repository/internal/internal.ts +213 -148
  215. package/src/Model/Repository/makeRepo.ts +12 -10
  216. package/src/Model/Repository/service.ts +31 -22
  217. package/src/Model/Repository/validation.ts +4 -4
  218. package/src/Model/Repository.ts +1 -0
  219. package/src/Model/dsl.ts +3 -3
  220. package/src/Model/filter/types/path/eager.ts +1 -2
  221. package/src/Model/query/dsl.ts +18 -18
  222. package/src/Model/query/new-kid-interpreter.ts +2 -2
  223. package/src/Model.ts +1 -0
  224. package/src/QueueMaker/SQLQueue.ts +144 -152
  225. package/src/QueueMaker/memQueue.ts +104 -103
  226. package/src/QueueMaker/sbqueue.ts +70 -86
  227. package/src/RequestContext.ts +14 -16
  228. package/src/RequestFiberSet.ts +2 -2
  229. package/src/Store/ContextMapContainer.ts +41 -2
  230. package/src/Store/Cosmos/query.ts +16 -20
  231. package/src/Store/Cosmos.ts +473 -348
  232. package/src/Store/Disk.ts +102 -65
  233. package/src/Store/Memory.ts +118 -83
  234. package/src/Store/SQL/Pg.ts +352 -0
  235. package/src/Store/SQL/query.ts +409 -0
  236. package/src/Store/SQL.ts +734 -0
  237. package/src/Store/codeFilter.ts +3 -1
  238. package/src/Store/index.ts +17 -2
  239. package/src/Store/service.ts +32 -8
  240. package/src/Store/utils.ts +23 -22
  241. package/src/adapters/SQL/Model.ts +41 -40
  242. package/src/adapters/ServiceBus.ts +125 -121
  243. package/src/adapters/cosmos-client.ts +2 -2
  244. package/src/adapters/index.ts +7 -0
  245. package/src/adapters/memQueue.ts +2 -2
  246. package/src/adapters/mongo-client.ts +2 -2
  247. package/src/adapters/redis-client.ts +2 -2
  248. package/src/api/ContextProvider.ts +12 -13
  249. package/src/api/internal/RequestContextMiddleware.ts +1 -1
  250. package/src/api/internal/auth.ts +246 -44
  251. package/src/api/internal/events.ts +13 -9
  252. package/src/api/layerUtils.ts +8 -8
  253. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  254. package/src/api/routing/middleware/middleware.ts +55 -14
  255. package/src/api/routing/middleware.ts +0 -2
  256. package/src/api/routing.ts +298 -128
  257. package/src/api/setupRequest.ts +28 -8
  258. package/src/arbs.ts +4 -2
  259. package/src/errorReporter.ts +62 -74
  260. package/src/logger/shared.ts +1 -1
  261. package/src/otel.ts +152 -0
  262. package/src/rateLimit.ts +30 -22
  263. package/src/test.ts +1 -1
  264. package/test/auth.test.ts +101 -0
  265. package/test/contextProvider.test.ts +11 -11
  266. package/test/controller.test.ts +21 -30
  267. package/test/dist/auth.test.d.ts.map +1 -0
  268. package/test/dist/contextProvider.test.d.ts.map +1 -1
  269. package/test/dist/controller.test.d.ts.map +1 -1
  270. package/test/dist/date-query.test.d.ts.map +1 -0
  271. package/test/dist/fixtures.d.ts +26 -12
  272. package/test/dist/fixtures.d.ts.map +1 -1
  273. package/test/dist/fixtures.js +12 -10
  274. package/test/dist/query.test.d.ts.map +1 -1
  275. package/test/dist/rawQuery.test.d.ts.map +1 -1
  276. package/test/dist/repository-ext.test.d.ts.map +1 -0
  277. package/test/dist/requires.test.d.ts.map +1 -1
  278. package/test/dist/router-generator.test.d.ts.map +1 -0
  279. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  280. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  281. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  282. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  283. package/test/dist/sql-store.test.d.ts.map +1 -0
  284. package/test/fixtures.ts +11 -9
  285. package/test/query.test.ts +216 -34
  286. package/test/rawQuery.test.ts +23 -19
  287. package/test/repository-ext.test.ts +60 -0
  288. package/test/requires.test.ts +6 -6
  289. package/test/router-generator.test.ts +183 -0
  290. package/test/routing-interruptibility.test.ts +63 -0
  291. package/test/rpc-e2e-invalidation.test.ts +251 -0
  292. package/test/rpc-multi-middleware.test.ts +78 -9
  293. package/test/rpc-stream-fullstack.test.ts +300 -0
  294. package/test/sql-store.test.ts +1064 -0
  295. package/test/validateSample.test.ts +15 -12
  296. package/tsconfig.examples.json +1 -1
  297. package/tsconfig.json +0 -1
  298. package/tsconfig.json.bak +2 -2
  299. package/tsconfig.src.json +35 -35
  300. package/tsconfig.test.json +2 -2
  301. package/dist/Operations.d.ts +0 -55
  302. package/dist/Operations.d.ts.map +0 -1
  303. package/dist/Operations.js +0 -102
  304. package/dist/OperationsRepo.d.ts +0 -41
  305. package/dist/OperationsRepo.d.ts.map +0 -1
  306. package/dist/OperationsRepo.js +0 -14
  307. package/eslint.config.mjs +0 -24
  308. package/src/Operations.ts +0 -235
  309. package/src/OperationsRepo.ts +0 -16
package/src/Store/Disk.ts CHANGED
@@ -5,6 +5,7 @@ import fs from "fs"
5
5
 
6
6
  import { Console, Effect, flow, Semaphore } from "effect-app"
7
7
  import type { FieldValues } from "../Model/filter/types.js"
8
+ import { annotateDb } from "../otel.js"
8
9
  import { makeMemoryStoreInt, storeId } from "./Memory.js"
9
10
  import { type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
10
11
 
@@ -26,51 +27,83 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
26
27
  }
27
28
  }
28
29
  const file = dir + "/" + prefix + name + ".json"
30
+ const fileExtra = { "disk.file.path": file }
29
31
  const fsStore = {
30
32
  get: fu
31
33
  .readTextFile(file)
32
34
  .pipe(
33
- Effect.withSpan("Disk.read.readFile [effect-app/infra/Store]", {}, { captureStackTrace: false }),
35
+ annotateDb({
36
+ operation: "read.readFile",
37
+ system: "disk",
38
+ collection: name,
39
+ namespace,
40
+ entity: name,
41
+ extra: fileExtra
42
+ }),
34
43
  Effect.flatMap((x) =>
35
44
  Effect.sync(() => JSON.parse(x) as PM[]).pipe(
36
- Effect.withSpan("Disk.read.parse [effect-app/infra/Store]", {}, { captureStackTrace: false })
45
+ annotateDb({
46
+ operation: "read.parse",
47
+ system: "disk",
48
+ collection: name,
49
+ namespace,
50
+ entity: name,
51
+ extra: fileExtra
52
+ })
37
53
  )
38
54
  ),
39
55
  Effect.orDie,
40
- Effect.withSpan("Disk.read [effect-app/infra/Store]", {
41
- attributes: { "disk.file": file }
42
- }, { captureStackTrace: false })
56
+ annotateDb({
57
+ operation: "read",
58
+ system: "disk",
59
+ collection: name,
60
+ namespace,
61
+ entity: name,
62
+ extra: fileExtra
63
+ })
43
64
  ),
44
65
  setRaw: (v: Iterable<PM>) =>
45
66
  Effect
46
67
  .sync(() => JSON.stringify([...v], undefined, 2))
47
68
  .pipe(
48
- Effect.withSpan("Disk.stringify [effect-app/infra/Store]", {
49
- attributes: { "disk.file": file }
50
- }, { captureStackTrace: false }),
69
+ annotateDb({
70
+ operation: "stringify",
71
+ system: "disk",
72
+ collection: name,
73
+ namespace,
74
+ entity: name,
75
+ extra: fileExtra
76
+ }),
51
77
  Effect
52
78
  .flatMap(
53
79
  (json) =>
54
80
  fu
55
81
  .writeTextFile(file, json)
56
- .pipe(Effect
57
- .withSpan("Disk.write.writeFile [effect-app/infra/Store]", {
58
- attributes: { "disk.file_size": json.length }
59
- }, { captureStackTrace: false }))
82
+ .pipe(annotateDb({
83
+ operation: "write.writeFile",
84
+ system: "disk",
85
+ collection: name,
86
+ namespace,
87
+ entity: name,
88
+ extra: { ...fileExtra, "disk.file.size": json.length }
89
+ }))
60
90
  ),
61
- Effect
62
- .withSpan("Disk.write [effect-app/infra/Store]", {
63
- attributes: { "disk.file": file }
64
- }, { captureStackTrace: false })
91
+ annotateDb({
92
+ operation: "write",
93
+ system: "disk",
94
+ collection: name,
95
+ namespace,
96
+ entity: name,
97
+ extra: fileExtra
98
+ })
65
99
  )
66
100
  }
67
101
 
68
102
  // lock file for cross-process coordination during initialization
69
- const lockFile = file + ".lock"
70
103
 
71
104
  // wrap initialization in file lock to prevent race conditions in multi-worker setups
72
105
  const store = yield* fu.withFileLock(
73
- lockFile,
106
+ file,
74
107
  Effect.gen(function*() {
75
108
  const shouldSeed = !(fs.existsSync(file))
76
109
 
@@ -133,61 +166,65 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
133
166
  fs.mkdirSync(dir)
134
167
  }
135
168
  return {
136
- make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
169
+ make: Effect.fnUntraced(function*<IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
137
170
  name: string,
138
171
  idKey: IdKey,
139
172
  seed?: Effect.Effect<Iterable<Encoded>, E, R>,
140
173
  config?: StoreConfig<Encoded>
141
- ) =>
142
- Effect.gen(function*() {
143
- const storesSem = Semaphore.makeUnsafe(1)
144
- const primary = yield* makeDiskStoreInt(prefix, idKey, "primary", dir, name, seed, config?.defaultValues)
145
- const stores = new Map<string, Store<IdKey, Encoded>>([["primary", primary]])
146
- const ctx = yield* Effect.services<R>()
147
- const getStore = !config?.allowNamespace
148
- ? Effect.succeed(primary)
149
- : storeId.asEffect().pipe(Effect.flatMap((namespace) => {
150
- const store = stores.get(namespace)
151
- if (store) {
152
- return Effect.succeed(store)
153
- }
154
- if (!config.allowNamespace!(namespace)) {
174
+ ) {
175
+ const primary = yield* makeDiskStoreInt(prefix, idKey, "primary", dir, name, seed, config?.defaultValues)
176
+ const stores = new Map<string, Store<IdKey, Encoded>>([["primary", primary]])
177
+ const ctx = yield* Effect.context<R>()
178
+ const semaphores = new Map<string, Semaphore.Semaphore>()
179
+ const getSem = (ns: string) => {
180
+ let sem = semaphores.get(ns)
181
+ if (!sem) {
182
+ sem = Semaphore.makeUnsafe(1)
183
+ semaphores.set(ns, sem)
184
+ }
185
+ return sem
186
+ }
187
+ const ensureStore = (namespace: string) =>
188
+ getSem(namespace).withPermits(1)(
189
+ Effect.suspend(() => {
190
+ const existing = stores.get(namespace)
191
+ if (existing) return Effect.succeed(existing)
192
+ if (config?.allowNamespace && !config.allowNamespace(namespace)) {
155
193
  throw new Error(`Namespace ${namespace} not allowed!`)
156
194
  }
157
- return storesSem.withPermits(1)(
158
- Effect.suspend(() => {
159
- const existing = stores.get(namespace)
160
- if (existing) return Effect.sync(() => existing)
161
- return makeDiskStoreInt<IdKey, Encoded, R, E>(
162
- prefix,
163
- idKey,
164
- namespace,
165
- dir,
166
- name,
167
- seed,
168
- config?.defaultValues
169
- )
170
- .pipe(
171
- Effect.orDie,
172
- Effect.provide(ctx),
173
- Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
174
- )
175
- })
195
+ return makeDiskStoreInt<IdKey, Encoded, R, E>(
196
+ prefix,
197
+ idKey,
198
+ namespace,
199
+ dir,
200
+ name,
201
+ seed,
202
+ config?.defaultValues
176
203
  )
177
- }))
204
+ .pipe(
205
+ Effect.orDie,
206
+ Effect.provide(ctx),
207
+ Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
208
+ )
209
+ })
210
+ )
211
+ const getStore = !config?.allowNamespace
212
+ ? Effect.succeed(primary)
213
+ : storeId.asEffect().pipe(Effect.flatMap((namespace) => ensureStore(namespace)))
178
214
 
179
- const s: Store<IdKey, Encoded> = {
180
- all: Effect.flatMap(getStore, (_) => _.all),
181
- find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
182
- filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
183
- set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
184
- batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
185
- bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
186
- batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args)),
187
- queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args))
188
- }
189
- return s
190
- })
215
+ const s: Store<IdKey, Encoded> = {
216
+ seedNamespace: (namespace) => ensureStore(namespace).pipe(Effect.asVoid),
217
+ all: Effect.flatMap(getStore, (_) => _.all),
218
+ find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
219
+ filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
220
+ set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
221
+ batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
222
+ bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
223
+ batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args)),
224
+ queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args))
225
+ }
226
+ return s
227
+ })
191
228
  }
192
229
  })
193
230
  }
@@ -1,14 +1,19 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
 
3
- import { Array, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore, ServiceMap, Struct } from "effect-app"
3
+ import { Array, Context, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore, Struct } from "effect-app"
4
4
  import { NonEmptyString255 } from "effect-app/Schema"
5
- import { get } from "effect-app/utils"
6
5
  import { InfraLogger } from "../logger.js"
7
6
  import type { FieldValues } from "../Model/filter/types.js"
7
+ import { annotateDb } from "../otel.js"
8
8
  import { codeFilter, codeFilter3_ } from "./codeFilter.js"
9
9
  import { type FilterArgs, type PersistenceModelType, type Store, type StoreConfig, StoreMaker } from "./service.js"
10
10
  import { makeUpdateETag } from "./utils.js"
11
11
 
12
+ /** Traverse an object by a dot-separated path string, e.g. `"a.b.c"`. */
13
+ export function get(obj: any, path: string): any {
14
+ return path.split(".").reduce((res: any, key: string) => (res != null ? res[key] : res), obj)
15
+ }
16
+
12
17
  export function memFilter<T extends FieldValues, U extends keyof T = never>(f: FilterArgs<T, U>) {
13
18
  type M = U extends undefined ? T : Pick<T, U>
14
19
  return ((c: T[]): M[] => {
@@ -27,7 +32,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
27
32
  n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys as never[]))
28
33
  })
29
34
  return n as M
30
- }) as any
35
+ })
31
36
  }
32
37
  const skip = f?.skip
33
38
  const limit = f?.limit
@@ -72,7 +77,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
72
77
  }
73
78
 
74
79
  const defaultNs: NonEmptyString255 = NonEmptyString255("primary")
75
- export class storeId extends ServiceMap.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
80
+ export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
76
81
 
77
82
  function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
78
83
  return InfraLogger
@@ -151,43 +156,55 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
151
156
  withPermit
152
157
  )
153
158
  const s: Store<IdKey, Encoded> = {
159
+ seedNamespace: () => Effect.void,
160
+
154
161
  queryRaw: (query) =>
155
162
  all
156
163
  .pipe(
157
164
  // Effect.tap(() => logQuery(query, defaultValues)),
158
165
  Effect.map(query.memory),
159
- Effect.withSpan("Memory.queryRaw [effect-app/infra/Store]", {
160
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
161
- }, { captureStackTrace: false })
166
+ annotateDb({
167
+ operation: "queryRaw",
168
+ system: "memory",
169
+ collection: modelName,
170
+ namespace,
171
+ entity: modelName
172
+ })
162
173
  ),
163
174
 
164
- all: all.pipe(Effect.withSpan("Memory.all [effect-app/infra/Store]", {
165
- attributes: {
166
- modelName,
167
- namespace
168
- }
169
- }, { captureStackTrace: false })),
175
+ all: all.pipe(annotateDb({
176
+ operation: "all",
177
+ system: "memory",
178
+ collection: modelName,
179
+ namespace,
180
+ entity: modelName
181
+ })),
170
182
  find: (id) =>
171
183
  Ref
172
184
  .get(store)
173
185
  .pipe(
174
186
  Effect.map((_) => Option.fromNullishOr(_.get(id))),
175
- Effect
176
- .withSpan("Memory.find [effect-app/infra/Store]", {
177
- attributes: {
178
- modelName,
179
- namespace
180
- }
181
- }, { captureStackTrace: false })
187
+ annotateDb({
188
+ operation: "find",
189
+ system: "memory",
190
+ collection: modelName,
191
+ namespace,
192
+ entity: modelName,
193
+ extra: { "app.entity.id": id as unknown }
194
+ })
182
195
  ),
183
196
  filter: (f) =>
184
197
  all
185
198
  .pipe(
186
199
  Effect.tap(() => logQuery(f, defaultValues)),
187
200
  Effect.map(memFilter(f)),
188
- Effect.withSpan("Memory.filter [effect-app/infra/Store]", {
189
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
190
- }, { captureStackTrace: false })
201
+ annotateDb({
202
+ operation: "filter",
203
+ system: "memory",
204
+ collection: modelName,
205
+ namespace,
206
+ entity: modelName
207
+ })
191
208
  ),
192
209
  set: (e) =>
193
210
  s
@@ -202,10 +219,14 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
202
219
  )
203
220
  ),
204
221
  withPermit,
205
- Effect
206
- .withSpan("Memory.set [effect-app/infra/Store]", {
207
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
208
- }, { captureStackTrace: false })
222
+ annotateDb({
223
+ operation: "set",
224
+ system: "memory",
225
+ collection: modelName,
226
+ namespace,
227
+ entity: modelName,
228
+ extra: { "app.entity.id": e[idKey] as unknown }
229
+ })
209
230
  ),
210
231
  batchRemove: (items: NonEmptyReadonlyArray<Encoded[IdKey]>) =>
211
232
  pipe(
@@ -216,10 +237,13 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
216
237
  Effect.filterOrFail((_) => _.length <= 100, () => "BatchRemove: a batch may not exceed 100 items"),
217
238
  Effect.orDie,
218
239
  Effect.andThen(batchRemove),
219
- Effect
220
- .withSpan("Memory.batchRemove [effect-app/infra/Store]", {
221
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
222
- }, { captureStackTrace: false })
240
+ annotateDb({
241
+ operation: "batchRemove",
242
+ system: "memory",
243
+ collection: modelName,
244
+ namespace,
245
+ entity: modelName
246
+ })
223
247
  )
224
248
  ),
225
249
  batchSet: (items: readonly [PM, ...PM[]]) =>
@@ -231,18 +255,25 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
231
255
  Effect.filterOrFail((_) => _.length <= 100, () => "BatchSet: a batch may not exceed 100 items"),
232
256
  Effect.orDie,
233
257
  Effect.andThen(batchSet),
234
- Effect
235
- .withSpan("Memory.batchSet [effect-app/infra/Store]", {
236
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
237
- }, { captureStackTrace: false })
258
+ annotateDb({
259
+ operation: "batchSet",
260
+ system: "memory",
261
+ collection: modelName,
262
+ namespace,
263
+ entity: modelName
264
+ })
238
265
  )
239
266
  ),
240
267
  bulkSet: flow(
241
268
  batchSet,
242
269
  (_) =>
243
- _.pipe(Effect.withSpan("Memory.bulkSet [effect-app/infra/Store]", {
244
- attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
245
- }, { captureStackTrace: false }))
270
+ _.pipe(annotateDb({
271
+ operation: "bulkSet",
272
+ system: "memory",
273
+ collection: modelName,
274
+ namespace,
275
+ entity: modelName
276
+ }))
246
277
  )
247
278
  }
248
279
  return s
@@ -250,56 +281,60 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
250
281
  }
251
282
 
252
283
  export const makeMemoryStore = () => ({
253
- make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
284
+ make: Effect.fnUntraced(function*<IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
254
285
  modelName: string,
255
286
  idKey: IdKey,
256
287
  seed?: Effect.Effect<Iterable<Encoded>, E, R>,
257
288
  config?: StoreConfig<Encoded>
258
- ) =>
259
- Effect.gen(function*() {
260
- const storesSem = Semaphore.makeUnsafe(1)
261
- const primary = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
262
- modelName,
263
- idKey,
264
- "primary",
265
- seed,
266
- config?.defaultValues
267
- )
268
- const ctx = yield* Effect.services<R>()
269
- const stores = new Map([["primary", primary]])
270
- const getStore = !config?.allowNamespace
271
- ? Effect.succeed(primary)
272
- : storeId.asEffect().pipe(Effect.flatMap((namespace) => {
273
- const store = stores.get(namespace)
274
- if (store) {
275
- return Effect.succeed(store)
276
- }
277
- if (!config.allowNamespace!(namespace)) {
278
- throw new Error(`Namespace ${namespace} not allowed!`)
279
- }
280
- return storesSem.withPermits(1)(Effect.suspend(() => {
281
- const store = stores.get(namespace)
282
- if (store) return Effect.sync(() => store)
283
- return makeMemoryStoreInt(modelName, idKey, namespace, seed, config?.defaultValues)
284
- .pipe(
285
- Effect.orDie,
286
- Effect.provide(ctx),
287
- Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
288
- )
289
- }))
290
- }))
291
- const s: Store<IdKey, Encoded> = {
292
- all: Effect.flatMap(getStore, (_) => _.all),
293
- queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args)),
294
- find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
295
- filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
296
- set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
297
- batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
298
- bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
299
- batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args))
289
+ ) {
290
+ const primary = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
291
+ modelName,
292
+ idKey,
293
+ "primary",
294
+ seed,
295
+ config?.defaultValues
296
+ )
297
+ const ctx = yield* Effect.context<R>()
298
+ const stores = new Map([["primary", primary]])
299
+ const semaphores = new Map<string, Semaphore.Semaphore>()
300
+ const getSem = (ns: string) => {
301
+ let sem = semaphores.get(ns)
302
+ if (!sem) {
303
+ sem = Semaphore.makeUnsafe(1)
304
+ semaphores.set(ns, sem)
300
305
  }
301
- return s
302
- })
306
+ return sem
307
+ }
308
+ const ensureStore = (namespace: string) =>
309
+ getSem(namespace).withPermits(1)(Effect.suspend(() => {
310
+ const store = stores.get(namespace)
311
+ if (store) return Effect.succeed(store)
312
+ if (config?.allowNamespace && !config.allowNamespace(namespace)) {
313
+ throw new Error(`Namespace ${namespace} not allowed!`)
314
+ }
315
+ return makeMemoryStoreInt(modelName, idKey, namespace, seed, config?.defaultValues)
316
+ .pipe(
317
+ Effect.orDie,
318
+ Effect.provide(ctx),
319
+ Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
320
+ )
321
+ }))
322
+ const getStore = !config?.allowNamespace
323
+ ? Effect.succeed(primary)
324
+ : storeId.asEffect().pipe(Effect.flatMap((namespace) => ensureStore(namespace)))
325
+ const s: Store<IdKey, Encoded> = {
326
+ seedNamespace: (namespace) => ensureStore(namespace).pipe(Effect.asVoid),
327
+ all: Effect.flatMap(getStore, (_) => _.all),
328
+ queryRaw: (...args) => Effect.flatMap(getStore, (_) => _.queryRaw(...args)),
329
+ find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
330
+ filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
331
+ set: (...args) => Effect.flatMap(getStore, (_) => _.set(...args)),
332
+ batchSet: (...args) => Effect.flatMap(getStore, (_) => _.batchSet(...args)),
333
+ bulkSet: (...args) => Effect.flatMap(getStore, (_) => _.bulkSet(...args)),
334
+ batchRemove: (...args) => Effect.flatMap(getStore, (_) => _.batchRemove(...args))
335
+ }
336
+ return s
337
+ })
303
338
  })
304
339
 
305
340
  export const MemoryStoreLive = StoreMaker.toLayer(Effect.sync(() => makeMemoryStore()))