@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
@@ -0,0 +1,361 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import { Effect, type NonEmptyReadonlyArray, Option, Struct } from "effect-app"
4
+ import { toNonEmptyArray } from "effect-app/Array"
5
+ import { SqlClient } from "effect/unstable/sql"
6
+ import { OptimisticConcurrencyException } from "../../errors.js"
7
+ import { InfraLogger } from "../../logger.js"
8
+ import type { FieldValues } from "../../Model/filter/types.js"
9
+ import type { ComputedProjectionIrExpression } from "../../Model/query.js"
10
+ import { annotateDb } from "../../otel.js"
11
+ import { storeId } from "../Memory.js"
12
+ import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "../service.js"
13
+ import { makeETag } from "../utils.js"
14
+ import { buildWhereSQLQuery, logQuery, pgDialect } from "./query.js"
15
+
16
+ const parseRow = <Encoded extends FieldValues>(
17
+ row: { id: string; _etag: string | null; data: unknown },
18
+ idKey: PropertyKey,
19
+ defaultValues: Partial<Encoded>
20
+ ): PersistenceModelType<Encoded> => {
21
+ const data = (typeof row.data === "string" ? JSON.parse(row.data) : row.data) as object
22
+ return { ...defaultValues, ...data, [idKey]: row.id, _etag: row._etag ?? undefined } as PersistenceModelType<Encoded>
23
+ }
24
+
25
+ const parseSelectRow = (
26
+ row: Record<string, unknown>,
27
+ idKey: PropertyKey,
28
+ defaultValues: Record<string, unknown>
29
+ ): any => {
30
+ const result: Record<string, unknown> = { ...defaultValues }
31
+ for (const [key, value] of Object.entries(row)) {
32
+ if (key === "id") {
33
+ result[idKey as string] = value
34
+ result["id"] = value
35
+ } else {
36
+ result[key] = value
37
+ }
38
+ }
39
+ return result
40
+ }
41
+
42
+ const makePgStore = Effect.fnUntraced(function*({ prefix }: StorageConfig) {
43
+ const sql = yield* SqlClient.SqlClient
44
+ return {
45
+ make: Effect.fnUntraced(function*<IdKey extends keyof Encoded, Encoded extends FieldValues, R = never, E = never>(
46
+ name: string,
47
+ idKey: IdKey,
48
+ seed?: Effect.Effect<Iterable<Encoded>, E, R>,
49
+ config?: StoreConfig<Encoded>
50
+ ) {
51
+ type PM = PersistenceModelType<Encoded>
52
+ const tableName = `${prefix}${name}`
53
+ const defaultValues = config?.defaultValues ?? {}
54
+
55
+ const resolveNamespace = !config?.allowNamespace
56
+ ? Effect.succeed("primary")
57
+ : storeId.asEffect().pipe(Effect.map((namespace) => {
58
+ if (namespace !== "primary" && !config.allowNamespace!(namespace)) {
59
+ throw new Error(`Namespace ${namespace} not allowed!`)
60
+ }
61
+ return namespace
62
+ }))
63
+
64
+ const ensureTable = sql
65
+ .unsafe(
66
+ `CREATE TABLE IF NOT EXISTS "${tableName}" (id TEXT NOT NULL, _namespace TEXT NOT NULL DEFAULT 'primary', _etag TEXT, data JSONB NOT NULL, PRIMARY KEY (id, _namespace))`
67
+ )
68
+ .pipe(
69
+ Effect.andThen(
70
+ sql.unsafe(
71
+ `CREATE TABLE IF NOT EXISTS "_migrations" (id TEXT NOT NULL, version TEXT NOT NULL, PRIMARY KEY (id, version))`
72
+ )
73
+ ),
74
+ Effect.orDie,
75
+ Effect.asVoid
76
+ )
77
+
78
+ const toRow = (e: PM) => {
79
+ const newE = makeETag(e)
80
+ const id = newE[idKey] as string
81
+ const { _etag, [idKey]: _id, ...rest } = newE as any
82
+ const data = JSON.stringify(rest)
83
+ return { id, _etag: newE._etag!, data, item: newE }
84
+ }
85
+
86
+ const exec = (query: string, params?: readonly unknown[]) => sql.unsafe(query, params as any).pipe(Effect.orDie)
87
+
88
+ const setInternal = Effect.fnUntraced(function*(e: PM, ns: string) {
89
+ const row = toRow(e)
90
+ if (e._etag) {
91
+ yield* exec(
92
+ `UPDATE "${tableName}" SET _etag = $1, data = $2 WHERE id = $3 AND _etag = $4 AND _namespace = $5`,
93
+ [row._etag, row.data, row.id, e._etag, ns]
94
+ )
95
+ const existing = yield* exec(
96
+ `SELECT _etag FROM "${tableName}" WHERE id = $1 AND _namespace = $2`,
97
+ [row.id, ns]
98
+ )
99
+ const current = (existing as any[])[0]
100
+ if (!current || current._etag !== row._etag) {
101
+ if (current) {
102
+ return yield* new OptimisticConcurrencyException({
103
+ type: name,
104
+ id: row.id,
105
+ current: current._etag,
106
+ found: e._etag,
107
+ code: 412
108
+ })
109
+ }
110
+ return yield* new OptimisticConcurrencyException({
111
+ type: name,
112
+ id: row.id,
113
+ current: "",
114
+ found: e._etag,
115
+ code: 404
116
+ })
117
+ }
118
+ } else {
119
+ yield* exec(
120
+ `INSERT INTO "${tableName}" (id, _namespace, _etag, data) VALUES ($1, $2, $3, $4)`,
121
+ [row.id, ns, row._etag, row.data]
122
+ )
123
+ }
124
+ return row.item
125
+ })
126
+
127
+ const bulkSetInternal = (items: NonEmptyReadonlyArray<PM>, ns: string) =>
128
+ sql
129
+ .withTransaction(Effect.forEach(items, (e) => setInternal(e, ns)))
130
+ .pipe(
131
+ Effect.orDie,
132
+ Effect.map((_) => _ as unknown as NonEmptyReadonlyArray<PM>)
133
+ )
134
+
135
+ const ctx = yield* Effect.context<R>()
136
+ const seedCache = new Map<string, Effect.Effect<void>>()
137
+ const makeSeedEffect = Effect.fnUntraced(function*(ns: string) {
138
+ yield* ensureTable
139
+ if (!seed) return
140
+ const existing = yield* exec(
141
+ `SELECT id FROM "_migrations" WHERE id = $1 AND version = $2`,
142
+ [`${tableName}::${ns}`, tableName]
143
+ )
144
+ if ((existing as any[]).length > 0) return
145
+ yield* InfraLogger.logInfo(`Seeding data for ${name} (namespace: ${ns})`)
146
+ const items = yield* seed.pipe(Effect.provide(ctx), Effect.orDie)
147
+ const ne = toNonEmptyArray([...items])
148
+ if (Option.isSome(ne)) yield* bulkSetInternal(ne.value, ns)
149
+ yield* exec(
150
+ `INSERT INTO "_migrations" (id, version) VALUES ($1, $2)`,
151
+ [`${tableName}::${ns}`, tableName]
152
+ )
153
+ })
154
+ const seedNamespace = (ns: string) => {
155
+ let cached = seedCache.get(ns)
156
+ if (!cached) {
157
+ cached = Effect.cached(Effect.uninterruptible(makeSeedEffect(ns))).pipe(Effect.runSync)
158
+ seedCache.set(ns, cached)
159
+ }
160
+ return cached
161
+ }
162
+ const s: Store<IdKey, Encoded> = {
163
+ seedNamespace: (ns) => seedNamespace(ns),
164
+
165
+ all: resolveNamespace.pipe(
166
+ Effect.flatMap((ns) => {
167
+ const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = $1`
168
+ return exec(sqlText, [ns])
169
+ .pipe(
170
+ Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
171
+ annotateDb({
172
+ operation: "all",
173
+ system: "postgresql",
174
+ collection: tableName,
175
+ namespace: ns,
176
+ entity: name,
177
+ query: sqlText
178
+ })
179
+ )
180
+ })
181
+ ),
182
+
183
+ find: (id) =>
184
+ resolveNamespace.pipe(Effect
185
+ .flatMap((ns) => {
186
+ const sqlText = `SELECT id, _etag, data FROM "${tableName}" WHERE id = $1 AND _namespace = $2`
187
+ return exec(sqlText, [id, ns])
188
+ .pipe(
189
+ Effect.map((rows) => {
190
+ const row = (rows as any[])[0]
191
+ return row
192
+ ? Option.some(parseRow<Encoded>(row, idKey, defaultValues))
193
+ : Option.none()
194
+ }),
195
+ annotateDb({
196
+ operation: "find",
197
+ system: "postgresql",
198
+ collection: tableName,
199
+ namespace: ns,
200
+ entity: name,
201
+ query: sqlText,
202
+ extra: { "app.entity.id": id }
203
+ })
204
+ )
205
+ })),
206
+
207
+ filter: <U extends keyof Encoded = never>(f: FilterArgs<Encoded, U>) => {
208
+ const filter = f
209
+ .filter
210
+ type M = U extends undefined ? Encoded : Pick<Encoded, U>
211
+ return resolveNamespace.pipe(Effect.flatMap((ns) =>
212
+ Effect
213
+ .sync(() => {
214
+ const q = buildWhereSQLQuery(
215
+ pgDialect,
216
+ idKey,
217
+ filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
218
+ tableName,
219
+ defaultValues,
220
+ f.select as
221
+ | NonEmptyReadonlyArray<
222
+ string | {
223
+ key: string
224
+ subKeys: readonly string[]
225
+ } | {
226
+ key: string
227
+ computed: ComputedProjectionIrExpression
228
+ }
229
+ >
230
+ | undefined,
231
+ f.order,
232
+ f.skip,
233
+ f.limit
234
+ )
235
+ const nsPlaceholder = pgDialect.placeholder(q.params.length + 1)
236
+ const hasWhere = q.sql.includes("WHERE")
237
+ const nsSql = hasWhere
238
+ ? q.sql.replace("WHERE", `WHERE _namespace = ${nsPlaceholder} AND`)
239
+ : q.sql.replace(
240
+ `FROM "${tableName}"`,
241
+ `FROM "${tableName}" WHERE _namespace = ${nsPlaceholder}`
242
+ )
243
+ return { sql: nsSql, params: [...q.params, ns] }
244
+ })
245
+ .pipe(
246
+ Effect.tap((q) => logQuery(q)),
247
+ Effect.tap((q) => Effect.annotateCurrentSpan({ "db.query.text": q.sql })),
248
+ Effect.flatMap((q) =>
249
+ exec(q.sql, q.params).pipe(
250
+ Effect.map((rows) => {
251
+ if (f.select) {
252
+ return (rows as any[]).map((r) => {
253
+ const selected = parseSelectRow(r, idKey, {})
254
+ return {
255
+ ...Struct.pick(
256
+ defaultValues as any,
257
+ f.select!.filter((_) => typeof _ === "string") as never[]
258
+ ),
259
+ ...selected
260
+ } as M
261
+ })
262
+ }
263
+ return (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues) as any as M)
264
+ })
265
+ )
266
+ ),
267
+ annotateDb({
268
+ operation: "filter",
269
+ system: "postgresql",
270
+ collection: tableName,
271
+ namespace: ns,
272
+ entity: name
273
+ })
274
+ )
275
+ ))
276
+ },
277
+
278
+ set: (e) =>
279
+ resolveNamespace.pipe(Effect.flatMap((ns) =>
280
+ setInternal(e, ns).pipe(
281
+ annotateDb({
282
+ operation: "set",
283
+ system: "postgresql",
284
+ collection: tableName,
285
+ namespace: ns,
286
+ entity: name,
287
+ extra: { "app.entity.id": e[idKey] }
288
+ })
289
+ )
290
+ )),
291
+
292
+ batchSet: (items) =>
293
+ resolveNamespace.pipe(Effect.flatMap((ns) =>
294
+ bulkSetInternal(items, ns).pipe(
295
+ annotateDb({
296
+ operation: "batchSet",
297
+ system: "postgresql",
298
+ collection: tableName,
299
+ namespace: ns,
300
+ entity: name
301
+ })
302
+ )
303
+ )),
304
+
305
+ bulkSet: (items) =>
306
+ resolveNamespace.pipe(Effect.flatMap((ns) =>
307
+ bulkSetInternal(items, ns).pipe(
308
+ annotateDb({
309
+ operation: "bulkSet",
310
+ system: "postgresql",
311
+ collection: tableName,
312
+ namespace: ns,
313
+ entity: name
314
+ })
315
+ )
316
+ )),
317
+
318
+ batchRemove: (ids) => {
319
+ const placeholders = ids.map((_, i) => `$${i + 1}`).join(", ")
320
+ const nsPlaceholder = `$${ids.length + 1}`
321
+ return resolveNamespace.pipe(Effect.flatMap((ns) => {
322
+ const sqlText = `DELETE FROM "${tableName}" WHERE id IN (${placeholders}) AND _namespace = ${nsPlaceholder}`
323
+ return exec(sqlText, [...ids, ns])
324
+ .pipe(
325
+ Effect.asVoid,
326
+ annotateDb({
327
+ operation: "batchRemove",
328
+ system: "postgresql",
329
+ collection: tableName,
330
+ namespace: ns,
331
+ entity: name,
332
+ query: sqlText
333
+ })
334
+ )
335
+ }))
336
+ },
337
+
338
+ queryRaw: (query) =>
339
+ s.all.pipe(
340
+ Effect.map(query.memory),
341
+ annotateDb({
342
+ operation: "queryRaw",
343
+ system: "postgresql",
344
+ collection: tableName,
345
+ entity: name
346
+ })
347
+ )
348
+ }
349
+
350
+ // Eagerly seed primary namespace on initialization
351
+ yield* seedNamespace("primary")
352
+
353
+ return s
354
+ })
355
+ }
356
+ })
357
+
358
+ export function PgStoreLayer(cfg: StorageConfig) {
359
+ return StoreMaker
360
+ .toLayer(makePgStore(cfg))
361
+ }