@effect-app/infra 4.0.0-beta.9 → 4.0.0-beta.90

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 (177) hide show
  1. package/CHANGELOG.md +589 -0
  2. package/dist/CUPS.d.ts +3 -3
  3. package/dist/CUPS.d.ts.map +1 -1
  4. package/dist/CUPS.js +3 -3
  5. package/dist/Emailer/Sendgrid.js +1 -1
  6. package/dist/Emailer/service.d.ts +3 -3
  7. package/dist/Emailer/service.d.ts.map +1 -1
  8. package/dist/Emailer/service.js +3 -3
  9. package/dist/MainFiberSet.d.ts +2 -2
  10. package/dist/MainFiberSet.d.ts.map +1 -1
  11. package/dist/MainFiberSet.js +3 -3
  12. package/dist/Model/Repository/internal/internal.d.ts +3 -3
  13. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  14. package/dist/Model/Repository/internal/internal.js +11 -7
  15. package/dist/Model/Repository/makeRepo.d.ts +2 -2
  16. package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
  17. package/dist/Model/Repository/makeRepo.js +1 -1
  18. package/dist/Model/Repository/validation.d.ts +5 -4
  19. package/dist/Model/Repository/validation.d.ts.map +1 -1
  20. package/dist/Model/query/dsl.d.ts +9 -9
  21. package/dist/Operations.d.ts +2 -2
  22. package/dist/Operations.d.ts.map +1 -1
  23. package/dist/Operations.js +3 -3
  24. package/dist/OperationsRepo.d.ts +2 -2
  25. package/dist/OperationsRepo.d.ts.map +1 -1
  26. package/dist/OperationsRepo.js +3 -3
  27. package/dist/QueueMaker/SQLQueue.d.ts +3 -5
  28. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  29. package/dist/QueueMaker/SQLQueue.js +9 -7
  30. package/dist/QueueMaker/errors.d.ts +1 -1
  31. package/dist/QueueMaker/errors.d.ts.map +1 -1
  32. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  33. package/dist/QueueMaker/memQueue.js +10 -9
  34. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  35. package/dist/QueueMaker/sbqueue.js +11 -9
  36. package/dist/RequestContext.d.ts +19 -14
  37. package/dist/RequestContext.d.ts.map +1 -1
  38. package/dist/RequestContext.js +5 -5
  39. package/dist/RequestFiberSet.d.ts +2 -2
  40. package/dist/RequestFiberSet.d.ts.map +1 -1
  41. package/dist/RequestFiberSet.js +5 -5
  42. package/dist/Store/ContextMapContainer.d.ts +14 -3
  43. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  44. package/dist/Store/ContextMapContainer.js +64 -3
  45. package/dist/Store/Cosmos.d.ts.map +1 -1
  46. package/dist/Store/Cosmos.js +91 -56
  47. package/dist/Store/Disk.d.ts.map +1 -1
  48. package/dist/Store/Disk.js +3 -4
  49. package/dist/Store/Memory.d.ts +2 -2
  50. package/dist/Store/Memory.d.ts.map +1 -1
  51. package/dist/Store/Memory.js +4 -4
  52. package/dist/Store/SQL/Pg.d.ts +4 -0
  53. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  54. package/dist/Store/SQL/Pg.js +186 -0
  55. package/dist/Store/SQL/query.d.ts +36 -0
  56. package/dist/Store/SQL/query.d.ts.map +1 -0
  57. package/dist/Store/SQL/query.js +385 -0
  58. package/dist/Store/SQL.d.ts +11 -0
  59. package/dist/Store/SQL.d.ts.map +1 -0
  60. package/dist/Store/SQL.js +212 -0
  61. package/dist/Store/index.d.ts +1 -1
  62. package/dist/Store/index.d.ts.map +1 -1
  63. package/dist/Store/index.js +11 -1
  64. package/dist/Store/service.d.ts +8 -5
  65. package/dist/Store/service.d.ts.map +1 -1
  66. package/dist/Store/service.js +14 -6
  67. package/dist/adapters/SQL/Model.d.ts +2 -5
  68. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  69. package/dist/adapters/SQL/Model.js +21 -13
  70. package/dist/adapters/ServiceBus.d.ts +6 -6
  71. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  72. package/dist/adapters/ServiceBus.js +9 -9
  73. package/dist/adapters/cosmos-client.d.ts +2 -2
  74. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  75. package/dist/adapters/cosmos-client.js +3 -3
  76. package/dist/adapters/logger.d.ts.map +1 -1
  77. package/dist/adapters/memQueue.d.ts +2 -2
  78. package/dist/adapters/memQueue.d.ts.map +1 -1
  79. package/dist/adapters/memQueue.js +3 -3
  80. package/dist/adapters/mongo-client.d.ts +2 -2
  81. package/dist/adapters/mongo-client.d.ts.map +1 -1
  82. package/dist/adapters/mongo-client.js +3 -3
  83. package/dist/adapters/redis-client.d.ts +3 -3
  84. package/dist/adapters/redis-client.d.ts.map +1 -1
  85. package/dist/adapters/redis-client.js +3 -3
  86. package/dist/api/ContextProvider.d.ts +6 -6
  87. package/dist/api/ContextProvider.d.ts.map +1 -1
  88. package/dist/api/ContextProvider.js +6 -6
  89. package/dist/api/internal/RequestContextMiddleware.d.ts +1 -1
  90. package/dist/api/internal/auth.d.ts +1 -1
  91. package/dist/api/internal/events.d.ts +2 -2
  92. package/dist/api/internal/events.d.ts.map +1 -1
  93. package/dist/api/internal/events.js +7 -5
  94. package/dist/api/layerUtils.d.ts +5 -5
  95. package/dist/api/layerUtils.d.ts.map +1 -1
  96. package/dist/api/layerUtils.js +5 -5
  97. package/dist/api/routing/middleware/RouterMiddleware.d.ts +3 -3
  98. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  99. package/dist/api/routing/middleware/middleware.d.ts +35 -1
  100. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  101. package/dist/api/routing/middleware/middleware.js +39 -1
  102. package/dist/api/routing/schema/jwt.d.ts +1 -1
  103. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  104. package/dist/api/routing/schema/jwt.js +1 -1
  105. package/dist/api/routing.d.ts +1 -5
  106. package/dist/api/routing.d.ts.map +1 -1
  107. package/dist/api/routing.js +3 -2
  108. package/dist/api/setupRequest.d.ts +6 -3
  109. package/dist/api/setupRequest.d.ts.map +1 -1
  110. package/dist/api/setupRequest.js +11 -6
  111. package/dist/errorReporter.d.ts +1 -1
  112. package/dist/errorReporter.d.ts.map +1 -1
  113. package/dist/errorReporter.js +1 -1
  114. package/dist/fileUtil.js +1 -1
  115. package/dist/logger.d.ts.map +1 -1
  116. package/dist/rateLimit.js +1 -1
  117. package/examples/query.ts +29 -25
  118. package/package.json +32 -18
  119. package/src/CUPS.ts +2 -2
  120. package/src/Emailer/Sendgrid.ts +1 -1
  121. package/src/Emailer/service.ts +2 -2
  122. package/src/MainFiberSet.ts +2 -2
  123. package/src/Model/Repository/internal/internal.ts +11 -8
  124. package/src/Model/Repository/makeRepo.ts +2 -2
  125. package/src/Operations.ts +2 -2
  126. package/src/OperationsRepo.ts +2 -2
  127. package/src/QueueMaker/SQLQueue.ts +10 -10
  128. package/src/QueueMaker/memQueue.ts +41 -42
  129. package/src/QueueMaker/sbqueue.ts +65 -62
  130. package/src/RequestContext.ts +4 -4
  131. package/src/RequestFiberSet.ts +4 -4
  132. package/src/Store/ContextMapContainer.ts +98 -2
  133. package/src/Store/Cosmos.ts +273 -207
  134. package/src/Store/Disk.ts +2 -3
  135. package/src/Store/Memory.ts +4 -6
  136. package/src/Store/SQL/Pg.ts +328 -0
  137. package/src/Store/SQL/query.ts +430 -0
  138. package/src/Store/SQL.ts +357 -0
  139. package/src/Store/index.ts +10 -0
  140. package/src/Store/service.ts +16 -7
  141. package/src/adapters/SQL/Model.ts +76 -71
  142. package/src/adapters/ServiceBus.ts +8 -8
  143. package/src/adapters/cosmos-client.ts +2 -2
  144. package/src/adapters/memQueue.ts +2 -2
  145. package/src/adapters/mongo-client.ts +2 -2
  146. package/src/adapters/redis-client.ts +2 -2
  147. package/src/api/ContextProvider.ts +11 -11
  148. package/src/api/internal/events.ts +7 -6
  149. package/src/api/layerUtils.ts +8 -8
  150. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  151. package/src/api/routing/middleware/middleware.ts +43 -0
  152. package/src/api/routing/schema/jwt.ts +2 -3
  153. package/src/api/routing.ts +7 -6
  154. package/src/api/setupRequest.ts +27 -7
  155. package/src/errorReporter.ts +1 -1
  156. package/src/fileUtil.ts +1 -1
  157. package/src/rateLimit.ts +2 -2
  158. package/test/contextProvider.test.ts +5 -5
  159. package/test/controller.test.ts +12 -9
  160. package/test/dist/contextProvider.test.d.ts.map +1 -1
  161. package/test/dist/controller.test.d.ts.map +1 -1
  162. package/test/dist/fixtures.d.ts +18 -8
  163. package/test/dist/fixtures.d.ts.map +1 -1
  164. package/test/dist/fixtures.js +11 -9
  165. package/test/dist/query.test.d.ts.map +1 -1
  166. package/test/dist/rawQuery.test.d.ts.map +1 -1
  167. package/test/dist/requires.test.d.ts.map +1 -1
  168. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  169. package/test/dist/sql-store.test.d.ts.map +1 -0
  170. package/test/fixtures.ts +10 -8
  171. package/test/query.test.ts +160 -14
  172. package/test/rawQuery.test.ts +19 -17
  173. package/test/requires.test.ts +6 -5
  174. package/test/rpc-multi-middleware.test.ts +73 -4
  175. package/test/sql-store.test.ts +776 -0
  176. package/test/validateSample.test.ts +1 -1
  177. package/tsconfig.json +0 -1
@@ -0,0 +1,328 @@
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 { storeId } from "../Memory.js"
10
+ import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "../service.js"
11
+ import { makeETag } from "../utils.js"
12
+ import { buildWhereSQLQuery, logQuery, pgDialect } from "./query.js"
13
+
14
+ const parseRow = <Encoded extends FieldValues>(
15
+ row: { id: string; _etag: string | null; data: unknown },
16
+ idKey: PropertyKey,
17
+ defaultValues: Partial<Encoded>
18
+ ): PersistenceModelType<Encoded> => {
19
+ const data = (typeof row.data === "string" ? JSON.parse(row.data) : row.data) as object
20
+ return { ...defaultValues, ...data, [idKey]: row.id, _etag: row._etag ?? undefined } as PersistenceModelType<Encoded>
21
+ }
22
+
23
+ const parseSelectRow = (
24
+ row: Record<string, unknown>,
25
+ idKey: PropertyKey,
26
+ defaultValues: Record<string, unknown>
27
+ ): any => {
28
+ const result: Record<string, unknown> = { ...defaultValues }
29
+ for (const [key, value] of Object.entries(row)) {
30
+ if (key === "id") {
31
+ result[idKey as string] = value
32
+ result["id"] = value
33
+ } else {
34
+ result[key] = value
35
+ }
36
+ }
37
+ return result
38
+ }
39
+
40
+ function makePgStore({ prefix }: StorageConfig) {
41
+ return Effect.gen(function*() {
42
+ const sql = yield* SqlClient.SqlClient
43
+ return {
44
+ make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R = never, E = never>(
45
+ name: string,
46
+ idKey: IdKey,
47
+ seed?: Effect.Effect<Iterable<Encoded>, E, R>,
48
+ config?: StoreConfig<Encoded>
49
+ ) =>
50
+ Effect.gen(function*() {
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
+ yield* 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(Effect.orDie)
69
+
70
+ const toRow = (e: PM) => {
71
+ const newE = makeETag(e)
72
+ const id = newE[idKey] as string
73
+ const { _etag, [idKey]: _id, ...rest } = newE as any
74
+ const data = JSON.stringify(rest)
75
+ return { id, _etag: newE._etag!, data, item: newE }
76
+ }
77
+
78
+ const exec = (query: string, params?: readonly unknown[]) =>
79
+ sql.unsafe(query, params as any).pipe(Effect.orDie)
80
+
81
+ const seedMarkerId = `__seed_marker__`
82
+
83
+ const setInternal = (e: PM, ns: string) =>
84
+ Effect.gen(function*() {
85
+ const row = toRow(e)
86
+ if (e._etag) {
87
+ yield* exec(
88
+ `UPDATE "${tableName}" SET _etag = $1, data = $2 WHERE id = $3 AND _etag = $4 AND _namespace = $5`,
89
+ [row._etag, row.data, row.id, e._etag, ns]
90
+ )
91
+ const existing = yield* exec(
92
+ `SELECT _etag FROM "${tableName}" WHERE id = $1 AND _namespace = $2`,
93
+ [row.id, ns]
94
+ )
95
+ const current = (existing as any[])[0]
96
+ if (!current || current._etag !== row._etag) {
97
+ if (current) {
98
+ return yield* new OptimisticConcurrencyException({
99
+ type: name,
100
+ id: row.id,
101
+ current: current._etag,
102
+ found: e._etag,
103
+ code: 412
104
+ })
105
+ }
106
+ return yield* new OptimisticConcurrencyException({
107
+ type: name,
108
+ id: row.id,
109
+ current: "",
110
+ found: e._etag,
111
+ code: 404
112
+ })
113
+ }
114
+ } else {
115
+ yield* exec(
116
+ `INSERT INTO "${tableName}" (id, _namespace, _etag, data) VALUES ($1, $2, $3, $4)`,
117
+ [row.id, ns, row._etag, row.data]
118
+ )
119
+ }
120
+ return row.item
121
+ })
122
+
123
+ const bulkSetInternal = (items: NonEmptyReadonlyArray<PM>, ns: string) =>
124
+ sql
125
+ .withTransaction(Effect.forEach(items, (e) => setInternal(e, ns)))
126
+ .pipe(
127
+ Effect.orDie,
128
+ Effect.map((_) => _ as unknown as NonEmptyReadonlyArray<PM>)
129
+ )
130
+
131
+ const ctx = yield* Effect.context<R>()
132
+ const seedCache = new Map<string, Effect.Effect<void>>()
133
+ const makeSeedEffect = (ns: string) =>
134
+ exec(
135
+ `SELECT id FROM "${tableName}" WHERE id = $1 AND _namespace = $2`,
136
+ [seedMarkerId, `__seed__::${ns}`]
137
+ )
138
+ .pipe(
139
+ Effect.flatMap((existing) => {
140
+ if ((existing as any[]).length > 0) return Effect.void
141
+ return InfraLogger.logInfo(`Seeding data for ${name} (namespace: ${ns})`).pipe(
142
+ Effect.andThen(seed!),
143
+ Effect.flatMap((items) =>
144
+ Effect.flatMapOption(
145
+ Effect.succeed(toNonEmptyArray([...items])),
146
+ (a) => bulkSetInternal(a, ns)
147
+ )
148
+ ),
149
+ Effect.andThen(
150
+ exec(
151
+ `INSERT INTO "${tableName}" (id, _namespace, _etag, data) VALUES ($1, $2, $3, $4)`,
152
+ [seedMarkerId, `__seed__::${ns}`, null, JSON.stringify({ _marker: true })]
153
+ )
154
+ ),
155
+ Effect.provide(ctx),
156
+ Effect.orDie
157
+ )
158
+ })
159
+ )
160
+ const seedNamespace = Effect.fn("seedNamespace")(function*(ns: string) {
161
+ if (!seed) return
162
+ let cached = seedCache.get(ns)
163
+ if (!cached) {
164
+ cached = yield* Effect.cached(makeSeedEffect(ns))
165
+ seedCache.set(ns, cached)
166
+ }
167
+ yield* cached
168
+ })
169
+ const resolveAndSeed = resolveNamespace.pipe(
170
+ Effect.tap((ns) => seedNamespace(ns))
171
+ )
172
+
173
+ const s: Store<IdKey, Encoded> = {
174
+ all: resolveAndSeed.pipe(Effect.flatMap((ns) =>
175
+ exec(`SELECT id, _etag, data FROM "${tableName}" WHERE _namespace = $1`, [ns])
176
+ .pipe(
177
+ Effect.map((rows) => (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues))),
178
+ Effect.withSpan("PgSQL.all [effect-app/infra/Store]", {
179
+ attributes: {
180
+ "repository.table_name": tableName,
181
+ "repository.model_name": name,
182
+ "repository.namespace": ns
183
+ }
184
+ }, { captureStackTrace: false })
185
+ )
186
+ )),
187
+
188
+ find: (id) =>
189
+ resolveAndSeed.pipe(Effect
190
+ .flatMap((ns) =>
191
+ exec(`SELECT id, _etag, data FROM "${tableName}" WHERE id = $1 AND _namespace = $2`, [id, ns])
192
+ .pipe(
193
+ Effect.map((rows) => {
194
+ const row = (rows as any[])[0]
195
+ return row
196
+ ? Option.some(parseRow<Encoded>(row, idKey, defaultValues))
197
+ : Option.none()
198
+ }),
199
+ Effect.withSpan("PgSQL.find [effect-app/infra/Store]", {
200
+ attributes: { "repository.table_name": tableName, "repository.model_name": name, id }
201
+ }, { captureStackTrace: false })
202
+ )
203
+ )),
204
+
205
+ filter: <U extends keyof Encoded = never>(f: FilterArgs<Encoded, U>) => {
206
+ const filter = f
207
+ .filter
208
+ type M = U extends undefined ? Encoded : Pick<Encoded, U>
209
+ return resolveAndSeed.pipe(Effect.flatMap((ns) =>
210
+ Effect
211
+ .sync(() => {
212
+ const q = buildWhereSQLQuery(
213
+ pgDialect,
214
+ idKey,
215
+ filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
216
+ tableName,
217
+ defaultValues,
218
+ f.select as
219
+ | NonEmptyReadonlyArray<string | { key: string; subKeys: readonly string[] }>
220
+ | undefined,
221
+ f.order as NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }> | undefined,
222
+ f.skip,
223
+ f.limit
224
+ )
225
+ const nsPlaceholder = pgDialect.placeholder(q.params.length + 1)
226
+ const hasWhere = q.sql.includes("WHERE")
227
+ const nsSql = hasWhere
228
+ ? q.sql.replace("WHERE", `WHERE _namespace = ${nsPlaceholder} AND`)
229
+ : q.sql.replace(
230
+ `FROM "${tableName}"`,
231
+ `FROM "${tableName}" WHERE _namespace = ${nsPlaceholder}`
232
+ )
233
+ return { sql: nsSql, params: [...q.params, ns] }
234
+ })
235
+ .pipe(
236
+ Effect.tap((q) => logQuery(q)),
237
+ Effect.flatMap((q) =>
238
+ exec(q.sql, q.params).pipe(
239
+ Effect.map((rows) => {
240
+ if (f.select) {
241
+ return (rows as any[]).map((r) => {
242
+ const selected = parseSelectRow(r, idKey, {})
243
+ return {
244
+ ...Struct.pick(
245
+ defaultValues as any,
246
+ f.select!.filter((_) => typeof _ === "string") as never[]
247
+ ),
248
+ ...selected
249
+ } as M
250
+ })
251
+ }
252
+ return (rows as any[]).map((r) => parseRow<Encoded>(r, idKey, defaultValues) as any as M)
253
+ })
254
+ )
255
+ ),
256
+ Effect.withSpan("PgSQL.filter [effect-app/infra/Store]", {
257
+ attributes: { "repository.table_name": tableName, "repository.model_name": name }
258
+ }, { captureStackTrace: false })
259
+ )
260
+ ))
261
+ },
262
+
263
+ set: (e) =>
264
+ resolveAndSeed.pipe(Effect.flatMap((ns) =>
265
+ setInternal(e, ns).pipe(
266
+ Effect.withSpan("PgSQL.set [effect-app/infra/Store]", {
267
+ attributes: { "repository.table_name": tableName, "repository.model_name": name, id: e[idKey] }
268
+ }, { captureStackTrace: false })
269
+ )
270
+ )),
271
+
272
+ batchSet: (items) =>
273
+ resolveAndSeed.pipe(Effect.flatMap((ns) =>
274
+ bulkSetInternal(items, ns).pipe(
275
+ Effect.withSpan("PgSQL.batchSet [effect-app/infra/Store]", {
276
+ attributes: { "repository.table_name": tableName, "repository.model_name": name }
277
+ }, { captureStackTrace: false })
278
+ )
279
+ )),
280
+
281
+ bulkSet: (items) =>
282
+ resolveAndSeed.pipe(Effect.flatMap((ns) =>
283
+ bulkSetInternal(items, ns).pipe(
284
+ Effect.withSpan("PgSQL.bulkSet [effect-app/infra/Store]", {
285
+ attributes: { "repository.table_name": tableName, "repository.model_name": name }
286
+ }, { captureStackTrace: false })
287
+ )
288
+ )),
289
+
290
+ batchRemove: (ids) => {
291
+ const placeholders = ids.map((_, i) => `$${i + 1}`).join(", ")
292
+ const nsPlaceholder = `$${ids.length + 1}`
293
+ return resolveAndSeed.pipe(Effect.flatMap((ns) =>
294
+ exec(
295
+ `DELETE FROM "${tableName}" WHERE id IN (${placeholders}) AND _namespace = ${nsPlaceholder}`,
296
+ [...ids, ns]
297
+ )
298
+ .pipe(
299
+ Effect.asVoid,
300
+ Effect.withSpan("PgSQL.batchRemove [effect-app/infra/Store]", {
301
+ attributes: { "repository.table_name": tableName, "repository.model_name": name }
302
+ }, { captureStackTrace: false })
303
+ )
304
+ ))
305
+ },
306
+
307
+ queryRaw: (query) =>
308
+ s.all.pipe(
309
+ Effect.map(query.memory),
310
+ Effect.withSpan("PgSQL.queryRaw [effect-app/infra/Store]", {
311
+ attributes: { "repository.table_name": tableName, "repository.model_name": name }
312
+ }, { captureStackTrace: false })
313
+ )
314
+ }
315
+
316
+ // Eagerly seed primary namespace on initialization
317
+ yield* seedNamespace("primary")
318
+
319
+ return s
320
+ })
321
+ }
322
+ })
323
+ }
324
+
325
+ export function PgStoreLayer(cfg: StorageConfig) {
326
+ return StoreMaker
327
+ .toLayer(makePgStore(cfg))
328
+ }