@effect-app/infra 4.0.0-beta.22 → 4.0.0-beta.220

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 (310) hide show
  1. package/CHANGELOG.md +1640 -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 +103 -51
  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 +139 -16
  53. package/dist/Model/query/dsl.d.ts.map +1 -1
  54. package/dist/Model/query/dsl.js +187 -1
  55. package/dist/Model/query/new-kid-interpreter.d.ts +76 -7
  56. package/dist/Model/query/new-kid-interpreter.d.ts.map +1 -1
  57. package/dist/Model/query/new-kid-interpreter.js +122 -6
  58. package/dist/Model/query.d.ts +1 -1
  59. package/dist/Model.d.ts +2 -1
  60. package/dist/Model.d.ts.map +1 -1
  61. package/dist/Model.js +2 -1
  62. package/dist/QueueMaker/SQLQueue.d.ts +5 -7
  63. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  64. package/dist/QueueMaker/SQLQueue.js +130 -116
  65. package/dist/QueueMaker/errors.d.ts +2 -2
  66. package/dist/QueueMaker/errors.d.ts.map +1 -1
  67. package/dist/QueueMaker/memQueue.d.ts +7 -4
  68. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  69. package/dist/QueueMaker/memQueue.js +75 -63
  70. package/dist/QueueMaker/sbqueue.d.ts +6 -3
  71. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  72. package/dist/QueueMaker/sbqueue.js +52 -53
  73. package/dist/QueueMaker/service.d.ts +1 -1
  74. package/dist/RequestContext.d.ts +74 -35
  75. package/dist/RequestContext.d.ts.map +1 -1
  76. package/dist/RequestContext.js +13 -14
  77. package/dist/RequestFiberSet.d.ts +7 -7
  78. package/dist/RequestFiberSet.d.ts.map +1 -1
  79. package/dist/RequestFiberSet.js +3 -3
  80. package/dist/Store/ContextMapContainer.d.ts +19 -3
  81. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  82. package/dist/Store/ContextMapContainer.js +13 -3
  83. package/dist/Store/Cosmos/query.d.ts +5 -1
  84. package/dist/Store/Cosmos/query.d.ts.map +1 -1
  85. package/dist/Store/Cosmos/query.js +113 -34
  86. package/dist/Store/Cosmos.d.ts +1 -1
  87. package/dist/Store/Cosmos.d.ts.map +1 -1
  88. package/dist/Store/Cosmos.js +335 -243
  89. package/dist/Store/Disk.d.ts +2 -2
  90. package/dist/Store/Disk.d.ts.map +1 -1
  91. package/dist/Store/Disk.js +72 -35
  92. package/dist/Store/Memory.d.ts +6 -4
  93. package/dist/Store/Memory.d.ts.map +1 -1
  94. package/dist/Store/Memory.js +242 -58
  95. package/dist/Store/SQL/Pg.d.ts +4 -0
  96. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  97. package/dist/Store/SQL/Pg.js +231 -0
  98. package/dist/Store/SQL/query.d.ts +42 -0
  99. package/dist/Store/SQL/query.d.ts.map +1 -0
  100. package/dist/Store/SQL/query.js +479 -0
  101. package/dist/Store/SQL.d.ts +20 -0
  102. package/dist/Store/SQL.d.ts.map +1 -0
  103. package/dist/Store/SQL.js +446 -0
  104. package/dist/Store/codeFilter.d.ts +1 -1
  105. package/dist/Store/codeFilter.d.ts.map +1 -1
  106. package/dist/Store/codeFilter.js +4 -2
  107. package/dist/Store/index.d.ts +5 -2
  108. package/dist/Store/index.d.ts.map +1 -1
  109. package/dist/Store/index.js +15 -3
  110. package/dist/Store/service.d.ts +22 -8
  111. package/dist/Store/service.d.ts.map +1 -1
  112. package/dist/Store/service.js +24 -6
  113. package/dist/Store/utils.d.ts +1 -1
  114. package/dist/Store/utils.d.ts.map +1 -1
  115. package/dist/Store/utils.js +3 -4
  116. package/dist/Store.d.ts +1 -1
  117. package/dist/adapters/SQL/Model.d.ts +31 -42
  118. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  119. package/dist/adapters/SQL/Model.js +29 -38
  120. package/dist/adapters/SQL.d.ts +1 -1
  121. package/dist/adapters/ServiceBus.d.ts +11 -11
  122. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  123. package/dist/adapters/ServiceBus.js +25 -21
  124. package/dist/adapters/cosmos-client.d.ts +3 -3
  125. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  126. package/dist/adapters/cosmos-client.js +3 -3
  127. package/dist/adapters/index.d.ts +8 -2
  128. package/dist/adapters/index.d.ts.map +1 -1
  129. package/dist/adapters/index.js +8 -2
  130. package/dist/adapters/logger.d.ts +1 -1
  131. package/dist/adapters/logger.d.ts.map +1 -1
  132. package/dist/adapters/memQueue.d.ts +3 -3
  133. package/dist/adapters/memQueue.d.ts.map +1 -1
  134. package/dist/adapters/memQueue.js +3 -3
  135. package/dist/adapters/mongo-client.d.ts +3 -3
  136. package/dist/adapters/mongo-client.d.ts.map +1 -1
  137. package/dist/adapters/mongo-client.js +3 -3
  138. package/dist/adapters/redis-client.d.ts +3 -3
  139. package/dist/adapters/redis-client.d.ts.map +1 -1
  140. package/dist/adapters/redis-client.js +3 -3
  141. package/dist/api/ContextProvider.d.ts +8 -8
  142. package/dist/api/ContextProvider.d.ts.map +1 -1
  143. package/dist/api/ContextProvider.js +6 -6
  144. package/dist/api/codec.d.ts +1 -1
  145. package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
  146. package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
  147. package/dist/api/internal/RequestContextMiddleware.js +9 -6
  148. package/dist/api/internal/auth.d.ts +44 -6
  149. package/dist/api/internal/auth.d.ts.map +1 -1
  150. package/dist/api/internal/auth.js +160 -29
  151. package/dist/api/internal/events.d.ts +3 -3
  152. package/dist/api/internal/events.d.ts.map +1 -1
  153. package/dist/api/internal/events.js +10 -8
  154. package/dist/api/internal/health.d.ts +1 -1
  155. package/dist/api/layerUtils.d.ts +6 -6
  156. package/dist/api/layerUtils.d.ts.map +1 -1
  157. package/dist/api/layerUtils.js +5 -5
  158. package/dist/api/middlewares.d.ts +1 -1
  159. package/dist/api/reportError.d.ts +1 -1
  160. package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
  161. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  162. package/dist/api/routing/middleware/middleware.d.ts +39 -3
  163. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  164. package/dist/api/routing/middleware/middleware.js +48 -16
  165. package/dist/api/routing/middleware.d.ts +1 -2
  166. package/dist/api/routing/middleware.d.ts.map +1 -1
  167. package/dist/api/routing/middleware.js +1 -2
  168. package/dist/api/routing/schema/jwt.d.ts +1 -1
  169. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  170. package/dist/api/routing/tsort.d.ts +1 -1
  171. package/dist/api/routing/tsort.d.ts.map +1 -1
  172. package/dist/api/routing/utils.d.ts +3 -3
  173. package/dist/api/routing/utils.d.ts.map +1 -1
  174. package/dist/api/routing.d.ts +80 -37
  175. package/dist/api/routing.d.ts.map +1 -1
  176. package/dist/api/routing.js +109 -41
  177. package/dist/api/setupRequest.d.ts +8 -5
  178. package/dist/api/setupRequest.d.ts.map +1 -1
  179. package/dist/api/setupRequest.js +12 -7
  180. package/dist/api/util.d.ts +1 -1
  181. package/dist/arbs.d.ts +1 -1
  182. package/dist/arbs.d.ts.map +1 -1
  183. package/dist/arbs.js +5 -3
  184. package/dist/errorReporter.d.ts +4 -4
  185. package/dist/errorReporter.d.ts.map +1 -1
  186. package/dist/errorReporter.js +20 -25
  187. package/dist/errors.d.ts +1 -1
  188. package/dist/fileUtil.d.ts +1 -1
  189. package/dist/fileUtil.d.ts.map +1 -1
  190. package/dist/index.d.ts +1 -1
  191. package/dist/logger/jsonLogger.d.ts +1 -1
  192. package/dist/logger/logFmtLogger.d.ts +1 -1
  193. package/dist/logger/shared.d.ts +1 -1
  194. package/dist/logger/shared.js +2 -2
  195. package/dist/logger.d.ts +1 -1
  196. package/dist/logger.d.ts.map +1 -1
  197. package/dist/otel.d.ts +75 -0
  198. package/dist/otel.d.ts.map +1 -0
  199. package/dist/otel.js +65 -0
  200. package/dist/rateLimit.d.ts +9 -3
  201. package/dist/rateLimit.d.ts.map +1 -1
  202. package/dist/rateLimit.js +5 -11
  203. package/dist/test.d.ts +2 -2
  204. package/dist/test.d.ts.map +1 -1
  205. package/dist/test.js +1 -1
  206. package/dist/vitest.d.ts +1 -1
  207. package/examples/query.ts +42 -38
  208. package/package.json +46 -37
  209. package/src/CUPS.ts +9 -11
  210. package/src/Emailer/Sendgrid.ts +17 -14
  211. package/src/Emailer/service.ts +9 -3
  212. package/src/MainFiberSet.ts +5 -6
  213. package/src/Model/Repository/Registry.ts +33 -0
  214. package/src/Model/Repository/ext.ts +96 -10
  215. package/src/Model/Repository/internal/internal.ts +218 -149
  216. package/src/Model/Repository/makeRepo.ts +12 -10
  217. package/src/Model/Repository/service.ts +31 -22
  218. package/src/Model/Repository/validation.ts +4 -4
  219. package/src/Model/Repository.ts +1 -0
  220. package/src/Model/dsl.ts +3 -3
  221. package/src/Model/filter/types/path/eager.ts +1 -2
  222. package/src/Model/query/dsl.ts +348 -18
  223. package/src/Model/query/new-kid-interpreter.ts +206 -6
  224. package/src/Model.ts +1 -0
  225. package/src/QueueMaker/SQLQueue.ts +144 -152
  226. package/src/QueueMaker/memQueue.ts +104 -103
  227. package/src/QueueMaker/sbqueue.ts +70 -86
  228. package/src/RequestContext.ts +14 -16
  229. package/src/RequestFiberSet.ts +2 -2
  230. package/src/Store/ContextMapContainer.ts +41 -2
  231. package/src/Store/Cosmos/query.ts +140 -43
  232. package/src/Store/Cosmos.ts +482 -349
  233. package/src/Store/Disk.ts +102 -65
  234. package/src/Store/Memory.ts +275 -87
  235. package/src/Store/SQL/Pg.ts +361 -0
  236. package/src/Store/SQL/query.ts +539 -0
  237. package/src/Store/SQL.ts +731 -0
  238. package/src/Store/codeFilter.ts +3 -1
  239. package/src/Store/index.ts +17 -2
  240. package/src/Store/service.ts +41 -10
  241. package/src/Store/utils.ts +23 -22
  242. package/src/adapters/SQL/Model.ts +41 -40
  243. package/src/adapters/ServiceBus.ts +125 -121
  244. package/src/adapters/cosmos-client.ts +2 -2
  245. package/src/adapters/index.ts +7 -0
  246. package/src/adapters/memQueue.ts +2 -2
  247. package/src/adapters/mongo-client.ts +2 -2
  248. package/src/adapters/redis-client.ts +2 -2
  249. package/src/api/ContextProvider.ts +12 -13
  250. package/src/api/internal/RequestContextMiddleware.ts +15 -5
  251. package/src/api/internal/auth.ts +246 -44
  252. package/src/api/internal/events.ts +13 -9
  253. package/src/api/layerUtils.ts +8 -8
  254. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  255. package/src/api/routing/middleware/middleware.ts +55 -14
  256. package/src/api/routing/middleware.ts +0 -2
  257. package/src/api/routing.ts +296 -131
  258. package/src/api/setupRequest.ts +28 -8
  259. package/src/arbs.ts +4 -2
  260. package/src/errorReporter.ts +62 -74
  261. package/src/logger/shared.ts +1 -1
  262. package/src/otel.ts +152 -0
  263. package/src/rateLimit.ts +30 -22
  264. package/src/test.ts +1 -1
  265. package/test/auth.test.ts +101 -0
  266. package/test/contextProvider.test.ts +11 -11
  267. package/test/controller.test.ts +21 -30
  268. package/test/dist/auth.test.d.ts.map +1 -0
  269. package/test/dist/contextProvider.test.d.ts.map +1 -1
  270. package/test/dist/controller.test.d.ts.map +1 -1
  271. package/test/dist/date-query.test.d.ts.map +1 -0
  272. package/test/dist/fixtures.d.ts +26 -12
  273. package/test/dist/fixtures.d.ts.map +1 -1
  274. package/test/dist/fixtures.js +12 -10
  275. package/test/dist/query.test.d.ts.map +1 -1
  276. package/test/dist/rawQuery.test.d.ts.map +1 -1
  277. package/test/dist/repository-ext.test.d.ts.map +1 -0
  278. package/test/dist/requires.test.d.ts.map +1 -1
  279. package/test/dist/router-generator.test.d.ts.map +1 -0
  280. package/test/dist/routing-interruptibility.test.d.ts.map +1 -0
  281. package/test/dist/rpc-e2e-invalidation.test.d.ts.map +1 -0
  282. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  283. package/test/dist/rpc-stream-fullstack.test.d.ts.map +1 -0
  284. package/test/dist/sql-store.test.d.ts.map +1 -0
  285. package/test/fixtures.ts +11 -9
  286. package/test/query.test.ts +813 -38
  287. package/test/rawQuery.test.ts +301 -20
  288. package/test/repository-ext.test.ts +60 -0
  289. package/test/requires.test.ts +6 -6
  290. package/test/router-generator.test.ts +183 -0
  291. package/test/routing-interruptibility.test.ts +63 -0
  292. package/test/rpc-e2e-invalidation.test.ts +251 -0
  293. package/test/rpc-multi-middleware.test.ts +78 -9
  294. package/test/rpc-stream-fullstack.test.ts +300 -0
  295. package/test/sql-store.test.ts +1592 -0
  296. package/test/validateSample.test.ts +15 -12
  297. package/tsconfig.examples.json +1 -1
  298. package/tsconfig.json +0 -1
  299. package/tsconfig.json.bak +2 -2
  300. package/tsconfig.src.json +35 -35
  301. package/tsconfig.test.json +2 -2
  302. package/dist/Operations.d.ts +0 -55
  303. package/dist/Operations.d.ts.map +0 -1
  304. package/dist/Operations.js +0 -102
  305. package/dist/OperationsRepo.d.ts +0 -41
  306. package/dist/OperationsRepo.d.ts.map +0 -1
  307. package/dist/OperationsRepo.js +0 -14
  308. package/eslint.config.mjs +0 -24
  309. package/src/Operations.ts +0 -235
  310. package/src/OperationsRepo.ts +0 -16
@@ -1,9 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
3
  import { Array, Option } from "effect-app"
4
- import { assertUnreachable, get } from "effect-app/utils"
4
+ import { assertUnreachable } from "effect-app/utils"
5
5
  import type { FilterR, FilterResult } from "../Model/filter/filterApi.js"
6
6
  import type { FieldValues } from "../Model/filter/types.js"
7
+ import { get } from "./Memory.js"
7
8
  import type { Filter } from "./service.js"
8
9
  import { compare, greaterThan, greaterThanExclusive, lowerThan, lowerThanExclusive } from "./utils.js"
9
10
 
@@ -186,6 +187,7 @@ export const codeFilter3_ = <E>(state: readonly FilterResult[], sut: E): boolean
186
187
  const statements: any[] = [] // must be defined here to be used by eval.
187
188
  // always put everything inside a root scope.
188
189
  const s = codeFilter3__([{ t: "where-scope", result: state, relation: "some" }], sut, statements, null, false)
190
+ // oxlint-disable-next-line no-eval
189
191
  return eval(s)
190
192
  }
191
193
 
@@ -1,12 +1,19 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { Effect, Layer, Redacted } from "effect-app"
3
+ import type { SqlClient } from "effect/unstable/sql"
3
4
  import { CosmosStoreLayer } from "./Cosmos.js"
4
5
  import { DiskStoreLayer } from "./Disk.js"
5
6
  import { MemoryStoreLive } from "./Memory.js"
6
7
  // import { RedisStoreLayer } from "./Redis.js"
8
+ import { RepositoryRegistryLive } from "../Model.js"
7
9
  import type { StorageConfig } from "./service.js"
10
+ import { SQLiteStoreLayer } from "./SQL.js"
11
+ import { PgStoreLayer } from "./SQL/Pg.js"
8
12
 
9
- export function StoreMakerLayer(cfg: StorageConfig) {
13
+ export function StoreMakerLayer(
14
+ cfg: StorageConfig,
15
+ options?: { makeSqlClientLayer?: (namespace: string) => Layer.Layer<SqlClient.SqlClient> }
16
+ ) {
10
17
  return Effect
11
18
  .sync(() => {
12
19
  const storageUrl = Redacted.value(cfg.url)
@@ -19,6 +26,14 @@ export function StoreMakerLayer(cfg: StorageConfig) {
19
26
  console.log("Using disk store at " + dir)
20
27
  return DiskStoreLayer(cfg, dir)
21
28
  }
29
+ if (storageUrl.startsWith("sqlite://")) {
30
+ console.log("Using SQLite store")
31
+ return SQLiteStoreLayer(cfg, options)
32
+ }
33
+ if (storageUrl.startsWith("pg://")) {
34
+ console.log("Using PostgreSQL store")
35
+ return PgStoreLayer(cfg)
36
+ }
22
37
  // if (storageUrl.startsWith("redis://")) {
23
38
  // console.log("Using Redis store")
24
39
  // return RedisStoreLayer(cfg)
@@ -27,7 +42,7 @@ export function StoreMakerLayer(cfg: StorageConfig) {
27
42
  console.log("Using Cosmos DB store")
28
43
  return CosmosStoreLayer(cfg)
29
44
  })
30
- .pipe(Layer.unwrap)
45
+ .pipe(Layer.unwrap, Layer.merge(RepositoryRegistryLive))
31
46
  }
32
47
 
33
48
  export * from "./service.js"
@@ -1,17 +1,18 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import type { UniqueKey } from "@azure/cosmos"
3
- import { Effect, type NonEmptyReadonlyArray, type Option, type Redacted, ServiceMap } from "effect-app"
3
+ import { Context, Effect, type NonEmptyReadonlyArray, type Option, type Redacted } from "effect-app"
4
+ import * as Semaphore from "effect/Semaphore"
4
5
  import type { OptimisticConcurrencyException } from "../errors.js"
5
6
  import type { FilterResult } from "../Model/filter/filterApi.js"
6
7
  import type { FieldValues } from "../Model/filter/types.js"
7
8
  import type { FieldPath } from "../Model/filter/types/path/index.js"
8
- import { type RawQuery } from "../Model/query.js"
9
+ import type { ComputedProjectionIrExpression, RawQuery } from "../Model/query.js"
9
10
 
10
11
  export interface StoreConfig<E> {
11
12
  partitionValue: (e?: E) => string
12
13
  /**
13
- * Primarily used for testing, creating namespaces in the database to separate data e.g to run multiple tests in isolation within the same database
14
- * currently only supported in disk/memory. CosmosDB is TODO.
14
+ * Primarily used for testing, creating namespaces in the database to separate data e.g to run multiple tests in isolation within the same database.
15
+ * Memory/Disk use separate store instances per namespace. CosmosDB uses namespace-prefixed partition keys. SQL uses a `_namespace` column.
15
16
  */
16
17
  allowNamespace?: (namespace: string) => boolean
17
18
  /**
@@ -60,7 +61,14 @@ export interface O<TFieldValues extends FieldValues> {
60
61
  export interface FilterArgs<Encoded extends FieldValues, U extends keyof Encoded = never> {
61
62
  t: Encoded
62
63
  filter?: Filter | undefined
63
- select?: NonEmptyReadonlyArray<U | { key: string; subKeys: readonly string[] }> | undefined
64
+ select?:
65
+ | NonEmptyReadonlyArray<
66
+ U | { key: string; subKeys: readonly string[] } | {
67
+ key: string
68
+ computed: ComputedProjectionIrExpression
69
+ }
70
+ >
71
+ | undefined
64
72
  order?: NonEmptyReadonlyArray<O<NoInfer<Encoded>>>
65
73
  limit?: number | undefined
66
74
  skip?: number | undefined
@@ -87,9 +95,14 @@ export interface Store<
87
95
  ) => Effect.Effect<NonEmptyReadonlyArray<PM>, OptimisticConcurrencyException>
88
96
  batchRemove: (ids: NonEmptyReadonlyArray<Encoded[IdKey]>, partitionKey?: string) => Effect.Effect<void>
89
97
  queryRaw: <Out>(query: RawQuery<Encoded, Out>) => Effect.Effect<readonly Out[]>
98
+ /**
99
+ * Explicitly seed a namespace. Primary is seeded eagerly on initialization.
100
+ * Non-primary namespaces must be seeded explicitly before use.
101
+ */
102
+ seedNamespace: (namespace: string) => Effect.Effect<void>
90
103
  }
91
104
 
92
- export class StoreMaker extends ServiceMap.Opaque<StoreMaker, {
105
+ export class StoreMaker extends Context.Opaque<StoreMaker, {
93
106
  make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R = never, E = never>(
94
107
  name: string,
95
108
  idKey: IdKey,
@@ -161,16 +174,34 @@ export const makeContextMap = () => {
161
174
  // }
162
175
  // }
163
176
 
177
+ const store = new Map<symbol, unknown>()
178
+ const sem = Semaphore.makeUnsafe(1)
179
+
164
180
  return {
165
181
  get: getEtag,
166
- set: setEtag
167
- // parserEnv
182
+ set: setEtag,
183
+ getOrCreateStore: <T>(key: symbol, make: () => T): T => {
184
+ let value = store.get(key) as T | undefined
185
+ if (value === undefined) {
186
+ value = make()
187
+ store.set(key, value)
188
+ }
189
+ return value
190
+ },
191
+ getOrCreateStoreEffect: <T, E, R>(key: symbol, make: Effect.Effect<T, E, R>): Effect.Effect<T, E, R> =>
192
+ sem.withPermits(1)(Effect.uninterruptible(Effect.gen(function*() {
193
+ const value = store.get(key) as T | undefined
194
+ if (value !== undefined) return value
195
+ const v = yield* make
196
+ store.set(key, v)
197
+ return v
198
+ })))
168
199
  }
169
200
  }
170
201
 
171
202
  const makeMap = Effect.sync(() => makeContextMap())
172
203
 
173
- export class ContextMap extends ServiceMap.Opaque<ContextMap>()("effect-app/ContextMap", { make: makeMap }) {
204
+ export class ContextMap extends Context.Opaque<ContextMap>()("effect-app/ContextMap", { make: makeMap }) {
174
205
  }
175
206
 
176
207
  export type PersistenceModelType<Encoded extends object> = Encoded & {
@@ -178,7 +209,7 @@ export type PersistenceModelType<Encoded extends object> = Encoded & {
178
209
  }
179
210
 
180
211
  export interface StorageConfig {
181
- url: Redacted.Redacted<string>
212
+ url: Redacted.Redacted
182
213
  prefix: string
183
214
  dbName: string
184
215
  }
@@ -12,33 +12,34 @@ export const makeETag = <E extends PersistenceModelType<{}>>(
12
12
  _etag: crypto.createHash("sha256").update(JSON.stringify(e)).digest("hex")
13
13
  }) as any
14
14
 
15
- export const makeUpdateETag =
16
- (type: string) =>
17
- <IdKey extends keyof E, E extends PersistenceModelType<{}>>(e: E, idKey: IdKey, current: Option.Option<E>) =>
18
- Effect.gen(function*() {
19
- if (e._etag) {
20
- if (Option.isNone(current)) {
21
- return yield* new OptimisticConcurrencyException({
22
- type,
23
- id: e[idKey] as string,
24
- current: "",
25
- found: e._etag,
26
- code: 409
27
- })
28
- }
29
- }
30
- if (Option.isSome(current) && current.value._etag !== e._etag) {
15
+ export const makeUpdateETag = (type: string) =>
16
+ Effect.fnUntraced(function*<IdKey extends keyof E, E extends PersistenceModelType<{}>>(
17
+ e: E,
18
+ idKey: IdKey,
19
+ current: Option.Option<E>
20
+ ) {
21
+ if (e._etag) {
22
+ if (Option.isNone(current)) {
31
23
  return yield* new OptimisticConcurrencyException({
32
24
  type,
33
- id: current.value[idKey] as string,
34
- current: current.value._etag,
25
+ id: e[idKey] as string,
26
+ current: "",
35
27
  found: e._etag,
36
- code: 412
28
+ code: 409
37
29
  })
38
30
  }
39
- const newE = makeETag(e)
40
- return newE
41
- })
31
+ }
32
+ if (Option.isSome(current) && current.value._etag !== e._etag) {
33
+ return yield* new OptimisticConcurrencyException({
34
+ type,
35
+ id: current.value[idKey] as string,
36
+ current: current.value._etag,
37
+ found: e._etag,
38
+ code: 412
39
+ })
40
+ }
41
+ return makeETag(e)
42
+ })
42
43
 
43
44
  export function lowercaseIfString<T>(val: T) {
44
45
  if (typeof val === "string") {
@@ -24,6 +24,7 @@ import * as VariantSchema from "effect/unstable/schema/VariantSchema"
24
24
  import { SqlClient } from "effect/unstable/sql/SqlClient"
25
25
  import * as SqlResolver from "effect/unstable/sql/SqlResolver"
26
26
  import * as SqlSchema from "effect/unstable/sql/SqlSchema"
27
+ import { type DbSystem, withDbSpan } from "../../otel.js"
27
28
 
28
29
  const {
29
30
  Class,
@@ -545,9 +546,15 @@ export const DateTimeUpdateFromNumber: DateTimeUpdateFromNumber = Field({
545
546
  */
546
547
  export interface JsonFromString<S extends Schema.Top> extends
547
548
  VariantSchema.Field<{
548
- readonly select: Schema.fromJsonString<S>
549
- readonly insert: Schema.fromJsonString<S>
550
- readonly update: Schema.fromJsonString<S>
549
+ readonly select: Schema.fromJsonString<
550
+ Schema.Codec<S["Type"], Schema.Json, S["DecodingServices"], S["EncodingServices"]>
551
+ >
552
+ readonly insert: Schema.fromJsonString<
553
+ Schema.Codec<S["Type"], Schema.Json, S["DecodingServices"], S["EncodingServices"]>
554
+ >
555
+ readonly update: Schema.fromJsonString<
556
+ Schema.Codec<S["Type"], Schema.Json, S["DecodingServices"], S["EncodingServices"]>
557
+ >
551
558
  readonly json: S
552
559
  readonly jsonCreate: S
553
560
  readonly jsonUpdate: S
@@ -565,7 +572,7 @@ export interface JsonFromString<S extends Schema.Top> extends
565
572
  export const JsonFromString = <S extends Schema.Top>(
566
573
  schema: S
567
574
  ): JsonFromString<S> => {
568
- const parsed = Schema.fromJsonString(schema)
575
+ const parsed = Schema.fromJsonString(Schema.toCodecJson(schema))
569
576
  return Field({
570
577
  select: parsed,
571
578
  insert: parsed,
@@ -590,6 +597,7 @@ export const makeRepository = <
590
597
  readonly spanPrefix: string
591
598
  readonly idColumn: Id
592
599
  readonly versionColumn?: string | undefined
600
+ readonly dbSystem?: DbSystem | undefined
593
601
  }): Effect.Effect<
594
602
  {
595
603
  readonly insert: (
@@ -623,6 +631,15 @@ export const makeRepository = <
623
631
  const idSchema = Model.fields[options.idColumn] as Schema.Top
624
632
  const idColumn = options.idColumn as string
625
633
  const versionColumn = options.versionColumn
634
+ const system: DbSystem = options.dbSystem ?? "other_sql"
635
+ const opSpan = (operation: string, idValue?: unknown) =>
636
+ withDbSpan({
637
+ operation,
638
+ system,
639
+ collection: options.tableName,
640
+ entity: options.spanPrefix,
641
+ ...(idValue !== undefined && { extra: { "app.entity.id": idValue } })
642
+ })
626
643
 
627
644
  const insertSchema = SqlSchema.findOne({
628
645
  Request: Model.insert,
@@ -644,9 +661,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID(
644
661
  ): Effect.Effect<S["Type"], Schema.SchemaError, S["DecodingServices"] | S["insert"]["EncodingServices"]> =>
645
662
  insertSchema(insert).pipe(
646
663
  Effect.catchTag("NoSuchElementError", Effect.die),
647
- Effect.withSpan(`${options.spanPrefix}.insert`, {}, {
648
- captureStackTrace: false
649
- })
664
+ opSpan("insert")
650
665
  ) as any
651
666
 
652
667
  const insertVoidSchema = SqlSchema.void({
@@ -657,9 +672,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID(
657
672
  insert: S["insert"]["Type"]
658
673
  ): Effect.Effect<void, Schema.SchemaError, S["insert"]["EncodingServices"]> =>
659
674
  insertVoidSchema(insert).pipe(
660
- Effect.withSpan(`${options.spanPrefix}.insertVoid`, {}, {
661
- captureStackTrace: false
662
- })
675
+ opSpan("insertVoid")
663
676
  ) as any
664
677
 
665
678
  const updateSchema = SqlSchema.findOne({
@@ -706,11 +719,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = ${request[idCol
706
719
  ): Effect.Effect<S["Type"], Schema.SchemaError, S["DecodingServices"] | S["update"]["EncodingServices"]> =>
707
720
  updateSchema(update).pipe(
708
721
  Effect.catchTag("NoSuchElementError", Effect.die),
709
- Effect.withSpan(`${options.spanPrefix}.update`, {
710
- attributes: { id: (update as any)[idColumn] }
711
- }, {
712
- captureStackTrace: false
713
- })
722
+ opSpan("update", (update as any)[idColumn])
714
723
  ) as any
715
724
 
716
725
  const updateVoidSchema = SqlSchema.void({
@@ -729,11 +738,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = ${request[idCol
729
738
  update: S["update"]["Type"]
730
739
  ): Effect.Effect<void, Schema.SchemaError, S["update"]["EncodingServices"]> =>
731
740
  updateVoidSchema(update).pipe(
732
- Effect.withSpan(`${options.spanPrefix}.updateVoid`, {
733
- attributes: { id: (update as any)[idColumn] }
734
- }, {
735
- captureStackTrace: false
736
- })
741
+ opSpan("updateVoid", (update as any)[idColumn])
737
742
  ) as any
738
743
 
739
744
  const findByIdSchema = SqlSchema.findOneOption({
@@ -749,9 +754,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = ${request[idCol
749
754
  S["DecodingServices"] | S["fields"][Id]["EncodingServices"]
750
755
  > =>
751
756
  findByIdSchema(id).pipe(
752
- Effect.withSpan(`${options.spanPrefix}.findById`, { attributes: { id } }, {
753
- captureStackTrace: false
754
- })
757
+ opSpan("findById", id)
755
758
  ) as any
756
759
 
757
760
  const deleteSchema = SqlSchema.void({
@@ -762,11 +765,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = ${request[idCol
762
765
  id: S["fields"][Id]["Type"]
763
766
  ): Effect.Effect<void, Schema.SchemaError, S["fields"][Id]["EncodingServices"]> =>
764
767
  deleteSchema(id).pipe(
765
- Effect.withSpan(`${options.spanPrefix}.delete`, {
766
- attributes: { id }
767
- }, {
768
- captureStackTrace: false
769
- })
768
+ opSpan("delete", id)
770
769
  ) as any
771
770
 
772
771
  return { insert, insertVoid, update, updateVoid, findById, delete: delete_ } as const
@@ -789,6 +788,7 @@ export const makeDataLoaders = <
789
788
  readonly idColumn: Id
790
789
  readonly window: Input
791
790
  readonly maxBatchSize?: number | undefined
791
+ readonly dbSystem?: DbSystem | undefined
792
792
  }
793
793
  ): Effect.Effect<
794
794
  {
@@ -821,6 +821,15 @@ export const makeDataLoaders = <
821
821
  const idSchema = Model.fields[options.idColumn] as Schema.Top
822
822
  const idColumn = options.idColumn as string
823
823
  const setMaxBatchSize = options.maxBatchSize ? RequestResolver.batchN(options.maxBatchSize) : identity
824
+ const system: DbSystem = options.dbSystem ?? "other_sql"
825
+ const opSpan = (operation: string, idValue?: unknown) =>
826
+ withDbSpan({
827
+ operation,
828
+ system,
829
+ collection: options.tableName,
830
+ entity: options.spanPrefix,
831
+ ...(idValue !== undefined && { extra: { "app.entity.id": idValue } })
832
+ })
824
833
 
825
834
  const insertResolver = SqlResolver
826
835
  .ordered({
@@ -854,9 +863,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID(
854
863
  > =>
855
864
  insertExecute(insert).pipe(
856
865
  Effect.catchTag("ResultLengthMismatch", Effect.die),
857
- Effect.withSpan(`${options.spanPrefix}.insert`, {}, {
858
- captureStackTrace: false
859
- })
866
+ opSpan("insert")
860
867
  ) as any
861
868
 
862
869
  const insertVoidResolver = SqlResolver
@@ -874,9 +881,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID(
874
881
  insert: S["insert"]["Type"]
875
882
  ): Effect.Effect<void, Schema.SchemaError, S["insert"]["EncodingServices"]> =>
876
883
  insertVoidExecute(insert).pipe(
877
- Effect.withSpan(`${options.spanPrefix}.insertVoid`, {}, {
878
- captureStackTrace: false
879
- })
884
+ opSpan("insertVoid")
880
885
  ) as any
881
886
 
882
887
  const findByIdResolver = SqlResolver
@@ -902,9 +907,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID(
902
907
  S["DecodingServices"] | S["fields"][Id]["EncodingServices"]
903
908
  > =>
904
909
  findByIdExecute(id).pipe(
905
- Effect.withSpan(`${options.spanPrefix}.findById`, { attributes: { id } }, {
906
- captureStackTrace: false
907
- })
910
+ opSpan("findById", id)
908
911
  ) as any
909
912
 
910
913
  const deleteResolver = SqlResolver
@@ -922,9 +925,7 @@ select * from ${sql(options.tableName)} where ${sql(idColumn)} = LAST_INSERT_ID(
922
925
  id: S["fields"][Id]["Type"]
923
926
  ): Effect.Effect<void, Schema.SchemaError, S["fields"][Id]["EncodingServices"]> =>
924
927
  deleteExecute(id).pipe(
925
- Effect.withSpan(`${options.spanPrefix}.delete`, { attributes: { id } }, {
926
- captureStackTrace: false
927
- })
928
+ opSpan("delete", id)
928
929
  ) as any
929
930
 
930
931
  return { insert, insertVoid, findById, delete: delete_ } as const