@effect-app/infra 4.0.0-beta.8 → 4.0.0-beta.82
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 +540 -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 +55 -32
- 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 +174 -0
- package/dist/Store/SQL/query.d.ts +34 -0
- package/dist/Store/SQL/query.d.ts.map +1 -0
- package/dist/Store/SQL/query.js +326 -0
- package/dist/Store/SQL.d.ts +4 -0
- package/dist/Store/SQL.d.ts.map +1 -0
- package/dist/Store/SQL.js +203 -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 +207 -172
- package/src/Store/Disk.ts +2 -3
- package/src/Store/Memory.ts +4 -6
- package/src/Store/SQL/Pg.ts +294 -0
- package/src/Store/SQL/query.ts +372 -0
- package/src/Store/SQL.ts +327 -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 +444 -0
- package/test/validateSample.test.ts +1 -1
- package/tsconfig.json +0 -1
package/src/Store/Cosmos.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { InfraLogger } from "../logger.js"
|
|
|
9
9
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
10
10
|
import { type RawQuery } from "../Model/query.js"
|
|
11
11
|
import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.js"
|
|
12
|
+
import { storeId } from "./Memory.js"
|
|
12
13
|
import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
13
14
|
|
|
14
15
|
const makeMapId =
|
|
@@ -50,7 +51,21 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
50
51
|
}))
|
|
51
52
|
)
|
|
52
53
|
|
|
53
|
-
const
|
|
54
|
+
const basePartitionKey = config?.partitionValue() ?? "primary"
|
|
55
|
+
const nsPrefix = (ns: string) => ns === "primary" ? "" : `${ns}::`
|
|
56
|
+
const nsPartitionValue = (ns: string, e?: Encoded) => {
|
|
57
|
+
const base = config?.partitionValue(e) ?? "primary"
|
|
58
|
+
return `${nsPrefix(ns)}${base}`
|
|
59
|
+
}
|
|
60
|
+
const resolveNamespace = !config?.allowNamespace
|
|
61
|
+
? Effect.succeed("primary")
|
|
62
|
+
: storeId.asEffect().pipe(Effect.map((namespace) => {
|
|
63
|
+
if (namespace !== "primary" && !config.allowNamespace!(namespace)) {
|
|
64
|
+
throw new Error(`Namespace ${namespace} not allowed!`)
|
|
65
|
+
}
|
|
66
|
+
return namespace
|
|
67
|
+
}))
|
|
68
|
+
const resolvePartitionKey = Effect.map(resolveNamespace, (ns) => `${nsPrefix(ns)}${basePartitionKey}`)
|
|
54
69
|
|
|
55
70
|
const defaultValues = config?.defaultValues ?? {}
|
|
56
71
|
const container = db.container(containerId)
|
|
@@ -63,6 +78,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
63
78
|
const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
|
|
64
79
|
Effect
|
|
65
80
|
.gen(function*() {
|
|
81
|
+
const ns = yield* resolveNamespace
|
|
66
82
|
// TODO: disable batching if need atomicity
|
|
67
83
|
// we delay and batch to keep low amount of RUs
|
|
68
84
|
const b = [...items]
|
|
@@ -77,7 +93,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
77
93
|
resourceBody: {
|
|
78
94
|
...Struct.omit(x, ["_etag", idKey]),
|
|
79
95
|
id: x[idKey],
|
|
80
|
-
_partitionKey:
|
|
96
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
81
97
|
}
|
|
82
98
|
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
83
99
|
// partitionKey: config?.partitionValue(x)
|
|
@@ -89,7 +105,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
89
105
|
resourceBody: {
|
|
90
106
|
...Struct.omit(x, ["_etag", idKey]),
|
|
91
107
|
id: x[idKey],
|
|
92
|
-
_partitionKey:
|
|
108
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
93
109
|
},
|
|
94
110
|
ifMatch: eTag
|
|
95
111
|
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
@@ -166,65 +182,68 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
166
182
|
}, { captureStackTrace: false }))
|
|
167
183
|
|
|
168
184
|
const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
|
|
169
|
-
return
|
|
170
|
-
.
|
|
171
|
-
|
|
172
|
-
(
|
|
173
|
-
[
|
|
174
|
-
x
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
185
|
+
return resolveNamespace
|
|
186
|
+
.pipe(Effect.flatMap((ns) =>
|
|
187
|
+
Effect
|
|
188
|
+
.suspend(() => {
|
|
189
|
+
const batch = [...items].map(
|
|
190
|
+
(x) =>
|
|
191
|
+
[
|
|
192
|
+
x,
|
|
193
|
+
Option.match(Option.fromNullishOr(x._etag), {
|
|
194
|
+
onNone: () => ({
|
|
195
|
+
operationType: "Create" as const,
|
|
196
|
+
resourceBody: {
|
|
197
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
198
|
+
id: x[idKey],
|
|
199
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
200
|
+
}
|
|
201
|
+
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
202
|
+
// partitionKey: config?.partitionValue(x)
|
|
203
|
+
}),
|
|
204
|
+
onSome: (eTag) => ({
|
|
205
|
+
operationType: "Replace" as const,
|
|
206
|
+
id: x[idKey],
|
|
207
|
+
resourceBody: {
|
|
208
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
209
|
+
id: x[idKey],
|
|
210
|
+
_partitionKey: nsPartitionValue(ns, x)
|
|
211
|
+
},
|
|
212
|
+
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
213
|
+
// partitionKey: config?.partitionValue(x)
|
|
214
|
+
ifMatch: eTag
|
|
215
|
+
})
|
|
216
|
+
})
|
|
217
|
+
] as const
|
|
218
|
+
)
|
|
201
219
|
|
|
202
|
-
|
|
220
|
+
const ex = batch.map(([, c]) => c)
|
|
203
221
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
222
|
+
return Effect
|
|
223
|
+
.promise(() => execBatch(ex, ex[0]?.resourceBody._partitionKey))
|
|
224
|
+
.pipe(Effect.flatMap(Effect.fnUntraced(function*(x) {
|
|
225
|
+
const result = x.result ?? []
|
|
226
|
+
const firstFailed = result.find(
|
|
227
|
+
(x: any) => x.statusCode > 299 || x.statusCode < 200
|
|
228
|
+
)
|
|
229
|
+
if (firstFailed) {
|
|
230
|
+
const code = firstFailed.statusCode ?? 0
|
|
231
|
+
if (code === 412 || code === 404 || code === 409) {
|
|
232
|
+
return yield* new OptimisticConcurrencyException({ type: name, id: "batch", code })
|
|
233
|
+
}
|
|
216
234
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
235
|
+
return yield* Effect.die(
|
|
236
|
+
new CosmosDbOperationError("not able to update record: " + code)
|
|
237
|
+
)
|
|
238
|
+
}
|
|
221
239
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
240
|
+
return batch.map(([e], i) => ({
|
|
241
|
+
...e,
|
|
242
|
+
_etag: result[i]?.eTag
|
|
243
|
+
})) as unknown as NonEmptyReadonlyArray<Encoded>
|
|
244
|
+
})))
|
|
245
|
+
})
|
|
246
|
+
))
|
|
228
247
|
.pipe(Effect
|
|
229
248
|
.withSpan("Cosmos.batchSet [effect-app/infra/Store]", {
|
|
230
249
|
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
@@ -234,14 +253,14 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
234
253
|
const s: Store<IdKey, Encoded> = {
|
|
235
254
|
queryRaw: <Out>(query: RawQuery<Encoded, Out>) =>
|
|
236
255
|
Effect
|
|
237
|
-
.sync(() => query.cosmos({ name }))
|
|
256
|
+
.all({ q: Effect.sync(() => query.cosmos({ name })), pk: resolvePartitionKey })
|
|
238
257
|
.pipe(
|
|
239
|
-
Effect.tap((q) => logQuery(q)),
|
|
240
|
-
Effect.flatMap((q) =>
|
|
258
|
+
Effect.tap(({ q }) => logQuery(q)),
|
|
259
|
+
Effect.flatMap(({ pk, q }) =>
|
|
241
260
|
Effect.promise(() =>
|
|
242
261
|
container
|
|
243
262
|
.items
|
|
244
|
-
.query<Out>(q, { partitionKey:
|
|
263
|
+
.query<Out>(q, { partitionKey: pk })
|
|
245
264
|
.fetchAll()
|
|
246
265
|
.then(({ resources }) =>
|
|
247
266
|
resources.map(
|
|
@@ -256,31 +275,36 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
256
275
|
}, { captureStackTrace: false })
|
|
257
276
|
),
|
|
258
277
|
batchRemove: (ids, partitionKey?: string) =>
|
|
259
|
-
Effect.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
278
|
+
resolvePartitionKey.pipe(Effect.flatMap((pk) =>
|
|
279
|
+
Effect.promise(() =>
|
|
280
|
+
execBatch(
|
|
281
|
+
mutable(ids.map((id) =>
|
|
282
|
+
dropUndefinedT({
|
|
283
|
+
operationType: "Delete" as const,
|
|
284
|
+
id
|
|
285
|
+
// don't use this or we get an error that the request and some item partition key dont match - makese no sense
|
|
286
|
+
// partitionKey: config?.partitionValue({ [idKey]: id } as Encoded)
|
|
287
|
+
})
|
|
288
|
+
)),
|
|
289
|
+
partitionKey ?? pk
|
|
290
|
+
)
|
|
270
291
|
)
|
|
271
|
-
),
|
|
292
|
+
)),
|
|
272
293
|
all: Effect
|
|
273
|
-
.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
294
|
+
.all({
|
|
295
|
+
q: Effect.sync(() => ({
|
|
296
|
+
query: `SELECT * FROM ${name}`,
|
|
297
|
+
parameters: []
|
|
298
|
+
})),
|
|
299
|
+
pk: resolvePartitionKey
|
|
300
|
+
})
|
|
277
301
|
.pipe(
|
|
278
|
-
Effect.tap((q) => logQuery(q)),
|
|
279
|
-
Effect.flatMap((q) =>
|
|
302
|
+
Effect.tap(({ q }) => logQuery(q)),
|
|
303
|
+
Effect.flatMap(({ pk, q }) =>
|
|
280
304
|
Effect.promise(() =>
|
|
281
305
|
container
|
|
282
306
|
.items
|
|
283
|
-
.query<PMCosmos>(q, { partitionKey:
|
|
307
|
+
.query<PMCosmos>(q, { partitionKey: pk })
|
|
284
308
|
.fetchAll()
|
|
285
309
|
.then(({ resources }) =>
|
|
286
310
|
resources.map(
|
|
@@ -305,42 +329,45 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
305
329
|
const filter = f.filter
|
|
306
330
|
type M = U extends undefined ? Encoded : Pick<Encoded, U>
|
|
307
331
|
return Effect
|
|
308
|
-
.
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
332
|
+
.all({
|
|
333
|
+
q: Effect.sync(() =>
|
|
334
|
+
buildWhereCosmosQuery3(
|
|
335
|
+
idKey,
|
|
336
|
+
filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
|
|
337
|
+
name,
|
|
338
|
+
defaultValues,
|
|
339
|
+
f.select as
|
|
340
|
+
| NonEmptyReadonlyArray<string | { key: string; subKeys: readonly string[] }>
|
|
341
|
+
| undefined,
|
|
342
|
+
f.order as NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }> | undefined,
|
|
343
|
+
skip,
|
|
344
|
+
limit
|
|
345
|
+
)
|
|
346
|
+
),
|
|
347
|
+
pk: resolvePartitionKey
|
|
348
|
+
})
|
|
320
349
|
.pipe(
|
|
321
|
-
Effect.tap((q) => logQuery(q)),
|
|
350
|
+
Effect.tap(({ q }) => logQuery(q)),
|
|
322
351
|
Effect
|
|
323
|
-
.flatMap((q) =>
|
|
352
|
+
.flatMap(({ pk, q }) =>
|
|
324
353
|
Effect.promise(() =>
|
|
325
354
|
f.select
|
|
326
355
|
? container
|
|
327
356
|
.items
|
|
328
|
-
.query<M>(q, { partitionKey:
|
|
357
|
+
.query<M>(q, { partitionKey: pk })
|
|
329
358
|
.fetchAll()
|
|
330
359
|
.then(({ resources }) =>
|
|
331
|
-
resources.map((_) =>
|
|
332
|
-
(
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}) as any
|
|
339
|
-
)
|
|
360
|
+
resources.map((_) => ({
|
|
361
|
+
...pipe(
|
|
362
|
+
defaultValues,
|
|
363
|
+
Struct.pick(f.select!.filter((_) => typeof _ === "string") as never[])
|
|
364
|
+
),
|
|
365
|
+
...mapReverseId(_ as any)
|
|
366
|
+
}))
|
|
340
367
|
)
|
|
341
368
|
: container
|
|
342
369
|
.items
|
|
343
|
-
.query<{ f: M }>(q, { partitionKey:
|
|
370
|
+
.query<{ f: M }>(q, { partitionKey: pk })
|
|
344
371
|
.fetchAll()
|
|
345
372
|
.then(({ resources }) =>
|
|
346
373
|
resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
|
|
@@ -355,78 +382,86 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
355
382
|
)
|
|
356
383
|
},
|
|
357
384
|
find: (id) =>
|
|
358
|
-
Effect
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
.pipe(Effect
|
|
368
|
-
.withSpan("Cosmos.find [effect-app/infra/Store]", {
|
|
369
|
-
attributes: {
|
|
370
|
-
"repository.container_id": containerId,
|
|
371
|
-
"repository.model_name": name,
|
|
372
|
-
partitionValue: config?.partitionValue({ [idKey]: id } as Encoded),
|
|
373
|
-
id
|
|
374
|
-
}
|
|
375
|
-
}, { captureStackTrace: false })),
|
|
376
|
-
set: (e) =>
|
|
377
|
-
Option
|
|
378
|
-
.match(
|
|
379
|
-
Option
|
|
380
|
-
.fromNullishOr(e._etag),
|
|
381
|
-
{
|
|
382
|
-
onNone: () =>
|
|
383
|
-
Effect.promise(() =>
|
|
384
|
-
container.items.create({
|
|
385
|
-
...mapId(e),
|
|
386
|
-
_partitionKey: config?.partitionValue(e)
|
|
387
|
-
})
|
|
388
|
-
),
|
|
389
|
-
onSome: (eTag) =>
|
|
390
|
-
Effect.promise(() =>
|
|
391
|
-
container.item(e[idKey], config?.partitionValue(e)).replace(
|
|
392
|
-
{ ...mapId(e), _partitionKey: config?.partitionValue(e) },
|
|
393
|
-
{
|
|
394
|
-
accessCondition: {
|
|
395
|
-
type: "IfMatch",
|
|
396
|
-
condition: eTag
|
|
397
|
-
}
|
|
398
|
-
}
|
|
385
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
386
|
+
Effect
|
|
387
|
+
.promise(() =>
|
|
388
|
+
container
|
|
389
|
+
.item(id, nsPartitionValue(ns, { [idKey]: id } as Encoded))
|
|
390
|
+
.read<Encoded>()
|
|
391
|
+
.then(({ resource }) =>
|
|
392
|
+
Option.fromNullishOr(resource).pipe(
|
|
393
|
+
Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
|
|
399
394
|
)
|
|
400
395
|
)
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
Effect
|
|
405
|
-
.flatMap((x) => {
|
|
406
|
-
if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
|
|
407
|
-
return Effect.fail(new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode }))
|
|
408
|
-
}
|
|
409
|
-
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
410
|
-
return Effect.die(
|
|
411
|
-
new CosmosDbOperationError(
|
|
412
|
-
"not able to update record: " + x.statusCode
|
|
413
|
-
)
|
|
414
|
-
)
|
|
415
|
-
}
|
|
416
|
-
return Effect.sync(() => ({
|
|
417
|
-
...e,
|
|
418
|
-
_etag: x.etag
|
|
419
|
-
}))
|
|
420
|
-
}),
|
|
421
|
-
Effect
|
|
422
|
-
.withSpan("Cosmos.set [effect-app/infra/Store]", {
|
|
396
|
+
)
|
|
397
|
+
.pipe(Effect
|
|
398
|
+
.withSpan("Cosmos.find [effect-app/infra/Store]", {
|
|
423
399
|
attributes: {
|
|
424
400
|
"repository.container_id": containerId,
|
|
425
401
|
"repository.model_name": name,
|
|
426
|
-
|
|
402
|
+
partitionValue: nsPartitionValue(ns, { [idKey]: id } as Encoded),
|
|
403
|
+
id
|
|
427
404
|
}
|
|
428
|
-
}, { captureStackTrace: false })
|
|
429
|
-
|
|
405
|
+
}, { captureStackTrace: false }))
|
|
406
|
+
)),
|
|
407
|
+
set: (e) =>
|
|
408
|
+
resolveNamespace.pipe(Effect.flatMap((ns) =>
|
|
409
|
+
Option
|
|
410
|
+
.match(
|
|
411
|
+
Option
|
|
412
|
+
.fromNullishOr(e._etag),
|
|
413
|
+
{
|
|
414
|
+
onNone: () =>
|
|
415
|
+
Effect.promise(() =>
|
|
416
|
+
container.items.create({
|
|
417
|
+
...mapId(e),
|
|
418
|
+
_partitionKey: nsPartitionValue(ns, e)
|
|
419
|
+
})
|
|
420
|
+
),
|
|
421
|
+
onSome: (eTag) =>
|
|
422
|
+
Effect.promise(() =>
|
|
423
|
+
container.item(e[idKey], nsPartitionValue(ns, e)).replace(
|
|
424
|
+
{ ...mapId(e), _partitionKey: nsPartitionValue(ns, e) },
|
|
425
|
+
{
|
|
426
|
+
accessCondition: {
|
|
427
|
+
type: "IfMatch",
|
|
428
|
+
condition: eTag
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
)
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
)
|
|
435
|
+
.pipe(
|
|
436
|
+
Effect
|
|
437
|
+
.flatMap((x) => {
|
|
438
|
+
if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
|
|
439
|
+
return Effect.fail(
|
|
440
|
+
new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode })
|
|
441
|
+
)
|
|
442
|
+
}
|
|
443
|
+
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
444
|
+
return Effect.die(
|
|
445
|
+
new CosmosDbOperationError(
|
|
446
|
+
"not able to update record: " + x.statusCode
|
|
447
|
+
)
|
|
448
|
+
)
|
|
449
|
+
}
|
|
450
|
+
return Effect.sync(() => ({
|
|
451
|
+
...e,
|
|
452
|
+
_etag: x.etag
|
|
453
|
+
}))
|
|
454
|
+
}),
|
|
455
|
+
Effect
|
|
456
|
+
.withSpan("Cosmos.set [effect-app/infra/Store]", {
|
|
457
|
+
attributes: {
|
|
458
|
+
"repository.container_id": containerId,
|
|
459
|
+
"repository.model_name": name,
|
|
460
|
+
id: e[idKey]
|
|
461
|
+
}
|
|
462
|
+
}, { captureStackTrace: false })
|
|
463
|
+
)
|
|
464
|
+
)),
|
|
430
465
|
batchSet,
|
|
431
466
|
bulkSet
|
|
432
467
|
}
|
package/src/Store/Disk.ts
CHANGED
|
@@ -66,11 +66,10 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// lock file for cross-process coordination during initialization
|
|
69
|
-
const lockFile = file + ".lock"
|
|
70
69
|
|
|
71
70
|
// wrap initialization in file lock to prevent race conditions in multi-worker setups
|
|
72
71
|
const store = yield* fu.withFileLock(
|
|
73
|
-
|
|
72
|
+
file,
|
|
74
73
|
Effect.gen(function*() {
|
|
75
74
|
const shouldSeed = !(fs.existsSync(file))
|
|
76
75
|
|
|
@@ -143,7 +142,7 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
|
|
|
143
142
|
const storesSem = Semaphore.makeUnsafe(1)
|
|
144
143
|
const primary = yield* makeDiskStoreInt(prefix, idKey, "primary", dir, name, seed, config?.defaultValues)
|
|
145
144
|
const stores = new Map<string, Store<IdKey, Encoded>>([["primary", primary]])
|
|
146
|
-
const ctx = yield* Effect.
|
|
145
|
+
const ctx = yield* Effect.context<R>()
|
|
147
146
|
const getStore = !config?.allowNamespace
|
|
148
147
|
? Effect.succeed(primary)
|
|
149
148
|
: storeId.asEffect().pipe(Effect.flatMap((namespace) => {
|
package/src/Store/Memory.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
|
|
3
|
-
import { Array, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore,
|
|
3
|
+
import { Array, Context, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore, Struct } from "effect-app"
|
|
4
4
|
import { NonEmptyString255 } from "effect-app/Schema"
|
|
5
5
|
import { get } from "effect-app/utils"
|
|
6
6
|
import { InfraLogger } from "../logger.js"
|
|
@@ -24,7 +24,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
24
24
|
)
|
|
25
25
|
const n = Struct.pick(i, keys)
|
|
26
26
|
subKeys.forEach((subKey) => {
|
|
27
|
-
n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys))
|
|
27
|
+
n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys as never[]))
|
|
28
28
|
})
|
|
29
29
|
return n as M
|
|
30
30
|
}) as any
|
|
@@ -72,9 +72,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
const defaultNs: NonEmptyString255 = NonEmptyString255("primary")
|
|
75
|
-
export class storeId
|
|
76
|
-
extends ServiceMap.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs })
|
|
77
|
-
{}
|
|
75
|
+
export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
|
|
78
76
|
|
|
79
77
|
function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
|
|
80
78
|
return InfraLogger
|
|
@@ -267,7 +265,7 @@ export const makeMemoryStore = () => ({
|
|
|
267
265
|
seed,
|
|
268
266
|
config?.defaultValues
|
|
269
267
|
)
|
|
270
|
-
const ctx = yield* Effect.
|
|
268
|
+
const ctx = yield* Effect.context<R>()
|
|
271
269
|
const stores = new Map([["primary", primary]])
|
|
272
270
|
const getStore = !config?.allowNamespace
|
|
273
271
|
? Effect.succeed(primary)
|