@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.
- package/CHANGELOG.md +589 -0
- package/dist/CUPS.d.ts +3 -3
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +3 -3
- package/dist/Emailer/Sendgrid.js +1 -1
- package/dist/Emailer/service.d.ts +3 -3
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/MainFiberSet.d.ts +2 -2
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +3 -3
- package/dist/Model/Repository/internal/internal.d.ts +3 -3
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +11 -7
- package/dist/Model/Repository/makeRepo.d.ts +2 -2
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +1 -1
- package/dist/Model/Repository/validation.d.ts +5 -4
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/query/dsl.d.ts +9 -9
- package/dist/Operations.d.ts +2 -2
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +3 -3
- package/dist/OperationsRepo.d.ts +2 -2
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +3 -3
- package/dist/QueueMaker/SQLQueue.d.ts +3 -5
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +9 -7
- package/dist/QueueMaker/errors.d.ts +1 -1
- package/dist/QueueMaker/errors.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +10 -9
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +11 -9
- package/dist/RequestContext.d.ts +19 -14
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +5 -5
- package/dist/RequestFiberSet.d.ts +2 -2
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +5 -5
- package/dist/Store/ContextMapContainer.d.ts +14 -3
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +64 -3
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +91 -56
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +3 -4
- package/dist/Store/Memory.d.ts +2 -2
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +4 -4
- package/dist/Store/SQL/Pg.d.ts +4 -0
- package/dist/Store/SQL/Pg.d.ts.map +1 -0
- package/dist/Store/SQL/Pg.js +186 -0
- package/dist/Store/SQL/query.d.ts +36 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +385 -0
- package/dist/Store/SQL.d.ts +11 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +212 -0
- package/dist/Store/index.d.ts +1 -1
- package/dist/Store/index.d.ts.map +1 -1
- package/dist/Store/index.js +11 -1
- package/dist/Store/service.d.ts +8 -5
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +14 -6
- package/dist/adapters/SQL/Model.d.ts +2 -5
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +21 -13
- package/dist/adapters/ServiceBus.d.ts +6 -6
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +9 -9
- package/dist/adapters/cosmos-client.d.ts +2 -2
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +3 -3
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +2 -2
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +3 -3
- package/dist/adapters/mongo-client.d.ts +2 -2
- package/dist/adapters/mongo-client.d.ts.map +1 -1
- package/dist/adapters/mongo-client.js +3 -3
- package/dist/adapters/redis-client.d.ts +3 -3
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +3 -3
- package/dist/api/ContextProvider.d.ts +6 -6
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +6 -6
- package/dist/api/internal/RequestContextMiddleware.d.ts +1 -1
- package/dist/api/internal/auth.d.ts +1 -1
- package/dist/api/internal/events.d.ts +2 -2
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +7 -5
- package/dist/api/layerUtils.d.ts +5 -5
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +3 -3
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +35 -1
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +39 -1
- package/dist/api/routing/schema/jwt.d.ts +1 -1
- package/dist/api/routing/schema/jwt.d.ts.map +1 -1
- package/dist/api/routing/schema/jwt.js +1 -1
- package/dist/api/routing.d.ts +1 -5
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +3 -2
- package/dist/api/setupRequest.d.ts +6 -3
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +11 -6
- package/dist/errorReporter.d.ts +1 -1
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +1 -1
- package/dist/fileUtil.js +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/rateLimit.js +1 -1
- package/examples/query.ts +29 -25
- package/package.json +32 -18
- package/src/CUPS.ts +2 -2
- package/src/Emailer/Sendgrid.ts +1 -1
- package/src/Emailer/service.ts +2 -2
- package/src/MainFiberSet.ts +2 -2
- package/src/Model/Repository/internal/internal.ts +11 -8
- package/src/Model/Repository/makeRepo.ts +2 -2
- package/src/Operations.ts +2 -2
- package/src/OperationsRepo.ts +2 -2
- package/src/QueueMaker/SQLQueue.ts +10 -10
- package/src/QueueMaker/memQueue.ts +41 -42
- package/src/QueueMaker/sbqueue.ts +65 -62
- package/src/RequestContext.ts +4 -4
- package/src/RequestFiberSet.ts +4 -4
- package/src/Store/ContextMapContainer.ts +98 -2
- package/src/Store/Cosmos.ts +273 -207
- package/src/Store/Disk.ts +2 -3
- package/src/Store/Memory.ts +4 -6
- package/src/Store/SQL/Pg.ts +328 -0
- package/src/Store/SQL/query.ts +430 -0
- package/src/Store/SQL.ts +357 -0
- package/src/Store/index.ts +10 -0
- package/src/Store/service.ts +16 -7
- package/src/adapters/SQL/Model.ts +76 -71
- package/src/adapters/ServiceBus.ts +8 -8
- package/src/adapters/cosmos-client.ts +2 -2
- package/src/adapters/memQueue.ts +2 -2
- package/src/adapters/mongo-client.ts +2 -2
- package/src/adapters/redis-client.ts +2 -2
- package/src/api/ContextProvider.ts +11 -11
- package/src/api/internal/events.ts +7 -6
- package/src/api/layerUtils.ts +8 -8
- package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
- package/src/api/routing/middleware/middleware.ts +43 -0
- package/src/api/routing/schema/jwt.ts +2 -3
- package/src/api/routing.ts +7 -6
- package/src/api/setupRequest.ts +27 -7
- package/src/errorReporter.ts +1 -1
- package/src/fileUtil.ts +1 -1
- package/src/rateLimit.ts +2 -2
- package/test/contextProvider.test.ts +5 -5
- package/test/controller.test.ts +12 -9
- package/test/dist/contextProvider.test.d.ts.map +1 -1
- package/test/dist/controller.test.d.ts.map +1 -1
- package/test/dist/fixtures.d.ts +18 -8
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +11 -9
- package/test/dist/query.test.d.ts.map +1 -1
- package/test/dist/rawQuery.test.d.ts.map +1 -1
- package/test/dist/requires.test.d.ts.map +1 -1
- package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
- package/test/dist/sql-store.test.d.ts.map +1 -0
- package/test/fixtures.ts +10 -8
- package/test/query.test.ts +160 -14
- package/test/rawQuery.test.ts +19 -17
- package/test/requires.test.ts +6 -5
- package/test/rpc-multi-middleware.test.ts +73 -4
- package/test/sql-store.test.ts +776 -0
- package/test/validateSample.test.ts +1 -1
- 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
|
+
}
|